/* * Copyright (C) 2006, 2007 OpenedHand Ltd. * Copyright (C) 2007 Zeeshan Ali. * Copyright (C) 2012 Intel Corporation * * Author: Jorn Baayen * Author: Zeeshan Ali (Khattak) * Author: Krzesimir Nowak * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include #include "xml-util.h" typedef struct _GUPnPXMLNamespaceDescription { const char *uri; const char *prefix; } GUPnPXMLNamespaceDescription; static GUPnPXMLNamespaceDescription gupnp_xml_namespaces[] = { { "urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/", NULL }, { "http://purl.org/dc/elements/1.1/", "dc" }, { "urn:schemas-dlna-org:metadata-1-0/", "dlna" }, { "http://www.pv.com/pvns/", "pv" }, { "urn:schemas-upnp-org:metadata-1-0/upnp/", "upnp" }, { NULL } }; GUPnPAVXMLDoc * xml_doc_new (xmlDoc *doc) { GUPnPAVXMLDoc *ret = NULL; g_return_val_if_fail (doc, NULL); ret = g_new0 (GUPnPAVXMLDoc, 1); ret->refcount = 1; ret->doc = doc; return ret; } GUPnPAVXMLDoc * xml_doc_ref (GUPnPAVXMLDoc *doc) { g_return_val_if_fail (doc, NULL); g_return_val_if_fail (doc->refcount > 0, NULL); g_atomic_int_inc (&doc->refcount); return doc; } void xml_doc_unref (GUPnPAVXMLDoc *doc) { g_return_if_fail (doc); g_return_if_fail (doc->refcount > 0); if (g_atomic_int_dec_and_test (&doc->refcount)) { xmlFreeDoc (doc->doc); doc->doc = NULL; } } xmlNode * xml_util_get_element (xmlNode *node, ...) { va_list var_args; va_start (var_args, node); while (TRUE) { const char *arg; arg = va_arg (var_args, const char *); if (!arg) break; for (node = node->children; node; node = node->next) { if (node->name == NULL) continue; if (!g_ascii_strcasecmp (arg, (char *) node->name)) break; } if (!node) break; } va_end (var_args); return node; } GList * xml_util_get_child_elements_by_name (xmlNode *node, const char *name) { GList *children = NULL; for (node = node->children; node; node = node->next) { if (node->name == NULL) continue; if (strcmp (name, (char *) node->name) == 0) { children = g_list_append (children, node); } } return children; } const char * xml_util_get_child_element_content (xmlNode *node, const char *child_name) { xmlNode *child_node; const char *content; child_node = xml_util_get_element (node, child_name, NULL); if (!child_node || !(child_node->children)) return NULL; content = (const char *) child_node->children->content; if (!content) return NULL; return content; } guint xml_util_get_uint_child_element (xmlNode *node, const char *child_name, guint default_value) { const char *content; content = xml_util_get_child_element_content (node, child_name); if (!content) return default_value; return strtoul (content, NULL, 0); } guint64 xml_util_get_uint64_child_element (xmlNode *node, const char *child_name, guint64 default_value) { const char *content; content = xml_util_get_child_element_content (node, child_name); if (!content) return default_value; return g_ascii_strtoull (content, NULL, 0); } const char * xml_util_get_attribute_content (xmlNode *node, const char *attribute_name) { xmlAttr *attribute; for (attribute = node->properties; attribute; attribute = attribute->next) { if (attribute->name == NULL) continue; if (strcmp (attribute_name, (char *) attribute->name) == 0) break; } if (attribute) return (const char *) attribute->children->content; else return NULL; } gboolean xml_util_get_boolean_attribute (xmlNode *node, const char *attribute_name) { const char *content; gchar *str; gboolean ret; content = xml_util_get_attribute_content (node, attribute_name); if (!content) return FALSE; str = (char *) content; if (g_ascii_strcasecmp (str, "true") == 0 || g_ascii_strcasecmp (str, "yes") == 0) ret = TRUE; else if (g_ascii_strcasecmp (str, "false") == 0 || g_ascii_strcasecmp (str, "no") == 0) ret = FALSE; else { int i; i = atoi (str); ret = i ? TRUE : FALSE; } return ret; } guint xml_util_get_uint_attribute (xmlNode *node, const char *attribute_name, guint default_value) { return (guint) xml_util_get_long_attribute (node, attribute_name, (glong) default_value); } gint xml_util_get_int_attribute (xmlNode *node, const char *attribute_name, gint default_value) { return (gint) xml_util_get_long_attribute (node, attribute_name, (glong) default_value); } glong xml_util_get_long_attribute (xmlNode *node, const char *attribute_name, glong default_value) { return (glong) xml_util_get_int64_attribute (node, attribute_name, (gint64) default_value); } gint64 xml_util_get_int64_attribute (xmlNode *node, const char *attribute_name, gint64 default_value) { const char *content; content = xml_util_get_attribute_content (node, attribute_name); if (!content) return default_value; return g_ascii_strtoll (content, NULL, 0); } xmlNode * xml_util_set_child (xmlNode *parent_node, GUPnPXMLNamespace ns, xmlNsPtr *xmlns, xmlDoc *doc, const char *name, const char *value) { xmlNode *node; xmlChar *escaped; node = xml_util_get_element (parent_node, name, NULL); if (node == NULL) { xmlNsPtr ns_ptr = NULL; ns_ptr = xml_util_get_ns (doc, ns, xmlns); node = xmlNewChild (parent_node, ns_ptr, (unsigned char *) name, NULL); } escaped = xmlEncodeSpecialChars (doc, (const unsigned char *) value); xmlNodeSetContent (node, escaped); xmlFree (escaped); return node; } void xml_util_unset_child (xmlNode *parent_node, const char *name) { xmlNode *node; node = xml_util_get_element (parent_node, name, NULL); if (node != NULL) { xmlUnlinkNode (node); xmlFreeNode (node); } } gboolean xml_util_verify_attribute_is_boolean (xmlNode *node, const char *attribute_name) { const char *content; char *str; content = xml_util_get_attribute_content (node, attribute_name); if (content == NULL) return FALSE; str = (char *) content; return g_ascii_strcasecmp (str, "true") == 0 || g_ascii_strcasecmp (str, "yes") == 0 || g_ascii_strcasecmp (str, "false") == 0 || g_ascii_strcasecmp (str, "no") == 0 || g_ascii_strcasecmp (str, "0") == 0 || g_ascii_strcasecmp (str, "1") == 0; } char * xml_util_get_child_string (xmlNode *parent_node, xmlDoc *doc, const char *name) { xmlBuffer *buffer; char *ret; xmlNode *node; node = xml_util_get_element (parent_node, name, NULL); if (!node) return NULL; buffer = xmlBufferCreate (); xmlNodeDump (buffer, doc, node, 0, 0); ret = g_strndup ((char *) xmlBufferContent (buffer), xmlBufferLength (buffer)); xmlBufferFree (buffer); return ret; } gboolean xml_util_node_deep_equal (xmlNode *first, xmlNode *second) { GHashTable *first_attributes; xmlAttr *attribute; gboolean equal; if (first == NULL && second == NULL) return TRUE; if (first == NULL || second == NULL) return FALSE; if (xmlStrcmp (first->name, second->name)) return FALSE; equal = FALSE; first_attributes = xml_util_get_attributes_map (first); /* compare attributes */ for (attribute = second->properties; attribute != NULL; attribute = attribute->next) { const xmlChar *value = NULL; const xmlChar *key = attribute->name; if (g_hash_table_lookup_extended (first_attributes, key, NULL, (gpointer *) &value)) if (!xmlStrcmp (value, attribute->children->content)) { g_hash_table_remove (first_attributes, key); continue; } goto out; } if (g_hash_table_size (first_attributes)) goto out; /* compare content */ if (xmlStrcmp (first->content, second->content)) goto out; equal = TRUE; out: g_hash_table_unref (first_attributes); if (equal) { xmlNode *first_child; xmlNode *second_child; for (first_child = first->children, second_child = second->children; first_child != NULL && second_child != NULL; first_child = first_child->next, second_child = second_child->next) if (!xml_util_node_deep_equal (first_child, second_child)) return FALSE; if (first_child != NULL || second_child != NULL) return FALSE; } return equal; } xmlNode * xml_util_find_node (xmlNode *haystack, xmlNode *needle) { xmlNodePtr iter; if (xml_util_node_deep_equal (haystack, needle)) return haystack; for (iter = haystack->children; iter != NULL; iter = iter->next) { xmlNodePtr found_node = xml_util_find_node (iter, needle); if (found_node != NULL) return found_node; } return NULL; } xmlNode * xml_util_copy_node (xmlNode *node) { xmlNode *dup = xmlCopyNode (node, 1); /* TODO: remove useless namespace definition. */ return dup; } GHashTable * xml_util_get_attributes_map (xmlNode *node) { xmlAttr *attribute; GHashTable *attributes_map = g_hash_table_new (g_str_hash, g_str_equal); for (attribute = node->properties; attribute != NULL; attribute = attribute->next) g_hash_table_insert (attributes_map, (gpointer) attribute->name, (gpointer) attribute->children->content); return attributes_map; } /** * xml_util_create_namespace: * @root: (allow-none): Document root node or %NULL for anonymous ns. * @ns: Namespace * @returns: Newly created namespace on root node */ xmlNsPtr xml_util_create_namespace (xmlNodePtr root, GUPnPXMLNamespace ns) { g_return_val_if_fail (ns < GUPNP_XML_NAMESPACE_COUNT, NULL); return xmlNewNs (root, (const xmlChar *) gupnp_xml_namespaces[ns].uri, (const xmlChar *) gupnp_xml_namespaces[ns].prefix); } /** * xml_util_lookup_namespace: * @doc: #xmlDoc * @ns: namespace to look up (except DIDL-Lite, which doesn't have a prefix) * @returns: %NULL if namespace does not exist, a pointer to the namespace * otherwise. */ xmlNsPtr xml_util_lookup_namespace (xmlDocPtr doc, GUPnPXMLNamespace ns) { xmlNsPtr *ns_list, *it, retval = NULL; const char *ns_prefix = NULL; const char *ns_uri = NULL; g_return_val_if_fail (ns < GUPNP_XML_NAMESPACE_COUNT, NULL); ns_prefix = gupnp_xml_namespaces[ns].prefix; ns_uri = gupnp_xml_namespaces[ns].uri; ns_list = xmlGetNsList (doc, xmlDocGetRootElement (doc)); if (ns_list == NULL) return NULL; for (it = ns_list; *it != NULL; it++) { const char *it_prefix = (const char *) (*it)->prefix; const char *it_uri = (const char *) (*it)->href; if (it_prefix == NULL) { if (ns_prefix != NULL) continue; if (g_ascii_strcasecmp (it_uri, ns_uri) == 0) { retval = *it; break; } continue; } if (g_ascii_strcasecmp (it_prefix, ns_prefix) == 0) { retval = *it; break; } } xmlFree (ns_list); return retval; } /** * xml_util_get_ns: * @doc: A #xmlDoc. * @ns: A #GUPnPXMLNamespace. * @ns_out: (out) (allow-none): return location for the namespace or %NULL. * * Lazy-create a XML namespace on @doc. * * If @ns_out is non-%NULL, the function will return @ns_out immediately. * @returns: either the existing #xmlNsPtr or a newly created one. */ xmlNsPtr xml_util_get_ns (xmlDocPtr doc, GUPnPXMLNamespace ns, xmlNsPtr *ns_out) { xmlNsPtr tmp_ns; /* User supplied namespace, just return that */ if (ns_out != NULL && *ns_out != NULL) return *ns_out; tmp_ns = xml_util_lookup_namespace (doc, ns); if (!tmp_ns) tmp_ns = xml_util_create_namespace (xmlDocGetRootElement (doc), ns); if (ns_out != NULL) *ns_out = tmp_ns; return tmp_ns; } G_DEFINE_BOXED_TYPE (GUPnPAVXMLDoc, xml_doc, xml_doc_ref, xml_doc_unref)