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