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