Migrate certificates, icons, logs to XDG dirs
[pidgin-git.git] / libpurple / xmlnode.c
blobae69ccb983d767da32937d3c5daf6bcec417bcc5
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);
77 #if 0
78 /* This would give PurpleXmlNodes more appropriate namespacing
79 * when creating them. Otherwise, unless an explicit namespace
80 * is set, purple_xmlnode_get_namespace() will return NULL, when
81 * there may be a default namespace.
83 * I'm unconvinced that it's useful, and concerned it may break things.
85 * _insert_child would need the same thing, probably (assuming
86 * xmlns->node == NULL)
88 purple_xmlnode_set_namespace(node, purple_xmlnode_get_default_namespace(node))
89 #endif
91 return node;
94 void
95 purple_xmlnode_insert_child(PurpleXmlNode *parent, PurpleXmlNode *child)
97 g_return_if_fail(parent != NULL);
98 g_return_if_fail(child != NULL);
100 child->parent = parent;
102 if(parent->lastchild) {
103 parent->lastchild->next = child;
104 } else {
105 parent->child = child;
108 parent->lastchild = child;
111 void
112 purple_xmlnode_insert_data(PurpleXmlNode *node, const char *data, gssize size)
114 PurpleXmlNode *child;
115 gsize real_size;
117 g_return_if_fail(node != NULL);
118 g_return_if_fail(data != NULL);
119 g_return_if_fail(size != 0);
121 real_size = size == -1 ? strlen(data) : (gsize)size;
123 child = new_node(NULL, PURPLE_XMLNODE_TYPE_DATA);
125 child->data = g_memdup(data, real_size);
126 child->data_sz = real_size;
128 purple_xmlnode_insert_child(node, child);
131 void
132 purple_xmlnode_remove_attrib(PurpleXmlNode *node, const char *attr)
134 PurpleXmlNode *attr_node, *sibling = NULL;
136 g_return_if_fail(node != NULL);
137 g_return_if_fail(attr != NULL);
139 attr_node = node->child;
140 while (attr_node) {
141 if(attr_node->type == PURPLE_XMLNODE_TYPE_ATTRIB &&
142 purple_strequal(attr_node->name, attr))
144 if (node->lastchild == attr_node) {
145 node->lastchild = sibling;
147 if (sibling == NULL) {
148 node->child = attr_node->next;
149 purple_xmlnode_free(attr_node);
150 attr_node = node->child;
151 } else {
152 sibling->next = attr_node->next;
153 sibling = attr_node->next;
154 purple_xmlnode_free(attr_node);
155 attr_node = sibling;
158 else
160 attr_node = attr_node->next;
162 sibling = attr_node;
166 void
167 purple_xmlnode_remove_attrib_with_namespace(PurpleXmlNode *node, const char *attr, const char *xmlns)
169 PurpleXmlNode *attr_node, *sibling = NULL;
171 g_return_if_fail(node != NULL);
172 g_return_if_fail(attr != NULL);
174 for(attr_node = node->child; attr_node; attr_node = attr_node->next)
176 if(attr_node->type == PURPLE_XMLNODE_TYPE_ATTRIB &&
177 purple_strequal(attr, attr_node->name) &&
178 purple_strequal(xmlns, attr_node->xmlns))
180 if(sibling == NULL) {
181 node->child = attr_node->next;
182 } else {
183 sibling->next = attr_node->next;
185 if (node->lastchild == attr_node) {
186 node->lastchild = sibling;
188 purple_xmlnode_free(attr_node);
189 return;
191 sibling = attr_node;
195 void
196 purple_xmlnode_set_attrib(PurpleXmlNode *node, const char *attr, const char *value)
198 purple_xmlnode_remove_attrib(node, attr);
199 purple_xmlnode_set_attrib_full(node, attr, NULL, NULL, value);
202 void
203 purple_xmlnode_set_attrib_full(PurpleXmlNode *node, const char *attr, const char *xmlns, const char *prefix, const char *value)
205 PurpleXmlNode *attrib_node;
207 g_return_if_fail(node != NULL);
208 g_return_if_fail(attr != NULL);
209 g_return_if_fail(value != NULL);
211 purple_xmlnode_remove_attrib_with_namespace(node, attr, xmlns);
212 attrib_node = new_node(attr, PURPLE_XMLNODE_TYPE_ATTRIB);
214 attrib_node->data = g_strdup(value);
215 attrib_node->xmlns = g_strdup(xmlns);
216 attrib_node->prefix = g_strdup(prefix);
218 purple_xmlnode_insert_child(node, attrib_node);
222 const char *
223 purple_xmlnode_get_attrib(const PurpleXmlNode *node, const char *attr)
225 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 && purple_strequal(attr, x->name)) {
232 return x->data;
236 return NULL;
239 const char *
240 purple_xmlnode_get_attrib_with_namespace(const PurpleXmlNode *node, const char *attr, const char *xmlns)
242 const PurpleXmlNode *x;
244 g_return_val_if_fail(node != NULL, NULL);
245 g_return_val_if_fail(attr != NULL, NULL);
247 for(x = node->child; x; x = x->next) {
248 if(x->type == PURPLE_XMLNODE_TYPE_ATTRIB &&
249 purple_strequal(attr, x->name) &&
250 purple_strequal(xmlns, x->xmlns)) {
251 return x->data;
255 return NULL;
259 void purple_xmlnode_set_namespace(PurpleXmlNode *node, const char *xmlns)
261 char *tmp;
262 g_return_if_fail(node != NULL);
264 tmp = node->xmlns;
265 node->xmlns = g_strdup(xmlns);
267 if (node->namespace_map) {
268 g_hash_table_insert(node->namespace_map,
269 g_strdup(""), g_strdup(xmlns));
272 g_free(tmp);
275 const char *purple_xmlnode_get_namespace(const PurpleXmlNode *node)
277 g_return_val_if_fail(node != NULL, NULL);
279 return node->xmlns;
282 const char *purple_xmlnode_get_default_namespace(const PurpleXmlNode *node)
284 const PurpleXmlNode *current_node;
285 const char *ns = NULL;
287 g_return_val_if_fail(node != NULL, NULL);
289 current_node = node;
290 while (current_node) {
291 /* If this node does *not* have a prefix, node->xmlns is the default
292 * namespace. Otherwise, it's the prefix namespace.
294 if (!current_node->prefix && current_node->xmlns) {
295 return current_node->xmlns;
296 } else if (current_node->namespace_map) {
297 ns = g_hash_table_lookup(current_node->namespace_map, "");
298 if (ns && *ns)
299 return ns;
302 current_node = current_node->parent;
305 return ns;
308 void purple_xmlnode_set_prefix(PurpleXmlNode *node, const char *prefix)
310 g_return_if_fail(node != NULL);
312 g_free(node->prefix);
313 node->prefix = g_strdup(prefix);
316 const char *purple_xmlnode_get_prefix(const PurpleXmlNode *node)
318 g_return_val_if_fail(node != NULL, NULL);
319 return node->prefix;
322 const char *purple_xmlnode_get_prefix_namespace(const PurpleXmlNode *node, const char *prefix)
324 const PurpleXmlNode *current_node;
326 g_return_val_if_fail(node != NULL, NULL);
327 g_return_val_if_fail(prefix != NULL, purple_xmlnode_get_default_namespace(node));
329 current_node = node;
330 while (current_node) {
331 if (current_node->prefix && g_str_equal(prefix, current_node->prefix) &&
332 current_node->xmlns) {
333 return current_node->xmlns;
334 } else if (current_node->namespace_map) {
335 const char *ns = g_hash_table_lookup(current_node->namespace_map, prefix);
336 if (ns && *ns) {
337 return ns;
341 current_node = current_node->parent;
344 return NULL;
347 void purple_xmlnode_strip_prefixes(PurpleXmlNode *node)
349 PurpleXmlNode *child;
350 const char *prefix;
352 g_return_if_fail(node != NULL);
354 for (child = node->child; child; child = child->next) {
355 if (child->type == PURPLE_XMLNODE_TYPE_TAG)
356 purple_xmlnode_strip_prefixes(child);
359 prefix = purple_xmlnode_get_prefix(node);
360 if (prefix) {
361 const char *ns = purple_xmlnode_get_prefix_namespace(node, prefix);
362 purple_xmlnode_set_namespace(node, ns);
363 purple_xmlnode_set_prefix(node, NULL);
364 } else {
365 purple_xmlnode_set_namespace(node, purple_xmlnode_get_default_namespace(node));
369 PurpleXmlNode *purple_xmlnode_get_parent(const PurpleXmlNode *child)
371 g_return_val_if_fail(child != NULL, NULL);
372 return child->parent;
375 void
376 purple_xmlnode_free(PurpleXmlNode *node)
378 PurpleXmlNode *x, *y;
380 g_return_if_fail(node != NULL);
382 /* if we're part of a tree, remove ourselves from the tree first */
383 if(NULL != node->parent) {
384 if(node->parent->child == node) {
385 node->parent->child = node->next;
386 if (node->parent->lastchild == node)
387 node->parent->lastchild = node->next;
388 } else {
389 PurpleXmlNode *prev = node->parent->child;
390 while(prev && prev->next != node) {
391 prev = prev->next;
393 if(prev) {
394 prev->next = node->next;
395 if (node->parent->lastchild == node)
396 node->parent->lastchild = prev;
401 /* now free our children */
402 x = node->child;
403 while(x) {
404 y = x->next;
405 purple_xmlnode_free(x);
406 x = y;
409 /* now dispose of ourselves */
410 g_free(node->name);
411 g_free(node->data);
412 g_free(node->xmlns);
413 g_free(node->prefix);
415 if(node->namespace_map)
416 g_hash_table_destroy(node->namespace_map);
418 // PURPLE_DBUS_UNREGISTER_POINTER(node);
419 g_free(node);
422 PurpleXmlNode*
423 purple_xmlnode_get_child(const PurpleXmlNode *parent, const char *name)
425 return purple_xmlnode_get_child_with_namespace(parent, name, NULL);
428 PurpleXmlNode *
429 purple_xmlnode_get_child_with_namespace(const PurpleXmlNode *parent, const char *name, const char *ns)
431 PurpleXmlNode *x, *ret = NULL;
432 char **names;
433 char *parent_name, *child_name;
435 g_return_val_if_fail(parent != NULL, NULL);
436 g_return_val_if_fail(name != NULL, NULL);
438 names = g_strsplit(name, "/", 2);
439 parent_name = names[0];
440 child_name = names[1];
442 for(x = parent->child; x; x = x->next) {
443 /* XXX: Is it correct to ignore the namespace for the match if none was specified? */
444 const char *xmlns = NULL;
445 if(ns)
446 xmlns = purple_xmlnode_get_namespace(x);
448 if(x->type == PURPLE_XMLNODE_TYPE_TAG && purple_strequal(parent_name, x->name)
449 && purple_strequal(ns, xmlns)) {
450 ret = x;
451 break;
455 if(child_name && ret)
456 ret = purple_xmlnode_get_child(ret, child_name);
458 g_strfreev(names);
459 return ret;
462 char *
463 purple_xmlnode_get_data(const PurpleXmlNode *node)
465 GString *str = NULL;
466 PurpleXmlNode *c;
468 g_return_val_if_fail(node != NULL, NULL);
470 for(c = node->child; c; c = c->next) {
471 if(c->type == PURPLE_XMLNODE_TYPE_DATA) {
472 if(!str)
473 str = g_string_new_len(c->data, c->data_sz);
474 else
475 str = g_string_append_len(str, c->data, c->data_sz);
479 if (str == NULL)
480 return NULL;
482 return g_string_free(str, FALSE);
485 char *
486 purple_xmlnode_get_data_unescaped(const PurpleXmlNode *node)
488 char *escaped = purple_xmlnode_get_data(node);
490 char *unescaped = escaped ? purple_unescape_html(escaped) : NULL;
492 g_free(escaped);
494 return unescaped;
497 static void
498 purple_xmlnode_to_str_foreach_append_ns(const char *key, const char *value,
499 GString *buf)
501 if (*key) {
502 g_string_append_printf(buf, " xmlns:%s='%s'", key, value);
503 } else {
504 g_string_append_printf(buf, " xmlns='%s'", value);
508 static char *
509 purple_xmlnode_to_str_helper(const PurpleXmlNode *node, int *len, gboolean formatting, int depth)
511 GString *text = g_string_new("");
512 const char *prefix;
513 const PurpleXmlNode *c;
514 char *node_name, *esc, *esc2, *tab = NULL;
515 gboolean need_end = FALSE, pretty = formatting;
517 g_return_val_if_fail(node != NULL, NULL);
519 if(pretty && depth) {
520 tab = g_strnfill(depth, '\t');
521 text = g_string_append(text, tab);
524 node_name = g_markup_escape_text(node->name, -1);
525 prefix = purple_xmlnode_get_prefix(node);
527 if (prefix) {
528 g_string_append_printf(text, "<%s:%s", prefix, node_name);
529 } else {
530 g_string_append_printf(text, "<%s", node_name);
533 if (node->namespace_map) {
534 g_hash_table_foreach(node->namespace_map,
535 (GHFunc)purple_xmlnode_to_str_foreach_append_ns, text);
536 } else {
537 /* Figure out if this node has a different default namespace from parent */
538 const char *xmlns = NULL;
539 const char *parent_xmlns = NULL;
540 if (!prefix)
541 xmlns = node->xmlns;
543 if (!xmlns)
544 xmlns = purple_xmlnode_get_default_namespace(node);
545 if (node->parent)
546 parent_xmlns = purple_xmlnode_get_default_namespace(node->parent);
547 if (!purple_strequal(xmlns, parent_xmlns))
549 char *escaped_xmlns = g_markup_escape_text(xmlns, -1);
550 g_string_append_printf(text, " xmlns='%s'", escaped_xmlns);
551 g_free(escaped_xmlns);
554 for(c = node->child; c; c = c->next)
556 if(c->type == PURPLE_XMLNODE_TYPE_ATTRIB) {
557 const char *aprefix = purple_xmlnode_get_prefix(c);
558 esc = g_markup_escape_text(c->name, -1);
559 esc2 = g_markup_escape_text(c->data, -1);
560 if (aprefix) {
561 g_string_append_printf(text, " %s:%s='%s'", aprefix, esc, esc2);
562 } else {
563 g_string_append_printf(text, " %s='%s'", esc, esc2);
565 g_free(esc);
566 g_free(esc2);
567 } else if(c->type == PURPLE_XMLNODE_TYPE_TAG || c->type == PURPLE_XMLNODE_TYPE_DATA) {
568 if(c->type == PURPLE_XMLNODE_TYPE_DATA)
569 pretty = FALSE;
570 need_end = TRUE;
574 if(need_end) {
575 g_string_append_printf(text, ">%s", pretty ? NEWLINE_S : "");
577 for(c = node->child; c; c = c->next)
579 if(c->type == PURPLE_XMLNODE_TYPE_TAG) {
580 int esc_len;
581 esc = purple_xmlnode_to_str_helper(c, &esc_len, pretty, depth+1);
582 text = g_string_append_len(text, esc, esc_len);
583 g_free(esc);
584 } else if(c->type == PURPLE_XMLNODE_TYPE_DATA && c->data_sz > 0) {
585 esc = g_markup_escape_text(c->data, c->data_sz);
586 text = g_string_append(text, esc);
587 g_free(esc);
591 if(tab && pretty)
592 text = g_string_append(text, tab);
593 if (prefix) {
594 g_string_append_printf(text, "</%s:%s>%s", prefix, node_name, formatting ? NEWLINE_S : "");
595 } else {
596 g_string_append_printf(text, "</%s>%s", node_name, formatting ? NEWLINE_S : "");
598 } else {
599 g_string_append_printf(text, "/>%s", formatting ? NEWLINE_S : "");
602 g_free(node_name);
604 g_free(tab);
606 if(len)
607 *len = text->len;
609 return g_string_free(text, FALSE);
612 char *
613 purple_xmlnode_to_str(const PurpleXmlNode *node, int *len)
615 return purple_xmlnode_to_str_helper(node, len, FALSE, 0);
618 char *
619 purple_xmlnode_to_formatted_str(const PurpleXmlNode *node, int *len)
621 char *xml, *xml_with_declaration;
623 g_return_val_if_fail(node != NULL, NULL);
625 xml = purple_xmlnode_to_str_helper(node, len, TRUE, 0);
626 xml_with_declaration =
627 g_strdup_printf("<?xml version='1.0' encoding='UTF-8' ?>" NEWLINE_S NEWLINE_S "%s", xml);
628 g_free(xml);
630 if (len)
631 *len += sizeof("<?xml version='1.0' encoding='UTF-8' ?>" NEWLINE_S NEWLINE_S) - 1;
633 return xml_with_declaration;
636 struct _xmlnode_parser_data {
637 PurpleXmlNode *current;
638 gboolean error;
641 static void
642 purple_xmlnode_parser_element_start_libxml(void *user_data,
643 const xmlChar *element_name, const xmlChar *prefix, const xmlChar *xmlns,
644 int nb_namespaces, const xmlChar **namespaces,
645 int nb_attributes, int nb_defaulted, const xmlChar **attributes)
647 struct _xmlnode_parser_data *xpd = user_data;
648 PurpleXmlNode *node;
649 int i, j;
651 if(!element_name || xpd->error) {
652 return;
653 } else {
654 if(xpd->current)
655 node = purple_xmlnode_new_child(xpd->current, (const char*) element_name);
656 else
657 node = purple_xmlnode_new((const char *) element_name);
659 purple_xmlnode_set_namespace(node, (const char *) xmlns);
660 purple_xmlnode_set_prefix(node, (const char *)prefix);
662 if (nb_namespaces != 0) {
663 node->namespace_map = g_hash_table_new_full(
664 g_str_hash, g_str_equal, g_free, g_free);
666 for (i = 0, j = 0; i < nb_namespaces; i++, j += 2) {
667 const char *key = (const char *)namespaces[j];
668 const char *val = (const char *)namespaces[j + 1];
669 g_hash_table_insert(node->namespace_map,
670 g_strdup(key ? key : ""), g_strdup(val ? val : ""));
674 for(i=0; i < nb_attributes * 5; i+=5) {
675 const char *name = (const char *)attributes[i];
676 const char *prefix = (const char *)attributes[i+1];
677 char *txt;
678 int attrib_len = attributes[i+4] - attributes[i+3];
679 char *attrib = g_strndup((const char *)attributes[i+3], attrib_len);
680 txt = attrib;
681 attrib = purple_unescape_text(txt);
682 g_free(txt);
683 purple_xmlnode_set_attrib_full(node, name, NULL, prefix, attrib);
684 g_free(attrib);
687 xpd->current = node;
691 static void
692 purple_xmlnode_parser_element_end_libxml(void *user_data, const xmlChar *element_name,
693 const xmlChar *prefix, const xmlChar *xmlns)
695 struct _xmlnode_parser_data *xpd = user_data;
697 if(!element_name || !xpd->current || xpd->error)
698 return;
700 if(xpd->current->parent) {
701 if(!xmlStrcmp((xmlChar*) xpd->current->name, element_name))
702 xpd->current = xpd->current->parent;
706 static void
707 purple_xmlnode_parser_element_text_libxml(void *user_data, const xmlChar *text, int text_len)
709 struct _xmlnode_parser_data *xpd = user_data;
711 if(!xpd->current || xpd->error)
712 return;
714 if(!text || !text_len)
715 return;
717 purple_xmlnode_insert_data(xpd->current, (const char*) text, text_len);
720 static void
721 purple_xmlnode_parser_error_libxml(void *user_data, const char *msg, ...)
723 struct _xmlnode_parser_data *xpd = user_data;
724 char errmsg[2048];
725 va_list args;
727 xpd->error = TRUE;
729 va_start(args, msg);
730 vsnprintf(errmsg, sizeof(errmsg), msg, args);
731 va_end(args);
733 purple_debug_error("xmlnode", "Error parsing xml file: %s", errmsg);
736 static void
737 purple_xmlnode_parser_structural_error_libxml(void *user_data, xmlErrorPtr error)
739 struct _xmlnode_parser_data *xpd = user_data;
741 if (error && (error->level == XML_ERR_ERROR ||
742 error->level == XML_ERR_FATAL)) {
743 xpd->error = TRUE;
744 purple_debug_error("xmlnode", "XML parser error for PurpleXmlNode %p: "
745 "Domain %i, code %i, level %i: %s",
746 user_data, error->domain, error->code, error->level,
747 error->message ? error->message : "(null)\n");
748 } else if (error)
749 purple_debug_warning("xmlnode", "XML parser error for PurpleXmlNode %p: "
750 "Domain %i, code %i, level %i: %s",
751 user_data, error->domain, error->code, error->level,
752 error->message ? error->message : "(null)\n");
753 else
754 purple_debug_warning("xmlnode", "XML parser error for PurpleXmlNode %p\n",
755 user_data);
758 static xmlSAXHandler purple_xmlnode_parser_libxml = {
759 NULL, /* internalSubset */
760 NULL, /* isStandalone */
761 NULL, /* hasInternalSubset */
762 NULL, /* hasExternalSubset */
763 NULL, /* resolveEntity */
764 NULL, /* getEntity */
765 NULL, /* entityDecl */
766 NULL, /* notationDecl */
767 NULL, /* attributeDecl */
768 NULL, /* elementDecl */
769 NULL, /* unparsedEntityDecl */
770 NULL, /* setDocumentLocator */
771 NULL, /* startDocument */
772 NULL, /* endDocument */
773 NULL, /* startElement */
774 NULL, /* endElement */
775 NULL, /* reference */
776 purple_xmlnode_parser_element_text_libxml, /* characters */
777 NULL, /* ignorableWhitespace */
778 NULL, /* processingInstruction */
779 NULL, /* comment */
780 NULL, /* warning */
781 purple_xmlnode_parser_error_libxml, /* error */
782 NULL, /* fatalError */
783 NULL, /* getParameterEntity */
784 NULL, /* cdataBlock */
785 NULL, /* externalSubset */
786 XML_SAX2_MAGIC, /* initialized */
787 NULL, /* _private */
788 purple_xmlnode_parser_element_start_libxml, /* startElementNs */
789 purple_xmlnode_parser_element_end_libxml, /* endElementNs */
790 purple_xmlnode_parser_structural_error_libxml, /* serror */
793 PurpleXmlNode *
794 purple_xmlnode_from_str(const char *str, gssize size)
796 struct _xmlnode_parser_data *xpd;
797 PurpleXmlNode *ret;
798 gsize real_size;
800 g_return_val_if_fail(str != NULL, NULL);
802 real_size = size < 0 ? strlen(str) : (gsize)size;
803 xpd = g_new0(struct _xmlnode_parser_data, 1);
805 if (xmlSAXUserParseMemory(&purple_xmlnode_parser_libxml, xpd, str, real_size) < 0) {
806 while(xpd->current && xpd->current->parent)
807 xpd->current = xpd->current->parent;
808 if(xpd->current)
809 purple_xmlnode_free(xpd->current);
810 xpd->current = NULL;
812 ret = xpd->current;
813 if (xpd->error) {
814 ret = NULL;
815 if (xpd->current)
816 purple_xmlnode_free(xpd->current);
819 g_free(xpd);
820 return ret;
823 PurpleXmlNode *
824 purple_xmlnode_from_file(const char *dir, const char *filename, const char *description, const char *process)
826 gchar *filename_full;
827 GError *error = NULL;
828 gchar *contents = NULL;
829 gsize length;
830 PurpleXmlNode *node = NULL;
832 g_return_val_if_fail(dir != NULL, NULL);
834 purple_debug_misc(process, "Reading file %s from directory %s\n",
835 filename, dir);
837 filename_full = g_build_filename(dir, filename, NULL);
839 if (!g_file_test(filename_full, G_FILE_TEST_EXISTS))
841 purple_debug_info(process, "File %s does not exist (this is not "
842 "necessarily an error)\n", filename_full);
843 g_free(filename_full);
844 return NULL;
847 if (!g_file_get_contents(filename_full, &contents, &length, &error))
849 purple_debug_error(process, "Error reading file %s: %s\n",
850 filename_full, error->message);
851 g_error_free(error);
854 if ((contents != NULL) && (length > 0))
856 node = purple_xmlnode_from_str(contents, length);
858 /* If we were unable to parse the file then save its contents to a backup file */
859 if (node == NULL)
861 gchar *filename_temp, *filename_temp_full;
863 filename_temp = g_strdup_printf("%s~", filename);
864 filename_temp_full = g_build_filename(dir, filename_temp, NULL);
866 purple_debug_error("util", "Error parsing file %s. Renaming old "
867 "file to %s\n", filename_full, filename_temp);
868 purple_util_write_data_to_file_absolute(filename_temp_full, contents, length);
870 g_free(filename_temp_full);
871 g_free(filename_temp);
874 g_free(contents);
877 /* If we could not parse the file then show the user an error message */
878 if (node == NULL)
880 gchar *title, *msg;
881 title = g_strdup_printf(_("Error Reading %s"), filename);
882 msg = g_strdup_printf(_("An error was encountered reading your "
883 "%s. The file has not been loaded, and the old file "
884 "has been renamed to %s~."), description, filename_full);
885 purple_notify_error(NULL, NULL, title, msg, NULL);
886 g_free(title);
887 g_free(msg);
890 g_free(filename_full);
892 return node;
895 static void
896 purple_xmlnode_copy_foreach_ns(gpointer key, gpointer value, gpointer user_data)
898 GHashTable *ret = (GHashTable *)user_data;
899 g_hash_table_insert(ret, g_strdup(key), g_strdup(value));
902 PurpleXmlNode *
903 purple_xmlnode_copy(const PurpleXmlNode *src)
905 PurpleXmlNode *ret;
906 PurpleXmlNode *child;
907 PurpleXmlNode *sibling = NULL;
909 g_return_val_if_fail(src != NULL, NULL);
911 ret = new_node(src->name, src->type);
912 ret->xmlns = g_strdup(src->xmlns);
913 if (src->data) {
914 if (src->data_sz) {
915 ret->data = g_memdup(src->data, src->data_sz);
916 ret->data_sz = src->data_sz;
917 } else {
918 ret->data = g_strdup(src->data);
921 ret->prefix = g_strdup(src->prefix);
922 if (src->namespace_map) {
923 ret->namespace_map = g_hash_table_new_full(g_str_hash, g_str_equal,
924 g_free, g_free);
925 g_hash_table_foreach(src->namespace_map, purple_xmlnode_copy_foreach_ns, ret->namespace_map);
928 for (child = src->child; child; child = child->next) {
929 if (sibling) {
930 sibling->next = purple_xmlnode_copy(child);
931 sibling = sibling->next;
932 } else {
933 ret->child = sibling = purple_xmlnode_copy(child);
935 sibling->parent = ret;
938 ret->lastchild = sibling;
940 return ret;
943 PurpleXmlNode *
944 purple_xmlnode_get_next_twin(PurpleXmlNode *node)
946 PurpleXmlNode *sibling;
947 const char *ns = purple_xmlnode_get_namespace(node);
949 g_return_val_if_fail(node != NULL, NULL);
950 g_return_val_if_fail(node->type == PURPLE_XMLNODE_TYPE_TAG, NULL);
952 for(sibling = node->next; sibling; sibling = sibling->next) {
953 /* XXX: Is it correct to ignore the namespace for the match if none was specified? */
954 const char *xmlns = NULL;
955 if(ns)
956 xmlns = purple_xmlnode_get_namespace(sibling);
958 if(sibling->type == PURPLE_XMLNODE_TYPE_TAG && purple_strequal(node->name, sibling->name) &&
959 purple_strequal(ns, xmlns))
960 return sibling;
963 return NULL;
966 GType
967 purple_xmlnode_get_type(void)
969 static GType type = 0;
971 if (type == 0) {
972 type = g_boxed_type_register_static("PurpleXmlNode",
973 (GBoxedCopyFunc)purple_xmlnode_copy,
974 (GBoxedFreeFunc)purple_xmlnode_free);
977 return type;