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