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