Convert all uses of PURPLE_UNSAFE_DEBUG to the new API I added.
[pidgin-git.git] / libpurple / xmlnode.c
blobae596873215ac8ee590d7526918d16d016eb01b7
1 /**
2 * @file xmlnode.c XML DOM functions
3 */
5 /* purple
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
9 * source distribution.
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_
32 #include "debug.h"
33 #include "internal.h"
35 #include <libxml/parser.h>
36 #include <string.h>
37 #include <glib.h>
39 #include "dbus-maybe.h"
40 #include "util.h"
41 #include "xmlnode.h"
43 #ifdef _WIN32
44 # define NEWLINE_S "\r\n"
45 #else
46 # define NEWLINE_S "\n"
47 #endif
49 static xmlnode*
50 new_node(const char *name, XMLNodeType type)
52 xmlnode *node = g_new0(xmlnode, 1);
54 node->name = g_strdup(name);
55 node->type = type;
57 PURPLE_DBUS_REGISTER_POINTER(node, xmlnode);
59 return node;
62 xmlnode*
63 xmlnode_new(const char *name)
65 g_return_val_if_fail(name != NULL, NULL);
67 return new_node(name, XMLNODE_TYPE_TAG);
70 xmlnode *
71 xmlnode_new_child(xmlnode *parent, const char *name)
73 xmlnode *node;
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);
82 return node;
85 void
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;
95 } else {
96 parent->child = child;
99 parent->lastchild = child;
102 void
103 xmlnode_insert_data(xmlnode *node, const char *data, gssize size)
105 xmlnode *child;
106 gsize real_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);
122 void
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;
131 while (attr_node) {
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;
142 } else {
143 sibling->next = attr_node->next;
144 sibling = attr_node->next;
145 xmlnode_free(attr_node);
146 attr_node = sibling;
149 else
151 attr_node = attr_node->next;
153 sibling = attr_node;
157 void
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;
173 } else {
174 sibling->next = attr_node->next;
176 if (node->lastchild == attr_node) {
177 node->lastchild = sibling;
179 xmlnode_free(attr_node);
180 return;
182 sibling = attr_node;
186 void
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);
193 void
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);
199 void
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);
205 void
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);
225 const char *
226 xmlnode_get_attrib(xmlnode *node, const char *attr)
228 xmlnode *x;
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)) {
235 return x->data;
239 return NULL;
242 const char *
243 xmlnode_get_attrib_with_namespace(xmlnode *node, const char *attr, const char *xmlns)
245 xmlnode *x;
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)) {
254 return x->data;
258 return NULL;
262 void xmlnode_set_namespace(xmlnode *node, const char *xmlns)
264 g_return_if_fail(node != NULL);
266 g_free(node->xmlns);
267 node->xmlns = g_strdup(xmlns);
270 const char *xmlnode_get_namespace(xmlnode *node)
272 g_return_val_if_fail(node != NULL, NULL);
274 return node->xmlns;
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);
288 return node->prefix;
291 xmlnode *xmlnode_get_parent(const xmlnode *child)
293 g_return_val_if_fail(child != NULL, NULL);
294 return child->parent;
297 void
298 xmlnode_free(xmlnode *node)
300 xmlnode *x, *y;
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;
310 } else {
311 xmlnode *prev = node->parent->child;
312 while(prev && prev->next != node) {
313 prev = prev->next;
315 if(prev) {
316 prev->next = node->next;
317 if (node->parent->lastchild == node)
318 node->parent->lastchild = prev;
323 /* now free our children */
324 x = node->child;
325 while(x) {
326 y = x->next;
327 xmlnode_free(x);
328 x = y;
331 /* now dispose of ourselves */
332 g_free(node->name);
333 g_free(node->data);
334 g_free(node->xmlns);
335 g_free(node->prefix);
337 if(node->namespace_map)
338 g_hash_table_destroy(node->namespace_map);
340 PURPLE_DBUS_UNREGISTER_POINTER(node);
341 g_free(node);
344 xmlnode*
345 xmlnode_get_child(const xmlnode *parent, const char *name)
347 return xmlnode_get_child_with_namespace(parent, name, NULL);
350 xmlnode *
351 xmlnode_get_child_with_namespace(const xmlnode *parent, const char *name, const char *ns)
353 xmlnode *x, *ret = NULL;
354 char **names;
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;
367 if(ns)
368 xmlns = xmlnode_get_namespace(x);
370 if(x->type == XMLNODE_TYPE_TAG && purple_strequal(parent_name, x->name)
371 && purple_strequal(ns, xmlns)) {
372 ret = x;
373 break;
377 if(child_name && ret)
378 ret = xmlnode_get_child(ret, child_name);
380 g_strfreev(names);
381 return ret;
384 char *
385 xmlnode_get_data(const xmlnode *node)
387 GString *str = NULL;
388 xmlnode *c;
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) {
394 if(!str)
395 str = g_string_new_len(c->data, c->data_sz);
396 else
397 str = g_string_append_len(str, c->data, c->data_sz);
401 if (str == NULL)
402 return NULL;
404 return g_string_free(str, FALSE);
407 char *
408 xmlnode_get_data_unescaped(const xmlnode *node)
410 char *escaped = xmlnode_get_data(node);
412 char *unescaped = escaped ? purple_unescape_html(escaped) : NULL;
414 g_free(escaped);
416 return unescaped;
419 static void
420 xmlnode_to_str_foreach_append_ns(const char *key, const char *value,
421 GString *buf)
423 if (*key) {
424 g_string_append_printf(buf, " xmlns:%s='%s'", key, value);
425 } else {
426 g_string_append_printf(buf, " xmlns='%s'", value);
430 static char *
431 xmlnode_to_str_helper(const xmlnode *node, int *len, gboolean formatting, int depth)
433 GString *text = g_string_new("");
434 const char *prefix;
435 const xmlnode *c;
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);
449 if (prefix) {
450 g_string_append_printf(text, "<%s:%s", prefix, node_name);
451 } else {
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);
463 g_free(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);
472 if (aprefix) {
473 g_string_append_printf(text, " %s:%s='%s'", aprefix, esc, esc2);
474 } else {
475 g_string_append_printf(text, " %s='%s'", esc, esc2);
477 g_free(esc);
478 g_free(esc2);
479 } else if(c->type == XMLNODE_TYPE_TAG || c->type == XMLNODE_TYPE_DATA) {
480 if(c->type == XMLNODE_TYPE_DATA)
481 pretty = FALSE;
482 need_end = TRUE;
486 if(need_end) {
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) {
492 int esc_len;
493 esc = xmlnode_to_str_helper(c, &esc_len, pretty, depth+1);
494 text = g_string_append_len(text, esc, esc_len);
495 g_free(esc);
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);
499 g_free(esc);
503 if(tab && pretty)
504 text = g_string_append(text, tab);
505 if (prefix) {
506 g_string_append_printf(text, "</%s:%s>%s", prefix, node_name, formatting ? NEWLINE_S : "");
507 } else {
508 g_string_append_printf(text, "</%s>%s", node_name, formatting ? NEWLINE_S : "");
510 } else {
511 g_string_append_printf(text, "/>%s", formatting ? NEWLINE_S : "");
514 g_free(node_name);
516 g_free(tab);
518 if(len)
519 *len = text->len;
521 return g_string_free(text, FALSE);
524 char *
525 xmlnode_to_str(const xmlnode *node, int *len)
527 return xmlnode_to_str_helper(node, len, FALSE, 0);
530 char *
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);
540 g_free(xml);
542 if (len)
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 {
549 xmlnode *current;
550 gboolean error;
553 static void
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;
560 xmlnode *node;
561 int i, j;
563 if(!element_name || xpd->error) {
564 return;
565 } else {
566 if(xpd->current)
567 node = xmlnode_new_child(xpd->current, (const char*) element_name);
568 else
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];
589 char *txt;
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';
594 txt = attrib;
595 attrib = purple_unescape_html(txt);
596 g_free(txt);
597 xmlnode_set_attrib_full(node, name, NULL, prefix, attrib);
598 g_free(attrib);
601 xpd->current = node;
605 static void
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)
612 return;
614 if(xpd->current->parent) {
615 if(!xmlStrcmp((xmlChar*) xpd->current->name, element_name))
616 xpd->current = xpd->current->parent;
620 static void
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)
626 return;
628 if(!text || !text_len)
629 return;
631 xmlnode_insert_data(xpd->current, (const char*) text, text_len);
634 static void
635 xmlnode_parser_error_libxml(void *user_data, const char *msg, ...)
637 struct _xmlnode_parser_data *xpd = user_data;
638 char errmsg[2048];
639 va_list args;
641 xpd->error = TRUE;
643 va_start(args, msg);
644 vsnprintf(errmsg, sizeof(errmsg), msg, args);
645 va_end(args);
647 purple_debug_error("xmlnode", "Error parsing xml file: %s", errmsg);
650 static void
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)) {
657 xpd->error = TRUE;
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");
662 } else if (error)
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");
667 else
668 purple_debug_warning("xmlnode", "XML parser error for xmlnode %p\n",
669 user_data);
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 */
693 NULL, /* comment */
694 NULL, /* warning */
695 xmlnode_parser_error_libxml, /* error */
696 NULL, /* fatalError */
697 NULL, /* getParameterEntity */
698 NULL, /* cdataBlock */
699 NULL, /* externalSubset */
700 XML_SAX2_MAGIC, /* initialized */
701 NULL, /* _private */
702 xmlnode_parser_element_start_libxml, /* startElementNs */
703 xmlnode_parser_element_end_libxml, /* endElementNs */
704 xmlnode_parser_structural_error_libxml, /* serror */
707 xmlnode *
708 xmlnode_from_str(const char *str, gssize size)
710 struct _xmlnode_parser_data *xpd;
711 xmlnode *ret;
712 gsize real_size;
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;
722 if(xpd->current)
723 xmlnode_free(xpd->current);
724 xpd->current = NULL;
726 ret = xpd->current;
727 if (xpd->error) {
728 ret = NULL;
729 if (xpd->current)
730 xmlnode_free(xpd->current);
733 g_free(xpd);
734 return ret;
737 xmlnode *
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;
743 gsize length;
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",
749 filename, dir);
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);
758 return NULL;
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);
765 g_error_free(error);
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 */
773 if (node == NULL)
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);
788 g_free(contents);
791 /* If we could not parse the file then show the user an error message */
792 if (node == NULL)
794 gchar *title, *msg;
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);
800 g_free(title);
801 g_free(msg);
804 g_free(filename_full);
806 return node;
809 static void
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));
816 xmlnode *
817 xmlnode_copy(const xmlnode *src)
819 xmlnode *ret;
820 xmlnode *child;
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);
827 if (src->data) {
828 if (src->data_sz) {
829 ret->data = g_memdup(src->data, src->data_sz);
830 ret->data_sz = src->data_sz;
831 } else {
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,
838 g_free, g_free);
839 g_hash_table_foreach(src->namespace_map, xmlnode_copy_foreach_ns, ret->namespace_map);
842 for (child = src->child; child; child = child->next) {
843 if (sibling) {
844 sibling->next = xmlnode_copy(child);
845 sibling = sibling->next;
846 } else {
847 ret->child = xmlnode_copy(child);
848 sibling = ret->child;
850 sibling->parent = ret;
853 ret->lastchild = sibling;
855 return ret;
858 xmlnode *
859 xmlnode_get_next_twin(xmlnode *node)
861 xmlnode *sibling;
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;
870 if(ns)
871 xmlns = xmlnode_get_namespace(sibling);
873 if(sibling->type == XMLNODE_TYPE_TAG && purple_strequal(node->name, sibling->name) &&
874 purple_strequal(ns, xmlns))
875 return sibling;
878 return NULL;