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