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