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