/*
* This file is part of libdom.
* Licensed under the MIT License,
* http://www.opensource.org/licenses/mit-license.php
* Copyright 2007 John-Mark Bell <jmb@netsurf-browser.org>
* Copyright 2009 Bo Yang <struggleyb.nku@gmail.com>
*/
#include <string.h>
typedef unsigned int uint32_t;
#include <dom/dom.h>
#include "utils/namespace.h"
#include "utils/validate.h"
#include "utils/utils.h"
/** XML prefix */
static dom_string *xml;
/** XMLNS prefix */
static dom_string *xmlns;
/* The namespace strings */
static const char *namespaces[DOM_NAMESPACE_COUNT] = {
NULL,
"http://www.w3.org/1999/xhtml",
"http://www.w3.org/1998/Math/MathML",
"http://www.w3.org/2000/svg",
"http://www.w3.org/1999/xlink",
"http://www.w3.org/XML/1998/namespace",
"http://www.w3.org/2000/xmlns/"
};
dom_string *dom_namespaces[DOM_NAMESPACE_COUNT] = {
NULL,
};
/**
* Initialise the namespace component
*
* \return DOM_NO_ERR on success.
*/
static dom_exception _dom_namespace_initialise(void)
{
int i;
dom_exception err;
err = dom_string_create((const uint8_t *) "xml", SLEN("xml"), &xml);
if (err != DOM_NO_ERR) {
return err;
}
err = dom_string_create((const uint8_t *) "xmlns", SLEN("xmlns"),
&xmlns);
if (err != DOM_NO_ERR) {
dom_string_unref(xml);
xml = NULL;
return err;
}
for (i = 1; i < DOM_NAMESPACE_COUNT; i++) {
err = dom_string_create(
(const uint8_t *) namespaces[i],
strlen(namespaces
[i
]), &dom_namespaces
[i
]);
if (err != DOM_NO_ERR) {
dom_string_unref(xmlns);
xmlns = NULL;
dom_string_unref(xml);
xml = NULL;
return err;
}
}
return DOM_NO_ERR;
}
#ifdef FINALISE_NAMESPACE
/**
* Finalise the namespace component
*
* \return DOM_NO_ERR on success.
*/
dom_exception _dom_namespace_finalise(void)
{
int i;
if (xmlns != NULL) {
dom_string_unref(xmlns);
xmlns = NULL;
}
if (xml != NULL) {
dom_string_unref(xml);
xml = NULL;
}
for (i = 1; i < DOM_NAMESPACE_COUNT; i++) {
if (dom_namespaces[i] != NULL) {
dom_string_unref(dom_namespaces[i]);
dom_namespaces[i] = NULL;
}
}
return DOM_NO_ERR;
}
#endif
/**
* Ensure a QName is valid
*
* \param qname The qname to validate
* \param namespace The namespace URI associated with the QName, or NULL
* \return DOM_NO_ERR if valid,
* DOM_INVALID_CHARACTER_ERR if ::qname contains an invalid character,
* DOM_NAMESPACE_ERR if ::qname is malformed, or it has a
* prefix and ::namespace is NULL, or
* ::qname has a prefix "xml" and
* ::namespace is not
* "http://www.w3.org/XML/1998/namespace",
* or ::qname has a prefix "xmlns" and
* ::namespace is not
* "http://www.w3.org/2000/xmlns", or
* ::namespace is
* "http://www.w3.org/2000/xmlns" and
* ::qname is not (or is not prefixed by)
* "xmlns".
*/
dom_exception _dom_namespace_validate_qname(dom_string *qname,
dom_string *namespace)
{
uint32_t colon, len;
if (xml == NULL) {
dom_exception err = _dom_namespace_initialise();
if (err != DOM_NO_ERR)
return err;
}
if (qname == NULL) {
if (namespace != NULL)
return DOM_NAMESPACE_ERR;
if (namespace == NULL)
return DOM_NO_ERR;
}
if (_dom_validate_name(qname) == false)
return DOM_NAMESPACE_ERR;
len = dom_string_length(qname);
/* Find colon */
colon = dom_string_index(qname, ':');
if (colon == (uint32_t) -1) {
/* No prefix */
/* If namespace URI is for xmlns, ensure qname == "xmlns" */
if (namespace != NULL &&
dom_string_isequal(namespace,
dom_namespaces[DOM_NAMESPACE_XMLNS]) &&
dom_string_isequal(qname, xmlns) == false) {
return DOM_NAMESPACE_ERR;
}
/* If qname == "xmlns", ensure namespace URI is for xmlns */
if (namespace != NULL &&
dom_string_isequal(qname, xmlns) &&
dom_string_isequal(namespace,
dom_namespaces[DOM_NAMESPACE_XMLNS]) == false) {
return DOM_NAMESPACE_ERR;
}
} else if (colon == 0) {
/* Some name like ":name" */
if (namespace != NULL)
return DOM_NAMESPACE_ERR;
} else {
/* Prefix */
dom_string *prefix;
dom_string *lname;
dom_exception err;
/* Ensure there is a namespace URI */
if (namespace == NULL) {
return DOM_NAMESPACE_ERR;
}
err = dom_string_substr(qname, 0, colon, &prefix);
if (err != DOM_NO_ERR) {
return err;
}
err = dom_string_substr(qname, colon + 1, len, &lname);
if (err != DOM_NO_ERR) {
return err;
}
if (_dom_validate_ncname(prefix) == false ||
_dom_validate_ncname(lname) == false) {
return DOM_NAMESPACE_ERR;
}
/* Test for invalid XML namespace */
if (dom_string_isequal(prefix, xml) &&
dom_string_isequal(namespace,
dom_namespaces[DOM_NAMESPACE_XML]) == false) {
dom_string_unref(prefix);
return DOM_NAMESPACE_ERR;
}
/* Test for invalid xmlns namespace */
if (dom_string_isequal(prefix, xmlns) &&
dom_string_isequal(namespace,
dom_namespaces[DOM_NAMESPACE_XMLNS]) == false) {
dom_string_unref(prefix);
return DOM_NAMESPACE_ERR;
}
/* Test for presence of xmlns namespace with non xmlns prefix */
if (dom_string_isequal(namespace,
dom_namespaces[DOM_NAMESPACE_XMLNS]) &&
dom_string_isequal(prefix, xmlns) == false) {
dom_string_unref(prefix);
return DOM_NAMESPACE_ERR;
}
dom_string_unref(prefix);
}
return DOM_NO_ERR;
}
/**
* Split a QName into a namespace prefix and localname string
*
* \param qname The qname to split
* \param prefix Pointer to location to receive prefix
* \param localname Pointer to location to receive localname
* \return DOM_NO_ERR on success.
*
* If there is no prefix present in ::qname, then ::prefix will be NULL.
*
* ::prefix and ::localname will be referenced. The caller should unreference
* them once finished.
*/
dom_exception _dom_namespace_split_qname(dom_string *qname,
dom_string **prefix, dom_string **localname)
{
uint32_t colon;
dom_exception err;
if (xml == NULL) {
err = _dom_namespace_initialise();
if (err != DOM_NO_ERR)
return err;
}
/* Find colon, if any */
colon = dom_string_index(qname, ':');
if (colon == (uint32_t) -1) {
/* None found => no prefix */
*prefix = NULL;
*localname = dom_string_ref(qname);
} else {
/* Found one => prefix */
err = dom_string_substr(qname, 0, colon, prefix);
if (err != DOM_NO_ERR) {
return err;
}
err = dom_string_substr(qname, colon + 1,
dom_string_length(qname), localname);
if (err != DOM_NO_ERR) {
dom_string_unref(*prefix);
*prefix = NULL;
return err;
}
}
return DOM_NO_ERR;
}
/**
* Get the XML prefix dom_string
*
* \return the xml prefix dom_string.
*
* Note: The client of this function may or may not call the dom_string_ref
* on the returned dom_string, because this string will only be destroyed when
* the dom_finalise is called. But if the client call dom_string_ref, it must
* call dom_string_unref to maintain a correct ref count of the dom_string.
*/
dom_string *_dom_namespace_get_xml_prefix(void)
{
if (xml == NULL) {
if (_dom_namespace_initialise() != DOM_NO_ERR)
return NULL;
}
return xml;
}
/**
* Get the XMLNS prefix dom_string.
*
* \return the xmlns prefix dom_string
*
* Note: The client of this function may or may not call the dom_string_ref
* on the returned dom_string, because this string will only be destroyed when
* the dom_finalise is called. But if the client call dom_string_ref, it must
* call dom_string_unref to maintain a correct ref count of the dom_string.
*/
dom_string *_dom_namespace_get_xmlns_prefix(void)
{
if (xml == NULL) {
if (_dom_namespace_initialise() != DOM_NO_ERR)
return NULL;
}
return xmlns;
}