/*1* namespaces.c: Implementation of the XSLT namespaces handling2*3* Reference:4* http://www.w3.org/TR/1999/REC-xslt-199911165*6* See Copyright for the status of this software.7*8* [email protected]9*/1011#define IN_LIBXSLT12#include "libxslt.h"1314#include <string.h>1516#ifndef XSLT_NEED_TRIO17#include <stdio.h>18#else19#include <trio.h>20#endif2122#include <libxml/xmlmemory.h>23#include <libxml/tree.h>24#include <libxml/hash.h>25#include <libxml/xmlerror.h>26#include <libxml/uri.h>27#include "xslt.h"28#include "xsltInternals.h"29#include "xsltutils.h"30#include "namespaces.h"31#include "imports.h"3233/************************************************************************34* *35* Module interfaces *36* *37************************************************************************/3839#ifdef XSLT_REFACTORED40static xsltNsAliasPtr41xsltNewNsAlias(xsltCompilerCtxtPtr cctxt)42{43xsltNsAliasPtr ret;4445if (cctxt == NULL)46return(NULL);4748ret = (xsltNsAliasPtr) xmlMalloc(sizeof(xsltNsAlias));49if (ret == NULL) {50xsltTransformError(NULL, cctxt->style, NULL,51"Internal error in xsltNewNsAlias(): Memory allocation failed.\n");52cctxt->style->errors++;53return(NULL);54}55memset(ret, 0, sizeof(xsltNsAlias));56/*57* TODO: Store the item at current stylesheet-level.58*/59ret->next = cctxt->nsAliases;60cctxt->nsAliases = ret;6162return(ret);63}64#endif /* XSLT_REFACTORED */65/**66* xsltNamespaceAlias:67* @style: the XSLT stylesheet68* @node: the xsl:namespace-alias node69*70* Read the stylesheet-prefix and result-prefix attributes, register71* them as well as the corresponding namespace.72*/73void74xsltNamespaceAlias(xsltStylesheetPtr style, xmlNodePtr node)75{76xmlChar *resultPrefix = NULL;77xmlChar *stylePrefix = NULL;78xmlNsPtr literalNs = NULL;79xmlNsPtr targetNs = NULL;8081#ifdef XSLT_REFACTORED82xsltNsAliasPtr alias;8384if ((style == NULL) || (node == NULL))85return;8687/*88* SPEC XSLT 1.0:89* "If a namespace URI is declared to be an alias for multiple90* different namespace URIs, then the declaration with the highest91* import precedence is used. It is an error if there is more than92* one such declaration. An XSLT processor may signal the error;93* if it does not signal the error, it must recover by choosing,94* from amongst the declarations with the highest import precedence,95* the one that occurs last in the stylesheet."96*97* SPEC TODO: Check for the errors mentioned above.98*/99/*100* NOTE that the XSLT 2.0 also *does* use the NULL namespace if101* "#default" is used and there's no default namespace is scope.102* I.e., this is *not* an error.103* Most XSLT 1.0 implementations work this way.104* The XSLT 1.0 spec has nothing to say on the subject.105*/106/*107* Attribute "stylesheet-prefix".108*/109stylePrefix = xmlGetNsProp(node, (const xmlChar *)"stylesheet-prefix", NULL);110if (stylePrefix == NULL) {111xsltTransformError(NULL, style, node,112"The attribute 'stylesheet-prefix' is missing.\n");113return;114}115if (xmlStrEqual(stylePrefix, (const xmlChar *)"#default"))116literalNs = xmlSearchNs(node->doc, node, NULL);117else {118literalNs = xmlSearchNs(node->doc, node, stylePrefix);119if (literalNs == NULL) {120xsltTransformError(NULL, style, node,121"Attribute 'stylesheet-prefix': There's no namespace "122"declaration in scope for the prefix '%s'.\n",123stylePrefix);124goto error;125}126}127/*128* Attribute "result-prefix".129*/130resultPrefix = xmlGetNsProp(node, (const xmlChar *)"result-prefix", NULL);131if (resultPrefix == NULL) {132xsltTransformError(NULL, style, node,133"The attribute 'result-prefix' is missing.\n");134goto error;135}136if (xmlStrEqual(resultPrefix, (const xmlChar *)"#default"))137targetNs = xmlSearchNs(node->doc, node, NULL);138else {139targetNs = xmlSearchNs(node->doc, node, resultPrefix);140141if (targetNs == NULL) {142xsltTransformError(NULL, style, node,143"Attribute 'result-prefix': There's no namespace "144"declaration in scope for the prefix '%s'.\n",145stylePrefix);146goto error;147}148}149/*150*151* Same alias for multiple different target namespace URIs:152* TODO: The one with the highest import precedence is used.153* Example:154* <xsl:namespace-alias stylesheet-prefix="foo"155* result-prefix="bar"/>156*157* <xsl:namespace-alias stylesheet-prefix="foo"158* result-prefix="zar"/>159*160* Same target namespace URI for multiple different aliases:161* All alias-definitions will be used.162* Example:163* <xsl:namespace-alias stylesheet-prefix="bar"164* result-prefix="foo"/>165*166* <xsl:namespace-alias stylesheet-prefix="zar"167* result-prefix="foo"/>168* Cases using #default:169* <xsl:namespace-alias stylesheet-prefix="#default"170* result-prefix="#default"/>171* TODO: Has this an effect at all?172*173* <xsl:namespace-alias stylesheet-prefix="foo"174* result-prefix="#default"/>175* From namespace to no namespace.176*177* <xsl:namespace-alias stylesheet-prefix="#default"178* result-prefix="foo"/>179* From no namespace to namespace.180*/181182183/*184* Store the ns-node in the alias-object.185*/186alias = xsltNewNsAlias(XSLT_CCTXT(style));187if (alias == NULL)188return;189alias->literalNs = literalNs;190alias->targetNs = targetNs;191XSLT_CCTXT(style)->hasNsAliases = 1;192193194#else /* XSLT_REFACTORED */195const xmlChar *literalNsName;196const xmlChar *targetNsName;197198199if ((style == NULL) || (node == NULL))200return;201202stylePrefix = xmlGetNsProp(node, (const xmlChar *)"stylesheet-prefix", NULL);203if (stylePrefix == NULL) {204xsltTransformError(NULL, style, node,205"namespace-alias: stylesheet-prefix attribute missing\n");206return;207}208resultPrefix = xmlGetNsProp(node, (const xmlChar *)"result-prefix", NULL);209if (resultPrefix == NULL) {210xsltTransformError(NULL, style, node,211"namespace-alias: result-prefix attribute missing\n");212goto error;213}214215if (xmlStrEqual(stylePrefix, (const xmlChar *)"#default")) {216literalNs = xmlSearchNs(node->doc, node, NULL);217if (literalNs == NULL) {218literalNsName = NULL;219} else220literalNsName = literalNs->href; /* Yes - set for nsAlias table */221} else {222literalNs = xmlSearchNs(node->doc, node, stylePrefix);223224if ((literalNs == NULL) || (literalNs->href == NULL)) {225xsltTransformError(NULL, style, node,226"namespace-alias: prefix %s not bound to any namespace\n",227stylePrefix);228goto error;229} else230literalNsName = literalNs->href;231}232233/*234* When "#default" is used for result, if a default namespace has not235* been explicitly declared the special value UNDEFINED_DEFAULT_NS is236* put into the nsAliases table237*/238if (xmlStrEqual(resultPrefix, (const xmlChar *)"#default")) {239targetNs = xmlSearchNs(node->doc, node, NULL);240if (targetNs == NULL) {241targetNsName = UNDEFINED_DEFAULT_NS;242} else243targetNsName = targetNs->href;244} else {245targetNs = xmlSearchNs(node->doc, node, resultPrefix);246247if ((targetNs == NULL) || (targetNs->href == NULL)) {248xsltTransformError(NULL, style, node,249"namespace-alias: prefix %s not bound to any namespace\n",250resultPrefix);251goto error;252} else253targetNsName = targetNs->href;254}255/*256* Special case: if #default is used for257* the stylesheet-prefix (literal namespace) and there's no default258* namespace in scope, we'll use style->defaultAlias for this.259*/260if (literalNsName == NULL) {261if (targetNs != NULL) {262/*263* BUG TODO: Is it not sufficient to have only 1 field for264* this, since subsequently alias declarations will265* overwrite this.266* Example:267* <xsl:namespace-alias result-prefix="foo"268* stylesheet-prefix="#default"/>269* <xsl:namespace-alias result-prefix="bar"270* stylesheet-prefix="#default"/>271* The mapping for "foo" won't be visible anymore.272*/273style->defaultAlias = targetNs->href;274}275} else {276if (style->nsAliases == NULL)277style->nsAliases = xmlHashCreate(10);278if (style->nsAliases == NULL) {279xsltTransformError(NULL, style, node,280"namespace-alias: cannot create hash table\n");281goto error;282}283xmlHashAddEntry((xmlHashTablePtr) style->nsAliases,284literalNsName, (void *) targetNsName);285}286#endif /* else of XSLT_REFACTORED */287288error:289if (stylePrefix != NULL)290xmlFree(stylePrefix);291if (resultPrefix != NULL)292xmlFree(resultPrefix);293}294295/**296* xsltGetSpecialNamespace:297* @ctxt: the transformation context298* @invocNode: the invoking node; e.g. a literal result element/attr;299* only used for error reports300* @nsName: the namespace name (or NULL)301* @nsPrefix: the suggested namespace prefix (or NULL)302* @target: the result element on which to anchor a namespace303*304* Find a matching (prefix and ns-name) ns-declaration305* for the requested @nsName and @nsPrefix in the result tree.306* If none is found then a new ns-declaration will be307* added to @resultElem. If, in this case, the given prefix is308* already in use, then a ns-declaration with a modified ns-prefix309* be we created. Note that this function's priority is to310* preserve ns-prefixes; it will only change a prefix if there's311* a namespace clash.312* If both @nsName and @nsPrefix are NULL, then this will try to313* "undeclare" a default namespace by declaring an xmlns="".314*315* Returns a namespace declaration or NULL.316*/317xmlNsPtr318xsltGetSpecialNamespace(xsltTransformContextPtr ctxt, xmlNodePtr invocNode,319const xmlChar *nsName, const xmlChar *nsPrefix,320xmlNodePtr target)321{322xmlNsPtr ns;323int prefixOccupied = 0;324325if ((ctxt == NULL) || (target == NULL) ||326(target->type != XML_ELEMENT_NODE))327return(NULL);328329/*330* NOTE: Namespace exclusion and ns-aliasing is performed at331* compilation-time in the refactored code; so this need not be done332* here (it was in the old code).333* NOTE: @invocNode was named @cur in the old code and was documented to334* be an input node; since it was only used to anchor an error report335* somewhere, we can safely change this to @invocNode, which now336* will be the XSLT instruction (also a literal result element/attribute),337* which was responsible for this call.338*/339/*340* OPTIMIZE TODO: This all could be optimized by keeping track of341* the ns-decls currently in-scope via a specialized context.342*/343if ((nsPrefix == NULL) && ((nsName == NULL) || (nsName[0] == 0))) {344/*345* NOTE: the "undeclaration" of the default namespace was346* part of the logic of the old xsltGetSpecialNamespace() code,347* so we'll keep that mechanism.348* Related to the old code: bug #302020:349*/350/*351* OPTIMIZE TODO: This all could be optimized by keeping track of352* the ns-decls currently in-scope via a specialized context.353*/354/*355* Search on the result element itself.356*/357if (target->nsDef != NULL) {358ns = target->nsDef;359do {360if (ns->prefix == NULL) {361if ((ns->href != NULL) && (ns->href[0] != 0)) {362/*363* Raise a namespace normalization error.364*/365xsltTransformError(ctxt, NULL, invocNode,366"Namespace normalization error: Cannot undeclare "367"the default namespace, since the default namespace "368"'%s' is already declared on the result element "369"'%s'.\n", ns->href, target->name);370return(NULL);371} else {372/*373* The default namespace was undeclared on the374* result element.375*/376return(NULL);377}378break;379}380ns = ns->next;381} while (ns != NULL);382}383if ((target->parent != NULL) &&384(target->parent->type == XML_ELEMENT_NODE))385{386/*387* The parent element is in no namespace, so assume388* that there is no default namespace in scope.389*/390if (target->parent->ns == NULL)391return(NULL);392393ns = xmlSearchNs(target->doc, target->parent,394NULL);395/*396* Fine if there's no default ns is scope, or if the397* default ns was undeclared.398*/399if ((ns == NULL) || (ns->href == NULL) || (ns->href[0] == 0))400return(NULL);401402/*403* Undeclare the default namespace.404*/405xmlNewNs(target, BAD_CAST "", NULL);406/* TODO: Check result */407return(NULL);408}409return(NULL);410}411/*412* Handle the XML namespace.413* QUESTION: Is this faster than using xmlStrEqual() anyway?414*/415if ((nsPrefix != NULL) &&416(nsPrefix[0] == 'x') && (nsPrefix[1] == 'm') &&417(nsPrefix[2] == 'l') && (nsPrefix[3] == 0))418{419return(xmlSearchNs(target->doc, target, nsPrefix));420}421/*422* First: search on the result element itself.423*/424if (target->nsDef != NULL) {425ns = target->nsDef;426do {427if ((ns->prefix == NULL) == (nsPrefix == NULL)) {428if (ns->prefix == nsPrefix) {429if (xmlStrEqual(ns->href, nsName))430return(ns);431prefixOccupied = 1;432break;433} else if (xmlStrEqual(ns->prefix, nsPrefix)) {434if (xmlStrEqual(ns->href, nsName))435return(ns);436prefixOccupied = 1;437break;438}439}440ns = ns->next;441} while (ns != NULL);442}443if (prefixOccupied) {444/*445* If the ns-prefix is occupied by an other ns-decl on the446* result element, then this means:447* 1) The desired prefix is shadowed448* 2) There's no way around changing the prefix449*450* Try a desperate search for an in-scope ns-decl451* with a matching ns-name before we use the last option,452* which is to recreate the ns-decl with a modified prefix.453*/454ns = xmlSearchNsByHref(target->doc, target, nsName);455if (ns != NULL)456return(ns);457458/*459* Fallback to changing the prefix.460*/461} else if ((target->parent != NULL) &&462(target->parent->type == XML_ELEMENT_NODE))463{464/*465* Try to find a matching ns-decl in the ancestor-axis.466*467* Check the common case: The parent element of the current468* result element is in the same namespace (with an equal ns-prefix).469*/470if ((target->parent->ns != NULL) &&471((target->parent->ns->prefix != NULL) == (nsPrefix != NULL)))472{473ns = target->parent->ns;474475if (nsPrefix == NULL) {476if (xmlStrEqual(ns->href, nsName))477return(ns);478} else if (xmlStrEqual(ns->prefix, nsPrefix) &&479xmlStrEqual(ns->href, nsName))480{481return(ns);482}483}484/*485* Lookup the remaining in-scope namespaces.486*/487ns = xmlSearchNs(target->doc, target->parent, nsPrefix);488if (ns != NULL) {489if (xmlStrEqual(ns->href, nsName))490return(ns);491/*492* Now check for a nasty case: We need to ensure that the new493* ns-decl won't shadow a prefix in-use by an existing attribute.494* <foo xmlns:a="urn:test:a">495* <bar a:a="val-a">496* <xsl:attribute xmlns:a="urn:test:b" name="a:b">497* val-b</xsl:attribute>498* </bar>499* </foo>500*/501if (target->properties) {502xmlAttrPtr attr = target->properties;503do {504if ((attr->ns) &&505xmlStrEqual(attr->ns->prefix, nsPrefix))506{507/*508* Bad, this prefix is already in use.509* Since we'll change the prefix anyway, try510* a search for a matching ns-decl based on the511* namespace name.512*/513ns = xmlSearchNsByHref(target->doc, target, nsName);514if (ns != NULL)515return(ns);516goto declare_new_prefix;517}518attr = attr->next;519} while (attr != NULL);520}521} else {522/*523* Either no matching ns-prefix was found or the namespace is524* shadowed.525* Create a new ns-decl on the current result element.526*527* Hmm, we could also try to reuse an in-scope528* namespace with a matching ns-name but a different529* ns-prefix.530* What has higher priority?531* 1) If keeping the prefix: create a new ns-decl.532* 2) If reusal: first lookup ns-names; then fallback533* to creation of a new ns-decl.534* REVISIT: this currently uses case 1) although535* the old way was use xmlSearchNsByHref() and to let change536* the prefix.537*/538#if 0539ns = xmlSearchNsByHref(target->doc, target, nsName);540if (ns != NULL)541return(ns);542#endif543}544/*545* Create the ns-decl on the current result element.546*/547ns = xmlNewNs(target, nsName, nsPrefix);548/* TODO: check errors */549return(ns);550} else {551/*552* This is either the root of the tree or something weird is going on.553*/554ns = xmlNewNs(target, nsName, nsPrefix);555/* TODO: Check result */556return(ns);557}558559declare_new_prefix:560/*561* Fallback: we need to generate a new prefix and declare the namespace562* on the result element.563*/564{565xmlChar pref[30];566int counter = 1;567568if (nsPrefix == NULL) {569nsPrefix = BAD_CAST "ns";570}571572do {573snprintf((char *) pref, 30, "%s_%d", nsPrefix, counter++);574ns = xmlSearchNs(target->doc, target, BAD_CAST pref);575if (counter > 1000) {576xsltTransformError(ctxt, NULL, invocNode,577"Internal error in xsltAcquireResultInScopeNs(): "578"Failed to compute a unique ns-prefix for the "579"generated element");580return(NULL);581}582} while (ns != NULL);583ns = xmlNewNs(target, nsName, BAD_CAST pref);584/* TODO: Check result */585return(ns);586}587return(NULL);588}589590/**591* xsltGetNamespace:592* @ctxt: a transformation context593* @cur: the input node594* @ns: the namespace595* @out: the output node (or its parent)596*597* Find a matching (prefix and ns-name) ns-declaration598* for the requested @ns->prefix and @ns->href in the result tree.599* If none is found then a new ns-declaration will be600* added to @resultElem. If, in this case, the given prefix is601* already in use, then a ns-declaration with a modified ns-prefix602* be we created.603*604* Called by:605* - xsltCopyPropList() (*not* anymore)606* - xsltShallowCopyElement()607* - xsltCopyTreeInternal() (*not* anymore)608* - xsltApplySequenceConstructor() (*not* in the refactored code),609* - xsltElement() (*not* anymore)610*611* Returns a namespace declaration or NULL in case of612* namespace fixup failures or API or internal errors.613*/614xmlNsPtr615xsltGetNamespace(xsltTransformContextPtr ctxt, xmlNodePtr cur, xmlNsPtr ns,616xmlNodePtr out)617{618619if (ns == NULL)620return(NULL);621622#ifdef XSLT_REFACTORED623/*624* Namespace exclusion and ns-aliasing is performed at625* compilation-time in the refactored code.626* Additionally, aliasing is not intended for non Literal627* Result Elements.628*/629return(xsltGetSpecialNamespace(ctxt, cur, ns->href, ns->prefix, out));630#else631{632xsltStylesheetPtr style;633const xmlChar *URI = NULL; /* the replacement URI */634635if ((ctxt == NULL) || (cur == NULL) || (out == NULL))636return(NULL);637638style = ctxt->style;639while (style != NULL) {640if (style->nsAliases != NULL)641URI = (const xmlChar *)642xmlHashLookup(style->nsAliases, ns->href);643if (URI != NULL)644break;645646style = xsltNextImport(style);647}648649650if (URI == UNDEFINED_DEFAULT_NS) {651return(xsltGetSpecialNamespace(ctxt, cur, NULL, NULL, out));652#if 0653/*654* TODO: Removed, since wrong. If there was no default655* namespace in the stylesheet then this must resolve to656* the NULL namespace.657*/658xmlNsPtr dflt;659dflt = xmlSearchNs(cur->doc, cur, NULL);660if (dflt != NULL)661URI = dflt->href;662else663return NULL;664#endif665} else if (URI == NULL)666URI = ns->href;667668return(xsltGetSpecialNamespace(ctxt, cur, URI, ns->prefix, out));669}670#endif671}672673/**674* xsltGetPlainNamespace:675* @ctxt: a transformation context676* @cur: the input node677* @ns: the namespace678* @out: the result element679*680* Obsolete.681* *Not* called by any Libxslt/Libexslt function.682* Exaclty the same as xsltGetNamespace().683*684* Returns a namespace declaration or NULL in case of685* namespace fixup failures or API or internal errors.686*/687xmlNsPtr688xsltGetPlainNamespace(xsltTransformContextPtr ctxt, xmlNodePtr cur,689xmlNsPtr ns, xmlNodePtr out)690{691return(xsltGetNamespace(ctxt, cur, ns, out));692}693694/**695* xsltCopyNamespaceList:696* @ctxt: a transformation context697* @node: the target node698* @cur: the first namespace699*700* Do a copy of an namespace list. If @node is non-NULL the701* new namespaces are added automatically. This handles namespaces702* aliases.703* This function is intended only for *internal* use at704* transformation-time for copying ns-declarations of Literal705* Result Elements.706*707* Called by:708* xsltCopyTreeInternal() (transform.c)709* xsltShallowCopyElem() (transform.c)710*711* REVISIT: This function won't be used in the refactored code.712*713* Returns: a new xmlNsPtr, or NULL in case of error.714*/715xmlNsPtr716xsltCopyNamespaceList(xsltTransformContextPtr ctxt, xmlNodePtr node,717xmlNsPtr cur) {718xmlNsPtr ret = NULL, tmp;719xmlNsPtr p = NULL,q;720721if (cur == NULL)722return(NULL);723if (cur->type != XML_NAMESPACE_DECL)724return(NULL);725726/*727* One can add namespaces only on element nodes728*/729if ((node != NULL) && (node->type != XML_ELEMENT_NODE))730node = NULL;731732while (cur != NULL) {733if (cur->type != XML_NAMESPACE_DECL)734break;735736/*737* Avoid duplicating namespace declarations in the tree if738* a matching declaration is in scope.739*/740if (node != NULL) {741if ((node->ns != NULL) &&742(xmlStrEqual(node->ns->prefix, cur->prefix)) &&743(xmlStrEqual(node->ns->href, cur->href))) {744cur = cur->next;745continue;746}747tmp = xmlSearchNs(node->doc, node, cur->prefix);748if ((tmp != NULL) && (xmlStrEqual(tmp->href, cur->href))) {749cur = cur->next;750continue;751}752}753#ifdef XSLT_REFACTORED754/*755* Namespace exclusion and ns-aliasing is performed at756* compilation-time in the refactored code.757*/758q = xmlNewNs(node, cur->href, cur->prefix);759if (p == NULL) {760ret = p = q;761} else {762p->next = q;763p = q;764}765#else766/*767* TODO: Remove this if the refactored code gets enabled.768*/769if (!xmlStrEqual(cur->href, XSLT_NAMESPACE)) {770const xmlChar *URI;771/* TODO apply cascading */772URI = (const xmlChar *) xmlHashLookup(ctxt->style->nsAliases,773cur->href);774if (URI == UNDEFINED_DEFAULT_NS) {775cur = cur->next;776continue;777}778if (URI != NULL) {779q = xmlNewNs(node, URI, cur->prefix);780} else {781q = xmlNewNs(node, cur->href, cur->prefix);782}783if (p == NULL) {784ret = p = q;785} else {786p->next = q;787p = q;788}789}790#endif791cur = cur->next;792}793return(ret);794}795796/**797* xsltCopyNamespace:798* @ctxt: a transformation context799* @elem: the target element node800* @ns: the namespace node801*802* Copies a namespace node (declaration). If @elem is not NULL,803* then the new namespace will be declared on @elem.804*805* Returns: a new xmlNsPtr, or NULL in case of an error.806*/807xmlNsPtr808xsltCopyNamespace(xsltTransformContextPtr ctxt ATTRIBUTE_UNUSED,809xmlNodePtr elem, xmlNsPtr ns)810{811if ((ns == NULL) || (ns->type != XML_NAMESPACE_DECL))812return(NULL);813/*814* One can add namespaces only on element nodes815*/816if ((elem != NULL) && (elem->type != XML_ELEMENT_NODE))817return(xmlNewNs(NULL, ns->href, ns->prefix));818else819return(xmlNewNs(elem, ns->href, ns->prefix));820}821822823/**824* xsltFreeNamespaceAliasHashes:825* @style: an XSLT stylesheet826*827* Free up the memory used by namespaces aliases828*/829void830xsltFreeNamespaceAliasHashes(xsltStylesheetPtr style) {831if (style->nsAliases != NULL)832xmlHashFree((xmlHashTablePtr) style->nsAliases, NULL);833style->nsAliases = NULL;834}835836837