(root)/
libxml2-2.12.3/
c14n.c
       1  /*
       2   * "Canonical XML" implementation
       3   * http://www.w3.org/TR/xml-c14n
       4   *
       5   * "Exclusive XML Canonicalization" implementation
       6   * http://www.w3.org/TR/xml-exc-c14n
       7   *
       8   * See Copyright for the status of this software.
       9   *
      10   * Author: Aleksey Sanin <aleksey@aleksey.com>
      11   */
      12  #define IN_LIBXML
      13  #include "libxml.h"
      14  #ifdef LIBXML_C14N_ENABLED
      15  
      16  #include <stdlib.h>
      17  #include <string.h>
      18  
      19  #include <libxml/tree.h>
      20  #include <libxml/parser.h>
      21  #include <libxml/uri.h>
      22  #include <libxml/xmlerror.h>
      23  #include <libxml/xpathInternals.h>
      24  #include <libxml/c14n.h>
      25  
      26  #include "private/buf.h"
      27  #include "private/error.h"
      28  
      29  /************************************************************************
      30   *									*
      31   *		Some declaration better left private ATM		*
      32   *									*
      33   ************************************************************************/
      34  
      35  typedef enum {
      36      XMLC14N_BEFORE_DOCUMENT_ELEMENT = 0,
      37      XMLC14N_INSIDE_DOCUMENT_ELEMENT = 1,
      38      XMLC14N_AFTER_DOCUMENT_ELEMENT = 2
      39  } xmlC14NPosition;
      40  
      41  typedef struct _xmlC14NVisibleNsStack {
      42      int nsCurEnd;           /* number of nodes in the set */
      43      int nsPrevStart;        /* the beginning of the stack for previous visible node */
      44      int nsPrevEnd;          /* the end of the stack for previous visible node */
      45      int nsMax;              /* size of the array as allocated */
      46      xmlNsPtr	*nsTab;	    /* array of ns in no particular order */
      47      xmlNodePtr	*nodeTab;   /* array of nodes in no particular order */
      48  } xmlC14NVisibleNsStack, *xmlC14NVisibleNsStackPtr;
      49  
      50  typedef struct _xmlC14NCtx {
      51      /* input parameters */
      52      xmlDocPtr doc;
      53      xmlC14NIsVisibleCallback is_visible_callback;
      54      void* user_data;
      55      int with_comments;
      56      xmlOutputBufferPtr buf;
      57  
      58      /* position in the XML document */
      59      xmlC14NPosition pos;
      60      int parent_is_doc;
      61      xmlC14NVisibleNsStackPtr ns_rendered;
      62  
      63      /* C14N mode */
      64      xmlC14NMode mode;
      65  
      66      /* exclusive canonicalization */
      67      xmlChar **inclusive_ns_prefixes;
      68  
      69      /* error number */
      70      int error;
      71  } xmlC14NCtx, *xmlC14NCtxPtr;
      72  
      73  static xmlC14NVisibleNsStackPtr	xmlC14NVisibleNsStackCreate	(void);
      74  static void     xmlC14NVisibleNsStackDestroy	(xmlC14NVisibleNsStackPtr cur);
      75  static void     xmlC14NVisibleNsStackAdd	    (xmlC14NVisibleNsStackPtr cur,
      76                                                   xmlNsPtr ns,
      77                                                   xmlNodePtr node);
      78  static void			xmlC14NVisibleNsStackSave	(xmlC14NVisibleNsStackPtr cur,
      79  								 xmlC14NVisibleNsStackPtr state);
      80  static void			xmlC14NVisibleNsStackRestore	(xmlC14NVisibleNsStackPtr cur,
      81  								 xmlC14NVisibleNsStackPtr state);
      82  static void			xmlC14NVisibleNsStackShift	(xmlC14NVisibleNsStackPtr cur);
      83  static int			xmlC14NVisibleNsStackFind	(xmlC14NVisibleNsStackPtr cur,
      84  								 xmlNsPtr ns);
      85  static int			xmlExcC14NVisibleNsStackFind	(xmlC14NVisibleNsStackPtr cur,
      86  								 xmlNsPtr ns,
      87  								 xmlC14NCtxPtr ctx);
      88  
      89  static int			xmlC14NIsNodeInNodeset		(void *user_data,
      90  								 xmlNodePtr node,
      91  								 xmlNodePtr parent);
      92  
      93  
      94  
      95  static int xmlC14NProcessNode(xmlC14NCtxPtr ctx, xmlNodePtr cur);
      96  static int xmlC14NProcessNodeList(xmlC14NCtxPtr ctx, xmlNodePtr cur);
      97  typedef enum {
      98      XMLC14N_NORMALIZE_ATTR = 0,
      99      XMLC14N_NORMALIZE_COMMENT = 1,
     100      XMLC14N_NORMALIZE_PI = 2,
     101      XMLC14N_NORMALIZE_TEXT = 3
     102  } xmlC14NNormalizationMode;
     103  
     104  static xmlChar *xmlC11NNormalizeString(const xmlChar * input,
     105                                         xmlC14NNormalizationMode mode);
     106  
     107  #define	xmlC11NNormalizeAttr( a ) \
     108      xmlC11NNormalizeString((a), XMLC14N_NORMALIZE_ATTR)
     109  #define	xmlC11NNormalizeComment( a ) \
     110      xmlC11NNormalizeString((a), XMLC14N_NORMALIZE_COMMENT)
     111  #define	xmlC11NNormalizePI( a )	\
     112      xmlC11NNormalizeString((a), XMLC14N_NORMALIZE_PI)
     113  #define	xmlC11NNormalizeText( a ) \
     114      xmlC11NNormalizeString((a), XMLC14N_NORMALIZE_TEXT)
     115  
     116  #define	xmlC14NIsVisible( ctx, node, parent ) \
     117       (((ctx)->is_visible_callback != NULL) ? \
     118  	(ctx)->is_visible_callback((ctx)->user_data, \
     119  		(xmlNodePtr)(node), (xmlNodePtr)(parent)) : 1)
     120  
     121  #define	xmlC14NIsExclusive( ctx ) \
     122      ( (ctx)->mode == XML_C14N_EXCLUSIVE_1_0 )
     123  
     124  /************************************************************************
     125   *									*
     126   *		Some factorized error routines				*
     127   *									*
     128   ************************************************************************/
     129  
     130  /**
     131   * xmlC14NErrMemory:
     132   * @extra:  extra information
     133   *
     134   * Handle a redefinition of memory error
     135   */
     136  static void
     137  xmlC14NErrMemory(const char *extra)
     138  {
     139      __xmlRaiseError(NULL, NULL, NULL, NULL, NULL, XML_FROM_C14N,
     140  		    XML_ERR_NO_MEMORY, XML_ERR_ERROR, NULL, 0, extra,
     141  		    NULL, NULL, 0, 0,
     142  		    "Memory allocation failed : %s\n", extra);
     143  }
     144  
     145  /**
     146   * xmlC14NErrParam:
     147   * @extra:  extra information
     148   *
     149   * Handle a redefinition of param error
     150   */
     151  static void
     152  xmlC14NErrParam(const char *extra)
     153  {
     154      __xmlRaiseError(NULL, NULL, NULL, NULL, NULL, XML_FROM_C14N,
     155  		    XML_ERR_INTERNAL_ERROR, XML_ERR_ERROR, NULL, 0, extra,
     156  		    NULL, NULL, 0, 0,
     157  		    "Invalid parameter : %s\n", extra);
     158  }
     159  
     160  /**
     161   * xmlC14NErrInternal:
     162   * @extra:  extra information
     163   *
     164   * Handle a redefinition of internal error
     165   */
     166  static void
     167  xmlC14NErrInternal(const char *extra)
     168  {
     169      __xmlRaiseError(NULL, NULL, NULL, NULL, NULL, XML_FROM_C14N,
     170  		    XML_ERR_INTERNAL_ERROR, XML_ERR_ERROR, NULL, 0, extra,
     171  		    NULL, NULL, 0, 0,
     172  		    "Internal error : %s\n", extra);
     173  }
     174  
     175  /**
     176   * xmlC14NErrInvalidNode:
     177   * @extra:  extra information
     178   *
     179   * Handle a redefinition of invalid node error
     180   */
     181  static void
     182  xmlC14NErrInvalidNode(const char *node_type, const char *extra)
     183  {
     184      __xmlRaiseError(NULL, NULL, NULL, NULL, NULL, XML_FROM_C14N,
     185  		    XML_C14N_INVALID_NODE, XML_ERR_ERROR, NULL, 0, extra,
     186  		    NULL, NULL, 0, 0,
     187  		    "Node %s is invalid here : %s\n", node_type, extra);
     188  }
     189  
     190  /**
     191   * xmlC14NErrUnknownNode:
     192   * @extra:  extra information
     193   *
     194   * Handle a redefinition of unknown node error
     195   */
     196  static void
     197  xmlC14NErrUnknownNode(int node_type, const char *extra)
     198  {
     199      __xmlRaiseError(NULL, NULL, NULL, NULL, NULL, XML_FROM_C14N,
     200  		    XML_C14N_UNKNOW_NODE, XML_ERR_ERROR, NULL, 0, extra,
     201  		    NULL, NULL, 0, 0,
     202  		    "Unknown node type %d found : %s\n", node_type, extra);
     203  }
     204  
     205  /**
     206   * xmlC14NErrRelativeNamespace:
     207   * @extra:  extra information
     208   *
     209   * Handle a redefinition of relative namespace error
     210   */
     211  static void
     212  xmlC14NErrRelativeNamespace(const char *ns_uri)
     213  {
     214      __xmlRaiseError(NULL, NULL, NULL, NULL, NULL, XML_FROM_C14N,
     215  		    XML_C14N_RELATIVE_NAMESPACE, XML_ERR_ERROR, NULL, 0, NULL,
     216  		    NULL, NULL, 0, 0,
     217  		    "Relative namespace UR is invalid here : %s\n", ns_uri);
     218  }
     219  
     220  
     221  
     222  /**
     223   * xmlC14NErr:
     224   * @ctxt:  a C14N evaluation context
     225   * @node:  the context node
     226   * @error:  the error code
     227   * @msg:  the message
     228   * @extra:  extra information
     229   *
     230   * Handle a redefinition of attribute error
     231   */
     232  static void
     233  xmlC14NErr(xmlC14NCtxPtr ctxt, xmlNodePtr node, int error,
     234             const char * msg)
     235  {
     236      if (ctxt != NULL)
     237          ctxt->error = error;
     238      __xmlRaiseError(NULL, NULL, NULL,
     239  		    ctxt, node, XML_FROM_C14N, error,
     240  		    XML_ERR_ERROR, NULL, 0,
     241  		    NULL, NULL, NULL, 0, 0, "%s", msg);
     242  }
     243  
     244  /************************************************************************
     245   *									*
     246   *		The implementation internals				*
     247   *									*
     248   ************************************************************************/
     249  #define XML_NAMESPACES_DEFAULT		16
     250  
     251  static int
     252  xmlC14NIsNodeInNodeset(void *user_data, xmlNodePtr node, xmlNodePtr parent) {
     253      xmlNodeSetPtr nodes = (xmlNodeSetPtr) user_data;
     254      if((nodes != NULL) && (node != NULL)) {
     255  	if(node->type != XML_NAMESPACE_DECL) {
     256  	    return(xmlXPathNodeSetContains(nodes, node));
     257  	} else {
     258  	    xmlNs ns;
     259  
     260  	    memcpy(&ns, node, sizeof(ns));
     261  
     262  	    /* this is a libxml hack! check xpath.c for details */
     263  	    if((parent != NULL) && (parent->type == XML_ATTRIBUTE_NODE)) {
     264  		ns.next = (xmlNsPtr)parent->parent;
     265  	    } else {
     266  		ns.next = (xmlNsPtr)parent;
     267  	    }
     268  
     269  	    /*
     270  	     * If the input is an XPath node-set, then the node-set must explicitly
     271  	     * contain every node to be rendered to the canonical form.
     272  	     */
     273  	    return(xmlXPathNodeSetContains(nodes, (xmlNodePtr)&ns));
     274  	}
     275      }
     276      return(1);
     277  }
     278  
     279  static xmlC14NVisibleNsStackPtr
     280  xmlC14NVisibleNsStackCreate(void) {
     281      xmlC14NVisibleNsStackPtr ret;
     282  
     283      ret = (xmlC14NVisibleNsStackPtr) xmlMalloc(sizeof(xmlC14NVisibleNsStack));
     284      if (ret == NULL) {
     285          xmlC14NErrMemory("creating namespaces stack");
     286  	return(NULL);
     287      }
     288      memset(ret, 0, sizeof(xmlC14NVisibleNsStack));
     289      return(ret);
     290  }
     291  
     292  static void
     293  xmlC14NVisibleNsStackDestroy(xmlC14NVisibleNsStackPtr cur) {
     294      if(cur == NULL) {
     295          xmlC14NErrParam("destroying namespaces stack");
     296          return;
     297      }
     298      if(cur->nsTab != NULL) {
     299  	memset(cur->nsTab, 0, cur->nsMax * sizeof(xmlNsPtr));
     300  	xmlFree(cur->nsTab);
     301      }
     302      if(cur->nodeTab != NULL) {
     303  	memset(cur->nodeTab, 0, cur->nsMax * sizeof(xmlNodePtr));
     304  	xmlFree(cur->nodeTab);
     305      }
     306      memset(cur, 0, sizeof(xmlC14NVisibleNsStack));
     307      xmlFree(cur);
     308  
     309  }
     310  
     311  static void
     312  xmlC14NVisibleNsStackAdd(xmlC14NVisibleNsStackPtr cur, xmlNsPtr ns, xmlNodePtr node) {
     313      if((cur == NULL) ||
     314         ((cur->nsTab == NULL) && (cur->nodeTab != NULL)) ||
     315         ((cur->nsTab != NULL) && (cur->nodeTab == NULL))) {
     316          xmlC14NErrParam("adding namespace to stack");
     317  	return;
     318      }
     319  
     320      if ((cur->nsTab == NULL) && (cur->nodeTab == NULL)) {
     321          cur->nsTab = (xmlNsPtr*) xmlMalloc(XML_NAMESPACES_DEFAULT * sizeof(xmlNsPtr));
     322          cur->nodeTab = (xmlNodePtr*) xmlMalloc(XML_NAMESPACES_DEFAULT * sizeof(xmlNodePtr));
     323  	if ((cur->nsTab == NULL) || (cur->nodeTab == NULL)) {
     324  	    xmlC14NErrMemory("adding node to stack");
     325  	    return;
     326  	}
     327  	memset(cur->nsTab, 0 , XML_NAMESPACES_DEFAULT * sizeof(xmlNsPtr));
     328  	memset(cur->nodeTab, 0 , XML_NAMESPACES_DEFAULT * sizeof(xmlNodePtr));
     329          cur->nsMax = XML_NAMESPACES_DEFAULT;
     330      } else if(cur->nsMax == cur->nsCurEnd) {
     331  	void *tmp;
     332  	int tmpSize;
     333  
     334  	tmpSize = 2 * cur->nsMax;
     335  	tmp = xmlRealloc(cur->nsTab, tmpSize * sizeof(xmlNsPtr));
     336  	if (tmp == NULL) {
     337  	    xmlC14NErrMemory("adding node to stack");
     338  	    return;
     339  	}
     340  	cur->nsTab = (xmlNsPtr*)tmp;
     341  
     342  	tmp = xmlRealloc(cur->nodeTab, tmpSize * sizeof(xmlNodePtr));
     343  	if (tmp == NULL) {
     344  	    xmlC14NErrMemory("adding node to stack");
     345  	    return;
     346  	}
     347  	cur->nodeTab = (xmlNodePtr*)tmp;
     348  
     349  	cur->nsMax = tmpSize;
     350      }
     351      cur->nsTab[cur->nsCurEnd] = ns;
     352      cur->nodeTab[cur->nsCurEnd] = node;
     353  
     354      ++cur->nsCurEnd;
     355  }
     356  
     357  static void
     358  xmlC14NVisibleNsStackSave(xmlC14NVisibleNsStackPtr cur, xmlC14NVisibleNsStackPtr state) {
     359      if((cur == NULL) || (state == NULL)) {
     360          xmlC14NErrParam("saving namespaces stack");
     361  	return;
     362      }
     363  
     364      state->nsCurEnd = cur->nsCurEnd;
     365      state->nsPrevStart = cur->nsPrevStart;
     366      state->nsPrevEnd = cur->nsPrevEnd;
     367  }
     368  
     369  static void
     370  xmlC14NVisibleNsStackRestore(xmlC14NVisibleNsStackPtr cur, xmlC14NVisibleNsStackPtr state) {
     371      if((cur == NULL) || (state == NULL)) {
     372          xmlC14NErrParam("restoring namespaces stack");
     373  	return;
     374      }
     375      cur->nsCurEnd = state->nsCurEnd;
     376      cur->nsPrevStart = state->nsPrevStart;
     377      cur->nsPrevEnd = state->nsPrevEnd;
     378  }
     379  
     380  static void
     381  xmlC14NVisibleNsStackShift(xmlC14NVisibleNsStackPtr cur) {
     382      if(cur == NULL) {
     383          xmlC14NErrParam("shifting namespaces stack");
     384  	return;
     385      }
     386      cur->nsPrevStart = cur->nsPrevEnd;
     387      cur->nsPrevEnd = cur->nsCurEnd;
     388  }
     389  
     390  static int
     391  xmlC14NStrEqual(const xmlChar *str1, const xmlChar *str2) {
     392      if (str1 == str2) return(1);
     393      if (str1 == NULL) return((*str2) == '\0');
     394      if (str2 == NULL) return((*str1) == '\0');
     395      do {
     396  	if (*str1++ != *str2) return(0);
     397      } while (*str2++);
     398      return(1);
     399  }
     400  
     401  /**
     402   * xmlC14NVisibleNsStackFind:
     403   * @ctx:		the C14N context
     404   * @ns:			the namespace to check
     405   *
     406   * Checks whether the given namespace was already rendered or not
     407   *
     408   * Returns 1 if we already wrote this namespace or 0 otherwise
     409   */
     410  static int
     411  xmlC14NVisibleNsStackFind(xmlC14NVisibleNsStackPtr cur, xmlNsPtr ns)
     412  {
     413      int i;
     414      const xmlChar *prefix;
     415      const xmlChar *href;
     416      int has_empty_ns;
     417  
     418      if(cur == NULL) {
     419          xmlC14NErrParam("searching namespaces stack (c14n)");
     420          return (0);
     421      }
     422  
     423      /*
     424       * if the default namespace xmlns="" is not defined yet then
     425       * we do not want to print it out
     426       */
     427      prefix = ((ns == NULL) || (ns->prefix == NULL)) ? BAD_CAST "" : ns->prefix;
     428      href = ((ns == NULL) || (ns->href == NULL)) ? BAD_CAST "" : ns->href;
     429      has_empty_ns = (xmlC14NStrEqual(prefix, NULL) && xmlC14NStrEqual(href, NULL));
     430  
     431      if (cur->nsTab != NULL) {
     432  	int start = (has_empty_ns) ? 0 : cur->nsPrevStart;
     433          for (i = cur->nsCurEnd - 1; i >= start; --i) {
     434              xmlNsPtr ns1 = cur->nsTab[i];
     435  
     436  	    if(xmlC14NStrEqual(prefix, (ns1 != NULL) ? ns1->prefix : NULL)) {
     437  		return(xmlC14NStrEqual(href, (ns1 != NULL) ? ns1->href : NULL));
     438  	    }
     439          }
     440      }
     441      return(has_empty_ns);
     442  }
     443  
     444  static int
     445  xmlExcC14NVisibleNsStackFind(xmlC14NVisibleNsStackPtr cur, xmlNsPtr ns, xmlC14NCtxPtr ctx) {
     446      int i;
     447      const xmlChar *prefix;
     448      const xmlChar *href;
     449      int has_empty_ns;
     450  
     451      if(cur == NULL) {
     452          xmlC14NErrParam("searching namespaces stack (exc c14n)");
     453          return (0);
     454      }
     455  
     456      /*
     457       * if the default namespace xmlns="" is not defined yet then
     458       * we do not want to print it out
     459       */
     460      prefix = ((ns == NULL) || (ns->prefix == NULL)) ? BAD_CAST "" : ns->prefix;
     461      href = ((ns == NULL) || (ns->href == NULL)) ? BAD_CAST "" : ns->href;
     462      has_empty_ns = (xmlC14NStrEqual(prefix, NULL) && xmlC14NStrEqual(href, NULL));
     463  
     464      if (cur->nsTab != NULL) {
     465  	int start = 0;
     466          for (i = cur->nsCurEnd - 1; i >= start; --i) {
     467              xmlNsPtr ns1 = cur->nsTab[i];
     468  
     469  	    if(xmlC14NStrEqual(prefix, (ns1 != NULL) ? ns1->prefix : NULL)) {
     470  		if(xmlC14NStrEqual(href, (ns1 != NULL) ? ns1->href : NULL)) {
     471  		    return(xmlC14NIsVisible(ctx, ns1, cur->nodeTab[i]));
     472  		} else {
     473  		    return(0);
     474  		}
     475  	    }
     476          }
     477      }
     478      return(has_empty_ns);
     479  }
     480  
     481  
     482  
     483  
     484  /**
     485   * xmlC14NIsXmlNs:
     486   * @ns:		the namespace to check
     487   *
     488   * Checks whether the given namespace is a default "xml:" namespace
     489   * with href="http://www.w3.org/XML/1998/namespace"
     490   *
     491   * Returns 1 if the node is default or 0 otherwise
     492   */
     493  
     494  /* todo: make it a define? */
     495  static int
     496  xmlC14NIsXmlNs(xmlNsPtr ns)
     497  {
     498      return ((ns != NULL) &&
     499              (xmlStrEqual(ns->prefix, BAD_CAST "xml")) &&
     500              (xmlStrEqual(ns->href, XML_XML_NAMESPACE)));
     501  }
     502  
     503  
     504  /**
     505   * xmlC14NNsCompare:
     506   * @ns1:		the pointer to first namespace
     507   * @ns2:		the pointer to second namespace
     508   *
     509   * Compares the namespaces by names (prefixes).
     510   *
     511   * Returns -1 if ns1 < ns2, 0 if ns1 == ns2 or 1 if ns1 > ns2.
     512   */
     513  static int
     514  xmlC14NNsCompare(const void *data1, const void *data2)
     515  {
     516      const xmlNsPtr ns1 = (const xmlNsPtr) data1;
     517      const xmlNsPtr ns2 = (const xmlNsPtr) data2;
     518      if (ns1 == ns2)
     519          return (0);
     520      if (ns1 == NULL)
     521          return (-1);
     522      if (ns2 == NULL)
     523          return (1);
     524  
     525      return (xmlStrcmp(ns1->prefix, ns2->prefix));
     526  }
     527  
     528  
     529  /**
     530   * xmlC14NPrintNamespaces:
     531   * @ns:			the pointer to namespace
     532   * @ctx:		the C14N context
     533   *
     534   * Prints the given namespace to the output buffer from C14N context.
     535   *
     536   * Returns 1 on success or 0 on fail.
     537   */
     538  static int
     539  xmlC14NPrintNamespaces(const xmlNsPtr ns, xmlC14NCtxPtr ctx)
     540  {
     541  
     542      if ((ns == NULL) || (ctx == NULL)) {
     543          xmlC14NErrParam("writing namespaces");
     544          return 0;
     545      }
     546  
     547      if (ns->prefix != NULL) {
     548          xmlOutputBufferWriteString(ctx->buf, " xmlns:");
     549          xmlOutputBufferWriteString(ctx->buf, (const char *) ns->prefix);
     550          xmlOutputBufferWriteString(ctx->buf, "=");
     551      } else {
     552          xmlOutputBufferWriteString(ctx->buf, " xmlns=");
     553      }
     554      if(ns->href != NULL) {
     555  	xmlBufWriteQuotedString(ctx->buf->buffer, ns->href);
     556      } else {
     557      	xmlOutputBufferWriteString(ctx->buf, "\"\"");
     558      }
     559      return (1);
     560  }
     561  
     562  static int
     563  xmlC14NPrintNamespacesWalker(const void *ns, void *ctx) {
     564      return xmlC14NPrintNamespaces((const xmlNsPtr) ns, (xmlC14NCtxPtr) ctx);
     565  }
     566  
     567  /**
     568   * xmlC14NProcessNamespacesAxis:
     569   * @ctx:		the C14N context
     570   * @node:		the current node
     571   *
     572   * Prints out canonical namespace axis of the current node to the
     573   * buffer from C14N context as follows
     574   *
     575   * Canonical XML v 1.0 (http://www.w3.org/TR/xml-c14n)
     576   *
     577   * Namespace Axis
     578   * Consider a list L containing only namespace nodes in the
     579   * axis and in the node-set in lexicographic order (ascending). To begin
     580   * processing L, if the first node is not the default namespace node (a node
     581   * with no namespace URI and no local name), then generate a space followed
     582   * by xmlns="" if and only if the following conditions are met:
     583   *    - the element E that owns the axis is in the node-set
     584   *    - The nearest ancestor element of E in the node-set has a default
     585   *	    namespace node in the node-set (default namespace nodes always
     586   *      have non-empty values in XPath)
     587   * The latter condition eliminates unnecessary occurrences of xmlns="" in
     588   * the canonical form since an element only receives an xmlns="" if its
     589   * default namespace is empty and if it has an immediate parent in the
     590   * canonical form that has a non-empty default namespace. To finish
     591   * processing  L, simply process every namespace node in L, except omit
     592   * namespace node with local name xml, which defines the xml prefix,
     593   * if its string value is http://www.w3.org/XML/1998/namespace.
     594   *
     595   * Exclusive XML Canonicalization v 1.0 (http://www.w3.org/TR/xml-exc-c14n)
     596   * Canonical XML applied to a document subset requires the search of the
     597   * ancestor nodes of each orphan element node for attributes in the xml
     598   * namespace, such as xml:lang and xml:space. These are copied into the
     599   * element node except if a declaration of the same attribute is already
     600   * in the attribute axis of the element (whether or not it is included in
     601   * the document subset). This search and copying are omitted from the
     602   * Exclusive XML Canonicalization method.
     603   *
     604   * Returns 0 on success or -1 on fail.
     605   */
     606  static int
     607  xmlC14NProcessNamespacesAxis(xmlC14NCtxPtr ctx, xmlNodePtr cur, int visible)
     608  {
     609      xmlNodePtr n;
     610      xmlNsPtr ns, tmp;
     611      xmlListPtr list;
     612      int already_rendered;
     613      int has_empty_ns = 0;
     614  
     615      if ((ctx == NULL) || (cur == NULL) || (cur->type != XML_ELEMENT_NODE)) {
     616          xmlC14NErrParam("processing namespaces axis (c14n)");
     617          return (-1);
     618      }
     619  
     620      /*
     621       * Create a sorted list to store element namespaces
     622       */
     623      list = xmlListCreate(NULL, xmlC14NNsCompare);
     624      if (list == NULL) {
     625          xmlC14NErrInternal("creating namespaces list (c14n)");
     626          return (-1);
     627      }
     628  
     629      /* check all namespaces */
     630      for(n = cur; n != NULL; n = n->parent) {
     631  	for(ns = n->nsDef; ns != NULL; ns = ns->next) {
     632  	    tmp = xmlSearchNs(cur->doc, cur, ns->prefix);
     633  
     634  	    if((tmp == ns) && !xmlC14NIsXmlNs(ns) && xmlC14NIsVisible(ctx, ns, cur)) {
     635  		already_rendered = xmlC14NVisibleNsStackFind(ctx->ns_rendered, ns);
     636  		if(visible) {
     637  	    xmlC14NVisibleNsStackAdd(ctx->ns_rendered, ns, cur);
     638  		}
     639  		if(!already_rendered) {
     640  		    xmlListInsert(list, ns);
     641  		}
     642  		if(xmlStrlen(ns->prefix) == 0) {
     643  		    has_empty_ns = 1;
     644  		}
     645  	    }
     646  	}
     647      }
     648  
     649      /**
     650       * if the first node is not the default namespace node (a node with no
     651       * namespace URI and no local name), then generate a space followed by
     652       * xmlns="" if and only if the following conditions are met:
     653       *  - the element E that owns the axis is in the node-set
     654       *  - the nearest ancestor element of E in the node-set has a default
     655       *     namespace node in the node-set (default namespace nodes always
     656       *     have non-empty values in XPath)
     657       */
     658      if(visible && !has_empty_ns) {
     659          static xmlNs ns_default;
     660  
     661          memset(&ns_default, 0, sizeof(ns_default));
     662          if(!xmlC14NVisibleNsStackFind(ctx->ns_rendered, &ns_default)) {
     663  	    xmlC14NPrintNamespaces(&ns_default, ctx);
     664  	}
     665      }
     666  
     667  
     668      /*
     669       * print out all elements from list
     670       */
     671      xmlListWalk(list, xmlC14NPrintNamespacesWalker, (void *) ctx);
     672  
     673      /*
     674       * Cleanup
     675       */
     676      xmlListDelete(list);
     677      return (0);
     678  }
     679  
     680  
     681  /**
     682   * xmlExcC14NProcessNamespacesAxis:
     683   * @ctx:		the C14N context
     684   * @node:		the current node
     685   *
     686   * Prints out exclusive canonical namespace axis of the current node to the
     687   * buffer from C14N context as follows
     688   *
     689   * Exclusive XML Canonicalization
     690   * http://www.w3.org/TR/xml-exc-c14n
     691   *
     692   * If the element node is in the XPath subset then output the node in
     693   * accordance with Canonical XML except for namespace nodes which are
     694   * rendered as follows:
     695   *
     696   * 1. Render each namespace node iff:
     697   *    * it is visibly utilized by the immediate parent element or one of
     698   *      its attributes, or is present in InclusiveNamespaces PrefixList, and
     699   *    * its prefix and value do not appear in ns_rendered. ns_rendered is
     700   *      obtained by popping the state stack in order to obtain a list of
     701   *      prefixes and their values which have already been rendered by
     702   *      an output ancestor of the namespace node's parent element.
     703   * 2. Append the rendered namespace node to the list ns_rendered of namespace
     704   * nodes rendered by output ancestors. Push ns_rendered on state stack and
     705   * recurse.
     706   * 3. After the recursion returns, pop thestate stack.
     707   *
     708   *
     709   * Returns 0 on success or -1 on fail.
     710   */
     711  static int
     712  xmlExcC14NProcessNamespacesAxis(xmlC14NCtxPtr ctx, xmlNodePtr cur, int visible)
     713  {
     714      xmlNsPtr ns;
     715      xmlListPtr list;
     716      xmlAttrPtr attr;
     717      int already_rendered;
     718      int has_empty_ns = 0;
     719      int has_visibly_utilized_empty_ns = 0;
     720      int has_empty_ns_in_inclusive_list = 0;
     721  
     722      if ((ctx == NULL) || (cur == NULL) || (cur->type != XML_ELEMENT_NODE)) {
     723          xmlC14NErrParam("processing namespaces axis (exc c14n)");
     724          return (-1);
     725      }
     726  
     727      if(!xmlC14NIsExclusive(ctx)) {
     728          xmlC14NErrParam("processing namespaces axis (exc c14n)");
     729          return (-1);
     730  
     731      }
     732  
     733      /*
     734       * Create a sorted list to store element namespaces
     735       */
     736      list = xmlListCreate(NULL, xmlC14NNsCompare);
     737      if (list == NULL) {
     738          xmlC14NErrInternal("creating namespaces list (exc c14n)");
     739          return (-1);
     740      }
     741  
     742      /*
     743       * process inclusive namespaces:
     744       * All namespace nodes appearing on inclusive ns list are
     745       * handled as provided in Canonical XML
     746       */
     747      if(ctx->inclusive_ns_prefixes != NULL) {
     748  	xmlChar *prefix;
     749  	int i;
     750  
     751  	for (i = 0; ctx->inclusive_ns_prefixes[i] != NULL; ++i) {
     752  	    prefix = ctx->inclusive_ns_prefixes[i];
     753  	    /*
     754  	     * Special values for namespace with empty prefix
     755  	     */
     756              if (xmlStrEqual(prefix, BAD_CAST "#default")
     757                  || xmlStrEqual(prefix, BAD_CAST "")) {
     758                  prefix = NULL;
     759  		has_empty_ns_in_inclusive_list = 1;
     760              }
     761  
     762  	    ns = xmlSearchNs(cur->doc, cur, prefix);
     763  	    if((ns != NULL) && !xmlC14NIsXmlNs(ns) && xmlC14NIsVisible(ctx, ns, cur)) {
     764  		already_rendered = xmlC14NVisibleNsStackFind(ctx->ns_rendered, ns);
     765  		if(visible) {
     766  		    xmlC14NVisibleNsStackAdd(ctx->ns_rendered, ns, cur);
     767  		}
     768  		if(!already_rendered) {
     769  		    xmlListInsert(list, ns);
     770  		}
     771  		if(xmlStrlen(ns->prefix) == 0) {
     772  		    has_empty_ns = 1;
     773  		}
     774  	    }
     775  	}
     776      }
     777  
     778      /* add node namespace */
     779      if(cur->ns != NULL) {
     780  	ns = cur->ns;
     781      } else {
     782          ns = xmlSearchNs(cur->doc, cur, NULL);
     783  	has_visibly_utilized_empty_ns = 1;
     784      }
     785      if((ns != NULL) && !xmlC14NIsXmlNs(ns)) {
     786  	if(visible && xmlC14NIsVisible(ctx, ns, cur)) {
     787  	    if(!xmlExcC14NVisibleNsStackFind(ctx->ns_rendered, ns, ctx)) {
     788  		xmlListInsert(list, ns);
     789  	    }
     790  	}
     791  	if(visible) {
     792  	    xmlC14NVisibleNsStackAdd(ctx->ns_rendered, ns, cur);
     793  	}
     794  	if(xmlStrlen(ns->prefix) == 0) {
     795  	    has_empty_ns = 1;
     796  	}
     797      }
     798  
     799  
     800      /* add attributes */
     801      for(attr = cur->properties; attr != NULL; attr = attr->next) {
     802          /*
     803           * we need to check that attribute is visible and has non
     804           * default namespace (XML Namespaces: "default namespaces
     805  	 * do not apply directly to attributes")
     806           */
     807  	if((attr->ns != NULL) && !xmlC14NIsXmlNs(attr->ns) && xmlC14NIsVisible(ctx, attr, cur)) {
     808  	    already_rendered = xmlExcC14NVisibleNsStackFind(ctx->ns_rendered, attr->ns, ctx);
     809  	    xmlC14NVisibleNsStackAdd(ctx->ns_rendered, attr->ns, cur);
     810  	    if(!already_rendered && visible) {
     811  		xmlListInsert(list, attr->ns);
     812  	    }
     813  	    if(xmlStrlen(attr->ns->prefix) == 0) {
     814  		has_empty_ns = 1;
     815  	    }
     816  	} else if((attr->ns != NULL) && (xmlStrlen(attr->ns->prefix) == 0) && (xmlStrlen(attr->ns->href) == 0)) {
     817  	    has_visibly_utilized_empty_ns = 1;
     818  	}
     819      }
     820  
     821      /*
     822       * Process xmlns=""
     823       */
     824      if(visible && has_visibly_utilized_empty_ns &&
     825  	    !has_empty_ns && !has_empty_ns_in_inclusive_list) {
     826          static xmlNs ns_default;
     827  
     828          memset(&ns_default, 0, sizeof(ns_default));
     829  
     830          already_rendered = xmlExcC14NVisibleNsStackFind(ctx->ns_rendered, &ns_default, ctx);
     831  	if(!already_rendered) {
     832  	    xmlC14NPrintNamespaces(&ns_default, ctx);
     833  	}
     834      } else if(visible && !has_empty_ns && has_empty_ns_in_inclusive_list) {
     835          static xmlNs ns_default;
     836  
     837          memset(&ns_default, 0, sizeof(ns_default));
     838          if(!xmlC14NVisibleNsStackFind(ctx->ns_rendered, &ns_default)) {
     839  	    xmlC14NPrintNamespaces(&ns_default, ctx);
     840  	}
     841      }
     842  
     843  
     844  
     845      /*
     846       * print out all elements from list
     847       */
     848      xmlListWalk(list, xmlC14NPrintNamespacesWalker, (void *) ctx);
     849  
     850      /*
     851       * Cleanup
     852       */
     853      xmlListDelete(list);
     854      return (0);
     855  }
     856  
     857  
     858  /**
     859   * xmlC14NIsXmlAttr:
     860   * @attr:		the attr to check
     861   *
     862   * Checks whether the given attribute is a default "xml:" namespace
     863   * with href="http://www.w3.org/XML/1998/namespace"
     864   *
     865   * Returns 1 if the node is default or 0 otherwise
     866   */
     867  
     868  /* todo: make it a define? */
     869  static int
     870  xmlC14NIsXmlAttr(xmlAttrPtr attr)
     871  {
     872      return ((attr->ns != NULL) &&
     873             (xmlC14NIsXmlNs(attr->ns) != 0));
     874  }
     875  
     876  
     877  /**
     878   * xmlC14NAttrsCompare:
     879   * @attr1:		the pointer tls o first attr
     880   * @attr2:		the pointer to second attr
     881   *
     882   * Prints the given attribute to the output buffer from C14N context.
     883   *
     884   * Returns -1 if attr1 < attr2, 0 if attr1 == attr2 or 1 if attr1 > attr2.
     885   */
     886  static int
     887  xmlC14NAttrsCompare(const void *data1, const void *data2)
     888  {
     889      const xmlAttrPtr attr1 = (const xmlAttrPtr) data1;
     890      const xmlAttrPtr attr2 = (const xmlAttrPtr) data2;
     891      int ret = 0;
     892  
     893      /*
     894       * Simple cases
     895       */
     896      if (attr1 == attr2)
     897          return (0);
     898      if (attr1 == NULL)
     899          return (-1);
     900      if (attr2 == NULL)
     901          return (1);
     902      if (attr1->ns == attr2->ns) {
     903          return (xmlStrcmp(attr1->name, attr2->name));
     904      }
     905  
     906      /*
     907       * Attributes in the default namespace are first
     908       * because the default namespace is not applied to
     909       * unqualified attributes
     910       */
     911      if (attr1->ns == NULL)
     912          return (-1);
     913      if (attr2->ns == NULL)
     914          return (1);
     915      if (attr1->ns->prefix == NULL)
     916          return (-1);
     917      if (attr2->ns->prefix == NULL)
     918          return (1);
     919  
     920      ret = xmlStrcmp(attr1->ns->href, attr2->ns->href);
     921      if (ret == 0) {
     922          ret = xmlStrcmp(attr1->name, attr2->name);
     923      }
     924      return (ret);
     925  }
     926  
     927  
     928  /**
     929   * xmlC14NPrintAttrs:
     930   * @attr:		the pointer to attr
     931   * @ctx:		the C14N context
     932   *
     933   * Prints out canonical attribute urrent node to the
     934   * buffer from C14N context as follows
     935   *
     936   * Canonical XML v 1.0 (http://www.w3.org/TR/xml-c14n)
     937   *
     938   * Returns 1 on success or 0 on fail.
     939   */
     940  static int
     941  xmlC14NPrintAttrs(const void *data, void *user)
     942  {
     943      const xmlAttrPtr attr = (const xmlAttrPtr) data;
     944      xmlC14NCtxPtr ctx = (xmlC14NCtxPtr) user;
     945      xmlChar *value;
     946      xmlChar *buffer;
     947  
     948      if ((attr == NULL) || (ctx == NULL)) {
     949          xmlC14NErrParam("writing attributes");
     950          return (0);
     951      }
     952  
     953      xmlOutputBufferWriteString(ctx->buf, " ");
     954      if (attr->ns != NULL && xmlStrlen(attr->ns->prefix) > 0) {
     955          xmlOutputBufferWriteString(ctx->buf,
     956                                     (const char *) attr->ns->prefix);
     957          xmlOutputBufferWriteString(ctx->buf, ":");
     958      }
     959      xmlOutputBufferWriteString(ctx->buf, (const char *) attr->name);
     960      xmlOutputBufferWriteString(ctx->buf, "=\"");
     961  
     962      value = xmlNodeListGetString(ctx->doc, attr->children, 1);
     963      /* todo: should we log an error if value==NULL ? */
     964      if (value != NULL) {
     965          buffer = xmlC11NNormalizeAttr(value);
     966          xmlFree(value);
     967          if (buffer != NULL) {
     968              xmlOutputBufferWriteString(ctx->buf, (const char *) buffer);
     969              xmlFree(buffer);
     970          } else {
     971              xmlC14NErrInternal("normalizing attributes axis");
     972              return (0);
     973          }
     974      }
     975      xmlOutputBufferWriteString(ctx->buf, "\"");
     976      return (1);
     977  }
     978  
     979  /**
     980   * xmlC14NFindHiddenParentAttr:
     981   *
     982   * Finds an attribute in a hidden parent node.
     983   *
     984   * Returns a pointer to the attribute node (if found) or NULL otherwise.
     985   */
     986  static xmlAttrPtr
     987  xmlC14NFindHiddenParentAttr(xmlC14NCtxPtr ctx, xmlNodePtr cur, const xmlChar * name, const xmlChar * ns)
     988  {
     989      xmlAttrPtr res;
     990      while((cur != NULL) && (!xmlC14NIsVisible(ctx, cur, cur->parent))) {
     991          res = xmlHasNsProp(cur, name, ns);
     992          if(res != NULL) {
     993              return res;
     994          }
     995  
     996          cur = cur->parent;
     997      }
     998  
     999      return NULL;
    1000  }
    1001  
    1002  /**
    1003   * xmlC14NFixupBaseAttr:
    1004   *
    1005   * Fixes up the xml:base attribute
    1006   *
    1007   * Returns the newly created attribute or NULL
    1008   */
    1009  static xmlAttrPtr
    1010  xmlC14NFixupBaseAttr(xmlC14NCtxPtr ctx, xmlAttrPtr xml_base_attr)
    1011  {
    1012      xmlChar * res = NULL;
    1013      xmlNodePtr cur;
    1014      xmlAttrPtr attr;
    1015      xmlChar * tmp_str;
    1016      xmlChar * tmp_str2;
    1017      int tmp_str_len;
    1018  
    1019      if ((ctx == NULL) || (xml_base_attr == NULL) || (xml_base_attr->parent == NULL)) {
    1020          xmlC14NErrParam("processing xml:base attribute");
    1021          return (NULL);
    1022      }
    1023  
    1024      /* start from current value */
    1025      res = xmlNodeListGetString(ctx->doc, xml_base_attr->children, 1);
    1026      if(res == NULL) {
    1027          xmlC14NErrInternal("processing xml:base attribute - can't get attr value");
    1028          return (NULL);
    1029      }
    1030  
    1031      /* go up the stack until we find a node that we rendered already */
    1032      cur = xml_base_attr->parent->parent;
    1033      while((cur != NULL) && (!xmlC14NIsVisible(ctx, cur, cur->parent))) {
    1034          attr = xmlHasNsProp(cur, BAD_CAST "base", XML_XML_NAMESPACE);
    1035          if(attr != NULL) {
    1036              /* get attr value */
    1037              tmp_str = xmlNodeListGetString(ctx->doc, attr->children, 1);
    1038              if(tmp_str == NULL) {
    1039                  xmlFree(res);
    1040  
    1041                  xmlC14NErrInternal("processing xml:base attribute - can't get attr value");
    1042                  return (NULL);
    1043              }
    1044  
    1045              /* we need to add '/' if our current base uri ends with '..' or '.'
    1046              to ensure that we are forced to go "up" all the time */
    1047              tmp_str_len = xmlStrlen(tmp_str);
    1048              if(tmp_str_len > 1 && tmp_str[tmp_str_len - 2] == '.') {
    1049                  tmp_str2 = xmlStrcat(tmp_str, BAD_CAST "/");
    1050                  if(tmp_str2 == NULL) {
    1051                      xmlFree(tmp_str);
    1052                      xmlFree(res);
    1053  
    1054                      xmlC14NErrInternal("processing xml:base attribute - can't modify uri");
    1055                      return (NULL);
    1056                  }
    1057  
    1058                  tmp_str = tmp_str2;
    1059              }
    1060  
    1061              /* build uri */
    1062              tmp_str2 = xmlBuildURI(res, tmp_str);
    1063              if(tmp_str2 == NULL) {
    1064                  xmlFree(tmp_str);
    1065                  xmlFree(res);
    1066  
    1067                  xmlC14NErrInternal("processing xml:base attribute - can't construct uri");
    1068                  return (NULL);
    1069              }
    1070  
    1071              /* cleanup and set the new res */
    1072              xmlFree(tmp_str);
    1073              xmlFree(res);
    1074              res = tmp_str2;
    1075          }
    1076  
    1077          /* next */
    1078          cur = cur->parent;
    1079      }
    1080  
    1081      /* check if result uri is empty or not */
    1082      if((res == NULL) || xmlStrEqual(res, BAD_CAST "")) {
    1083          xmlFree(res);
    1084          return (NULL);
    1085      }
    1086  
    1087      /* create and return the new attribute node */
    1088      attr = xmlNewNsProp(NULL, xml_base_attr->ns, BAD_CAST "base", res);
    1089      if(attr == NULL) {
    1090          xmlFree(res);
    1091  
    1092          xmlC14NErrInternal("processing xml:base attribute - can't construct attribute");
    1093          return (NULL);
    1094      }
    1095  
    1096      /* done */
    1097      xmlFree(res);
    1098      return (attr);
    1099  }
    1100  
    1101  /**
    1102   * xmlC14NProcessAttrsAxis:
    1103   * @ctx:		the C14N context
    1104   * @cur:		the current node
    1105   * @parent_visible:	the visibility of parent node
    1106   * @all_parents_visible: the visibility of all parent nodes
    1107   *
    1108   * Prints out canonical attribute axis of the current node to the
    1109   * buffer from C14N context as follows
    1110   *
    1111   * Canonical XML v 1.0 (http://www.w3.org/TR/xml-c14n)
    1112   *
    1113   * Attribute Axis
    1114   * In lexicographic order (ascending), process each node that
    1115   * is in the element's attribute axis and in the node-set.
    1116   *
    1117   * The processing of an element node E MUST be modified slightly
    1118   * when an XPath node-set is given as input and the element's
    1119   * parent is omitted from the node-set.
    1120   *
    1121   *
    1122   * Exclusive XML Canonicalization v 1.0 (http://www.w3.org/TR/xml-exc-c14n)
    1123   *
    1124   * Canonical XML applied to a document subset requires the search of the
    1125   * ancestor nodes of each orphan element node for attributes in the xml
    1126   * namespace, such as xml:lang and xml:space. These are copied into the
    1127   * element node except if a declaration of the same attribute is already
    1128   * in the attribute axis of the element (whether or not it is included in
    1129   * the document subset). This search and copying are omitted from the
    1130   * Exclusive XML Canonicalization method.
    1131   *
    1132   * Returns 0 on success or -1 on fail.
    1133   */
    1134  static int
    1135  xmlC14NProcessAttrsAxis(xmlC14NCtxPtr ctx, xmlNodePtr cur, int parent_visible)
    1136  {
    1137      xmlAttrPtr attr;
    1138      xmlListPtr list;
    1139      xmlAttrPtr attrs_to_delete = NULL;
    1140  
    1141      /* special processing for 1.1 spec */
    1142      xmlAttrPtr xml_base_attr = NULL;
    1143      xmlAttrPtr xml_lang_attr = NULL;
    1144      xmlAttrPtr xml_space_attr = NULL;
    1145  
    1146      if ((ctx == NULL) || (cur == NULL) || (cur->type != XML_ELEMENT_NODE)) {
    1147          xmlC14NErrParam("processing attributes axis");
    1148          return (-1);
    1149      }
    1150  
    1151      /*
    1152       * Create a sorted list to store element attributes
    1153       */
    1154      list = xmlListCreate(NULL, xmlC14NAttrsCompare);
    1155      if (list == NULL) {
    1156          xmlC14NErrInternal("creating attributes list");
    1157          return (-1);
    1158      }
    1159  
    1160      switch(ctx->mode) {
    1161      case XML_C14N_1_0:
    1162          /* The processing of an element node E MUST be modified slightly when an XPath node-set is
    1163           * given as input and the element's parent is omitted from the node-set. The method for processing
    1164           * the attribute axis of an element E in the node-set is enhanced. All element nodes along E's
    1165           * ancestor axis are examined for nearest occurrences of attributes in the xml namespace, such
    1166           * as xml:lang and xml:space (whether or not they are in the node-set). From this list of attributes,
    1167           * remove any that are in E's attribute axis (whether or not they are in the node-set). Then,
    1168           * lexicographically merge this attribute list with the nodes of E's attribute axis that are in
    1169           * the node-set. The result of visiting the attribute axis is computed by processing the attribute
    1170           * nodes in this merged attribute list.
    1171           */
    1172  
    1173          /*
    1174           * Add all visible attributes from current node.
    1175           */
    1176          attr = cur->properties;
    1177          while (attr != NULL) {
    1178              /* check that attribute is visible */
    1179              if (xmlC14NIsVisible(ctx, attr, cur)) {
    1180                  xmlListInsert(list, attr);
    1181              }
    1182              attr = attr->next;
    1183          }
    1184  
    1185          /*
    1186           * Handle xml attributes
    1187           */
    1188          if (parent_visible && (cur->parent != NULL) &&
    1189              (!xmlC14NIsVisible(ctx, cur->parent, cur->parent->parent)))
    1190          {
    1191              xmlNodePtr tmp;
    1192  
    1193              /*
    1194               * If XPath node-set is not specified then the parent is always
    1195               * visible!
    1196               */
    1197              tmp = cur->parent;
    1198              while (tmp != NULL) {
    1199                  attr = tmp->properties;
    1200                  while (attr != NULL) {
    1201                      if (xmlC14NIsXmlAttr(attr) != 0) {
    1202                          if (xmlListSearch(list, attr) == NULL) {
    1203                              xmlListInsert(list, attr);
    1204                          }
    1205                      }
    1206                      attr = attr->next;
    1207                  }
    1208                  tmp = tmp->parent;
    1209              }
    1210          }
    1211  
    1212          /* done */
    1213          break;
    1214      case XML_C14N_EXCLUSIVE_1_0:
    1215          /* attributes in the XML namespace, such as xml:lang and xml:space
    1216           * are not imported into orphan nodes of the document subset
    1217           */
    1218  
    1219          /*
    1220           * Add all visible attributes from current node.
    1221           */
    1222          attr = cur->properties;
    1223          while (attr != NULL) {
    1224              /* check that attribute is visible */
    1225              if (xmlC14NIsVisible(ctx, attr, cur)) {
    1226                  xmlListInsert(list, attr);
    1227              }
    1228              attr = attr->next;
    1229          }
    1230  
    1231          /* do nothing special for xml attributes */
    1232          break;
    1233      case XML_C14N_1_1:
    1234          /* The processing of an element node E MUST be modified slightly when an XPath node-set is
    1235           * given as input and some of the element's ancestors are omitted from the node-set.
    1236           *
    1237           * Simple inheritable attributes are attributes that have a value that requires at most a simple
    1238           * redeclaration. This redeclaration is done by supplying a new value in the child axis. The
    1239           * redeclaration of a simple inheritable attribute A contained in one of E's ancestors is done
    1240           * by supplying a value to an attribute Ae inside E with the same name. Simple inheritable attributes
    1241           * are xml:lang and xml:space.
    1242           *
    1243           * The method for processing the attribute axis of an element E in the node-set is hence enhanced.
    1244           * All element nodes along E's ancestor axis are examined for the nearest occurrences of simple
    1245           * inheritable attributes in the xml namespace, such as xml:lang and xml:space (whether or not they
    1246           * are in the node-set). From this list of attributes, any simple inheritable attributes that are
    1247           * already in E's attribute axis (whether or not they are in the node-set) are removed. Then,
    1248           * lexicographically merge this attribute list with the nodes of E's attribute axis that are in
    1249           * the node-set. The result of visiting the attribute axis is computed by processing the attribute
    1250           * nodes in this merged attribute list.
    1251           *
    1252           * The xml:id attribute is not a simple inheritable attribute and no processing of these attributes is
    1253           * performed.
    1254           *
    1255           * The xml:base attribute is not a simple inheritable attribute and requires special processing beyond
    1256           * a simple redeclaration.
    1257           *
    1258           * Attributes in the XML namespace other than xml:base, xml:id, xml:lang, and xml:space MUST be processed
    1259           * as ordinary attributes.
    1260           */
    1261  
    1262          /*
    1263           * Add all visible attributes from current node.
    1264           */
    1265          attr = cur->properties;
    1266          while (attr != NULL) {
    1267              /* special processing for XML attribute kiks in only when we have invisible parents */
    1268              if ((!parent_visible) || (xmlC14NIsXmlAttr(attr) == 0)) {
    1269                  /* check that attribute is visible */
    1270                  if (xmlC14NIsVisible(ctx, attr, cur)) {
    1271                      xmlListInsert(list, attr);
    1272                  }
    1273              } else {
    1274                  int matched = 0;
    1275  
    1276                  /* check for simple inheritance attributes */
    1277                  if((!matched) && (xml_lang_attr == NULL) && xmlStrEqual(attr->name, BAD_CAST "lang")) {
    1278                      xml_lang_attr = attr;
    1279                      matched = 1;
    1280                  }
    1281                  if((!matched) && (xml_space_attr == NULL) && xmlStrEqual(attr->name, BAD_CAST "space")) {
    1282                      xml_space_attr = attr;
    1283                      matched = 1;
    1284                  }
    1285  
    1286                  /* check for base attr */
    1287                  if((!matched) && (xml_base_attr == NULL) && xmlStrEqual(attr->name, BAD_CAST "base")) {
    1288                      xml_base_attr = attr;
    1289                      matched = 1;
    1290                  }
    1291  
    1292                  /* otherwise, it is a normal attribute, so just check if it is visible */
    1293                  if((!matched) && xmlC14NIsVisible(ctx, attr, cur)) {
    1294                      xmlListInsert(list, attr);
    1295                  }
    1296              }
    1297  
    1298              /* move to the next one */
    1299              attr = attr->next;
    1300          }
    1301  
    1302          /* special processing for XML attribute kiks in only when we have invisible parents */
    1303          if ((parent_visible)) {
    1304  
    1305              /* simple inheritance attributes - copy */
    1306              if(xml_lang_attr == NULL) {
    1307                  xml_lang_attr = xmlC14NFindHiddenParentAttr(ctx, cur->parent, BAD_CAST "lang", XML_XML_NAMESPACE);
    1308              }
    1309              if(xml_lang_attr != NULL) {
    1310                  xmlListInsert(list, xml_lang_attr);
    1311              }
    1312              if(xml_space_attr == NULL) {
    1313                  xml_space_attr = xmlC14NFindHiddenParentAttr(ctx, cur->parent, BAD_CAST "space", XML_XML_NAMESPACE);
    1314              }
    1315              if(xml_space_attr != NULL) {
    1316                  xmlListInsert(list, xml_space_attr);
    1317              }
    1318  
    1319              /* base uri attribute - fix up */
    1320              if(xml_base_attr == NULL) {
    1321                  /* if we don't have base uri attribute, check if we have a "hidden" one above */
    1322                  xml_base_attr = xmlC14NFindHiddenParentAttr(ctx, cur->parent, BAD_CAST "base", XML_XML_NAMESPACE);
    1323              }
    1324              if(xml_base_attr != NULL) {
    1325                  xml_base_attr = xmlC14NFixupBaseAttr(ctx, xml_base_attr);
    1326                  if(xml_base_attr != NULL) {
    1327                      xmlListInsert(list, xml_base_attr);
    1328  
    1329                      /* note that we MUST delete returned attr node ourselves! */
    1330                      xml_base_attr->next = attrs_to_delete;
    1331                      attrs_to_delete = xml_base_attr;
    1332                  }
    1333              }
    1334          }
    1335  
    1336          /* done */
    1337          break;
    1338      }
    1339  
    1340      /*
    1341       * print out all elements from list
    1342       */
    1343      xmlListWalk(list, xmlC14NPrintAttrs, (void *) ctx);
    1344  
    1345      /*
    1346       * Cleanup
    1347       */
    1348      xmlFreePropList(attrs_to_delete);
    1349      xmlListDelete(list);
    1350      return (0);
    1351  }
    1352  
    1353  /**
    1354   * xmlC14NCheckForRelativeNamespaces:
    1355   * @ctx:		the C14N context
    1356   * @cur:		the current element node
    1357   *
    1358   * Checks that current element node has no relative namespaces defined
    1359   *
    1360   * Returns 0 if the node has no relative namespaces or -1 otherwise.
    1361   */
    1362  static int
    1363  xmlC14NCheckForRelativeNamespaces(xmlC14NCtxPtr ctx, xmlNodePtr cur)
    1364  {
    1365      xmlNsPtr ns;
    1366  
    1367      if ((ctx == NULL) || (cur == NULL) || (cur->type != XML_ELEMENT_NODE)) {
    1368          xmlC14NErrParam("checking for relative namespaces");
    1369          return (-1);
    1370      }
    1371  
    1372      ns = cur->nsDef;
    1373      while (ns != NULL) {
    1374          if (xmlStrlen(ns->href) > 0) {
    1375              xmlURIPtr uri;
    1376  
    1377              uri = xmlParseURI((const char *) ns->href);
    1378              if (uri == NULL) {
    1379                  xmlC14NErrInternal("parsing namespace uri");
    1380                  return (-1);
    1381              }
    1382              if (xmlStrlen((const xmlChar *) uri->scheme) == 0) {
    1383                  xmlC14NErrRelativeNamespace(uri->scheme);
    1384                  xmlFreeURI(uri);
    1385                  return (-1);
    1386              }
    1387              xmlFreeURI(uri);
    1388          }
    1389          ns = ns->next;
    1390      }
    1391      return (0);
    1392  }
    1393  
    1394  /**
    1395   * xmlC14NProcessElementNode:
    1396   * @ctx:		the pointer to C14N context object
    1397   * @cur:		the node to process
    1398   * @visible:    this node is visible
    1399   * @all_parents_visible: whether all the parents of this node are visible
    1400   *
    1401   * Canonical XML v 1.0 (http://www.w3.org/TR/xml-c14n)
    1402   *
    1403   * Element Nodes
    1404   * If the element is not in the node-set, then the result is obtained
    1405   * by processing the namespace axis, then the attribute axis, then
    1406   * processing the child nodes of the element that are in the node-set
    1407   * (in document order). If the element is in the node-set, then the result
    1408   * is an open angle bracket (<), the element QName, the result of
    1409   * processing the namespace axis, the result of processing the attribute
    1410   * axis, a close angle bracket (>), the result of processing the child
    1411   * nodes of the element that are in the node-set (in document order), an
    1412   * open angle bracket, a forward slash (/), the element QName, and a close
    1413   * angle bracket.
    1414   *
    1415   * Returns non-negative value on success or negative value on fail
    1416   */
    1417  static int
    1418  xmlC14NProcessElementNode(xmlC14NCtxPtr ctx, xmlNodePtr cur, int visible)
    1419  {
    1420      int ret;
    1421      xmlC14NVisibleNsStack state;
    1422      int parent_is_doc = 0;
    1423  
    1424      if ((ctx == NULL) || (cur == NULL) || (cur->type != XML_ELEMENT_NODE)) {
    1425          xmlC14NErrParam("processing element node");
    1426          return (-1);
    1427      }
    1428  
    1429      /*
    1430       * Check relative relative namespaces:
    1431       * implementations of XML canonicalization MUST report an operation
    1432       * failure on documents containing relative namespace URIs.
    1433       */
    1434      if (xmlC14NCheckForRelativeNamespaces(ctx, cur) < 0) {
    1435          xmlC14NErrInternal("checking for relative namespaces");
    1436          return (-1);
    1437      }
    1438  
    1439  
    1440      /*
    1441       * Save ns_rendered stack position
    1442       */
    1443      memset(&state, 0, sizeof(state));
    1444      xmlC14NVisibleNsStackSave(ctx->ns_rendered, &state);
    1445  
    1446      if (visible) {
    1447          if (ctx->parent_is_doc) {
    1448  	    /* save this flag into the stack */
    1449  	    parent_is_doc = ctx->parent_is_doc;
    1450  	    ctx->parent_is_doc = 0;
    1451              ctx->pos = XMLC14N_INSIDE_DOCUMENT_ELEMENT;
    1452          }
    1453          xmlOutputBufferWriteString(ctx->buf, "<");
    1454  
    1455          if ((cur->ns != NULL) && (xmlStrlen(cur->ns->prefix) > 0)) {
    1456              xmlOutputBufferWriteString(ctx->buf,
    1457                                         (const char *) cur->ns->prefix);
    1458              xmlOutputBufferWriteString(ctx->buf, ":");
    1459          }
    1460          xmlOutputBufferWriteString(ctx->buf, (const char *) cur->name);
    1461      }
    1462  
    1463      if (!xmlC14NIsExclusive(ctx)) {
    1464          ret = xmlC14NProcessNamespacesAxis(ctx, cur, visible);
    1465      } else {
    1466          ret = xmlExcC14NProcessNamespacesAxis(ctx, cur, visible);
    1467      }
    1468      if (ret < 0) {
    1469          xmlC14NErrInternal("processing namespaces axis");
    1470          return (-1);
    1471      }
    1472      /* todo: shouldn't this go to "visible only"? */
    1473      if(visible) {
    1474  	xmlC14NVisibleNsStackShift(ctx->ns_rendered);
    1475      }
    1476  
    1477      ret = xmlC14NProcessAttrsAxis(ctx, cur, visible);
    1478      if (ret < 0) {
    1479  	xmlC14NErrInternal("processing attributes axis");
    1480  	return (-1);
    1481      }
    1482  
    1483      if (visible) {
    1484          xmlOutputBufferWriteString(ctx->buf, ">");
    1485      }
    1486      if (cur->children != NULL) {
    1487          ret = xmlC14NProcessNodeList(ctx, cur->children);
    1488          if (ret < 0) {
    1489              xmlC14NErrInternal("processing childrens list");
    1490              return (-1);
    1491          }
    1492      }
    1493      if (visible) {
    1494          xmlOutputBufferWriteString(ctx->buf, "</");
    1495          if ((cur->ns != NULL) && (xmlStrlen(cur->ns->prefix) > 0)) {
    1496              xmlOutputBufferWriteString(ctx->buf,
    1497                                         (const char *) cur->ns->prefix);
    1498              xmlOutputBufferWriteString(ctx->buf, ":");
    1499          }
    1500          xmlOutputBufferWriteString(ctx->buf, (const char *) cur->name);
    1501          xmlOutputBufferWriteString(ctx->buf, ">");
    1502          if (parent_is_doc) {
    1503  	    /* restore this flag from the stack for next node */
    1504              ctx->parent_is_doc = parent_is_doc;
    1505  	    ctx->pos = XMLC14N_AFTER_DOCUMENT_ELEMENT;
    1506          }
    1507      }
    1508  
    1509      /*
    1510       * Restore ns_rendered stack position
    1511       */
    1512      xmlC14NVisibleNsStackRestore(ctx->ns_rendered, &state);
    1513      return (0);
    1514  }
    1515  
    1516  /**
    1517   * xmlC14NProcessNode:
    1518   * @ctx:		the pointer to C14N context object
    1519   * @cur:		the node to process
    1520   *
    1521   * Processes the given node
    1522   *
    1523   * Returns non-negative value on success or negative value on fail
    1524   */
    1525  static int
    1526  xmlC14NProcessNode(xmlC14NCtxPtr ctx, xmlNodePtr cur)
    1527  {
    1528      int ret = 0;
    1529      int visible;
    1530  
    1531      if ((ctx == NULL) || (cur == NULL)) {
    1532          xmlC14NErrParam("processing node");
    1533          return (-1);
    1534      }
    1535  
    1536      visible = xmlC14NIsVisible(ctx, cur, cur->parent);
    1537      switch (cur->type) {
    1538          case XML_ELEMENT_NODE:
    1539              ret = xmlC14NProcessElementNode(ctx, cur, visible);
    1540              break;
    1541          case XML_CDATA_SECTION_NODE:
    1542          case XML_TEXT_NODE:
    1543              /*
    1544               * Text Nodes
    1545               * the string value, except all ampersands are replaced
    1546               * by &amp;, all open angle brackets (<) are replaced by &lt;, all closing
    1547               * angle brackets (>) are replaced by &gt;, and all #xD characters are
    1548               * replaced by &#xD;.
    1549               */
    1550              /* cdata sections are processed as text nodes */
    1551              /* todo: verify that cdata sections are included in XPath nodes set */
    1552              if ((visible) && (cur->content != NULL)) {
    1553                  xmlChar *buffer;
    1554  
    1555                  buffer = xmlC11NNormalizeText(cur->content);
    1556                  if (buffer != NULL) {
    1557                      xmlOutputBufferWriteString(ctx->buf,
    1558                                                 (const char *) buffer);
    1559                      xmlFree(buffer);
    1560                  } else {
    1561                      xmlC14NErrInternal("normalizing text node");
    1562                      return (-1);
    1563                  }
    1564              }
    1565              break;
    1566          case XML_PI_NODE:
    1567              /*
    1568               * Processing Instruction (PI) Nodes-
    1569               * The opening PI symbol (<?), the PI target name of the node,
    1570               * a leading space and the string value if it is not empty, and
    1571               * the closing PI symbol (?>). If the string value is empty,
    1572               * then the leading space is not added. Also, a trailing #xA is
    1573               * rendered after the closing PI symbol for PI children of the
    1574               * root node with a lesser document order than the document
    1575               * element, and a leading #xA is rendered before the opening PI
    1576               * symbol of PI children of the root node with a greater document
    1577               * order than the document element.
    1578               */
    1579              if (visible) {
    1580                  if (ctx->pos == XMLC14N_AFTER_DOCUMENT_ELEMENT) {
    1581                      xmlOutputBufferWriteString(ctx->buf, "\x0A<?");
    1582                  } else {
    1583                      xmlOutputBufferWriteString(ctx->buf, "<?");
    1584                  }
    1585  
    1586                  xmlOutputBufferWriteString(ctx->buf,
    1587                                             (const char *) cur->name);
    1588                  if ((cur->content != NULL) && (*(cur->content) != '\0')) {
    1589                      xmlChar *buffer;
    1590  
    1591                      xmlOutputBufferWriteString(ctx->buf, " ");
    1592  
    1593                      /* todo: do we need to normalize pi? */
    1594                      buffer = xmlC11NNormalizePI(cur->content);
    1595                      if (buffer != NULL) {
    1596                          xmlOutputBufferWriteString(ctx->buf,
    1597                                                     (const char *) buffer);
    1598                          xmlFree(buffer);
    1599                      } else {
    1600                          xmlC14NErrInternal("normalizing pi node");
    1601                          return (-1);
    1602                      }
    1603                  }
    1604  
    1605                  if (ctx->pos == XMLC14N_BEFORE_DOCUMENT_ELEMENT) {
    1606                      xmlOutputBufferWriteString(ctx->buf, "?>\x0A");
    1607                  } else {
    1608                      xmlOutputBufferWriteString(ctx->buf, "?>");
    1609                  }
    1610              }
    1611              break;
    1612          case XML_COMMENT_NODE:
    1613              /*
    1614               * Comment Nodes
    1615               * Nothing if generating canonical XML without  comments. For
    1616               * canonical XML with comments, generate the opening comment
    1617               * symbol (<!--), the string value of the node, and the
    1618               * closing comment symbol (-->). Also, a trailing #xA is rendered
    1619               * after the closing comment symbol for comment children of the
    1620               * root node with a lesser document order than the document
    1621               * element, and a leading #xA is rendered before the opening
    1622               * comment symbol of comment children of the root node with a
    1623               * greater document order than the document element. (Comment
    1624               * children of the root node represent comments outside of the
    1625               * top-level document element and outside of the document type
    1626               * declaration).
    1627               */
    1628              if (visible && ctx->with_comments) {
    1629                  if (ctx->pos == XMLC14N_AFTER_DOCUMENT_ELEMENT) {
    1630                      xmlOutputBufferWriteString(ctx->buf, "\x0A<!--");
    1631                  } else {
    1632                      xmlOutputBufferWriteString(ctx->buf, "<!--");
    1633                  }
    1634  
    1635                  if (cur->content != NULL) {
    1636                      xmlChar *buffer;
    1637  
    1638                      /* todo: do we need to normalize comment? */
    1639                      buffer = xmlC11NNormalizeComment(cur->content);
    1640                      if (buffer != NULL) {
    1641                          xmlOutputBufferWriteString(ctx->buf,
    1642                                                     (const char *) buffer);
    1643                          xmlFree(buffer);
    1644                      } else {
    1645                          xmlC14NErrInternal("normalizing comment node");
    1646                          return (-1);
    1647                      }
    1648                  }
    1649  
    1650                  if (ctx->pos == XMLC14N_BEFORE_DOCUMENT_ELEMENT) {
    1651                      xmlOutputBufferWriteString(ctx->buf, "-->\x0A");
    1652                  } else {
    1653                      xmlOutputBufferWriteString(ctx->buf, "-->");
    1654                  }
    1655              }
    1656              break;
    1657          case XML_DOCUMENT_NODE:
    1658          case XML_DOCUMENT_FRAG_NODE:   /* should be processed as document? */
    1659  #ifdef LIBXML_HTML_ENABLED
    1660          case XML_HTML_DOCUMENT_NODE:   /* should be processed as document? */
    1661  #endif
    1662              if (cur->children != NULL) {
    1663                  ctx->pos = XMLC14N_BEFORE_DOCUMENT_ELEMENT;
    1664                  ctx->parent_is_doc = 1;
    1665                  ret = xmlC14NProcessNodeList(ctx, cur->children);
    1666              }
    1667              break;
    1668  
    1669          case XML_ATTRIBUTE_NODE:
    1670              xmlC14NErrInvalidNode("XML_ATTRIBUTE_NODE", "processing node");
    1671              return (-1);
    1672          case XML_NAMESPACE_DECL:
    1673              xmlC14NErrInvalidNode("XML_NAMESPACE_DECL", "processing node");
    1674              return (-1);
    1675          case XML_ENTITY_REF_NODE:
    1676              xmlC14NErrInvalidNode("XML_ENTITY_REF_NODE", "processing node");
    1677              return (-1);
    1678          case XML_ENTITY_NODE:
    1679              xmlC14NErrInvalidNode("XML_ENTITY_NODE", "processing node");
    1680              return (-1);
    1681  
    1682          case XML_DOCUMENT_TYPE_NODE:
    1683          case XML_NOTATION_NODE:
    1684          case XML_DTD_NODE:
    1685          case XML_ELEMENT_DECL:
    1686          case XML_ATTRIBUTE_DECL:
    1687          case XML_ENTITY_DECL:
    1688  #ifdef LIBXML_XINCLUDE_ENABLED
    1689          case XML_XINCLUDE_START:
    1690          case XML_XINCLUDE_END:
    1691  #endif
    1692              /*
    1693               * should be ignored according to "W3C Canonical XML"
    1694               */
    1695              break;
    1696          default:
    1697              xmlC14NErrUnknownNode(cur->type, "processing node");
    1698              return (-1);
    1699      }
    1700  
    1701      return (ret);
    1702  }
    1703  
    1704  /**
    1705   * xmlC14NProcessNodeList:
    1706   * @ctx:		the pointer to C14N context object
    1707   * @cur:		the node to start from
    1708   *
    1709   * Processes all nodes in the row starting from cur.
    1710   *
    1711   * Returns non-negative value on success or negative value on fail
    1712   */
    1713  static int
    1714  xmlC14NProcessNodeList(xmlC14NCtxPtr ctx, xmlNodePtr cur)
    1715  {
    1716      int ret;
    1717  
    1718      if (ctx == NULL) {
    1719          xmlC14NErrParam("processing node list");
    1720          return (-1);
    1721      }
    1722  
    1723      for (ret = 0; cur != NULL && ret >= 0; cur = cur->next) {
    1724          ret = xmlC14NProcessNode(ctx, cur);
    1725      }
    1726      return (ret);
    1727  }
    1728  
    1729  
    1730  /**
    1731   * xmlC14NFreeCtx:
    1732   * @ctx: the pointer to C14N context object
    1733   *
    1734   * Cleanups the C14N context object.
    1735   */
    1736  
    1737  static void
    1738  xmlC14NFreeCtx(xmlC14NCtxPtr ctx)
    1739  {
    1740      if (ctx == NULL) {
    1741          xmlC14NErrParam("freeing context");
    1742          return;
    1743      }
    1744  
    1745      if (ctx->ns_rendered != NULL) {
    1746          xmlC14NVisibleNsStackDestroy(ctx->ns_rendered);
    1747      }
    1748      xmlFree(ctx);
    1749  }
    1750  
    1751  /**
    1752   * xmlC14NNewCtx:
    1753   * @doc:		the XML document for canonization
    1754   * @is_visible_callback:the function to use to determine is node visible
    1755   *			or not
    1756   * @user_data:		the first parameter for @is_visible_callback function
    1757   *			(in most cases, it is nodes set)
    1758   * @mode:   the c14n mode (see @xmlC14NMode)
    1759   * @inclusive_ns_prefixe the list of inclusive namespace prefixes
    1760   *			ended with a NULL or NULL if there is no
    1761   *			inclusive namespaces (only for `
    1762   *			canonicalization)
    1763   * @with_comments:	include comments in the result (!=0) or not (==0)
    1764   * @buf:		the output buffer to store canonical XML; this
    1765   *			buffer MUST have encoder==NULL because C14N requires
    1766   *			UTF-8 output
    1767   *
    1768   * Creates new C14N context object to store C14N parameters.
    1769   *
    1770   * Returns pointer to newly created object (success) or NULL (fail)
    1771   */
    1772  static xmlC14NCtxPtr
    1773  xmlC14NNewCtx(xmlDocPtr doc,
    1774  	      xmlC14NIsVisibleCallback is_visible_callback, void* user_data,
    1775                xmlC14NMode mode, xmlChar ** inclusive_ns_prefixes,
    1776                int with_comments, xmlOutputBufferPtr buf)
    1777  {
    1778      xmlC14NCtxPtr ctx = NULL;
    1779  
    1780      if ((doc == NULL) || (buf == NULL)) {
    1781          xmlC14NErrParam("creating new context");
    1782          return (NULL);
    1783      }
    1784  
    1785      /*
    1786       *  Validate the encoding output buffer encoding
    1787       */
    1788      if (buf->encoder != NULL) {
    1789          xmlC14NErr(ctx, (xmlNodePtr) doc, XML_C14N_REQUIRES_UTF8,
    1790  "xmlC14NNewCtx: output buffer encoder != NULL but C14N requires UTF8 output\n");
    1791          return (NULL);
    1792      }
    1793  
    1794      /*
    1795       * Allocate a new xmlC14NCtxPtr and fill the fields.
    1796       */
    1797      ctx = (xmlC14NCtxPtr) xmlMalloc(sizeof(xmlC14NCtx));
    1798      if (ctx == NULL) {
    1799  	xmlC14NErrMemory("creating context");
    1800          return (NULL);
    1801      }
    1802      memset(ctx, 0, sizeof(xmlC14NCtx));
    1803  
    1804      /*
    1805       * initialize C14N context
    1806       */
    1807      ctx->doc = doc;
    1808      ctx->with_comments = with_comments;
    1809      ctx->is_visible_callback = is_visible_callback;
    1810      ctx->user_data = user_data;
    1811      ctx->buf = buf;
    1812      ctx->parent_is_doc = 1;
    1813      ctx->pos = XMLC14N_BEFORE_DOCUMENT_ELEMENT;
    1814      ctx->ns_rendered = xmlC14NVisibleNsStackCreate();
    1815  
    1816      if(ctx->ns_rendered == NULL) {
    1817          xmlC14NErr(ctx, (xmlNodePtr) doc, XML_C14N_CREATE_STACK,
    1818  		   "xmlC14NNewCtx: xmlC14NVisibleNsStackCreate failed\n");
    1819  	xmlC14NFreeCtx(ctx);
    1820          return (NULL);
    1821      }
    1822  
    1823      /*
    1824       * Set "mode" flag and remember list of inclusive prefixes
    1825       * for exclusive c14n
    1826       */
    1827      ctx->mode = mode;
    1828      if(xmlC14NIsExclusive(ctx)) {
    1829          ctx->inclusive_ns_prefixes = inclusive_ns_prefixes;
    1830      }
    1831      return (ctx);
    1832  }
    1833  
    1834  /**
    1835   * xmlC14NExecute:
    1836   * @doc:		the XML document for canonization
    1837   * @is_visible_callback:the function to use to determine is node visible
    1838   *			or not
    1839   * @user_data:		the first parameter for @is_visible_callback function
    1840   *			(in most cases, it is nodes set)
    1841   * @mode:	the c14n mode (see @xmlC14NMode)
    1842   * @inclusive_ns_prefixes: the list of inclusive namespace prefixes
    1843   *			ended with a NULL or NULL if there is no
    1844   *			inclusive namespaces (only for exclusive
    1845   *			canonicalization, ignored otherwise)
    1846   * @with_comments:	include comments in the result (!=0) or not (==0)
    1847   * @buf:		the output buffer to store canonical XML; this
    1848   *			buffer MUST have encoder==NULL because C14N requires
    1849   *			UTF-8 output
    1850   *
    1851   * Dumps the canonized image of given XML document into the provided buffer.
    1852   * For details see "Canonical XML" (http://www.w3.org/TR/xml-c14n) or
    1853   * "Exclusive XML Canonicalization" (http://www.w3.org/TR/xml-exc-c14n)
    1854   *
    1855   * Returns non-negative value on success or a negative value on fail
    1856   */
    1857  int
    1858  xmlC14NExecute(xmlDocPtr doc, xmlC14NIsVisibleCallback is_visible_callback,
    1859  	 void* user_data, int mode, xmlChar **inclusive_ns_prefixes,
    1860  	 int with_comments, xmlOutputBufferPtr buf) {
    1861  
    1862      xmlC14NCtxPtr ctx;
    1863      xmlC14NMode c14n_mode = XML_C14N_1_0;
    1864      int ret;
    1865  
    1866      if ((buf == NULL) || (doc == NULL)) {
    1867          xmlC14NErrParam("executing c14n");
    1868          return (-1);
    1869      }
    1870  
    1871      /* for backward compatibility, we have to have "mode" as "int"
    1872         and here we check that user gives valid value */
    1873      switch(mode) {
    1874      case XML_C14N_1_0:
    1875      case XML_C14N_EXCLUSIVE_1_0:
    1876      case XML_C14N_1_1:
    1877           c14n_mode = (xmlC14NMode)mode;
    1878           break;
    1879      default:
    1880          xmlC14NErrParam("invalid mode for executing c14n");
    1881          return (-1);
    1882      }
    1883  
    1884      /*
    1885       *  Validate the encoding output buffer encoding
    1886       */
    1887      if (buf->encoder != NULL) {
    1888          xmlC14NErr(NULL, (xmlNodePtr) doc, XML_C14N_REQUIRES_UTF8,
    1889  "xmlC14NExecute: output buffer encoder != NULL but C14N requires UTF8 output\n");
    1890          return (-1);
    1891      }
    1892  
    1893      ctx = xmlC14NNewCtx(doc, is_visible_callback, user_data,
    1894  	            c14n_mode, inclusive_ns_prefixes,
    1895                      with_comments, buf);
    1896      if (ctx == NULL) {
    1897          xmlC14NErr(NULL, (xmlNodePtr) doc, XML_C14N_CREATE_CTXT,
    1898  		   "xmlC14NExecute: unable to create C14N context\n");
    1899          return (-1);
    1900      }
    1901  
    1902  
    1903  
    1904      /*
    1905       * Root Node
    1906       * The root node is the parent of the top-level document element. The
    1907       * result of processing each of its child nodes that is in the node-set
    1908       * in document order. The root node does not generate a byte order mark,
    1909       * XML declaration, nor anything from within the document type
    1910       * declaration.
    1911       */
    1912      if (doc->children != NULL) {
    1913          ret = xmlC14NProcessNodeList(ctx, doc->children);
    1914          if (ret < 0) {
    1915              xmlC14NErrInternal("processing docs children list");
    1916              xmlC14NFreeCtx(ctx);
    1917              return (-1);
    1918          }
    1919      }
    1920  
    1921      /*
    1922       * Flush buffer to get number of bytes written
    1923       */
    1924      ret = xmlOutputBufferFlush(buf);
    1925      if (ret < 0) {
    1926          xmlC14NErrInternal("flushing output buffer");
    1927          xmlC14NFreeCtx(ctx);
    1928          return (-1);
    1929      }
    1930  
    1931      /*
    1932       * Cleanup
    1933       */
    1934      xmlC14NFreeCtx(ctx);
    1935      return (ret);
    1936  }
    1937  
    1938  /**
    1939   * xmlC14NDocSaveTo:
    1940   * @doc:		the XML document for canonization
    1941   * @nodes:		the nodes set to be included in the canonized image
    1942   *		or NULL if all document nodes should be included
    1943   * @mode:		the c14n mode (see @xmlC14NMode)
    1944   * @inclusive_ns_prefixes: the list of inclusive namespace prefixes
    1945   *			ended with a NULL or NULL if there is no
    1946   *			inclusive namespaces (only for exclusive
    1947   *			canonicalization, ignored otherwise)
    1948   * @with_comments:	include comments in the result (!=0) or not (==0)
    1949   * @buf:		the output buffer to store canonical XML; this
    1950   *			buffer MUST have encoder==NULL because C14N requires
    1951   *			UTF-8 output
    1952   *
    1953   * Dumps the canonized image of given XML document into the provided buffer.
    1954   * For details see "Canonical XML" (http://www.w3.org/TR/xml-c14n) or
    1955   * "Exclusive XML Canonicalization" (http://www.w3.org/TR/xml-exc-c14n)
    1956   *
    1957   * Returns non-negative value on success or a negative value on fail
    1958   */
    1959  int
    1960  xmlC14NDocSaveTo(xmlDocPtr doc, xmlNodeSetPtr nodes,
    1961                   int mode, xmlChar ** inclusive_ns_prefixes,
    1962                   int with_comments, xmlOutputBufferPtr buf) {
    1963      return(xmlC14NExecute(doc,
    1964  			xmlC14NIsNodeInNodeset,
    1965  			nodes,
    1966  			mode,
    1967  			inclusive_ns_prefixes,
    1968  			with_comments,
    1969  			buf));
    1970  }
    1971  
    1972  
    1973  /**
    1974   * xmlC14NDocDumpMemory:
    1975   * @doc:		the XML document for canonization
    1976   * @nodes:		the nodes set to be included in the canonized image
    1977   *		or NULL if all document nodes should be included
    1978   * @mode:		the c14n mode (see @xmlC14NMode)
    1979   * @inclusive_ns_prefixes: the list of inclusive namespace prefixes
    1980   *			ended with a NULL or NULL if there is no
    1981   *			inclusive namespaces (only for exclusive
    1982   *			canonicalization, ignored otherwise)
    1983   * @with_comments:	include comments in the result (!=0) or not (==0)
    1984   * @doc_txt_ptr:	the memory pointer for allocated canonical XML text;
    1985   *			the caller of this functions is responsible for calling
    1986   *			xmlFree() to free allocated memory
    1987   *
    1988   * Dumps the canonized image of given XML document into memory.
    1989   * For details see "Canonical XML" (http://www.w3.org/TR/xml-c14n) or
    1990   * "Exclusive XML Canonicalization" (http://www.w3.org/TR/xml-exc-c14n)
    1991   *
    1992   * Returns the number of bytes written on success or a negative value on fail
    1993   */
    1994  int
    1995  xmlC14NDocDumpMemory(xmlDocPtr doc, xmlNodeSetPtr nodes,
    1996                       int mode, xmlChar ** inclusive_ns_prefixes,
    1997                       int with_comments, xmlChar ** doc_txt_ptr)
    1998  {
    1999      int ret;
    2000      xmlOutputBufferPtr buf;
    2001  
    2002      if (doc_txt_ptr == NULL) {
    2003          xmlC14NErrParam("dumping doc to memory");
    2004          return (-1);
    2005      }
    2006  
    2007      *doc_txt_ptr = NULL;
    2008  
    2009      /*
    2010       * create memory buffer with UTF8 (default) encoding
    2011       */
    2012      buf = xmlAllocOutputBuffer(NULL);
    2013      if (buf == NULL) {
    2014          xmlC14NErrMemory("creating output buffer");
    2015          return (-1);
    2016      }
    2017  
    2018      /*
    2019       * canonize document and write to buffer
    2020       */
    2021      ret = xmlC14NDocSaveTo(doc, nodes, mode, inclusive_ns_prefixes,
    2022                             with_comments, buf);
    2023      if (ret < 0) {
    2024          xmlC14NErrInternal("saving doc to output buffer");
    2025          (void) xmlOutputBufferClose(buf);
    2026          return (-1);
    2027      }
    2028  
    2029      ret = xmlBufUse(buf->buffer);
    2030      if (ret >= 0) {
    2031          *doc_txt_ptr = xmlStrndup(xmlBufContent(buf->buffer), ret);
    2032      }
    2033      (void) xmlOutputBufferClose(buf);
    2034  
    2035      if ((*doc_txt_ptr == NULL) && (ret >= 0)) {
    2036          xmlC14NErrMemory("copying canonicalized document");
    2037          return (-1);
    2038      }
    2039      return (ret);
    2040  }
    2041  
    2042  /**
    2043   * xmlC14NDocSave:
    2044   * @doc:		the XML document for canonization
    2045   * @nodes:		the nodes set to be included in the canonized image
    2046   *		or NULL if all document nodes should be included
    2047   * @mode:		the c14n mode (see @xmlC14NMode)
    2048   * @inclusive_ns_prefixes: the list of inclusive namespace prefixes
    2049   *			ended with a NULL or NULL if there is no
    2050   *			inclusive namespaces (only for exclusive
    2051   *			canonicalization, ignored otherwise)
    2052   * @with_comments:	include comments in the result (!=0) or not (==0)
    2053   * @filename:		the filename to store canonical XML image
    2054   * @compression:	the compression level (zlib required):
    2055   *				-1 - libxml default,
    2056   *				 0 - uncompressed,
    2057   *				>0 - compression level
    2058   *
    2059   * Dumps the canonized image of given XML document into the file.
    2060   * For details see "Canonical XML" (http://www.w3.org/TR/xml-c14n) or
    2061   * "Exclusive XML Canonicalization" (http://www.w3.org/TR/xml-exc-c14n)
    2062   *
    2063   * Returns the number of bytes written success or a negative value on fail
    2064   */
    2065  int
    2066  xmlC14NDocSave(xmlDocPtr doc, xmlNodeSetPtr nodes,
    2067                 int mode, xmlChar ** inclusive_ns_prefixes,
    2068                 int with_comments, const char *filename, int compression)
    2069  {
    2070      xmlOutputBufferPtr buf;
    2071      int ret;
    2072  
    2073      if (filename == NULL) {
    2074          xmlC14NErrParam("saving doc");
    2075          return (-1);
    2076      }
    2077  #ifdef LIBXML_ZLIB_ENABLED
    2078      if (compression < 0)
    2079          compression = xmlGetCompressMode();
    2080  #endif
    2081  
    2082      /*
    2083       * save the content to a temp buffer, use default UTF8 encoding.
    2084       */
    2085      buf = xmlOutputBufferCreateFilename(filename, NULL, compression);
    2086      if (buf == NULL) {
    2087          xmlC14NErrInternal("creating temporary filename");
    2088          return (-1);
    2089      }
    2090  
    2091      /*
    2092       * canonize document and write to buffer
    2093       */
    2094      ret = xmlC14NDocSaveTo(doc, nodes, mode, inclusive_ns_prefixes,
    2095                             with_comments, buf);
    2096      if (ret < 0) {
    2097          xmlC14NErrInternal("canonize document to buffer");
    2098          (void) xmlOutputBufferClose(buf);
    2099          return (-1);
    2100      }
    2101  
    2102      /*
    2103       * get the numbers of bytes written
    2104       */
    2105      ret = xmlOutputBufferClose(buf);
    2106      return (ret);
    2107  }
    2108  
    2109  
    2110  
    2111  /*
    2112   * Macro used to grow the current buffer.
    2113   */
    2114  #define growBufferReentrant() {						\
    2115      buffer_size *= 2;							\
    2116      buffer = (xmlChar *)						\
    2117  		xmlRealloc(buffer, buffer_size);			\
    2118      if (buffer == NULL) {						\
    2119  	xmlC14NErrMemory("growing buffer");				\
    2120  	return(NULL);							\
    2121      }									\
    2122  }
    2123  
    2124  /**
    2125   * xmlC11NNormalizeString:
    2126   * @input:		the input string
    2127   * @mode:		the normalization mode (attribute, comment, PI or text)
    2128   *
    2129   * Converts a string to a canonical (normalized) format. The code is stolen
    2130   * from xmlEncodeEntitiesReentrant(). Added normalization of \x09, \x0a, \x0A
    2131   * and the @mode parameter
    2132   *
    2133   * Returns a normalized string (caller is responsible for calling xmlFree())
    2134   * or NULL if an error occurs
    2135   */
    2136  static xmlChar *
    2137  xmlC11NNormalizeString(const xmlChar * input,
    2138                         xmlC14NNormalizationMode mode)
    2139  {
    2140      const xmlChar *cur = input;
    2141      xmlChar *buffer = NULL;
    2142      xmlChar *out = NULL;
    2143      int buffer_size = 0;
    2144  
    2145      if (input == NULL)
    2146          return (NULL);
    2147  
    2148      /*
    2149       * allocate an translation buffer.
    2150       */
    2151      buffer_size = 1000;
    2152      buffer = (xmlChar *) xmlMallocAtomic(buffer_size);
    2153      if (buffer == NULL) {
    2154  	xmlC14NErrMemory("allocating buffer");
    2155          return (NULL);
    2156      }
    2157      out = buffer;
    2158  
    2159      while (*cur != '\0') {
    2160          if ((out - buffer) > (buffer_size - 10)) {
    2161              int indx = out - buffer;
    2162  
    2163              growBufferReentrant();
    2164              out = &buffer[indx];
    2165          }
    2166  
    2167          if ((*cur == '<') && ((mode == XMLC14N_NORMALIZE_ATTR) ||
    2168                                (mode == XMLC14N_NORMALIZE_TEXT))) {
    2169              *out++ = '&';
    2170              *out++ = 'l';
    2171              *out++ = 't';
    2172              *out++ = ';';
    2173          } else if ((*cur == '>') && (mode == XMLC14N_NORMALIZE_TEXT)) {
    2174              *out++ = '&';
    2175              *out++ = 'g';
    2176              *out++ = 't';
    2177              *out++ = ';';
    2178          } else if ((*cur == '&') && ((mode == XMLC14N_NORMALIZE_ATTR) ||
    2179                                       (mode == XMLC14N_NORMALIZE_TEXT))) {
    2180              *out++ = '&';
    2181              *out++ = 'a';
    2182              *out++ = 'm';
    2183              *out++ = 'p';
    2184              *out++ = ';';
    2185          } else if ((*cur == '"') && (mode == XMLC14N_NORMALIZE_ATTR)) {
    2186              *out++ = '&';
    2187              *out++ = 'q';
    2188              *out++ = 'u';
    2189              *out++ = 'o';
    2190              *out++ = 't';
    2191              *out++ = ';';
    2192          } else if ((*cur == '\x09') && (mode == XMLC14N_NORMALIZE_ATTR)) {
    2193              *out++ = '&';
    2194              *out++ = '#';
    2195              *out++ = 'x';
    2196              *out++ = '9';
    2197              *out++ = ';';
    2198          } else if ((*cur == '\x0A') && (mode == XMLC14N_NORMALIZE_ATTR)) {
    2199              *out++ = '&';
    2200              *out++ = '#';
    2201              *out++ = 'x';
    2202              *out++ = 'A';
    2203              *out++ = ';';
    2204          } else if ((*cur == '\x0D') && ((mode == XMLC14N_NORMALIZE_ATTR) ||
    2205                                          (mode == XMLC14N_NORMALIZE_TEXT) ||
    2206                                          (mode == XMLC14N_NORMALIZE_COMMENT) ||
    2207  					(mode == XMLC14N_NORMALIZE_PI))) {
    2208              *out++ = '&';
    2209              *out++ = '#';
    2210              *out++ = 'x';
    2211              *out++ = 'D';
    2212              *out++ = ';';
    2213          } else {
    2214              /*
    2215               * Works because on UTF-8, all extended sequences cannot
    2216               * result in bytes in the ASCII range.
    2217               */
    2218              *out++ = *cur;
    2219          }
    2220          cur++;
    2221      }
    2222      *out = 0;
    2223      return (buffer);
    2224  }
    2225  
    2226  #endif /* LIBXML_C14N_ENABLED */