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 */
34 #include <libxml/parser.h>
38 #include "dbus-maybe.h"
43 # define NEWLINE_S "\r\n"
45 # define NEWLINE_S "\n"
49 new_node(const char *name
, XMLNodeType type
)
51 xmlnode
*node
= g_new0(xmlnode
, 1);
53 node
->name
= g_strdup(name
);
56 PURPLE_DBUS_REGISTER_POINTER(node
, xmlnode
);
62 xmlnode_new(const char *name
)
64 g_return_val_if_fail(name
!= NULL
, NULL
);
66 return new_node(name
, XMLNODE_TYPE_TAG
);
70 xmlnode_new_child(xmlnode
*parent
, const char *name
)
74 g_return_val_if_fail(parent
!= NULL
, NULL
);
75 g_return_val_if_fail(name
!= NULL
, NULL
);
77 node
= new_node(name
, XMLNODE_TYPE_TAG
);
79 xmlnode_insert_child(parent
, node
);
85 xmlnode_insert_child(xmlnode
*parent
, xmlnode
*child
)
87 g_return_if_fail(parent
!= NULL
);
88 g_return_if_fail(child
!= NULL
);
90 child
->parent
= parent
;
92 if(parent
->lastchild
) {
93 parent
->lastchild
->next
= child
;
95 parent
->child
= child
;
98 parent
->lastchild
= child
;
102 xmlnode_insert_data(xmlnode
*node
, const char *data
, gssize size
)
107 g_return_if_fail(node
!= NULL
);
108 g_return_if_fail(data
!= NULL
);
109 g_return_if_fail(size
!= 0);
111 real_size
= size
== -1 ? strlen(data
) : size
;
113 child
= new_node(NULL
, XMLNODE_TYPE_DATA
);
115 child
->data
= g_memdup(data
, real_size
);
116 child
->data_sz
= real_size
;
118 xmlnode_insert_child(node
, child
);
122 xmlnode_remove_attrib(xmlnode
*node
, const char *attr
)
124 xmlnode
*attr_node
, *sibling
= NULL
;
126 g_return_if_fail(node
!= NULL
);
127 g_return_if_fail(attr
!= NULL
);
129 for(attr_node
= node
->child
; attr_node
; attr_node
= attr_node
->next
)
131 if(attr_node
->type
== XMLNODE_TYPE_ATTRIB
&&
132 !strcmp(attr_node
->name
, attr
))
134 if(sibling
== NULL
) {
135 node
->child
= attr_node
->next
;
137 sibling
->next
= attr_node
->next
;
139 if (node
->lastchild
== attr_node
) {
140 node
->lastchild
= sibling
;
142 xmlnode_free(attr_node
);
149 /* Compare two nullable xmlns strings.
150 * They are considered equal if they're both NULL or the strings are equal
152 static gboolean
_xmlnode_compare_xmlns(const char *xmlns1
, const char *xmlns2
) {
153 gboolean equal
= FALSE
;
155 if (xmlns1
== NULL
&& xmlns2
== NULL
)
157 else if (xmlns1
!= NULL
&& xmlns2
!= NULL
&& !strcmp(xmlns1
, xmlns2
))
164 xmlnode_remove_attrib_with_namespace(xmlnode
*node
, const char *attr
, const char *xmlns
)
166 xmlnode
*attr_node
, *sibling
= NULL
;
168 g_return_if_fail(node
!= NULL
);
169 g_return_if_fail(attr
!= NULL
);
171 for(attr_node
= node
->child
; attr_node
; attr_node
= attr_node
->next
)
173 if(attr_node
->type
== XMLNODE_TYPE_ATTRIB
&&
174 !strcmp(attr_node
->name
, attr
) &&
175 _xmlnode_compare_xmlns(xmlns
, attr_node
->xmlns
))
177 if(sibling
== NULL
) {
178 node
->child
= attr_node
->next
;
180 sibling
->next
= attr_node
->next
;
182 if (node
->lastchild
== attr_node
) {
183 node
->lastchild
= sibling
;
185 xmlnode_free(attr_node
);
193 xmlnode_set_attrib(xmlnode
*node
, const char *attr
, const char *value
)
195 xmlnode
*attrib_node
;
197 g_return_if_fail(node
!= NULL
);
198 g_return_if_fail(attr
!= NULL
);
199 g_return_if_fail(value
!= NULL
);
201 xmlnode_remove_attrib(node
, attr
);
203 attrib_node
= new_node(attr
, XMLNODE_TYPE_ATTRIB
);
205 attrib_node
->data
= g_strdup(value
);
207 xmlnode_insert_child(node
, attrib_node
);
211 xmlnode_set_attrib_with_namespace(xmlnode
*node
, const char *attr
, const char *xmlns
, const char *value
)
213 xmlnode
*attrib_node
;
215 g_return_if_fail(node
!= NULL
);
216 g_return_if_fail(attr
!= NULL
);
217 g_return_if_fail(value
!= NULL
);
219 xmlnode_remove_attrib_with_namespace(node
, attr
, xmlns
);
220 attrib_node
= new_node(attr
, XMLNODE_TYPE_ATTRIB
);
222 attrib_node
->data
= g_strdup(value
);
223 attrib_node
->xmlns
= g_strdup(xmlns
);
225 xmlnode_insert_child(node
, attrib_node
);
229 xmlnode_set_attrib_with_prefix(xmlnode
*node
, const char *attr
, const char *prefix
, const char *value
)
231 xmlnode
*attrib_node
;
233 g_return_if_fail(node
!= NULL
);
234 g_return_if_fail(attr
!= NULL
);
235 g_return_if_fail(value
!= NULL
);
237 attrib_node
= new_node(attr
, XMLNODE_TYPE_ATTRIB
);
239 attrib_node
->data
= g_strdup(value
);
240 attrib_node
->prefix
= g_strdup(prefix
);
242 xmlnode_insert_child(node
, attrib_node
);
247 xmlnode_get_attrib(xmlnode
*node
, const char *attr
)
251 g_return_val_if_fail(node
!= NULL
, NULL
);
252 g_return_val_if_fail(attr
!= NULL
, NULL
);
254 for(x
= node
->child
; x
; x
= x
->next
) {
255 if(x
->type
== XMLNODE_TYPE_ATTRIB
&& !strcmp(attr
, x
->name
)) {
264 xmlnode_get_attrib_with_namespace(xmlnode
*node
, const char *attr
, const char *xmlns
)
268 g_return_val_if_fail(node
!= NULL
, NULL
);
269 g_return_val_if_fail(attr
!= NULL
, NULL
);
271 for(x
= node
->child
; x
; x
= x
->next
) {
272 if(x
->type
== XMLNODE_TYPE_ATTRIB
&&
273 !strcmp(attr
, x
->name
) &&
274 _xmlnode_compare_xmlns(xmlns
, x
->xmlns
)) {
283 void xmlnode_set_namespace(xmlnode
*node
, const char *xmlns
)
285 g_return_if_fail(node
!= NULL
);
288 node
->xmlns
= g_strdup(xmlns
);
291 const char *xmlnode_get_namespace(xmlnode
*node
)
293 g_return_val_if_fail(node
!= NULL
, NULL
);
298 void xmlnode_set_prefix(xmlnode
*node
, const char *prefix
)
300 g_return_if_fail(node
!= NULL
);
302 g_free(node
->prefix
);
303 node
->prefix
= g_strdup(prefix
);
306 const char *xmlnode_get_prefix(const xmlnode
*node
)
308 g_return_val_if_fail(node
!= NULL
, NULL
);
313 xmlnode_free(xmlnode
*node
)
317 g_return_if_fail(node
!= NULL
);
319 /* if we're part of a tree, remove ourselves from the tree first */
320 if(NULL
!= node
->parent
) {
321 if(node
->parent
->child
== node
) {
322 node
->parent
->child
= node
->next
;
323 if (node
->parent
->lastchild
== node
)
324 node
->parent
->lastchild
= node
->next
;
326 xmlnode
*prev
= node
->parent
->child
;
327 while(prev
&& prev
->next
!= node
) {
331 prev
->next
= node
->next
;
332 if (node
->parent
->lastchild
== node
)
333 node
->parent
->lastchild
= prev
;
338 /* now free our children */
346 /* now dispose of ourselves */
350 g_free(node
->prefix
);
352 if(node
->namespace_map
)
353 g_hash_table_destroy(node
->namespace_map
);
355 PURPLE_DBUS_UNREGISTER_POINTER(node
);
360 xmlnode_get_child(const xmlnode
*parent
, const char *name
)
362 return xmlnode_get_child_with_namespace(parent
, name
, NULL
);
366 xmlnode_get_child_with_namespace(const xmlnode
*parent
, const char *name
, const char *ns
)
368 xmlnode
*x
, *ret
= NULL
;
370 char *parent_name
, *child_name
;
372 g_return_val_if_fail(parent
!= NULL
, NULL
);
373 g_return_val_if_fail(name
!= NULL
, NULL
);
375 names
= g_strsplit(name
, "/", 2);
376 parent_name
= names
[0];
377 child_name
= names
[1];
379 for(x
= parent
->child
; x
; x
= x
->next
) {
380 /* XXX: Is it correct to ignore the namespace for the match if none was specified? */
381 const char *xmlns
= NULL
;
383 xmlns
= xmlnode_get_namespace(x
);
385 if(x
->type
== XMLNODE_TYPE_TAG
&& name
&& !strcmp(parent_name
, x
->name
)
386 && (!ns
|| (xmlns
&& !strcmp(ns
, xmlns
)))) {
392 if(child_name
&& ret
)
393 ret
= xmlnode_get_child(ret
, child_name
);
400 xmlnode_get_data(xmlnode
*node
)
405 g_return_val_if_fail(node
!= NULL
, NULL
);
407 for(c
= node
->child
; c
; c
= c
->next
) {
408 if(c
->type
== XMLNODE_TYPE_DATA
) {
410 str
= g_string_new_len(c
->data
, c
->data_sz
);
412 str
= g_string_append_len(str
, c
->data
, c
->data_sz
);
419 return g_string_free(str
, FALSE
);
423 xmlnode_get_data_unescaped(xmlnode
*node
)
425 char *escaped
= xmlnode_get_data(node
);
427 char *unescaped
= escaped
? purple_unescape_html(escaped
) : NULL
;
435 xmlnode_to_str_foreach_append_ns(const char *key
, const char *value
,
439 g_string_append_printf(buf
, " xmlns:%s='%s'", key
, value
);
441 g_string_append_printf(buf
, " xmlns='%s'", value
);
446 xmlnode_to_str_helper(const xmlnode
*node
, int *len
, gboolean formatting
, int depth
)
448 GString
*text
= g_string_new("");
451 char *node_name
, *esc
, *esc2
, *tab
= NULL
;
452 gboolean need_end
= FALSE
, pretty
= formatting
;
454 g_return_val_if_fail(node
!= NULL
, NULL
);
456 if(pretty
&& depth
) {
457 tab
= g_strnfill(depth
, '\t');
458 text
= g_string_append(text
, tab
);
461 node_name
= g_markup_escape_text(node
->name
, -1);
462 prefix
= xmlnode_get_prefix(node
);
465 g_string_append_printf(text
, "<%s:%s", prefix
, node_name
);
467 g_string_append_printf(text
, "<%s", node_name
);
470 if (node
->namespace_map
) {
471 g_hash_table_foreach(node
->namespace_map
,
472 (GHFunc
)xmlnode_to_str_foreach_append_ns
, text
);
473 } else if (node
->xmlns
) {
474 if(!node
->parent
|| !node
->parent
->xmlns
|| strcmp(node
->xmlns
, node
->parent
->xmlns
))
476 char *xmlns
= g_markup_escape_text(node
->xmlns
, -1);
477 g_string_append_printf(text
, " xmlns='%s'", xmlns
);
481 for(c
= node
->child
; c
; c
= c
->next
)
483 if(c
->type
== XMLNODE_TYPE_ATTRIB
) {
484 const char *aprefix
= xmlnode_get_prefix(c
);
485 esc
= g_markup_escape_text(c
->name
, -1);
486 esc2
= g_markup_escape_text(c
->data
, -1);
488 g_string_append_printf(text
, " %s:%s='%s'", aprefix
, esc
, esc2
);
490 g_string_append_printf(text
, " %s='%s'", esc
, esc2
);
494 } else if(c
->type
== XMLNODE_TYPE_TAG
|| c
->type
== XMLNODE_TYPE_DATA
) {
495 if(c
->type
== XMLNODE_TYPE_DATA
)
502 g_string_append_printf(text
, ">%s", pretty
? NEWLINE_S
: "");
504 for(c
= node
->child
; c
; c
= c
->next
)
506 if(c
->type
== XMLNODE_TYPE_TAG
) {
508 esc
= xmlnode_to_str_helper(c
, &esc_len
, pretty
, depth
+1);
509 text
= g_string_append_len(text
, esc
, esc_len
);
511 } else if(c
->type
== XMLNODE_TYPE_DATA
&& c
->data_sz
> 0) {
512 esc
= g_markup_escape_text(c
->data
, c
->data_sz
);
513 text
= g_string_append(text
, esc
);
519 text
= g_string_append(text
, tab
);
521 g_string_append_printf(text
, "</%s:%s>%s", prefix
, node_name
, formatting
? NEWLINE_S
: "");
523 g_string_append_printf(text
, "</%s>%s", node_name
, formatting
? NEWLINE_S
: "");
526 g_string_append_printf(text
, "/>%s", formatting
? NEWLINE_S
: "");
536 return g_string_free(text
, FALSE
);
540 xmlnode_to_str(const xmlnode
*node
, int *len
)
542 return xmlnode_to_str_helper(node
, len
, FALSE
, 0);
546 xmlnode_to_formatted_str(const xmlnode
*node
, int *len
)
548 char *xml
, *xml_with_declaration
;
550 g_return_val_if_fail(node
!= NULL
, NULL
);
552 xml
= xmlnode_to_str_helper(node
, len
, TRUE
, 0);
553 xml_with_declaration
=
554 g_strdup_printf("<?xml version='1.0' encoding='UTF-8' ?>" NEWLINE_S NEWLINE_S
"%s", xml
);
558 *len
+= sizeof("<?xml version='1.0' encoding='UTF-8' ?>" NEWLINE_S NEWLINE_S
) - 1;
560 return xml_with_declaration
;
563 struct _xmlnode_parser_data
{
569 xmlnode_parser_element_start_libxml(void *user_data
,
570 const xmlChar
*element_name
, const xmlChar
*prefix
, const xmlChar
*xmlns
,
571 int nb_namespaces
, const xmlChar
**namespaces
,
572 int nb_attributes
, int nb_defaulted
, const xmlChar
**attributes
)
574 struct _xmlnode_parser_data
*xpd
= user_data
;
578 if(!element_name
|| xpd
->error
) {
582 node
= xmlnode_new_child(xpd
->current
, (const char*) element_name
);
584 node
= xmlnode_new((const char *) element_name
);
586 xmlnode_set_namespace(node
, (const char *) xmlns
);
587 xmlnode_set_prefix(node
, (const char *)prefix
);
589 if (nb_namespaces
!= 0) {
590 node
->namespace_map
= g_hash_table_new_full(
591 g_str_hash
, g_str_equal
, g_free
, g_free
);
593 for (i
= 0, j
= 0; i
< nb_namespaces
; i
++, j
+= 2) {
594 const char *key
= (const char *)namespaces
[j
];
595 const char *val
= (const char *)namespaces
[j
+ 1];
596 g_hash_table_insert(node
->namespace_map
,
597 g_strdup(key
? key
: ""), g_strdup(val
? val
: ""));
601 for(i
=0; i
< nb_attributes
* 5; i
+=5) {
602 const char *prefix
= (const char *)attributes
[i
+ 1];
604 int attrib_len
= attributes
[i
+4] - attributes
[i
+3];
605 char *attrib
= g_malloc(attrib_len
+ 1);
606 memcpy(attrib
, attributes
[i
+3], attrib_len
);
607 attrib
[attrib_len
] = '\0';
609 attrib
= purple_unescape_html(txt
);
611 if (prefix
&& *prefix
) {
612 xmlnode_set_attrib_with_prefix(node
, (const char*) attributes
[i
], prefix
, attrib
);
614 xmlnode_set_attrib(node
, (const char*) attributes
[i
], attrib
);
624 xmlnode_parser_element_end_libxml(void *user_data
, const xmlChar
*element_name
,
625 const xmlChar
*prefix
, const xmlChar
*xmlns
)
627 struct _xmlnode_parser_data
*xpd
= user_data
;
629 if(!element_name
|| !xpd
->current
|| xpd
->error
)
632 if(xpd
->current
->parent
) {
633 if(!xmlStrcmp((xmlChar
*) xpd
->current
->name
, element_name
))
634 xpd
->current
= xpd
->current
->parent
;
639 xmlnode_parser_element_text_libxml(void *user_data
, const xmlChar
*text
, int text_len
)
641 struct _xmlnode_parser_data
*xpd
= user_data
;
643 if(!xpd
->current
|| xpd
->error
)
646 if(!text
|| !text_len
)
649 xmlnode_insert_data(xpd
->current
, (const char*) text
, text_len
);
653 xmlnode_parser_error_libxml(void *user_data
, const char *msg
, ...)
655 struct _xmlnode_parser_data
*xpd
= user_data
;
662 vsnprintf(errmsg
, sizeof(errmsg
), msg
, args
);
665 purple_debug_error("xmlnode", "Error parsing xml file: %s", errmsg
);
669 xmlnode_parser_structural_error_libxml(void *user_data
, xmlErrorPtr error
)
671 struct _xmlnode_parser_data
*xpd
= user_data
;
673 if (error
&& (error
->level
== XML_ERR_ERROR
||
674 error
->level
== XML_ERR_FATAL
)) {
676 purple_debug_error("xmlnode", "XML parser error for xmlnode %p: "
677 "Domain %i, code %i, level %i: %s",
678 user_data
, error
->domain
, error
->code
, error
->level
,
679 error
->message
? error
->message
: "(null)\n");
681 purple_debug_warning("xmlnode", "XML parser error for xmlnode %p: "
682 "Domain %i, code %i, level %i: %s",
683 user_data
, error
->domain
, error
->code
, error
->level
,
684 error
->message
? error
->message
: "(null)\n");
686 purple_debug_warning("xmlnode", "XML parser error for xmlnode %p\n",
690 static xmlSAXHandler xmlnode_parser_libxml
= {
691 NULL
, /* internalSubset */
692 NULL
, /* isStandalone */
693 NULL
, /* hasInternalSubset */
694 NULL
, /* hasExternalSubset */
695 NULL
, /* resolveEntity */
696 NULL
, /* getEntity */
697 NULL
, /* entityDecl */
698 NULL
, /* notationDecl */
699 NULL
, /* attributeDecl */
700 NULL
, /* elementDecl */
701 NULL
, /* unparsedEntityDecl */
702 NULL
, /* setDocumentLocator */
703 NULL
, /* startDocument */
704 NULL
, /* endDocument */
705 NULL
, /* startElement */
706 NULL
, /* endElement */
707 NULL
, /* reference */
708 xmlnode_parser_element_text_libxml
, /* characters */
709 NULL
, /* ignorableWhitespace */
710 NULL
, /* processingInstruction */
713 xmlnode_parser_error_libxml
, /* error */
714 NULL
, /* fatalError */
715 NULL
, /* getParameterEntity */
716 NULL
, /* cdataBlock */
717 NULL
, /* externalSubset */
718 XML_SAX2_MAGIC
, /* initialized */
720 xmlnode_parser_element_start_libxml
, /* startElementNs */
721 xmlnode_parser_element_end_libxml
, /* endElementNs */
722 xmlnode_parser_structural_error_libxml
, /* serror */
726 xmlnode_from_str(const char *str
, gssize size
)
728 struct _xmlnode_parser_data
*xpd
;
732 g_return_val_if_fail(str
!= NULL
, NULL
);
734 real_size
= size
< 0 ? strlen(str
) : size
;
735 xpd
= g_new0(struct _xmlnode_parser_data
, 1);
737 if (xmlSAXUserParseMemory(&xmlnode_parser_libxml
, xpd
, str
, real_size
) < 0) {
738 while(xpd
->current
&& xpd
->current
->parent
)
739 xpd
->current
= xpd
->current
->parent
;
741 xmlnode_free(xpd
->current
);
748 xmlnode_free(xpd
->current
);
756 xmlnode_copy_foreach_ns(gpointer key
, gpointer value
, gpointer user_data
)
758 GHashTable
*ret
= (GHashTable
*)user_data
;
759 g_hash_table_insert(ret
, g_strdup(key
), g_strdup(value
));
763 xmlnode_copy(const xmlnode
*src
)
767 xmlnode
*sibling
= NULL
;
769 g_return_val_if_fail(src
!= NULL
, NULL
);
771 ret
= new_node(src
->name
, src
->type
);
772 ret
->xmlns
= g_strdup(src
->xmlns
);
775 ret
->data
= g_memdup(src
->data
, src
->data_sz
);
776 ret
->data_sz
= src
->data_sz
;
778 ret
->data
= g_strdup(src
->data
);
781 ret
->prefix
= g_strdup(src
->prefix
);
782 if (src
->namespace_map
) {
783 ret
->namespace_map
= g_hash_table_new_full(g_str_hash
, g_str_equal
,
785 g_hash_table_foreach(src
->namespace_map
, xmlnode_copy_foreach_ns
, ret
->namespace_map
);
788 for (child
= src
->child
; child
; child
= child
->next
) {
790 sibling
->next
= xmlnode_copy(child
);
791 sibling
= sibling
->next
;
793 ret
->child
= xmlnode_copy(child
);
794 sibling
= ret
->child
;
796 sibling
->parent
= ret
;
799 ret
->lastchild
= sibling
;
805 xmlnode_get_next_twin(xmlnode
*node
)
808 const char *ns
= xmlnode_get_namespace(node
);
810 g_return_val_if_fail(node
!= NULL
, NULL
);
811 g_return_val_if_fail(node
->type
== XMLNODE_TYPE_TAG
, NULL
);
813 for(sibling
= node
->next
; sibling
; sibling
= sibling
->next
) {
814 /* XXX: Is it correct to ignore the namespace for the match if none was specified? */
815 const char *xmlns
= NULL
;
817 xmlns
= xmlnode_get_namespace(sibling
);
819 if(sibling
->type
== XMLNODE_TYPE_TAG
&& !strcmp(node
->name
, sibling
->name
) &&
820 (!ns
|| (xmlns
&& !strcmp(ns
, xmlns
))))