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
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_
31 #include <libxml/parser.h>
35 #include "dbus-maybe.h"
40 # define NEWLINE_S "\r\n"
42 # define NEWLINE_S "\n"
46 new_node(const char *name
, PurpleXmlNodeType type
)
48 PurpleXmlNode
*node
= g_new0(PurpleXmlNode
, 1);
50 node
->name
= g_strdup(name
);
53 // PURPLE_DBUS_REGISTER_POINTER(node, 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
);
67 purple_xmlnode_new_child(PurpleXmlNode
*parent
, const char *name
)
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 /* 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
))
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
;
105 parent
->child
= child
;
108 parent
->lastchild
= child
;
112 purple_xmlnode_insert_data(PurpleXmlNode
*node
, const char *data
, gssize size
)
114 PurpleXmlNode
*child
;
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
);
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
;
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
;
152 sibling
->next
= attr_node
->next
;
153 sibling
= attr_node
->next
;
154 purple_xmlnode_free(attr_node
);
160 attr_node
= attr_node
->next
;
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
;
183 sibling
->next
= attr_node
->next
;
185 if (node
->lastchild
== attr_node
) {
186 node
->lastchild
= sibling
;
188 purple_xmlnode_free(attr_node
);
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
);
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
);
223 purple_xmlnode_get_attrib(const PurpleXmlNode
*node
, const char *attr
)
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
)) {
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
)) {
259 void purple_xmlnode_set_namespace(PurpleXmlNode
*node
, const char *xmlns
)
262 g_return_if_fail(node
!= NULL
);
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
));
275 const char *purple_xmlnode_get_namespace(const PurpleXmlNode
*node
)
277 g_return_val_if_fail(node
!= NULL
, NULL
);
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
);
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
, "");
302 current_node
= current_node
->parent
;
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
);
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
));
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
);
341 current_node
= current_node
->parent
;
347 void purple_xmlnode_strip_prefixes(PurpleXmlNode
*node
)
349 PurpleXmlNode
*child
;
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
);
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
);
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
;
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
;
389 PurpleXmlNode
*prev
= node
->parent
->child
;
390 while(prev
&& prev
->next
!= node
) {
394 prev
->next
= node
->next
;
395 if (node
->parent
->lastchild
== node
)
396 node
->parent
->lastchild
= prev
;
401 /* now free our children */
405 purple_xmlnode_free(x
);
409 /* now dispose of ourselves */
413 g_free(node
->prefix
);
415 if(node
->namespace_map
)
416 g_hash_table_destroy(node
->namespace_map
);
418 // PURPLE_DBUS_UNREGISTER_POINTER(node);
423 purple_xmlnode_get_child(const PurpleXmlNode
*parent
, const char *name
)
425 return purple_xmlnode_get_child_with_namespace(parent
, name
, NULL
);
429 purple_xmlnode_get_child_with_namespace(const PurpleXmlNode
*parent
, const char *name
, const char *ns
)
431 PurpleXmlNode
*x
, *ret
= NULL
;
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
;
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
)) {
455 if(child_name
&& ret
)
456 ret
= purple_xmlnode_get_child(ret
, child_name
);
463 purple_xmlnode_get_data(const PurpleXmlNode
*node
)
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
) {
473 str
= g_string_new_len(c
->data
, c
->data_sz
);
475 str
= g_string_append_len(str
, c
->data
, c
->data_sz
);
482 return g_string_free(str
, FALSE
);
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
;
498 purple_xmlnode_to_str_foreach_append_ns(const char *key
, const char *value
,
502 g_string_append_printf(buf
, " xmlns:%s='%s'", key
, value
);
504 g_string_append_printf(buf
, " xmlns='%s'", value
);
509 purple_xmlnode_to_str_helper(const PurpleXmlNode
*node
, int *len
, gboolean formatting
, int depth
)
511 GString
*text
= g_string_new("");
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
);
528 g_string_append_printf(text
, "<%s:%s", prefix
, node_name
);
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
);
537 /* Figure out if this node has a different default namespace from parent */
538 const char *xmlns
= NULL
;
539 const char *parent_xmlns
= NULL
;
544 xmlns
= purple_xmlnode_get_default_namespace(node
);
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);
561 g_string_append_printf(text
, " %s:%s='%s'", aprefix
, esc
, esc2
);
563 g_string_append_printf(text
, " %s='%s'", esc
, esc2
);
567 } else if(c
->type
== PURPLE_XMLNODE_TYPE_TAG
|| c
->type
== PURPLE_XMLNODE_TYPE_DATA
) {
568 if(c
->type
== PURPLE_XMLNODE_TYPE_DATA
)
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
) {
581 esc
= purple_xmlnode_to_str_helper(c
, &esc_len
, pretty
, depth
+1);
582 text
= g_string_append_len(text
, esc
, esc_len
);
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
);
592 text
= g_string_append(text
, tab
);
594 g_string_append_printf(text
, "</%s:%s>%s", prefix
, node_name
, formatting
? NEWLINE_S
: "");
596 g_string_append_printf(text
, "</%s>%s", node_name
, formatting
? NEWLINE_S
: "");
599 g_string_append_printf(text
, "/>%s", formatting
? NEWLINE_S
: "");
609 return g_string_free(text
, FALSE
);
613 purple_xmlnode_to_str(const PurpleXmlNode
*node
, int *len
)
615 return purple_xmlnode_to_str_helper(node
, len
, FALSE
, 0);
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
);
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
;
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
;
651 if(!element_name
|| xpd
->error
) {
655 node
= purple_xmlnode_new_child(xpd
->current
, (const char*) element_name
);
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];
678 int attrib_len
= attributes
[i
+4] - attributes
[i
+3];
679 char *attrib
= g_strndup((const char *)attributes
[i
+3], attrib_len
);
681 attrib
= purple_unescape_text(txt
);
683 purple_xmlnode_set_attrib_full(node
, name
, NULL
, prefix
, attrib
);
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
)
700 if(xpd
->current
->parent
) {
701 if(!xmlStrcmp((xmlChar
*) xpd
->current
->name
, element_name
))
702 xpd
->current
= xpd
->current
->parent
;
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
)
714 if(!text
|| !text_len
)
717 purple_xmlnode_insert_data(xpd
->current
, (const char*) text
, text_len
);
721 purple_xmlnode_parser_error_libxml(void *user_data
, const char *msg
, ...)
723 struct _xmlnode_parser_data
*xpd
= user_data
;
730 vsnprintf(errmsg
, sizeof(errmsg
), msg
, args
);
733 purple_debug_error("xmlnode", "Error parsing xml file: %s", errmsg
);
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
)) {
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");
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");
754 purple_debug_warning("xmlnode", "XML parser error for PurpleXmlNode %p\n",
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 */
781 purple_xmlnode_parser_error_libxml
, /* error */
782 NULL
, /* fatalError */
783 NULL
, /* getParameterEntity */
784 NULL
, /* cdataBlock */
785 NULL
, /* externalSubset */
786 XML_SAX2_MAGIC
, /* initialized */
788 purple_xmlnode_parser_element_start_libxml
, /* startElementNs */
789 purple_xmlnode_parser_element_end_libxml
, /* endElementNs */
790 purple_xmlnode_parser_structural_error_libxml
, /* serror */
794 purple_xmlnode_from_str(const char *str
, gssize size
)
796 struct _xmlnode_parser_data
*xpd
;
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
;
809 purple_xmlnode_free(xpd
->current
);
816 purple_xmlnode_free(xpd
->current
);
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
;
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",
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
);
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
);
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 */
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
);
877 /* If we could not parse the file then show the user an error message */
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
);
890 g_free(filename_full
);
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
));
903 purple_xmlnode_copy(const PurpleXmlNode
*src
)
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
);
915 ret
->data
= g_memdup(src
->data
, src
->data_sz
);
916 ret
->data_sz
= src
->data_sz
;
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
,
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
) {
930 sibling
->next
= purple_xmlnode_copy(child
);
931 sibling
= sibling
->next
;
933 ret
->child
= sibling
= purple_xmlnode_copy(child
);
935 sibling
->parent
= ret
;
938 ret
->lastchild
= sibling
;
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
;
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
))
967 purple_xmlnode_get_type(void)
969 static GType type
= 0;
972 type
= g_boxed_type_register_static("PurpleXmlNode",
973 (GBoxedCopyFunc
)purple_xmlnode_copy
,
974 (GBoxedFreeFunc
)purple_xmlnode_free
);