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