2 * @file xmlnode.c XML DOM functions
7 * Purple is the legal property of its developers, whose names are too numerous
8 * to list here. Please refer to the COPYRIGHT file distributed with this
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
26 /* A lot of this code at least resembles the code in libxode, but since
27 * libxode uses memory pools that we simply have no need for, I decided to
28 * write my own stuff. Also, re-writing this lets me be as lightweight
29 * as I want to be. Thank you libxode for giving me a good starting point */
30 #define _PURPLE_XMLNODE_C_
35 #include <libxml/parser.h>
39 #include "dbus-maybe.h"
44 # define NEWLINE_S "\r\n"
46 # define NEWLINE_S "\n"
50 new_node(const char *name
, XMLNodeType type
)
52 xmlnode
*node
= g_new0(xmlnode
, 1);
54 node
->name
= g_strdup(name
);
57 PURPLE_DBUS_REGISTER_POINTER(node
, xmlnode
);
63 xmlnode_new(const char *name
)
65 g_return_val_if_fail(name
!= NULL
, NULL
);
67 return new_node(name
, XMLNODE_TYPE_TAG
);
71 xmlnode_new_child(xmlnode
*parent
, const char *name
)
75 g_return_val_if_fail(parent
!= NULL
, NULL
);
76 g_return_val_if_fail(name
!= NULL
, NULL
);
78 node
= new_node(name
, XMLNODE_TYPE_TAG
);
80 xmlnode_insert_child(parent
, node
);
86 xmlnode_insert_child(xmlnode
*parent
, xmlnode
*child
)
88 g_return_if_fail(parent
!= NULL
);
89 g_return_if_fail(child
!= NULL
);
91 child
->parent
= parent
;
93 if(parent
->lastchild
) {
94 parent
->lastchild
->next
= child
;
96 parent
->child
= child
;
99 parent
->lastchild
= child
;
103 xmlnode_insert_data(xmlnode
*node
, const char *data
, gssize size
)
108 g_return_if_fail(node
!= NULL
);
109 g_return_if_fail(data
!= NULL
);
110 g_return_if_fail(size
!= 0);
112 real_size
= size
== -1 ? strlen(data
) : size
;
114 child
= new_node(NULL
, XMLNODE_TYPE_DATA
);
116 child
->data
= g_memdup(data
, real_size
);
117 child
->data_sz
= real_size
;
119 xmlnode_insert_child(node
, child
);
123 xmlnode_remove_attrib(xmlnode
*node
, const char *attr
)
125 xmlnode
*attr_node
, *sibling
= NULL
;
127 g_return_if_fail(node
!= NULL
);
128 g_return_if_fail(attr
!= NULL
);
130 attr_node
= node
->child
;
132 if(attr_node
->type
== XMLNODE_TYPE_ATTRIB
&&
133 purple_strequal(attr_node
->name
, attr
))
135 if (node
->lastchild
== attr_node
) {
136 node
->lastchild
= sibling
;
138 if (sibling
== NULL
) {
139 node
->child
= attr_node
->next
;
140 xmlnode_free(attr_node
);
141 attr_node
= node
->child
;
143 sibling
->next
= attr_node
->next
;
144 sibling
= attr_node
->next
;
145 xmlnode_free(attr_node
);
151 attr_node
= attr_node
->next
;
158 xmlnode_remove_attrib_with_namespace(xmlnode
*node
, const char *attr
, const char *xmlns
)
160 xmlnode
*attr_node
, *sibling
= NULL
;
162 g_return_if_fail(node
!= NULL
);
163 g_return_if_fail(attr
!= NULL
);
165 for(attr_node
= node
->child
; attr_node
; attr_node
= attr_node
->next
)
167 if(attr_node
->type
== XMLNODE_TYPE_ATTRIB
&&
168 purple_strequal(attr
, attr_node
->name
) &&
169 purple_strequal(xmlns
, attr_node
->xmlns
))
171 if(sibling
== NULL
) {
172 node
->child
= attr_node
->next
;
174 sibling
->next
= attr_node
->next
;
176 if (node
->lastchild
== attr_node
) {
177 node
->lastchild
= sibling
;
179 xmlnode_free(attr_node
);
187 xmlnode_set_attrib(xmlnode
*node
, const char *attr
, const char *value
)
189 xmlnode_remove_attrib(node
, attr
);
190 xmlnode_set_attrib_full(node
, attr
, NULL
, NULL
, value
);
194 xmlnode_set_attrib_with_namespace(xmlnode
*node
, const char *attr
, const char *xmlns
, const char *value
)
196 xmlnode_set_attrib_full(node
, attr
, xmlns
, NULL
, value
);
200 xmlnode_set_attrib_with_prefix(xmlnode
*node
, const char *attr
, const char *prefix
, const char *value
)
202 xmlnode_set_attrib_full(node
, attr
, NULL
, prefix
, value
);
206 xmlnode_set_attrib_full(xmlnode
*node
, const char *attr
, const char *xmlns
, const char *prefix
, const char *value
)
208 xmlnode
*attrib_node
;
210 g_return_if_fail(node
!= NULL
);
211 g_return_if_fail(attr
!= NULL
);
212 g_return_if_fail(value
!= NULL
);
214 xmlnode_remove_attrib_with_namespace(node
, attr
, xmlns
);
215 attrib_node
= new_node(attr
, XMLNODE_TYPE_ATTRIB
);
217 attrib_node
->data
= g_strdup(value
);
218 attrib_node
->xmlns
= g_strdup(xmlns
);
219 attrib_node
->prefix
= g_strdup(prefix
);
221 xmlnode_insert_child(node
, attrib_node
);
226 xmlnode_get_attrib(xmlnode
*node
, const char *attr
)
230 g_return_val_if_fail(node
!= NULL
, NULL
);
231 g_return_val_if_fail(attr
!= NULL
, NULL
);
233 for(x
= node
->child
; x
; x
= x
->next
) {
234 if(x
->type
== XMLNODE_TYPE_ATTRIB
&& purple_strequal(attr
, x
->name
)) {
243 xmlnode_get_attrib_with_namespace(xmlnode
*node
, const char *attr
, const char *xmlns
)
247 g_return_val_if_fail(node
!= NULL
, NULL
);
248 g_return_val_if_fail(attr
!= NULL
, NULL
);
250 for(x
= node
->child
; x
; x
= x
->next
) {
251 if(x
->type
== XMLNODE_TYPE_ATTRIB
&&
252 purple_strequal(attr
, x
->name
) &&
253 purple_strequal(xmlns
, x
->xmlns
)) {
262 void xmlnode_set_namespace(xmlnode
*node
, const char *xmlns
)
264 g_return_if_fail(node
!= NULL
);
267 node
->xmlns
= g_strdup(xmlns
);
270 const char *xmlnode_get_namespace(xmlnode
*node
)
272 g_return_val_if_fail(node
!= NULL
, NULL
);
277 void xmlnode_set_prefix(xmlnode
*node
, const char *prefix
)
279 g_return_if_fail(node
!= NULL
);
281 g_free(node
->prefix
);
282 node
->prefix
= g_strdup(prefix
);
285 const char *xmlnode_get_prefix(const xmlnode
*node
)
287 g_return_val_if_fail(node
!= NULL
, NULL
);
291 xmlnode
*xmlnode_get_parent(const xmlnode
*child
)
293 g_return_val_if_fail(child
!= NULL
, NULL
);
294 return child
->parent
;
298 xmlnode_free(xmlnode
*node
)
302 g_return_if_fail(node
!= NULL
);
304 /* if we're part of a tree, remove ourselves from the tree first */
305 if(NULL
!= node
->parent
) {
306 if(node
->parent
->child
== node
) {
307 node
->parent
->child
= node
->next
;
308 if (node
->parent
->lastchild
== node
)
309 node
->parent
->lastchild
= node
->next
;
311 xmlnode
*prev
= node
->parent
->child
;
312 while(prev
&& prev
->next
!= node
) {
316 prev
->next
= node
->next
;
317 if (node
->parent
->lastchild
== node
)
318 node
->parent
->lastchild
= prev
;
323 /* now free our children */
331 /* now dispose of ourselves */
335 g_free(node
->prefix
);
337 if(node
->namespace_map
)
338 g_hash_table_destroy(node
->namespace_map
);
340 PURPLE_DBUS_UNREGISTER_POINTER(node
);
345 xmlnode_get_child(const xmlnode
*parent
, const char *name
)
347 return xmlnode_get_child_with_namespace(parent
, name
, NULL
);
351 xmlnode_get_child_with_namespace(const xmlnode
*parent
, const char *name
, const char *ns
)
353 xmlnode
*x
, *ret
= NULL
;
355 char *parent_name
, *child_name
;
357 g_return_val_if_fail(parent
!= NULL
, NULL
);
358 g_return_val_if_fail(name
!= NULL
, NULL
);
360 names
= g_strsplit(name
, "/", 2);
361 parent_name
= names
[0];
362 child_name
= names
[1];
364 for(x
= parent
->child
; x
; x
= x
->next
) {
365 /* XXX: Is it correct to ignore the namespace for the match if none was specified? */
366 const char *xmlns
= NULL
;
368 xmlns
= xmlnode_get_namespace(x
);
370 if(x
->type
== XMLNODE_TYPE_TAG
&& purple_strequal(parent_name
, x
->name
)
371 && purple_strequal(ns
, xmlns
)) {
377 if(child_name
&& ret
)
378 ret
= xmlnode_get_child(ret
, child_name
);
385 xmlnode_get_data(const xmlnode
*node
)
390 g_return_val_if_fail(node
!= NULL
, NULL
);
392 for(c
= node
->child
; c
; c
= c
->next
) {
393 if(c
->type
== XMLNODE_TYPE_DATA
) {
395 str
= g_string_new_len(c
->data
, c
->data_sz
);
397 str
= g_string_append_len(str
, c
->data
, c
->data_sz
);
404 return g_string_free(str
, FALSE
);
408 xmlnode_get_data_unescaped(const xmlnode
*node
)
410 char *escaped
= xmlnode_get_data(node
);
412 char *unescaped
= escaped
? purple_unescape_html(escaped
) : NULL
;
420 xmlnode_to_str_foreach_append_ns(const char *key
, const char *value
,
424 g_string_append_printf(buf
, " xmlns:%s='%s'", key
, value
);
426 g_string_append_printf(buf
, " xmlns='%s'", value
);
431 xmlnode_to_str_helper(const xmlnode
*node
, int *len
, gboolean formatting
, int depth
)
433 GString
*text
= g_string_new("");
436 char *node_name
, *esc
, *esc2
, *tab
= NULL
;
437 gboolean need_end
= FALSE
, pretty
= formatting
;
439 g_return_val_if_fail(node
!= NULL
, NULL
);
441 if(pretty
&& depth
) {
442 tab
= g_strnfill(depth
, '\t');
443 text
= g_string_append(text
, tab
);
446 node_name
= g_markup_escape_text(node
->name
, -1);
447 prefix
= xmlnode_get_prefix(node
);
450 g_string_append_printf(text
, "<%s:%s", prefix
, node_name
);
452 g_string_append_printf(text
, "<%s", node_name
);
455 if (node
->namespace_map
) {
456 g_hash_table_foreach(node
->namespace_map
,
457 (GHFunc
)xmlnode_to_str_foreach_append_ns
, text
);
458 } else if (node
->xmlns
) {
459 if(!node
->parent
|| !purple_strequal(node
->xmlns
, node
->parent
->xmlns
))
461 char *xmlns
= g_markup_escape_text(node
->xmlns
, -1);
462 g_string_append_printf(text
, " xmlns='%s'", xmlns
);
466 for(c
= node
->child
; c
; c
= c
->next
)
468 if(c
->type
== XMLNODE_TYPE_ATTRIB
) {
469 const char *aprefix
= xmlnode_get_prefix(c
);
470 esc
= g_markup_escape_text(c
->name
, -1);
471 esc2
= g_markup_escape_text(c
->data
, -1);
473 g_string_append_printf(text
, " %s:%s='%s'", aprefix
, esc
, esc2
);
475 g_string_append_printf(text
, " %s='%s'", esc
, esc2
);
479 } else if(c
->type
== XMLNODE_TYPE_TAG
|| c
->type
== XMLNODE_TYPE_DATA
) {
480 if(c
->type
== XMLNODE_TYPE_DATA
)
487 g_string_append_printf(text
, ">%s", pretty
? NEWLINE_S
: "");
489 for(c
= node
->child
; c
; c
= c
->next
)
491 if(c
->type
== XMLNODE_TYPE_TAG
) {
493 esc
= xmlnode_to_str_helper(c
, &esc_len
, pretty
, depth
+1);
494 text
= g_string_append_len(text
, esc
, esc_len
);
496 } else if(c
->type
== XMLNODE_TYPE_DATA
&& c
->data_sz
> 0) {
497 esc
= g_markup_escape_text(c
->data
, c
->data_sz
);
498 text
= g_string_append(text
, esc
);
504 text
= g_string_append(text
, tab
);
506 g_string_append_printf(text
, "</%s:%s>%s", prefix
, node_name
, formatting
? NEWLINE_S
: "");
508 g_string_append_printf(text
, "</%s>%s", node_name
, formatting
? NEWLINE_S
: "");
511 g_string_append_printf(text
, "/>%s", formatting
? NEWLINE_S
: "");
521 return g_string_free(text
, FALSE
);
525 xmlnode_to_str(const xmlnode
*node
, int *len
)
527 return xmlnode_to_str_helper(node
, len
, FALSE
, 0);
531 xmlnode_to_formatted_str(const xmlnode
*node
, int *len
)
533 char *xml
, *xml_with_declaration
;
535 g_return_val_if_fail(node
!= NULL
, NULL
);
537 xml
= xmlnode_to_str_helper(node
, len
, TRUE
, 0);
538 xml_with_declaration
=
539 g_strdup_printf("<?xml version='1.0' encoding='UTF-8' ?>" NEWLINE_S NEWLINE_S
"%s", xml
);
543 *len
+= sizeof("<?xml version='1.0' encoding='UTF-8' ?>" NEWLINE_S NEWLINE_S
) - 1;
545 return xml_with_declaration
;
548 struct _xmlnode_parser_data
{
554 xmlnode_parser_element_start_libxml(void *user_data
,
555 const xmlChar
*element_name
, const xmlChar
*prefix
, const xmlChar
*xmlns
,
556 int nb_namespaces
, const xmlChar
**namespaces
,
557 int nb_attributes
, int nb_defaulted
, const xmlChar
**attributes
)
559 struct _xmlnode_parser_data
*xpd
= user_data
;
563 if(!element_name
|| xpd
->error
) {
567 node
= xmlnode_new_child(xpd
->current
, (const char*) element_name
);
569 node
= xmlnode_new((const char *) element_name
);
571 xmlnode_set_namespace(node
, (const char *) xmlns
);
572 xmlnode_set_prefix(node
, (const char *)prefix
);
574 if (nb_namespaces
!= 0) {
575 node
->namespace_map
= g_hash_table_new_full(
576 g_str_hash
, g_str_equal
, g_free
, g_free
);
578 for (i
= 0, j
= 0; i
< nb_namespaces
; i
++, j
+= 2) {
579 const char *key
= (const char *)namespaces
[j
];
580 const char *val
= (const char *)namespaces
[j
+ 1];
581 g_hash_table_insert(node
->namespace_map
,
582 g_strdup(key
? key
: ""), g_strdup(val
? val
: ""));
586 for(i
=0; i
< nb_attributes
* 5; i
+=5) {
587 const char *name
= (const char *)attributes
[i
];
588 const char *prefix
= (const char *)attributes
[i
+1];
590 int attrib_len
= attributes
[i
+4] - attributes
[i
+3];
591 char *attrib
= g_malloc(attrib_len
+ 1);
592 memcpy(attrib
, attributes
[i
+3], attrib_len
);
593 attrib
[attrib_len
] = '\0';
595 attrib
= purple_unescape_html(txt
);
597 xmlnode_set_attrib_full(node
, name
, NULL
, prefix
, attrib
);
606 xmlnode_parser_element_end_libxml(void *user_data
, const xmlChar
*element_name
,
607 const xmlChar
*prefix
, const xmlChar
*xmlns
)
609 struct _xmlnode_parser_data
*xpd
= user_data
;
611 if(!element_name
|| !xpd
->current
|| xpd
->error
)
614 if(xpd
->current
->parent
) {
615 if(!xmlStrcmp((xmlChar
*) xpd
->current
->name
, element_name
))
616 xpd
->current
= xpd
->current
->parent
;
621 xmlnode_parser_element_text_libxml(void *user_data
, const xmlChar
*text
, int text_len
)
623 struct _xmlnode_parser_data
*xpd
= user_data
;
625 if(!xpd
->current
|| xpd
->error
)
628 if(!text
|| !text_len
)
631 xmlnode_insert_data(xpd
->current
, (const char*) text
, text_len
);
635 xmlnode_parser_error_libxml(void *user_data
, const char *msg
, ...)
637 struct _xmlnode_parser_data
*xpd
= user_data
;
644 vsnprintf(errmsg
, sizeof(errmsg
), msg
, args
);
647 purple_debug_error("xmlnode", "Error parsing xml file: %s", errmsg
);
651 xmlnode_parser_structural_error_libxml(void *user_data
, xmlErrorPtr error
)
653 struct _xmlnode_parser_data
*xpd
= user_data
;
655 if (error
&& (error
->level
== XML_ERR_ERROR
||
656 error
->level
== XML_ERR_FATAL
)) {
658 purple_debug_error("xmlnode", "XML parser error for xmlnode %p: "
659 "Domain %i, code %i, level %i: %s",
660 user_data
, error
->domain
, error
->code
, error
->level
,
661 error
->message
? error
->message
: "(null)\n");
663 purple_debug_warning("xmlnode", "XML parser error for xmlnode %p: "
664 "Domain %i, code %i, level %i: %s",
665 user_data
, error
->domain
, error
->code
, error
->level
,
666 error
->message
? error
->message
: "(null)\n");
668 purple_debug_warning("xmlnode", "XML parser error for xmlnode %p\n",
672 static xmlSAXHandler xmlnode_parser_libxml
= {
673 NULL
, /* internalSubset */
674 NULL
, /* isStandalone */
675 NULL
, /* hasInternalSubset */
676 NULL
, /* hasExternalSubset */
677 NULL
, /* resolveEntity */
678 NULL
, /* getEntity */
679 NULL
, /* entityDecl */
680 NULL
, /* notationDecl */
681 NULL
, /* attributeDecl */
682 NULL
, /* elementDecl */
683 NULL
, /* unparsedEntityDecl */
684 NULL
, /* setDocumentLocator */
685 NULL
, /* startDocument */
686 NULL
, /* endDocument */
687 NULL
, /* startElement */
688 NULL
, /* endElement */
689 NULL
, /* reference */
690 xmlnode_parser_element_text_libxml
, /* characters */
691 NULL
, /* ignorableWhitespace */
692 NULL
, /* processingInstruction */
695 xmlnode_parser_error_libxml
, /* error */
696 NULL
, /* fatalError */
697 NULL
, /* getParameterEntity */
698 NULL
, /* cdataBlock */
699 NULL
, /* externalSubset */
700 XML_SAX2_MAGIC
, /* initialized */
702 xmlnode_parser_element_start_libxml
, /* startElementNs */
703 xmlnode_parser_element_end_libxml
, /* endElementNs */
704 xmlnode_parser_structural_error_libxml
, /* serror */
708 xmlnode_from_str(const char *str
, gssize size
)
710 struct _xmlnode_parser_data
*xpd
;
714 g_return_val_if_fail(str
!= NULL
, NULL
);
716 real_size
= size
< 0 ? strlen(str
) : size
;
717 xpd
= g_new0(struct _xmlnode_parser_data
, 1);
719 if (xmlSAXUserParseMemory(&xmlnode_parser_libxml
, xpd
, str
, real_size
) < 0) {
720 while(xpd
->current
&& xpd
->current
->parent
)
721 xpd
->current
= xpd
->current
->parent
;
723 xmlnode_free(xpd
->current
);
730 xmlnode_free(xpd
->current
);
738 xmlnode_from_file(const char *dir
,const char *filename
, const char *description
, const char *process
)
740 gchar
*filename_full
;
741 GError
*error
= NULL
;
742 gchar
*contents
= NULL
;
744 xmlnode
*node
= NULL
;
746 g_return_val_if_fail(dir
!= NULL
, NULL
);
748 purple_debug_info(process
, "Reading file %s from directory %s\n",
751 filename_full
= g_build_filename(dir
, filename
, NULL
);
753 if (!g_file_test(filename_full
, G_FILE_TEST_EXISTS
))
755 purple_debug_info(process
, "File %s does not exist (this is not "
756 "necessarily an error)\n", filename_full
);
757 g_free(filename_full
);
761 if (!g_file_get_contents(filename_full
, &contents
, &length
, &error
))
763 purple_debug_error(process
, "Error reading file %s: %s\n",
764 filename_full
, error
->message
);
768 if ((contents
!= NULL
) && (length
> 0))
770 node
= xmlnode_from_str(contents
, length
);
772 /* If we were unable to parse the file then save its contents to a backup file */
775 gchar
*filename_temp
, *filename_temp_full
;
777 filename_temp
= g_strdup_printf("%s~", filename
);
778 filename_temp_full
= g_build_filename(dir
, filename_temp
, NULL
);
780 purple_debug_error("util", "Error parsing file %s. Renaming old "
781 "file to %s\n", filename_full
, filename_temp
);
782 purple_util_write_data_to_file_absolute(filename_temp_full
, contents
, length
);
784 g_free(filename_temp_full
);
785 g_free(filename_temp
);
791 /* If we could not parse the file then show the user an error message */
795 title
= g_strdup_printf(_("Error Reading %s"), filename
);
796 msg
= g_strdup_printf(_("An error was encountered reading your "
797 "%s. The file has not been loaded, and the old file "
798 "has been renamed to %s~."), description
, filename_full
);
799 purple_notify_error(NULL
, NULL
, title
, msg
);
804 g_free(filename_full
);
810 xmlnode_copy_foreach_ns(gpointer key
, gpointer value
, gpointer user_data
)
812 GHashTable
*ret
= (GHashTable
*)user_data
;
813 g_hash_table_insert(ret
, g_strdup(key
), g_strdup(value
));
817 xmlnode_copy(const xmlnode
*src
)
821 xmlnode
*sibling
= NULL
;
823 g_return_val_if_fail(src
!= NULL
, NULL
);
825 ret
= new_node(src
->name
, src
->type
);
826 ret
->xmlns
= g_strdup(src
->xmlns
);
829 ret
->data
= g_memdup(src
->data
, src
->data_sz
);
830 ret
->data_sz
= src
->data_sz
;
832 ret
->data
= g_strdup(src
->data
);
835 ret
->prefix
= g_strdup(src
->prefix
);
836 if (src
->namespace_map
) {
837 ret
->namespace_map
= g_hash_table_new_full(g_str_hash
, g_str_equal
,
839 g_hash_table_foreach(src
->namespace_map
, xmlnode_copy_foreach_ns
, ret
->namespace_map
);
842 for (child
= src
->child
; child
; child
= child
->next
) {
844 sibling
->next
= xmlnode_copy(child
);
845 sibling
= sibling
->next
;
847 ret
->child
= xmlnode_copy(child
);
848 sibling
= ret
->child
;
850 sibling
->parent
= ret
;
853 ret
->lastchild
= sibling
;
859 xmlnode_get_next_twin(xmlnode
*node
)
862 const char *ns
= xmlnode_get_namespace(node
);
864 g_return_val_if_fail(node
!= NULL
, NULL
);
865 g_return_val_if_fail(node
->type
== XMLNODE_TYPE_TAG
, NULL
);
867 for(sibling
= node
->next
; sibling
; sibling
= sibling
->next
) {
868 /* XXX: Is it correct to ignore the namespace for the match if none was specified? */
869 const char *xmlns
= NULL
;
871 xmlns
= xmlnode_get_namespace(sibling
);
873 if(sibling
->type
== XMLNODE_TYPE_TAG
&& purple_strequal(node
->name
, sibling
->name
) &&
874 purple_strequal(ns
, xmlns
))