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