diff --git a/ChangeLog b/ChangeLog index cb54df1a..7e8d829b 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,18 @@ +Mon Mar 4 17:59:29 CET 2002 Daniel Veillard + + * xpath.c: fixing #61290 "namespace nodes have no parent" + long standing divergence from the XPath REC. NodeSets + simply hold a copy of namespace nodes and those node ->next + points to the parent (which may not be the node carrying the + definition). + * include/libxml/xpath.h: flagged but didn't added a possible + speedup + * DOCBparser.c HTMLparser.c: removed some warnings from push + parser due to new state being added. + * tree.c: new fix from Boris Erdmann + * configure.in c14n.c include/libxml/c14n.h testC14N.c: added + the XML Canonalization support from Aleksey Sanin + Sun Mar 3 15:12:42 CET 2002 Daniel Veillard * tree.c: patch from Boris Erdmann fixing some namespace odities diff --git a/DOCBparser.c b/DOCBparser.c index 8b196e9a..0b4012a6 100644 --- a/DOCBparser.c +++ b/DOCBparser.c @@ -5750,6 +5750,16 @@ docbParseTryOrFinish(docbParserCtxtPtr ctxt, int terminate) { "HPP: entering CONTENT\n"); #endif break; + case XML_PARSER_PUBLIC_LITERAL: + xmlGenericError(xmlGenericErrorContext, + "HPP: internal error, state == XML_PARSER_LITERAL\n"); + ctxt->instate = XML_PARSER_CONTENT; + ctxt->checkIndex = 0; +#ifdef DEBUG_PUSH + xmlGenericError(xmlGenericErrorContext, + "HPP: entering CONTENT\n"); +#endif + break; } } done: diff --git a/HTMLparser.c b/HTMLparser.c index 014acc97..24394ead 100644 --- a/HTMLparser.c +++ b/HTMLparser.c @@ -4605,6 +4605,17 @@ htmlParseTryOrFinish(htmlParserCtxtPtr ctxt, int terminate) { "HPP: entering CONTENT\n"); #endif break; + case XML_PARSER_PUBLIC_LITERAL: + xmlGenericError(xmlGenericErrorContext, + "HPP: internal error, state == XML_PARSER_LITERAL\n"); + ctxt->instate = XML_PARSER_CONTENT; + ctxt->checkIndex = 0; +#ifdef DEBUG_PUSH + xmlGenericError(xmlGenericErrorContext, + "HPP: entering CONTENT\n"); +#endif + break; + } } done: diff --git a/Makefile.am b/Makefile.am index b16d7587..7047c0a5 100644 --- a/Makefile.am +++ b/Makefile.am @@ -5,7 +5,8 @@ SUBDIRS = include . doc example python INCLUDES = -I@srcdir@/include -I$(top_builddir)/include @THREAD_CFLAGS@ @Z_CFLAGS@ -noinst_PROGRAMS=testSAX testHTML testXPath testURI testDocbook testThreads +noinst_PROGRAMS=testSAX testHTML testXPath testURI testDocbook testThreads \ + testC14N bin_PROGRAMS = xmllint xmlcatalog @@ -21,14 +22,14 @@ libxml2_la_SOURCES = SAX.c entities.c encoding.c error.c parserInternals.c \ parser.c tree.c hash.c list.c xmlIO.c xmlmemory.c uri.c \ valid.c xlink.c HTMLparser.c HTMLtree.c debugXML.c xpath.c \ xpointer.c xinclude.c nanohttp.c nanoftp.c DOCBparser.c \ - catalog.c globals.c threads.c triostr.c trio.c + catalog.c globals.c threads.c c14n.c triostr.c trio.c else libxml2_la_SOURCES = SAX.c entities.c encoding.c error.c parserInternals.c \ parser.c tree.c hash.c list.c xmlIO.c xmlmemory.c uri.c \ valid.c xlink.c HTMLparser.c HTMLtree.c debugXML.c xpath.c \ xpointer.c xinclude.c nanohttp.c nanoftp.c DOCBparser.c \ - catalog.c globals.c threads.c + catalog.c globals.c threads.c c14n.c endif @@ -70,6 +71,11 @@ testXPath_LDFLAGS = testXPath_DEPENDENCIES = $(DEPS) testXPath_LDADD= $(LDADDS) +testC14N_SOURCES=testC14N.c +testC14N_LDFLAGS = +testC14N_DEPENDENCIES = $(DEPS) +testC14N_LDADD= $(LDADDS) + testThreads_SOURCES=testThreads.c testThreads_LDFLAGS = testThreads_DEPENDENCIES = $(DEPS) diff --git a/c14n.c b/c14n.c new file mode 100644 index 00000000..1db73fc8 --- /dev/null +++ b/c14n.c @@ -0,0 +1,1556 @@ +/* + * "Canonical XML" implementation + * http://www.w3.org/TR/xml-c14n + * + * "Exclusive XML Canonicalization" implementation + * http://www.w3.org/TR/xml-exc-c14n + * + * See Copyright for the status of this software. + * + * Author: Aleksey Sanin + */ +#include "libxml.h" +#ifdef LIBXML_C14N_ENABLED + +#ifdef HAVE_STDLIB_H +#include +#endif +#include + +#include +#include +#include +#include +#include +#include +#include + +/************************************************************************ + * * + * Some declaration better left private ATM * + * * + ************************************************************************/ + +typedef enum { + XMLC14N_BEFORE_DOCUMENT_ELEMENT= 0, + XMLC14N_INSIDE_DOCUMENT_ELEMENT= 1, + XMLC14N_AFTER_DOCUMENT_ELEMENT= 2 +} xmlC14NPosition; + +typedef struct _xmlC14NCtx { + /* input parameters */ + xmlDocPtr doc; + xmlNodeSetPtr visible_nodes; + int with_comments; + xmlOutputBufferPtr buf; + + /* position in the XML document */ + xmlC14NPosition pos; + int parent_is_doc; + + /* exclusive canonicalization */ + int exclusive; + xmlNodeSetPtr ns_rendered; + xmlChar **inclusive_ns_prefixes; +} xmlC14NCtx, *xmlC14NCtxPtr; + + +static int xmlC14NProcessNode (xmlC14NCtxPtr ctx, + xmlNodePtr cur); +static int xmlC14NProcessNodeList (xmlC14NCtxPtr ctx, + xmlNodePtr cur); +typedef enum { + XMLC14N_NORMALIZE_ATTR= 0, + XMLC14N_NORMALIZE_COMMENT= 1, + XMLC14N_NORMALIZE_PI= 2, + XMLC14N_NORMALIZE_TEXT= 3 +} xmlC14NNormalizationMode; + +static xmlChar* xmlC11NNormalizeString (const xmlChar *input, + xmlC14NNormalizationMode mode); + +#define xmlC11NNormalizeAttr( a ) \ + xmlC11NNormalizeString((a), XMLC14N_NORMALIZE_ATTR) +#define xmlC11NNormalizeComment( a ) \ + xmlC11NNormalizeString((a), XMLC14N_NORMALIZE_COMMENT) +#define xmlC11NNormalizePI( a ) \ + xmlC11NNormalizeString((a), XMLC14N_NORMALIZE_PI) +#define xmlC11NNormalizeText( a ) \ + xmlC11NNormalizeString((a), XMLC14N_NORMALIZE_TEXT) + +/************************************************************************ + * * + * The implementation internals * + * * + ************************************************************************/ + +/** + * xmlC14NIsVisible: + * @ctx: the C14N context + * @node: the node to check + * + * Checks whether the given node is visible. If the XML document normalization + * was called for the whole document then it is always "true". + * + * Returns 1 if the node is visible or 0 otherwise. + */ +/* todo: make it a define? */ +static int +xmlC14NIsVisible(xmlC14NCtxPtr ctx, void *node) { + /* + * If the input is an XPath node-set, then the node-set must explicitly + * contain every node to be rendered to the canonical form. + */ + if(ctx->visible_nodes != NULL && + !xmlXPathNodeSetContains(ctx->visible_nodes, (xmlNodePtr)node)) { + return(0); + } + + return(1); +} + +/** + * xmlC14NIsXmlNs: + * @ns: the namespace to check + * + * Checks whether the given namespace is a default "xml:" namespace + * with href="http://www.w3.org/XML/1998/namespace" + * + * Returns 1 if the node is default or 0 otherwise + */ +/* todo: make it a define? */ +static int +xmlC14NIsXmlNs(xmlNsPtr ns) { + return (ns != NULL && + xmlStrEqual(ns->prefix, BAD_CAST "xml") && + xmlStrEqual(ns->href, BAD_CAST "http://www.w3.org/XML/1998/namespace")); +} + +/** + * xmlExcC14NIsRendered: + * @ctx the C14N context + * @ns the namespace to check + * + * Checks whether the given namespace was already rendered or not + * + * Returns 1 if we already wrote this namespace or 0 otherwise + */ +static int +xmlExcC14NIsRendered(xmlC14NCtxPtr ctx, xmlNsPtr ns) { + int i; + + if(ctx == NULL || ctx->ns_rendered == NULL || ns == NULL) { + return(0); + } + + if(ctx->ns_rendered->nodeTab != NULL) { + for(i = ctx->ns_rendered->nodeNr - 1; i >= 0; --i) { + xmlNsPtr ns1 = (xmlNsPtr)ctx->ns_rendered->nodeTab[i]; + if(xmlStrEqual(ns1->prefix, ns->prefix)) { + return (xmlStrEqual(ns1->href, ns->href)); + } + } + } + /* + * if the default namespace xmlns="" is not defined yet then + * we do not want to print it out + */ + return(xmlStrlen(ns->prefix) == 0 && xmlStrlen(ns->href) == 0); +} + +/** + * xmlC14NNamespacesCompare: + * @ns1: the pointer to first namespace + * @ns2: the pointer to second namespace + * + * Compares the namespaces by names (prefixes). + * + * Returns -1 if ns1 < ns2, 0 if ns1 == ns2 or 1 if ns1 > ns2. + */ +static int +xmlC14NNamespacesCompare(xmlNsPtr ns1, xmlNsPtr ns2) { + if(ns1 == ns2) return(0); + if(ns1 == NULL) return(-1); + if(ns2 == NULL) return(1); + + return(xmlStrcmp(ns1->prefix, ns2->prefix)); +} + + +/** + * xmlC14NPrintNamespaces: + * @ns: the pointer to namespace + * @ctx: the C14N context + * + * Prints the given namespace to the output buffer from C14N context. + * + * Returns 1 on success or 0 on fail. + */ +static int +xmlC14NPrintNamespaces(const xmlNsPtr ns, xmlC14NCtxPtr ctx) { + + if(ns == NULL || ctx == NULL) { +#ifdef DEBUG_C14N + xmlGenericError(xmlGenericErrorContext, + "xmlC14NPrintNamespace: namespace or context pointer is null\n"); +#endif + return 0; + } + + if(ns->prefix != NULL) { + xmlOutputBufferWriteString(ctx->buf, " xmlns:"); + xmlOutputBufferWriteString(ctx->buf, (const char *)ns->prefix); + xmlOutputBufferWriteString(ctx->buf, "=\""); + } else { + xmlOutputBufferWriteString(ctx->buf, " xmlns=\""); + } + xmlOutputBufferWriteString(ctx->buf, (const char *) ns->href); + xmlOutputBufferWriteString(ctx->buf, "\""); + return(1); +} + +/** + * xmlC14NProcessNamespacesAxis: + * @ctx: the C14N context + * @node: the current node + * + * Prints out canonical namespace axis of the current node to the + * buffer from C14N context as follows + * + * Canonical XML v 1.0 (http://www.w3.org/TR/xml-c14n) + * + * Namespace Axis + * Consider a list L containing only namespace nodes in the + * axis and in the node-set in lexicographic order (ascending). To begin + * processing L, if the first node is not the default namespace node (a node + * with no namespace URI and no local name), then generate a space followed + * by xmlns="" if and only if the following conditions are met: + * - the element E that owns the axis is in the node-set + * - The nearest ancestor element of E in the node-set has a default + * namespace node in the node-set (default namespace nodes always + * have non-empty values in XPath) + * The latter condition eliminates unnecessary occurrences of xmlns="" in + * the canonical form since an element only receives an xmlns="" if its + * default namespace is empty and if it has an immediate parent in the + * canonical form that has a non-empty default namespace. To finish + * processing L, simply process every namespace node in L, except omit + * namespace node with local name xml, which defines the xml prefix, + * if its string value is http://www.w3.org/XML/1998/namespace. + * + * Exclusive XML Canonicalization v 1.0 (http://www.w3.org/TR/xml-exc-c14n) + * Canonical XML applied to a document subset requires the search of the + * ancestor nodes of each orphan element node for attributes in the xml + * namespace, such as xml:lang and xml:space. These are copied into the + * element node except if a declaration of the same attribute is already + * in the attribute axis of the element (whether or not it is included in + * the document subset). This search and copying are omitted from the + * Exclusive XML Canonicalization method. + * + * Returns 0 on success or -1 on fail. + */ +static int +xmlC14NProcessNamespacesAxis(xmlC14NCtxPtr ctx, xmlNodePtr cur) { + xmlNsPtr ns; + xmlListPtr list; + xmlNodePtr visible_parent; + xmlNsPtr prev; + + if(ctx == NULL || cur == NULL || cur->type != XML_ELEMENT_NODE) { +#ifdef DEBUG_C14N + xmlGenericError(xmlGenericErrorContext, + "xmlC14NProcessNamespacesAxis: Null context or node pointer or type != XML_ELEMENT_NODE.\n"); +#endif + return(-1); + } + + /* + * Create a sorted list to store element namespaces + */ + list = xmlListCreate(NULL, (xmlListDataCompare)xmlC14NNamespacesCompare); + if(list == NULL) { +#ifdef DEBUG_C14N + xmlGenericError(xmlGenericErrorContext, + "xmlC14NProcessNamespacesAxis: list creation failed\n"); +#endif + return(-1); + } + + /* find nearest visible parent */ + visible_parent = cur->parent; + while(visible_parent != NULL && !xmlC14NIsVisible(ctx, visible_parent)) { + visible_parent = visible_parent->parent; + } + + /* + * todo: the libxml XPath implementation does not create + * nodes for all namespaces known to the node (i.e. for namespaces + * defined in node parents). By this we need to now walk thru + * all namespace in current node and all invisible ancesstors + */ + while(cur != visible_parent) { + for(ns = cur->nsDef; ns != NULL; ns = ns->next) { + /* + * first of all ignore default "xml" namespace and + * already included namespace + */ + if(xmlC14NIsXmlNs(ns) || xmlListSearch(list, ns) != NULL) { + continue; + } + + /* + * Lookup nearest namespace after visible parent having + * the same prefix. Namespace included if and only if one of + * the following: + * - another namespace having the same prefix but + * different value found or + * - there is no namespaces having the same prefix and + * it is not a default xmlns="" namespace (empty prefix + * and empty href) + */ + prev = xmlSearchNs(ctx->doc, visible_parent, ns->prefix); + if(prev == NULL && (xmlStrlen(ns->prefix) > 0 || xmlStrlen(ns->href) > 0)) { + xmlListInsert(list, ns); + } else if(prev != NULL && !xmlStrEqual(ns->href, prev->href)) { + xmlListInsert(list, ns); + } + } + cur = cur->parent; + } + + /* + * print out all elements from list + */ + xmlListWalk(list, (xmlListWalker)xmlC14NPrintNamespaces, (const void*)ctx); + + /* + * Cleanup + */ + xmlListDelete(list); + return(0); +} + +/** + * xmlExcC14NProcessNamespacesAxis: + * @ctx: the C14N context + * @node: the current node + * + * Prints out exclusive canonical namespace axis of the current node to the + * buffer from C14N context as follows + * + * Exclusive XML Canonicalization + * http://www.w3.org/TR/xml-exc-c14n + * + * If the element node is in the XPath subset then output the node in + * accordance with Canonical XML except for namespace nodes which are + * rendered as follows: + * + * 1. Render each namespace node iff: + * * it is visibly utilized by the immediate parent element or one of + * its attributes, or is present in InclusiveNamespaces PrefixList, and + * * its prefix and value do not appear in ns_rendered. ns_rendered is + * obtained by popping the state stack in order to obtain a list of + * prefixes and their values which have already been rendered by + * an output ancestor of the namespace node's parent element. + * 2. Append the rendered namespace node to the list ns_rendered of namespace + * nodes rendered by output ancestors. Push ns_rendered on state stack and + * recurse. + * 3. After the recursion returns, pop thestate stack. + * + * + * Returns 0 on success or -1 on fail. + */ +static int +xmlExcC14NProcessNamespacesAxis(xmlC14NCtxPtr ctx, xmlNodePtr cur) { + xmlListPtr list; + xmlAttrPtr attr; + xmlNsPtr ns; + + if(ctx == NULL || cur == NULL || cur->type != XML_ELEMENT_NODE) { +#ifdef DEBUG_C14N + xmlGenericError(xmlGenericErrorContext, + "xmlExcC14NProcessNamespacesAxis: Null context or node pointer or type != XML_ELEMENT_NODE.\n"); +#endif + return(-1); + } + + if(!ctx->exclusive || ctx->ns_rendered == NULL) { +#ifdef DEBUG_C14N + xmlGenericError(xmlGenericErrorContext, + "xmlExcC14NProcessNamespacesAxis: called for non-exclusive canonization or rendered stack is NULL.\n"); +#endif + return(-1); + + } + + /* + * Create a sorted list to store element namespaces + */ + list = xmlListCreate(NULL, (xmlListDataCompare)xmlC14NNamespacesCompare); + if(list == NULL) { +#ifdef DEBUG_C14N + xmlGenericError(xmlGenericErrorContext, + "xmlExcC14NProcessNamespacesAxis: list creation failed\n"); +#endif + return(-1); + } + + /* + * First of all, add all namespaces required by current node + * (i.e. node namespace and all attribute namespaces) + * todo: do we need to check for default "xml:" namespace + */ + ns = (cur->ns != NULL) ? cur->ns : xmlSearchNs(ctx->doc, cur, NULL); + if(ns != NULL && !xmlC14NIsXmlNs(ns) && xmlListSearch(list, ns) == NULL) { + if(!xmlExcC14NIsRendered(ctx, ns)) { + xmlListInsert(list, ns); + xmlXPathNodeSetAdd(ctx->ns_rendered, (xmlNodePtr)ns); + } + } + attr = cur->properties; + while (attr != NULL) { + /* + * todo: do we need to check that attribute is visible and has non + * default namespace + */ + if(xmlC14NIsVisible(ctx, attr)) { + ns = (attr->ns != NULL) ? attr->ns : xmlSearchNs(ctx->doc, cur, NULL); + if(ns != NULL && xmlC14NIsVisible(ctx, attr) && !xmlC14NIsXmlNs(ns)) { + if(xmlListSearch(list, ns) == NULL && + !xmlExcC14NIsRendered(ctx, ns)) { + xmlListInsert(list, ns); + xmlXPathNodeSetAdd(ctx->ns_rendered, (xmlNodePtr)ns); + } + } + } + attr = attr->next; + } + + /* + * Next add all inclusive namespaces if needed. + */ + if(ctx->inclusive_ns_prefixes != NULL) { + int i; + xmlChar *prefix; + + for(i = 0; ctx->inclusive_ns_prefixes[i] != NULL; ++i) { + prefix = ctx->inclusive_ns_prefixes[i]; + /* + * Special values for namespace with empty prefix + */ + if(xmlStrEqual(prefix, BAD_CAST "#default") || xmlStrEqual(prefix, BAD_CAST "")) { + prefix = NULL; + } + ns = xmlSearchNs(ctx->doc, cur, prefix); + if(ns != NULL && !xmlC14NIsXmlNs(ns)) { + if(xmlListSearch(list, ns) == NULL && + !xmlExcC14NIsRendered(ctx, ns)) { + xmlListInsert(list, ns); + xmlXPathNodeSetAdd(ctx->ns_rendered, (xmlNodePtr)ns); + } + } + } + } + + /* + * print out all elements from list + */ + xmlListWalk(list, (xmlListWalker)xmlC14NPrintNamespaces, (const void*)ctx); + + /* + * Cleanup + */ + xmlListDelete(list); + return(0); +} + + +/** + * xmlC14NAttrsCompare: + * @attr1: the pointer to first attr + * @attr2: the pointer to second attr + * + * Prints the given attribute to the output buffer from C14N context. + * + * Returns -1 if attr1 < attr2, 0 if attr1 == attr2 or 1 if attr1 > attr2. + */ +static int +xmlC14NAttrsCompare(xmlAttrPtr attr1, xmlAttrPtr attr2) { + int ret = 0; + + /* + * Simple cases + */ + if(attr1 == attr2) return(0); + if(attr1 == NULL) return(-1); + if(attr2 == NULL) return(1); + if(attr1->ns == attr2->ns) { + return(xmlStrcmp(attr1->name, attr2->name)); + } + + /* + * Attributes in the default namespace are first + * because the default namespace is not applied to + * unqualified attributes + */ + if(attr1->ns == NULL) return(-1); + if(attr2->ns == NULL) return(1); + if(attr1->ns->prefix == NULL) return(-1); + if(attr2->ns->prefix == NULL) return(1); + + ret = xmlStrcmp(attr1->ns->href, attr2->ns->href); + if(ret == 0) { + ret = xmlStrcmp(attr1->name, attr2->name); + } + return(ret); +} + + +/** + * xmlC14NPrintAttrs: + * @attr: the pointer to attr + * @ctx: the C14N context + * + * Prints out canonical attribute urrent node to the + * buffer from C14N context as follows + * + * Canonical XML v 1.0 (http://www.w3.org/TR/xml-c14n) + * + * Returns 1 on success or 0 on fail. + */ +static int +xmlC14NPrintAttrs(const xmlAttrPtr attr, xmlC14NCtxPtr ctx) { + xmlChar *value; + xmlChar *buffer; + + if(attr == NULL || ctx == NULL) { +#ifdef DEBUG_C14N + xmlGenericError(xmlGenericErrorContext, + "xmlC14NPrintAttrs: attr == NULL or ctx == NULL\n"); +#endif + return(0); + } + + xmlOutputBufferWriteString(ctx->buf, " "); + if(attr->ns != NULL && xmlStrlen(attr->ns->prefix) > 0) { + xmlOutputBufferWriteString(ctx->buf, (const char *) attr->ns->prefix); + xmlOutputBufferWriteString(ctx->buf, ":"); + } + xmlOutputBufferWriteString(ctx->buf, (const char *) attr->name); + xmlOutputBufferWriteString(ctx->buf, "=\""); + + value = xmlNodeListGetString(attr->doc, attr->children, 1); + /* todo: should we log an error if value==NULL ? */ + if(value != NULL) { + buffer = xmlC11NNormalizeAttr(value); + xmlFree(value); + if (buffer != NULL) { + xmlOutputBufferWriteString(ctx->buf, (const char *)buffer); + xmlFree(buffer); + } else { +#ifdef DEBUG_C14N + xmlGenericError(xmlGenericErrorContext, + "xmlC14NPrintAttrs: xmlC11NNormalizeAttr failed\n"); +#endif + return(0); + } + } + xmlOutputBufferWriteString(ctx->buf, "\""); + return(1); +} + +/** + * xmlC14NProcessAttrsAxis: + * @ctx: the C14N context + * @cur: the current node + * + * Prints out canonical attribute axis of the current node to the + * buffer from C14N context as follows + * + * Canonical XML v 1.0 (http://www.w3.org/TR/xml-c14n) + * + * Attribute Axis + * In lexicographic order (ascending), process each node that + * is in the element's attribute axis and in the node-set. + * + * The processing of an element node E MUST be modified slightly + * when an XPath node-set is given as input and the element's + * parent is omitted from the node-set. + * + * + * Exclusive XML Canonicalization v 1.0 (http://www.w3.org/TR/xml-exc-c14n) + * + * Canonical XML applied to a document subset requires the search of the + * ancestor nodes of each orphan element node for attributes in the xml + * namespace, such as xml:lang and xml:space. These are copied into the + * element node except if a declaration of the same attribute is already + * in the attribute axis of the element (whether or not it is included in + * the document subset). This search and copying are omitted from the + * Exclusive XML Canonicalization method. + * + * Returns 0 on success or -1 on fail. + */ +static int +xmlC14NProcessAttrsAxis(xmlC14NCtxPtr ctx, xmlNodePtr cur) { + xmlAttrPtr attr; + xmlListPtr list; + + if(ctx == NULL || cur == NULL || cur->type != XML_ELEMENT_NODE) { +#ifdef DEBUG_C14N + xmlGenericError(xmlGenericErrorContext, + "xmlC14NProcessAttrsAxis: Null context or node pointer or type != XML_ELEMENT_NODE.\n"); +#endif + return(-1); + } + + /* + * Create a sorted list to store element attributes + */ + list = xmlListCreate(NULL, (xmlListDataCompare)xmlC14NAttrsCompare); + if(list == NULL) { +#ifdef DEBUG_C14N + xmlGenericError(xmlGenericErrorContext, + "xmlC14NProcessAttrsAxis: list creation failed\n"); +#endif + return(-1); + } + + /* + * Add all visible attributes from current node. + */ + attr = cur->properties; + while (attr != NULL) { + /* check that attribute is visible */ + if(xmlC14NIsVisible(ctx, attr)) { + xmlListInsert(list, attr); + } + attr = attr->next; + } + + /* + * include attributes in "xml" namespace defined in ancestors + * (only for non-exclusive XML Canonicalization) + */ + if(!ctx->exclusive && cur->parent != NULL && !xmlC14NIsVisible(ctx, cur->parent)) { + /* + * If XPath node-set is not specified then the parent is always + * visible! + */ + cur = cur->parent; + while(cur != NULL) { + attr = cur->properties; + while (attr != NULL) { + if(attr->ns != NULL && xmlStrEqual(attr->ns->prefix, BAD_CAST "xml")) { + if(xmlListSearch(list, attr) == NULL) { + xmlListInsert(list, attr); + } + } + attr = attr->next; + } + cur = cur->parent; + } + } + + /* + * print out all elements from list + */ + xmlListWalk(list, (xmlListWalker)xmlC14NPrintAttrs, (const void*)ctx); + + /* + * Cleanup + */ + xmlListDelete(list); + return(0); +} + +/** + * xmlC14NCheckForRelativeNamespaces: + * @ctx: the C14N context + * @cur: the current element node + * + * Checks that current element node has no relative namespaces defined + * + * Returns 0 if the node has no relative namespaces or -1 otherwise. + */ +static int +xmlC14NCheckForRelativeNamespaces(xmlC14NCtxPtr ctx, xmlNodePtr cur) { + xmlNsPtr ns; + + if(ctx == NULL || cur == NULL || cur->type != XML_ELEMENT_NODE) { +#ifdef DEBUG_C14N + xmlGenericError(xmlGenericErrorContext, + "xmlC14NCheckForRelativeNamespaces: Null context or node pointer or type != XML_ELEMENT_NODE.\n"); +#endif + return(-1); + } + + ns = cur->nsDef; + while(ns != NULL) { + if(xmlStrlen(ns->href) > 0) { + xmlURIPtr uri; + + uri = xmlParseURI((const char *) ns->href); + if(uri == NULL) { +#ifdef DEBUG_C14N + xmlGenericError(xmlGenericErrorContext, + "xmlC14NCheckForRelativeNamespaces: unable to parse uri=\"%s\".\n", ns->href); +#endif + return(-1); + } + if(xmlStrlen((const xmlChar *) uri->scheme) == 0) { + xmlFreeURI(uri); + return(-1); + } + if(!xmlStrEqual((const xmlChar *) uri->scheme, BAD_CAST "urn") && xmlStrlen((const xmlChar *) uri->server) == 0) { + xmlFreeURI(uri); + return(-1); + } + xmlFreeURI(uri); + } + ns = ns->next; + } + return(0); +} + +/** + * xmlC14NProcessElementNode: + * @ctx: the pointer to C14N context object + * @cur: the node to process + * + * Canonical XML v 1.0 (http://www.w3.org/TR/xml-c14n) + * + * Element Nodes + * If the element is not in the node-set, then the result is obtained + * by processing the namespace axis, then the attribute axis, then + * processing the child nodes of the element that are in the node-set + * (in document order). If the element is in the node-set, then the result + * is an open angle bracket (<), the element QName, the result of + * processing the namespace axis, the result of processing the attribute + * axis, a close angle bracket (>), the result of processing the child + * nodes of the element that are in the node-set (in document order), an + * open angle bracket, a forward slash (/), the element QName, and a close + * angle bracket. + * + * Returns non-negative value on success or negative value on fail + */ +static int +xmlC14NProcessElementNode(xmlC14NCtxPtr ctx, xmlNodePtr cur, int visible) { + int ret; + int ns_rendered_pos = 0; + + if(ctx == NULL || cur == NULL || cur->type != XML_ELEMENT_NODE) { +#ifdef DEBUG_C14N + xmlGenericError(xmlGenericErrorContext, + "xmlC14NProcessElementNode: Null context or node pointer or type != XML_ELEMENT_NODE.\n"); +#endif + return(-1); + } + + /* + * Check relative relative namespaces: + * implementations of XML canonicalization MUST report an operation + * failure on documents containing relative namespace URIs. + */ + if(xmlC14NCheckForRelativeNamespaces(ctx, cur) < 0) { +#ifdef DEBUG_C14N + xmlGenericError(xmlGenericErrorContext, + "xmlC14NProcessElementNode: xmlC14NCheckForRelativeNamespaces failed.\n"); +#endif + return(-1); + } + + + /* + * Save ns_rendered stack position for exclusive + * processing + */ + if(ctx->exclusive && ctx->ns_rendered != NULL) { + ns_rendered_pos = ctx->ns_rendered->nodeNr; + } + + if(visible) { + if(ctx->parent_is_doc) { + ctx->pos = XMLC14N_INSIDE_DOCUMENT_ELEMENT; + } + xmlOutputBufferWriteString(ctx->buf, "<"); + + if(cur->ns != NULL && xmlStrlen(cur->ns->prefix) > 0) { + xmlOutputBufferWriteString(ctx->buf, (const char *)cur->ns->prefix); + xmlOutputBufferWriteString(ctx->buf, ":"); + } + xmlOutputBufferWriteString(ctx->buf, (const char *)cur->name); + + if(ctx->exclusive) { + ret = xmlExcC14NProcessNamespacesAxis(ctx, cur); + } else { + ret = xmlC14NProcessNamespacesAxis(ctx, cur); + } + if(ret < 0) { +#ifdef DEBUG_C14N + xmlGenericError(xmlGenericErrorContext, + "xmlC14NProcessElementNode: xmlC14NProcessNamespacesAxis failed.\n"); +#endif + return(-1); + } + + ret = xmlC14NProcessAttrsAxis(ctx, cur); + if(ret < 0) { +#ifdef DEBUG_C14N + xmlGenericError(xmlGenericErrorContext, + "xmlC14NProcessElementNode: xmlC14NProcessAttrsAxis failed.\n"); +#endif + return(-1); + } + + xmlOutputBufferWriteString(ctx->buf, ">"); + } + if (cur->children != NULL) { + ret = xmlC14NProcessNodeList(ctx, cur->children); + if(ret < 0) { +#ifdef DEBUG_C14N + xmlGenericError(xmlGenericErrorContext, + "xmlC14NProcessElementNode: xmlC14NProcessNodeList failed.\n"); +#endif + return(-1); + } + } + if(visible) { + xmlOutputBufferWriteString(ctx->buf, "ns != NULL && xmlStrlen(cur->ns->prefix) > 0) { + xmlOutputBufferWriteString(ctx->buf, (const char *)cur->ns->prefix); + xmlOutputBufferWriteString(ctx->buf, ":"); + } + xmlOutputBufferWriteString(ctx->buf, (const char *)cur->name); + xmlOutputBufferWriteString(ctx->buf, ">"); + if(ctx->parent_is_doc) { + ctx->pos = XMLC14N_AFTER_DOCUMENT_ELEMENT; + } + } + + /* + * Restore ns_rendered stack position for exclusive + * processing + */ + if(ctx->exclusive && ctx->ns_rendered != NULL) { + ctx->ns_rendered->nodeNr = ns_rendered_pos; + } + return(0); +} + +/** + * xmlC14NProcessNode: + * @ctx: the pointer to C14N context object + * @cur: the node to process + * + * Processes the given node + * + * Returns non-negative value on success or negative value on fail + */ +static int +xmlC14NProcessNode(xmlC14NCtxPtr ctx, xmlNodePtr cur) { + int ret = 0; + int visible; + + if(ctx == NULL || cur == NULL) { +#ifdef DEBUG_C14N + xmlGenericError(xmlGenericErrorContext, + "xmlC14NProcessNode: Null context or node pointer.\n"); +#endif + return(-1); + } + + visible = xmlC14NIsVisible(ctx, cur); + switch(cur->type) { + case XML_ELEMENT_NODE: + ret = xmlC14NProcessElementNode(ctx, cur, visible); + break; + case XML_CDATA_SECTION_NODE: + case XML_TEXT_NODE: + /* + * Text Nodes + * the string value, except all ampersands are replaced + * by &, all open angle brackets (<) are replaced by <, all closing + * angle brackets (>) are replaced by >, and all #xD characters are + * replaced by . + */ + /* cdata sections are processed as text nodes */ + /* todo: verify that cdata sections are included in XPath nodes set */ + if(visible && cur->content != NULL) { + xmlChar *buffer; + +#ifndef XML_USE_BUFFER_CONTENT + buffer = xmlC11NNormalizeText(cur->content); +#else + buffer = xmlC11NNormalizeText(xmlBufferContent(cur->content)); +#endif + if(buffer != NULL) { + xmlOutputBufferWriteString(ctx->buf, (const char *)buffer); + xmlFree(buffer); + } else { +#ifdef DEBUG_C14N + xmlGenericError(xmlGenericErrorContext, + "xmlC14NProcessNode: xmlC11NNormalizeText() failed\n"); +#endif + return(-1); + } + } + break; + case XML_PI_NODE: + /* + * Processing Instruction (PI) Nodes- + * The opening PI symbol (). If the string value is empty, + * then the leading space is not added. Also, a trailing #xA is + * rendered after the closing PI symbol for PI children of the + * root node with a lesser document order than the document + * element, and a leading #xA is rendered before the opening PI + * symbol of PI children of the root node with a greater document + * order than the document element. + */ + if(visible) { + if(ctx->pos == XMLC14N_AFTER_DOCUMENT_ELEMENT) { + xmlOutputBufferWriteString(ctx->buf, "\x0Abuf, "buf, (const char *)cur->name); + if(cur->content != NULL && *(cur->content) != '\0') { + xmlChar *buffer; + xmlOutputBufferWriteString(ctx->buf, " "); + + /* todo: do we need to normalize pi? */ +#ifndef XML_USE_BUFFER_CONTENT + buffer = xmlC11NNormalizePI(cur->content); +#else + buffer = xmlC11NNormalizePI(xmlBufferContent(cur->content)); +#endif + if (buffer != NULL) { + xmlOutputBufferWriteString(ctx->buf, (const char *)buffer); + xmlFree(buffer); + } else { +#ifdef DEBUG_C14N + xmlGenericError(xmlGenericErrorContext, + "xmlC14NProcessNode: xmlC11NNormalizePI() failed\n"); +#endif + return(-1); + } + } + + if(ctx->pos == XMLC14N_BEFORE_DOCUMENT_ELEMENT) { + xmlOutputBufferWriteString(ctx->buf, "?>\x0A"); + } else { + xmlOutputBufferWriteString(ctx->buf, "?>"); + } + } + break; + case XML_COMMENT_NODE: + /* + * Comment Nodes + * Nothing if generating canonical XML without comments. For + * canonical XML with comments, generate the opening comment + * symbol (). Also, a trailing #xA is rendered + * after the closing comment symbol for comment children of the + * root node with a lesser document order than the document + * element, and a leading #xA is rendered before the opening + * comment symbol of comment children of the root node with a + * greater document order than the document element. (Comment + * children of the root node represent comments outside of the + * top-level document element and outside of the document type + * declaration). + */ + if(visible && ctx->with_comments) { + if(ctx->pos == XMLC14N_AFTER_DOCUMENT_ELEMENT) { + xmlOutputBufferWriteString(ctx->buf, "\x0A\x0A"); + } else { + xmlOutputBufferWriteString(ctx->buf, "-->"); + } + } + break; + case XML_DOCUMENT_NODE: + case XML_DOCUMENT_FRAG_NODE: /* should be processed as document? */ + case XML_DOCB_DOCUMENT_NODE: /* should be processed as document? */ + case XML_HTML_DOCUMENT_NODE: /* should be processed as document? */ + if (cur->children != NULL) { + ctx->pos = XMLC14N_BEFORE_DOCUMENT_ELEMENT; + ctx->parent_is_doc = 1; + ret = xmlC14NProcessNodeList(ctx, cur->children); + } + break; + + case XML_ATTRIBUTE_NODE: + xmlGenericError(xmlGenericErrorContext, + "xmlC14NProcessNode: XML_ATTRIBUTE_NODE is illegal here\n"); + return(-1); + case XML_NAMESPACE_DECL: + xmlGenericError(xmlGenericErrorContext, + "xmlC14NProcessNode: XML_NAMESPACE_DECL is illegal here\n"); + return(-1); + case XML_ENTITY_REF_NODE: + xmlGenericError(xmlGenericErrorContext, + "xmlC14NProcessNode: XML_ENTITY_REF_NODE is illegal here\n"); + return(-1); + case XML_ENTITY_NODE: + xmlGenericError(xmlGenericErrorContext, + "xmlC14NProcessNode: XML_ENTITY_NODE is illegal here\n"); + return(-1); + + case XML_DOCUMENT_TYPE_NODE: + case XML_NOTATION_NODE: + case XML_DTD_NODE: + case XML_ELEMENT_DECL: + case XML_ATTRIBUTE_DECL: + case XML_ENTITY_DECL: + case XML_XINCLUDE_START: + case XML_XINCLUDE_END: + /* + * should be ignored according to "W3C Canonical XML" + */ + break; + default: +#ifdef DEBUG_C14N + xmlGenericError(xmlGenericErrorContext, + "xmlC14NProcessNode: unknown node type = %d\n", cur->type); +#endif + return(-1); + } + + return(ret); +} + +/** + * xmlC14NProcessNodeList: + * @ctx: the pointer to C14N context object + * @cur: the node to start from + * + * Processes all nodes in the row starting from cur. + * + * Returns non-negative value on success or negative value on fail + */ +static int +xmlC14NProcessNodeList(xmlC14NCtxPtr ctx, xmlNodePtr cur) { + int ret; + + if(ctx == NULL) { +#ifdef DEBUG_C14N + xmlGenericError(xmlGenericErrorContext, + "xmlC14NProcessNodeList: Null context pointer.\n"); +#endif + return(-1); + } + + for(ret = 0; cur != NULL && ret >= 0; cur = cur->next) { + ret = xmlC14NProcessNode(ctx, cur); + } + return(ret); +} + + +/** + * xmlC14NFreeCtx: + * @ctx: the pointer to C14N context object + * + * Cleanups the C14N context object. + */ + +static void +xmlC14NFreeCtx(xmlC14NCtxPtr ctx) { + if(ctx == NULL) { +#ifdef DEBUG_C14N + xmlGenericError(xmlGenericErrorContext, + "xmlC14NFreeCtx: ctx == NULL\n"); +#endif + return; + } + + if(ctx->ns_rendered != NULL) { + xmlXPathFreeNodeSet(ctx->ns_rendered); + } + xmlFree(ctx); +} + +/** + * xmlC14NNewCtx: + * @doc: the XML document for canonization + * @nodes: the nodes set to be included in the canonized image + * or NULL if all document nodes should be included + * @exclusive: the exclusive flag (0 - non-exclusive canonicalization; + * otherwise - exclusive canonicalization) + * @inclusive_ns_prefixe the list of inclusive namespace prefixes + * ended with a NULL or NULL if there is no + * inclusive namespaces (only for exclusive + * canonicalization) + * @with_comments: include comments in the result (!=0) or not (==0) + * @buf: the output buffer to store canonical XML; this + * buffer MUST have encoder==NULL because C14N requires + * UTF-8 output + * + * Creates new C14N context object to store C14N parameters. + * + * Returns pointer to newly created object (success) or NULL (fail) + */ +static xmlC14NCtxPtr +xmlC14NNewCtx(xmlDocPtr doc, xmlNodeSetPtr nodes, + int exclusive, xmlChar **inclusive_ns_prefixes, + int with_comments, + xmlOutputBufferPtr buf) { + xmlC14NCtxPtr ctx; + + if(doc == NULL || buf == NULL) { +#ifdef DEBUG_C14N + xmlGenericError(xmlGenericErrorContext, + "xmlC14NNewCtx: pointer to document or output buffer is NULL\n"); +#endif + return(NULL); + } + + /* + * Validate the encoding output buffer encoding + */ + if(buf->encoder != NULL) { + xmlGenericError(xmlGenericErrorContext, + "xmlC14NNewCtx: output buffer encoder != NULL but C14N requires UTF8 output\n"); + return(NULL); + } + + /* + * Validate the XML document encoding value, if provided. + */ + if(doc->charset != XML_CHAR_ENCODING_UTF8) { + xmlGenericError(xmlGenericErrorContext, + "xmlC14NNewCtx: source document not in UTF8\n"); + return(NULL); + } + + /* + * Allocate a new xmlC14NCtxPtr and fill the fields. + */ + ctx = (xmlC14NCtxPtr) xmlMalloc(sizeof(xmlC14NCtx)); + if (ctx == NULL) { + xmlGenericError(xmlGenericErrorContext, + "xmlC14NNewCtx: malloc failed\n"); + return(NULL); + } + memset(ctx, 0, sizeof(xmlC14NCtx)); + + /* + * initialize C14N context + */ + ctx->doc = doc; + ctx->with_comments = with_comments; + ctx->visible_nodes = nodes; + ctx->buf = buf; + ctx->parent_is_doc = 1; + ctx->pos = XMLC14N_BEFORE_DOCUMENT_ELEMENT; + + /* + * Set "exclusive" flag, create a nodes set for namespaces + * stack and remember list of incluseve prefixes + */ + if(exclusive) { + ctx->exclusive = 1; + ctx->ns_rendered = xmlXPathNodeSetCreate(NULL); + ctx->inclusive_ns_prefixes = inclusive_ns_prefixes; + } + return(ctx); +} + +/** + * xmlC14NDocSaveTo: + * @doc: the XML document for canonization + * @nodes: the nodes set to be included in the canonized image + * or NULL if all document nodes should be included + * @exclusive: the exclusive flag (0 - non-exclusive canonicalization; + * otherwise - exclusive canonicalization) + * @inclusive_ns_prefixe the list of inclusive namespace prefixes + * ended with a NULL or NULL if there is no + * inclusive namespaces (only for exclusive + * canonicalization, ignored otherwise) + * @with_comments: include comments in the result (!=0) or not (==0) + * @buf: the output buffer to store canonical XML; this + * buffer MUST have encoder==NULL because C14N requires + * UTF-8 output + * + * Dumps the canonized image of given XML document into the provided buffer. + * For details see "Canonical XML" (http://www.w3.org/TR/xml-c14n) or + * "Exclusive XML Canonicalization" (http://www.w3.org/TR/xml-exc-c14n) + * + * Returns non-negative value on success or a negative value on fail + */ +int +xmlC14NDocSaveTo(xmlDocPtr doc, xmlNodeSetPtr nodes, + int exclusive, xmlChar **inclusive_ns_prefixes, + int with_comments, + xmlOutputBufferPtr buf) { + xmlC14NCtxPtr ctx; + int ret; + + if(buf == NULL || doc == NULL) { +#ifdef DEBUG_C14N + xmlGenericError(xmlGenericErrorContext, + "xmlC14NDocSaveTo: null return buffer or doc pointer\n"); +#endif + return(-1); + } + + /* + * Validate the encoding output buffer encoding + */ + if(buf->encoder != NULL) { + xmlGenericError(xmlGenericErrorContext, + "xmlC14NDocSaveTo: output buffer encoder != NULL but C14N requires UTF8 output\n"); + return(-1); + } + + ctx = xmlC14NNewCtx(doc, nodes, exclusive, inclusive_ns_prefixes, + with_comments, buf); + if(ctx == NULL) { + xmlGenericError(xmlGenericErrorContext, + "xmlC14NDocSaveTo: unable to create C14N context\n"); + return(-1); + } + + + + /* + * Root Node + * The root node is the parent of the top-level document element. The + * result of processing each of its child nodes that is in the node-set + * in document order. The root node does not generate a byte order mark, + * XML declaration, nor anything from within the document type + * declaration. + */ + if(doc->children != NULL) { + ret = xmlC14NProcessNodeList(ctx, doc->children); + if(ret < 0) { +#ifdef DEBUG_C14N + xmlGenericError(xmlGenericErrorContext, + "xmlC14NDocSaveTo: process childrens' list failed.\n"); +#endif + xmlC14NFreeCtx(ctx); + return(-1); + } + } + + /* + * Flush buffer to get number of bytes written + */ + ret = xmlOutputBufferFlush(buf); + if(ret < 0) { +#ifdef DEBUG_C14N + xmlGenericError(xmlGenericErrorContext, + "xmlC14NDocSaveTo: buffer flush failed.\n"); +#endif + xmlC14NFreeCtx(ctx); + return(-1); + } + + /* + * Cleanup + */ + xmlC14NFreeCtx(ctx); + return(ret); +} + +/** + * xmlC14NDocDumpMemory: + * @doc: the XML document for canonization + * @nodes: the nodes set to be included in the canonized image + * or NULL if all document nodes should be included + * @exclusive: the exclusive flag (0 - non-exclusive canonicalization; + * otherwise - exclusive canonicalization) + * @inclusive_ns_prefixe the list of inclusive namespace prefixes + * ended with a NULL or NULL if there is no + * inclusive namespaces (only for exclusive + * canonicalization, ignored otherwise) + * @with_comments: include comments in the result (!=0) or not (==0) + * @doc_txt_ptr: the memory pointer for allocated canonical XML text; + * the caller of this functions is responsible for calling + * xmlFree() to free allocated memory + * + * Dumps the canonized image of given XML document into memory. + * For details see "Canonical XML" (http://www.w3.org/TR/xml-c14n) or + * "Exclusive XML Canonicalization" (http://www.w3.org/TR/xml-exc-c14n) + * + * Returns the number of bytes written on success or a negative value on fail + */ +int +xmlC14NDocDumpMemory(xmlDocPtr doc, xmlNodeSetPtr nodes, + int exclusive, xmlChar **inclusive_ns_prefixes, + int with_comments, + xmlChar **doc_txt_ptr) { + int ret; + xmlOutputBufferPtr buf; + + if (doc_txt_ptr == NULL) { +#ifdef DEBUG_C14N + xmlGenericError(xmlGenericErrorContext, + "xmlC14NDocDumpMemory: null return buffer pointer\n"); +#endif + return(-1); + } + + *doc_txt_ptr = NULL; + + /* + * create memory buffer with UTF8 (default) encoding + */ + buf = xmlAllocOutputBuffer(NULL); + if(buf == NULL ) { +#ifdef DEBUG_C14N + xmlGenericError(xmlGenericErrorContext, + "xmlC14NDocDumpMemory: failed to allocate output buffer.\n"); +#endif + return(-1); + } + + /* + * canonize document and write to buffer + */ + ret = xmlC14NDocSaveTo(doc, nodes, exclusive, inclusive_ns_prefixes, + with_comments, buf); + if(ret < 0) { +#ifdef DEBUG_C14N + xmlGenericError(xmlGenericErrorContext, + "xmlC14NDocDumpMemory: xmlC14NDocSaveTo failed.\n"); +#endif + (void)xmlOutputBufferClose(buf); + return(-1); + } + + ret = buf->buffer->use; + if(ret > 0) { + *doc_txt_ptr = xmlStrndup(buf->buffer->content, ret); + } + (void)xmlOutputBufferClose(buf); + + if(*doc_txt_ptr == NULL && ret > 0) { +#ifdef DEBUG_C14N + xmlGenericError(xmlGenericErrorContext, + "xmlC14NDocDumpMemory: failed to allocate memory for document text representation\n"); +#endif + return(-1); + } + return(ret); +} + +/** + * xmlC14NDocSave: + * @doc: the XML document for canonization + * @nodes: the nodes set to be included in the canonized image + * or NULL if all document nodes should be included + * @exclusive: the exclusive flag (0 - non-exclusive canonicalization; + * otherwise - exclusive canonicalization) + * @inclusive_ns_prefixe the list of inclusive namespace prefixes + * ended with a NULL or NULL if there is no + * inclusive namespaces (only for exclusive + * canonicalization, ignored otherwise) + * @with_comments: include comments in the result (!=0) or not (==0) + * @filename: the filename to store canonical XML image + * @compression: the compression level (zlib requred): + * -1 - libxml default, + * 0 - uncompressed, + * >0 - compression level + * + * Dumps the canonized image of given XML document into the file. + * For details see "Canonical XML" (http://www.w3.org/TR/xml-c14n) or + * "Exclusive XML Canonicalization" (http://www.w3.org/TR/xml-exc-c14n) + * + * Returns the number of bytes written success or a negative value on fail + */ +int +xmlC14NDocSave(xmlDocPtr doc, xmlNodeSetPtr nodes, + int exclusive, xmlChar **inclusive_ns_prefixes, + int with_comments, + const char* filename, int compression) { + xmlOutputBufferPtr buf; + int ret; + + if(filename == NULL) { +#ifdef DEBUG_C14N + xmlGenericError(xmlGenericErrorContext, + "xmlC14NDocSave: filename is NULL\n"); +#endif + return(-1); + } + +#ifdef HAVE_ZLIB_H + if(compression < 0) compression = xmlGetCompressMode(); +#endif + + /* + * save the content to a temp buffer, use default UTF8 encoding. + */ + buf = xmlOutputBufferCreateFilename(filename, NULL, compression); + if (buf == NULL) { +#ifdef DEBUG_C14N + xmlGenericError(xmlGenericErrorContext, + "xmlC14NDocSave: unable to create buffer for file=\"%s\" with compressin=%d\n", filename, compression); +#endif + return(-1); + } + + /* + * canonize document and write to buffer + */ + ret = xmlC14NDocSaveTo(doc, nodes, exclusive, inclusive_ns_prefixes, + with_comments, buf); + if(ret < 0) { +#ifdef DEBUG_C14N + xmlGenericError(xmlGenericErrorContext, + "xmlC14NDocSave: xmlC14NDocSaveTo failed.\n"); +#endif + (void)xmlOutputBufferClose(buf); + return(-1); + } + + /* + * get the numbers of bytes written + */ + ret = xmlOutputBufferClose(buf); + return(ret); +} + + + +/* + * Macro used to grow the current buffer. + */ +#define growBufferReentrant() { \ + buffer_size *= 2; \ + buffer = (xmlChar *) \ + xmlRealloc(buffer, buffer_size * sizeof(xmlChar)); \ + if (buffer == NULL) { \ + perror("realloc failed"); \ + return(NULL); \ + } \ +} + +/** + * xmlC11NNormalizeString: + * @input: the input string + * @mode: the normalization mode (attribute, comment, PI or text) + * + * Converts a string to a canonical (normalized) format. The code is stolen + * from xmlEncodeEntitiesReentrant(). Added normalization of \x09, \x0a, \x0A + * and the @mode parameter + * + * Returns a normalized string (caller is responsible for calling xmlFree()) + * or NULL if an error occurs + */ +static xmlChar * +xmlC11NNormalizeString(const xmlChar *input, xmlC14NNormalizationMode mode) { + const xmlChar *cur = input; + xmlChar *buffer = NULL; + xmlChar *out = NULL; + int buffer_size = 0; + + if (input == NULL) return(NULL); + + /* + * allocate an translation buffer. + */ + buffer_size = 1000; + buffer = (xmlChar *) xmlMalloc(buffer_size * sizeof(xmlChar)); + if (buffer == NULL) { + perror("malloc failed"); + return(NULL); + } + out = buffer; + + while (*cur != '\0') { + if (out - buffer > buffer_size - 10) { + int indx = out - buffer; + + growBufferReentrant(); + out = &buffer[indx]; + } + + if (*cur == '<' && (mode == XMLC14N_NORMALIZE_ATTR || + mode == XMLC14N_NORMALIZE_TEXT)) { + *out++ = '&'; + *out++ = 'l'; + *out++ = 't'; + *out++ = ';'; + } else if (*cur == '>' && (mode == XMLC14N_NORMALIZE_TEXT)) { + *out++ = '&'; + *out++ = 'g'; + *out++ = 't'; + *out++ = ';'; + } else if (*cur == '&' && (mode == XMLC14N_NORMALIZE_ATTR || + mode == XMLC14N_NORMALIZE_TEXT)) { + *out++ = '&'; + *out++ = 'a'; + *out++ = 'm'; + *out++ = 'p'; + *out++ = ';'; + } else if (*cur == '"' && (mode == XMLC14N_NORMALIZE_ATTR)) { + *out++ = '&'; + *out++ = 'q'; + *out++ = 'u'; + *out++ = 'o'; + *out++ = 't'; + *out++ = ';'; + } else if (*cur == '\x09' && (mode == XMLC14N_NORMALIZE_ATTR)) { + *out++ = '&'; + *out++ = '#'; + *out++ = 'x'; + *out++ = '9'; + *out++ = ';'; + } else if (*cur == '\x0A' && (mode == XMLC14N_NORMALIZE_ATTR)) { + *out++ = '&'; + *out++ = '#'; + *out++ = 'x'; + *out++ = 'A'; + *out++ = ';'; + } else if (*cur == '\x0D' && (mode == XMLC14N_NORMALIZE_ATTR || + mode == XMLC14N_NORMALIZE_TEXT || + mode == XMLC14N_NORMALIZE_COMMENT || + mode == XMLC14N_NORMALIZE_PI)) { + *out++ = '&'; + *out++ = '#'; + *out++ = 'x'; + *out++ = 'D'; + *out++ = ';'; + } else { + /* + * Works because on UTF-8, all extended sequences cannot + * result in bytes in the ASCII range. + */ + *out++ = *cur; + } + cur++; + } + *out++ = 0; + return(buffer); +} + +#endif /* LIBXML_C14N_ENABLED */ diff --git a/configure.in b/configure.in index 765f02ab..2319c2cb 100644 --- a/configure.in +++ b/configure.in @@ -420,6 +420,7 @@ AC_ARG_WITH(xpath, [ --with-xpath Add the XPATH support (on)]) if test "$with_xpath" = "no" ; then echo Disabling XPATH support with_xptr="no" + with-c14n="no" WITH_XPATH=0 XPATH_OBJ= else @@ -441,6 +442,18 @@ fi AC_SUBST(WITH_XPTR) AC_SUBST(XPTR_OBJ) +AC_ARG_WITH(c14n, [ --with-c14n Add the Canonicalization support (on)]) +if test "$with_c14n" = "no" ; then + echo Disabling C14N support + WITH_C14N=0 + C14N_OBJ= +else + WITH_C14N=1 + C14N_OBJ="c14n.c" +fi +AC_SUBST(WITH_C14N) +AC_SUBST(C14N_OBJ) + AC_ARG_WITH(xinclude, [ --with-xinclude Add the XInclude support (on)]) if test "$with_xinclude" = "no" ; then echo Disabling XInclude support diff --git a/include/libxml/c14n.h b/include/libxml/c14n.h new file mode 100644 index 00000000..43e53040 --- /dev/null +++ b/include/libxml/c14n.h @@ -0,0 +1,74 @@ +/* + * "Canonical XML" implementation + * http://www.w3.org/TR/xml-c14n + * + * "Exclusive XML Canonicalization" implementation + * http://www.w3.org/TR/xml-exc-c14n + + * See Copyright for the status of this software. + * + * Author: Aleksey Sanin + */ +#ifndef __XML_C14N_H__ +#define __XML_C14N_H__ + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#include +#include + +/* + * XML Canonicazation + * http://www.w3.org/TR/xml-c14n + * + * Exclusive XML Canonicazation + * http://www.w3.org/TR/xml-exc-c14n + * + * Canonical form of an XML document could be created if and only if + * a) default attributes (if any) are added to all nodes + * b) all character and parsed entity references are resolved + * In order to achive this in libxml2 the document MUST be loaded with + * following global setings: + * + * xmlLoadExtDtdDefaultValue = XML_DETECT_IDS | XML_COMPLETE_ATTRS; + * xmlSubstituteEntitiesDefault(1); + * + * or corresponding parser context setting: + * xmlParserCtxtPtr ctxt; + * + * ... + * ctxt->loadsubset = XML_DETECT_IDS | XML_COMPLETE_ATTRS; + * ctxt->replaceEntities = 1; + * ... + */ +int xmlC14NDocSaveTo (xmlDocPtr doc, + xmlNodeSetPtr nodes, + int exclusive, + xmlChar **inclusive_ns_prefixes, + int with_comments, + xmlOutputBufferPtr buf); + +int xmlC14NDocDumpMemory (xmlDocPtr doc, + xmlNodeSetPtr nodes, + int exclusive, + xmlChar **inclusive_ns_prefixes, + int with_comments, + xmlChar **doc_txt_ptr); + +int xmlC14NDocSave (xmlDocPtr doc, + xmlNodeSetPtr nodes, + int exclusive, + xmlChar **inclusive_ns_prefixes, + int with_comments, + const char* filename, + int compression); + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* __XML_C14N_H__ */ + diff --git a/include/libxml/xmlversion.h.in b/include/libxml/xmlversion.h.in index ef84c0d6..7b273c18 100644 --- a/include/libxml/xmlversion.h.in +++ b/include/libxml/xmlversion.h.in @@ -108,6 +108,15 @@ extern void xmlCheckVersion(int version); #define LIBXML_HTML_ENABLED #endif +/** + * LIBXML_C14N_ENABLED: + * + * Whether the Canonicalization support is configured in + */ +#if @WITH_C14N@ +#define LIBXML_C14N_ENABLED +#endif + /** * LIBXML_CATALOG_ENABLED: * diff --git a/include/libxml/xmlwin32version.h b/include/libxml/xmlwin32version.h index 40caf798..6a51a286 100644 --- a/include/libxml/xmlwin32version.h +++ b/include/libxml/xmlwin32version.h @@ -139,6 +139,15 @@ extern void xmlCheckVersion(int version); #define LIBXML_XPTR_ENABLED #endif +/** + * LIBXML_C14N_ENABLED: + * + * Whether the Canonicalization support is configured in + */ +#if 0 +#define LIBXML_C14N_ENABLED +#endif + /** * LIBXML_XINCLUDE_ENABLED: * diff --git a/include/libxml/xmlwin32version.h.in b/include/libxml/xmlwin32version.h.in index 614667ff..6d83fa3f 100644 --- a/include/libxml/xmlwin32version.h.in +++ b/include/libxml/xmlwin32version.h.in @@ -139,6 +139,15 @@ extern void xmlCheckVersion(int version); #define LIBXML_XPTR_ENABLED #endif +/** + * LIBXML_C14N_ENABLED: + * + * Whether the Canonicalization support is configured in + */ +#if 0 +#define LIBXML_C14N_ENABLED +#endif + /** * LIBXML_XINCLUDE_ENABLED: * diff --git a/include/libxml/xpath.h b/include/libxml/xpath.h index 00281d61..ef77e134 100644 --- a/include/libxml/xpath.h +++ b/include/libxml/xpath.h @@ -62,6 +62,7 @@ struct _xmlNodeSet { int nodeNr; /* number of nodes in the set */ int nodeMax; /* size of the array as allocated */ xmlNodePtr *nodeTab; /* array of nodes in no particular order */ + /* @@ with_ns to check wether namespace nodes should be looked at @@ */ }; /* diff --git a/testC14N.c b/testC14N.c new file mode 100644 index 00000000..9bd9e432 --- /dev/null +++ b/testC14N.c @@ -0,0 +1,333 @@ +/* + * Canonical XML implementation test program + * (http://www.w3.org/TR/2001/REC-xml-c14n-20010315) + * + * See Copyright for the status of this software. + * + * Author: Aleksey Sanin + */ +#include "libxml.h" +#if defined(LIBXML_C14N_ENABLED) + +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif +#ifdef HAVE_STDLIB_H +#include +#endif + +#include +#include +#include +#include + +#include + + +static void usage(const char *name) { + fprintf(stderr, + "Usage: %s [] []\n", + name); + fprintf(stderr, "where is one of following:\n"); + fprintf(stderr, + "--with-comments \t XML file canonization w comments\n"); + fprintf(stderr, + "--without-comments \t XML file canonization w/o comments\n"); + fprintf(stderr, + "--exc-with-comments \t Exclusive XML file canonization w comments\n"); + fprintf(stderr, + "--exc-without-comments\t Exclusive XML file canonization w/o comments\n"); +} + +xmlXPathObjectPtr +load_xpath_expr (xmlDocPtr parent_doc, const char* filename); + +xmlChar **parse_list(xmlChar *str); + +void +print_xpath_nodes(xmlXPathObjectPtr ptr); + +static int +test_c14n(const char* xml_filename, int with_comments, int exclusive, + const char* xpath_filename, xmlChar **inclusive_namespaces) { + xmlDocPtr doc; + xmlXPathObjectPtr xpath = NULL; + xmlChar *result = NULL; + int ret; + + /* + * build an XML tree from a the file; we need to add default + * attributes and resolve all character and entities references + */ + xmlLoadExtDtdDefaultValue = XML_DETECT_IDS | XML_COMPLETE_ATTRS; + xmlSubstituteEntitiesDefault(1); + + doc = xmlParseFile(xml_filename); + if (doc == NULL) { + fprintf(stderr, "Error: unable to parse file \"%s\"\n", xml_filename); + return(-1); + } + + /* + * Check the document is of the right kind + */ + if(xmlDocGetRootElement(doc) == NULL) { + fprintf(stderr,"Error: empty document for file \"%s\"\n", xml_filename); + xmlFreeDoc(doc); + return(-1); + } + + /* + * load xpath file if specified + */ + if(xpath_filename) { + xpath = load_xpath_expr(doc, xpath_filename); + if(xpath == NULL) { + fprintf(stderr,"Error: unable to evaluate xpath expression\n"); + xmlFreeDoc(doc); + return(-1); + } + } + + /* + * Canonical form + */ + /* fprintf(stderr,"File \"%s\" loaded: start canonization\n", xml_filename); */ + ret = xmlC14NDocDumpMemory(doc, + (xpath) ? xpath->nodesetval : NULL, + exclusive, inclusive_namespaces, + with_comments, &result); + if(ret >= 0) { + if(result != NULL) { + write(1, result, ret); + xmlFree(result); + } + } else { + fprintf(stderr,"Error: failed to canonicalize XML file \"%s\" (ret=%d)\n", xml_filename, ret); + if(result != NULL) xmlFree(result); + xmlFreeDoc(doc); + return(-1); + } + + /* + * Cleanup + */ + if(xpath != NULL) xmlXPathFreeObject(xpath); + xmlFreeDoc(doc); + + return(ret); +} + +int main(int argc, char **argv) { + int ret = -1; + + /* + * Init libxml + */ + xmlInitParser(); + LIBXML_TEST_VERSION + + /* + * Parse command line and process file + */ + if( argc < 3 ) { + fprintf(stderr, "Error: wrong number of arguments.\n"); + usage(argv[0]); + } else if(strcmp(argv[1], "--with-comments") == 0) { + ret = test_c14n(argv[2], 1, 0, (argc > 3) ? argv[3] : NULL, NULL); + } else if(strcmp(argv[1], "--without-comments") == 0) { + ret = test_c14n(argv[2], 0, 0, (argc > 3) ? argv[3] : NULL, NULL); + } else if(strcmp(argv[1], "--exc-with-comments") == 0) { + xmlChar **list; + + /* load exclusive namespace from command line */ + list = (argc > 4) ? parse_list((xmlChar *)argv[4]) : NULL; + ret = test_c14n(argv[2], 1, 1, (argc > 3) ? argv[3] : NULL, list); + if(list != NULL) xmlFree(list); + } else if(strcmp(argv[1], "--exc-without-comments") == 0) { + xmlChar **list; + + /* load exclusive namespace from command line */ + list = (argc > 4) ? parse_list((xmlChar *)argv[4]) : NULL; + ret = test_c14n(argv[2], 0, 1, (argc > 3) ? argv[3] : NULL, list); + if(list != NULL) xmlFree(list); + } else { + fprintf(stderr, "Error: bad option.\n"); + usage(argv[0]); + } + + /* + * Shutdown libxml + */ + xmlCleanupParser(); + xmlMemoryDump(); + + return((ret >= 0) ? 0 : 1); +} + +/* + * Macro used to grow the current buffer. + */ +#define growBufferReentrant() { \ + buffer_size *= 2; \ + buffer = (xmlChar **) \ + xmlRealloc(buffer, buffer_size * sizeof(xmlChar*)); \ + if (buffer == NULL) { \ + perror("realloc failed"); \ + return(NULL); \ + } \ +} + +xmlChar **parse_list(xmlChar *str) { + xmlChar **buffer; + xmlChar **out = NULL; + int buffer_size = 0; + + if(str == NULL) { + return(NULL); + } + + /* + * allocate an translation buffer. + */ + buffer_size = 1000; + buffer = (xmlChar **) xmlMalloc(buffer_size * sizeof(xmlChar*)); + if (buffer == NULL) { + perror("malloc failed"); + return(NULL); + } + out = buffer; + + while(*str != '\0') { + if (out - buffer > buffer_size - 10) { + int indx = out - buffer; + + growBufferReentrant(); + out = &buffer[indx]; + } + (*out++) = str; + while(*str != ',' && *str != '\0') ++str; + if(*str == ',') *(str++) = '\0'; + } + (*out) = NULL; + return buffer; +} + +xmlXPathObjectPtr +load_xpath_expr (xmlDocPtr parent_doc, const char* filename) { + xmlXPathObjectPtr xpath; + xmlDocPtr doc; + xmlChar *expr; + xmlXPathContextPtr ctx; + xmlNodePtr node; + xmlNsPtr ns; + + /* + * load XPath expr as a file + */ + xmlLoadExtDtdDefaultValue = XML_DETECT_IDS | XML_COMPLETE_ATTRS; + xmlSubstituteEntitiesDefault(1); + + doc = xmlParseFile(filename); + if (doc == NULL) { + fprintf(stderr, "Error: unable to parse file \"%s\"\n", filename); + return(NULL); + } + + /* + * Check the document is of the right kind + */ + if(xmlDocGetRootElement(doc) == NULL) { + fprintf(stderr,"Error: empty document for file \"%s\"\n", filename); + xmlFreeDoc(doc); + return(NULL); + } + + node = doc->children; + while(node != NULL && !xmlStrEqual(node->name, (const xmlChar *)"XPath")) { + node = node->next; + } + + if(node == NULL) { + fprintf(stderr,"Error: XPath element expected in the file \"%s\"\n", filename); + xmlFreeDoc(doc); + return(NULL); + } + + expr = xmlNodeGetContent(node); + if(expr == NULL) { + fprintf(stderr,"Error: XPath content element is NULL \"%s\"\n", filename); + xmlFreeDoc(doc); + return(NULL); + } + + ctx = xmlXPathNewContext(parent_doc); + if(ctx == NULL) { + fprintf(stderr,"Error: unable to create new context\n"); + xmlFree(expr); + xmlFreeDoc(doc); + return(NULL); + } + + /* + * Register namespaces + */ + ns = node->nsDef; + while(ns != NULL) { + if(xmlXPathRegisterNs(ctx, ns->prefix, ns->href) != 0) { + fprintf(stderr,"Error: unable to register NS with prefix=\"%s\" and href=\"%s\"\n", ns->prefix, ns->href); + xmlFree(expr); + xmlXPathFreeContext(ctx); + xmlFreeDoc(doc); + return(NULL); + } + ns = ns->next; + } + + /* + * Evaluate xpath + */ + xpath = xmlXPathEvalExpression(expr, ctx); + if(xpath == NULL) { + fprintf(stderr,"Error: unable to evaluate xpath expression\n"); + xmlFree(expr); + xmlXPathFreeContext(ctx); + xmlFreeDoc(doc); + return(NULL); + } + + /* print_xpath_nodes(xpath); */ + + xmlFree(expr); + xmlXPathFreeContext(ctx); + xmlFreeDoc(doc); + return(xpath); +} + +void +print_xpath_nodes(xmlXPathObjectPtr ptr) { + xmlNodePtr cur; + int i; + + if(ptr == NULL || ptr->nodesetval == NULL ){ + fprintf(stderr, "Error: no nodes set defined\n"); + return; + } + + for(i = 0; i < ptr->nodesetval->nodeNr; ++i) { + cur = ptr->nodesetval->nodeTab[i]; + fprintf(stderr, "node %s. type %d\n", cur->name, cur->type); + } +} + +#else +#include +int main(int argc, char **argv) { + printf("%s : XPath/Canonicalization support not compiled in\n", argv[0]); + return(0); +} +#endif /* LIBXML_C14N_ENABLED */ + + diff --git a/tree.c b/tree.c index 3c370953..c9a4bfb5 100644 --- a/tree.c +++ b/tree.c @@ -2859,7 +2859,7 @@ xmlCopyProp(xmlNodePtr target, xmlAttrPtr cur) { * we cant be sure, that the namespce we found is identified * by the prefix */ - if (xmlStrEqual(ns->href, ret->ns->href)) { + if (xmlStrEqual(ns->href, cur->ns->href)) { /* this is the nice case */ ret->ns = ns; } else { @@ -6907,8 +6907,9 @@ xmlSetDocCompressMode (xmlDocPtr doc, int mode) { * Returns 0 (uncompressed) to 9 (max compression) */ int - xmlGetCompressMode(void) { - return(xmlCompressMode); +xmlGetCompressMode(void) +{ + return (xmlCompressMode); } /** diff --git a/xpath.c b/xpath.c index 3464c924..6930812b 100644 --- a/xpath.c +++ b/xpath.c @@ -1386,6 +1386,67 @@ xmlXPathNodeSetSort(xmlNodeSetPtr set) { } #define XML_NODESET_DEFAULT 10 +/** + * xmlXPathNodeSetDupNs: + * @node: the parent node of the namespace XPath node + * @ns: the libxml namespace declaration node. + * + * Namespace node in libxml don't match the XPath semantic. In a node set + * the namespace nodes are duplicated and the next pointer is set to the + * parent node in the XPath semantic. + * + * Returns the newly created object. + */ +static xmlNodePtr +xmlXPathNodeSetDupNs(xmlNodePtr node, xmlNsPtr ns) { + xmlNsPtr cur; + + if ((ns == NULL) || (ns->type != XML_NAMESPACE_DECL)) + return(NULL); + if ((node == NULL) || (node->type == XML_NAMESPACE_DECL)) + return((xmlNodePtr) ns); + + /* + * Allocate a new Namespace and fill the fields. + */ + cur = (xmlNsPtr) xmlMalloc(sizeof(xmlNs)); + if (cur == NULL) { + xmlGenericError(xmlGenericErrorContext, + "xmlXPathNodeSetDupNs : malloc failed\n"); + return(NULL); + } + memset(cur, 0, sizeof(xmlNs)); + cur->type = XML_NAMESPACE_DECL; + if (ns->href != NULL) + cur->href = xmlStrdup(ns->href); + if (ns->prefix != NULL) + cur->prefix = xmlStrdup(ns->prefix); + cur->next = (xmlNsPtr) node; + return((xmlNodePtr) cur); +} + +/** + * xmlXPathNodeSetFreeNs: + * @ns: the XPath namespace node found in a nodeset. + * + * Namespace node in libxml don't match the XPath semantic. In a node set + * the namespace nodes are duplicated and the next pointer is set to the + * parent node in the XPath semantic. Check if such a node need to be freed + */ +static void +xmlXPathNodeSetFreeNs(xmlNsPtr ns) { + if ((ns == NULL) || (ns->type != XML_NAMESPACE_DECL)) + return; + + if ((ns->next != NULL) && (ns->next->type != XML_NAMESPACE_DECL)) { + if (ns->href != NULL) + xmlFree((xmlChar *)ns->href); + if (ns->prefix != NULL) + xmlFree((xmlChar *)ns->prefix); + xmlFree(ns); + } +} + /** * xmlXPathNodeSetCreate: * @val: an initial xmlNodePtr, or NULL @@ -1416,7 +1477,13 @@ xmlXPathNodeSetCreate(xmlNodePtr val) { memset(ret->nodeTab, 0 , XML_NODESET_DEFAULT * (size_t) sizeof(xmlNodePtr)); ret->nodeMax = XML_NODESET_DEFAULT; - ret->nodeTab[ret->nodeNr++] = val; + if (val->type == XML_NAMESPACE_DECL) { + xmlNsPtr ns = (xmlNsPtr) val; + + ret->nodeTab[ret->nodeNr++] = + xmlXPathNodeSetDupNs((xmlNodePtr) ns->next, ns); + } else + ret->nodeTab[ret->nodeNr++] = val; } return(ret); } @@ -1434,13 +1501,87 @@ int xmlXPathNodeSetContains (xmlNodeSetPtr cur, xmlNodePtr val) { int i; - for (i = 0; i < cur->nodeNr; i++) { - if (cur->nodeTab[i] == val) - return(1); + if (val->type == XML_NAMESPACE_DECL) { + for (i = 0; i < cur->nodeNr; i++) { + if (cur->nodeTab[i]->type == XML_NAMESPACE_DECL) { + xmlNsPtr ns1, ns2; + + ns1 = (xmlNsPtr) val; + ns2 = (xmlNsPtr) cur->nodeTab[i]; + if (ns1 == ns2) + return(1); + if ((ns1->next != NULL) && (ns2->next == ns1->next) && + (xmlStrEqual(ns1->prefix, ns2->prefix))) + return(1); + } + } + } else { + for (i = 0; i < cur->nodeNr; i++) { + if (cur->nodeTab[i] == val) + return(1); + } } return(0); } +/** + * xmlXPathNodeSetAddNs: + * @cur: the initial node set + * @node: the hosting node + * @ns: a the namespace node + * + * add a new namespace node to an existing NodeSet + */ +static void +xmlXPathNodeSetAddNs(xmlNodeSetPtr cur, xmlNodePtr node, xmlNsPtr ns) { + int i; + + if ((ns == NULL) || (node == NULL) || (ns->type != XML_NAMESPACE_DECL) || + (node->type != XML_ELEMENT_NODE)) + return; + + /* @@ with_ns to check wether namespace nodes should be looked at @@ */ + /* + * check against doublons + */ + for (i = 0;i < cur->nodeNr;i++) { + if ((cur->nodeTab[i] != NULL) && + (cur->nodeTab[i]->type == XML_NAMESPACE_DECL) && + (cur->nodeTab[i]->next == node) && + (xmlStrEqual(ns->prefix, ((xmlNsPtr)cur->nodeTab[i])->prefix))) + return; + } + + /* + * grow the nodeTab if needed + */ + if (cur->nodeMax == 0) { + cur->nodeTab = (xmlNodePtr *) xmlMalloc(XML_NODESET_DEFAULT * + sizeof(xmlNodePtr)); + if (cur->nodeTab == NULL) { + xmlGenericError(xmlGenericErrorContext, + "xmlXPathNodeSetAdd: out of memory\n"); + return; + } + memset(cur->nodeTab, 0 , + XML_NODESET_DEFAULT * (size_t) sizeof(xmlNodePtr)); + cur->nodeMax = XML_NODESET_DEFAULT; + } else if (cur->nodeNr == cur->nodeMax) { + xmlNodePtr *temp; + + cur->nodeMax *= 2; + temp = (xmlNodePtr *) xmlRealloc(cur->nodeTab, cur->nodeMax * + sizeof(xmlNodePtr)); + if (temp == NULL) { + xmlGenericError(xmlGenericErrorContext, + "xmlXPathNodeSetAdd: out of memory\n"); + return; + } + cur->nodeTab = temp; + } + cur->nodeTab[cur->nodeNr++] = xmlXPathNodeSetDupNs(node, ns); +} + /** * xmlXPathNodeSetAdd: * @cur: the initial node set @@ -1454,6 +1595,7 @@ xmlXPathNodeSetAdd(xmlNodeSetPtr cur, xmlNodePtr val) { if (val == NULL) return; + /* @@ with_ns to check wether namespace nodes should be looked at @@ */ /* * check against doublons */ @@ -1487,7 +1629,13 @@ xmlXPathNodeSetAdd(xmlNodeSetPtr cur, xmlNodePtr val) { } cur->nodeTab = temp; } - cur->nodeTab[cur->nodeNr++] = val; + if (val->type == XML_NAMESPACE_DECL) { + xmlNsPtr ns = (xmlNsPtr) val; + + cur->nodeTab[cur->nodeNr++] = + xmlXPathNodeSetDupNs((xmlNodePtr) ns->next, ns); + } else + cur->nodeTab[cur->nodeNr++] = val; } /** @@ -1502,6 +1650,7 @@ void xmlXPathNodeSetAddUnique(xmlNodeSetPtr cur, xmlNodePtr val) { if (val == NULL) return; + /* @@ with_ns to check wether namespace nodes should be looked at @@ */ /* * grow the nodeTab if needed */ @@ -1529,7 +1678,13 @@ xmlXPathNodeSetAddUnique(xmlNodeSetPtr cur, xmlNodePtr val) { } cur->nodeTab = temp; } - cur->nodeTab[cur->nodeNr++] = val; + if (val->type == XML_NAMESPACE_DECL) { + xmlNsPtr ns = (xmlNsPtr) val; + + cur->nodeTab[cur->nodeNr++] = + xmlXPathNodeSetDupNs((xmlNodePtr) ns->next, ns); + } else + cur->nodeTab[cur->nodeNr++] = val; } /** @@ -1551,6 +1706,7 @@ xmlXPathNodeSetMerge(xmlNodeSetPtr val1, xmlNodeSetPtr val2) { val1 = xmlXPathNodeSetCreate(NULL); } + /* @@ with_ns to check wether namespace nodes should be looked at @@ */ initNr = val1->nodeNr; for (i = 0;i < val2->nodeNr;i++) { @@ -1562,6 +1718,16 @@ xmlXPathNodeSetMerge(xmlNodeSetPtr val1, xmlNodeSetPtr val2) { if (val1->nodeTab[j] == val2->nodeTab[i]) { skip = 1; break; + } else if ((val1->nodeTab[j]->type == XML_NAMESPACE_DECL) && + (val2->nodeTab[i]->type == XML_NAMESPACE_DECL)) { + xmlNsPtr ns1, ns2; + ns1 = (xmlNsPtr) val1->nodeTab[j]; + ns2 = (xmlNsPtr) val2->nodeTab[i]; + if ((ns1->next == ns2->next) && + (xmlStrEqual(ns1->prefix, ns2->prefix))) { + skip = 1; + break; + } } } if (skip) @@ -1594,7 +1760,13 @@ xmlXPathNodeSetMerge(xmlNodeSetPtr val1, xmlNodeSetPtr val2) { } val1->nodeTab = temp; } - val1->nodeTab[val1->nodeNr++] = val2->nodeTab[i]; + if (val2->nodeTab[i]->type == XML_NAMESPACE_DECL) { + xmlNsPtr ns = (xmlNsPtr) val2->nodeTab[i]; + + val1->nodeTab[val1->nodeNr++] = + xmlXPathNodeSetDupNs((xmlNodePtr) ns->next, ns); + } else + val1->nodeTab[val1->nodeNr++] = val2->nodeTab[i]; } return(val1); @@ -1628,6 +1800,9 @@ xmlXPathNodeSetDel(xmlNodeSetPtr cur, xmlNodePtr val) { #endif return; } + if ((cur->nodeTab[i] != NULL) && + (cur->nodeTab[i]->type == XML_NAMESPACE_DECL)) + xmlXPathNodeSetFreeNs((xmlNsPtr) cur->nodeTab[i]); cur->nodeNr--; for (;i < cur->nodeNr;i++) cur->nodeTab[i] = cur->nodeTab[i + 1]; @@ -1645,6 +1820,9 @@ void xmlXPathNodeSetRemove(xmlNodeSetPtr cur, int val) { if (cur == NULL) return; if (val >= cur->nodeNr) return; + if ((cur->nodeTab[val] != NULL) && + (cur->nodeTab[val]->type == XML_NAMESPACE_DECL)) + xmlXPathNodeSetFreeNs((xmlNsPtr) cur->nodeTab[val]); cur->nodeNr--; for (;val < cur->nodeNr;val++) cur->nodeTab[val] = cur->nodeTab[val + 1]; @@ -1661,6 +1839,13 @@ void xmlXPathFreeNodeSet(xmlNodeSetPtr obj) { if (obj == NULL) return; if (obj->nodeTab != NULL) { + int i; + + /* @@ with_ns to check wether namespace nodes should be looked at @@ */ + for (i = 0;i < obj->nodeNr;i++) + if ((obj->nodeTab[i] != NULL) && + (obj->nodeTab[i]->type == XML_NAMESPACE_DECL)) + xmlXPathNodeSetFreeNs((xmlNsPtr) obj->nodeTab[i]); xmlFree(obj->nodeTab); } xmlFree(obj); @@ -1678,11 +1863,17 @@ xmlXPathFreeValueTree(xmlNodeSetPtr obj) { int i; if (obj == NULL) return; - for (i = 0;i < obj->nodeNr;i++) - if (obj->nodeTab[i] != NULL) - xmlFreeNodeList(obj->nodeTab[i]); if (obj->nodeTab != NULL) { + for (i = 0;i < obj->nodeNr;i++) { + if (obj->nodeTab[i] != NULL) { + if (obj->nodeTab[i]->type == XML_NAMESPACE_DECL) { + xmlXPathNodeSetFreeNs((xmlNsPtr) obj->nodeTab[i]); + } else { + xmlFreeNodeList(obj->nodeTab[i]); + } + } + } xmlFree(obj->nodeTab); } xmlFree(obj); @@ -1752,6 +1943,7 @@ xmlXPathNewNodeSet(xmlNodePtr val) { ret->type = XPATH_NODESET; ret->boolval = 0; ret->nodesetval = xmlXPathNodeSetCreate(val); + /* @@ with_ns to check wether namespace nodes should be looked at @@ */ return(ret); } @@ -1792,22 +1984,22 @@ xmlXPathNewValueTree(xmlNodePtr val) { * Returns the newly created object. */ xmlXPathObjectPtr -xmlXPathNewNodeSetList(xmlNodeSetPtr val) { +xmlXPathNewNodeSetList(xmlNodeSetPtr val) +{ xmlXPathObjectPtr ret; int i; if (val == NULL) - ret = NULL; + ret = NULL; else if (val->nodeTab == NULL) - ret = xmlXPathNewNodeSet(NULL); - else - { - ret = xmlXPathNewNodeSet(val->nodeTab[0]); - for (i = 1; i < val->nodeNr; ++i) - xmlXPathNodeSetAddUnique(ret->nodesetval, val->nodeTab[i]); - } + ret = xmlXPathNewNodeSet(NULL); + else { + ret = xmlXPathNewNodeSet(val->nodeTab[0]); + for (i = 1; i < val->nodeNr; ++i) + xmlXPathNodeSetAddUnique(ret->nodesetval, val->nodeTab[i]); + } - return(ret); + return (ret); } /** @@ -4668,13 +4860,14 @@ xmlXPathNextParent(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) { case XML_DOCB_DOCUMENT_NODE: #endif return(NULL); - case XML_NAMESPACE_DECL: - /* - * TODO !!! may require extending struct _xmlNs with - * parent field - * C.f. Infoset case... - */ + case XML_NAMESPACE_DECL: { + xmlNsPtr ns = (xmlNsPtr) ctxt->context->node; + + if ((ns->next != NULL) && + (ns->next->type != XML_NAMESPACE_DECL)) + return((xmlNodePtr) ns->next); return(NULL); + } } } return(NULL); @@ -4734,13 +4927,15 @@ xmlXPathNextAncestor(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) { case XML_DOCB_DOCUMENT_NODE: #endif return(NULL); - case XML_NAMESPACE_DECL: - /* - * TODO !!! may require extending struct _xmlNs with - * parent field - * C.f. Infoset case... - */ + case XML_NAMESPACE_DECL: { + xmlNsPtr ns = (xmlNsPtr) ctxt->context->node; + + if ((ns->next != NULL) && + (ns->next->type != XML_NAMESPACE_DECL)) + return((xmlNodePtr) ns->next); + /* Bad, how did that namespace ended-up there ? */ return(NULL); + } } return(NULL); } @@ -4779,9 +4974,8 @@ xmlXPathNextAncestor(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) { return(NULL); case XML_NAMESPACE_DECL: /* - * TODO !!! may require extending struct _xmlNs with - * parent field - * C.f. Infoset case... + * this should not hapen a namespace can't be + * the ancestor of another node */ return(NULL); } @@ -8260,7 +8454,8 @@ xmlXPathNodeCollectAndTest(xmlXPathParserContextPtr ctxt, #ifdef DEBUG_STEP n++; #endif - addNode(list, cur); + xmlXPathNodeSetAddNs(list, ctxt->context->node, + (xmlNsPtr) cur); } } else { if (cur->type == XML_ELEMENT_NODE) { @@ -8343,7 +8538,8 @@ xmlXPathNodeCollectAndTest(xmlXPathParserContextPtr ctxt, #ifdef DEBUG_STEP n++; #endif - addNode(list, cur); + xmlXPathNodeSetAddNs(list, + ctxt->context->node, (xmlNsPtr) cur); } } break; @@ -8674,7 +8870,8 @@ xmlXPathNodeCollectAndTestNth(xmlXPathParserContextPtr ctxt, if (cur->type == XML_NAMESPACE_DECL) { n++; if (n == indx) - addNode(list, cur); + xmlXPathNodeSetAddNs(list, ctxt->context->node, + (xmlNsPtr) cur); } } else { if (cur->type == XML_ELEMENT_NODE) { @@ -8748,7 +8945,8 @@ xmlXPathNodeCollectAndTestNth(xmlXPathParserContextPtr ctxt, && (xmlStrEqual(ns->prefix, name))) { n++; if (n == indx) - addNode(list, cur); + xmlXPathNodeSetAddNs(list, + ctxt->context->node, (xmlNsPtr) cur); } } break;