missing project/build files
[client-tools.git] / src / external / 3rd / library / libxml / c14n.c
blobe80536e56e7a97d2b099f2d54fad0a57c7dd5882
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
8 * See Copyright for the status of this software.
9 *
10 * Author: Aleksey Sanin <aleksey@aleksey.com>
12 #define IN_LIBXML
13 #include "libxml.h"
14 #ifdef LIBXML_C14N_ENABLED
16 #ifdef HAVE_STDLIB_H
17 #include <stdlib.h>
18 #endif
19 #include <string.h>
21 #include <libxml/tree.h>
22 #include <libxml/parser.h>
23 #include <libxml/uri.h>
24 #include <libxml/xmlerror.h>
25 #include <libxml/globals.h>
26 #include <libxml/xpathInternals.h>
27 #include <libxml/c14n.h>
29 /************************************************************************
30 * *
31 * Some declaration better left private ATM *
32 * *
33 ************************************************************************/
35 typedef enum {
36 XMLC14N_BEFORE_DOCUMENT_ELEMENT = 0,
37 XMLC14N_INSIDE_DOCUMENT_ELEMENT = 1,
38 XMLC14N_AFTER_DOCUMENT_ELEMENT = 2
39 } xmlC14NPosition;
41 typedef struct _xmlC14NVisibleNsStack {
42 int nsCurEnd; /* number of nodes in the set */
43 int nsPrevStart; /* the begginning of the stack for previous visible node */
44 int nsPrevEnd; /* the end of the stack for previous visible node */
45 int nsMax; /* size of the array as allocated */
46 xmlNsPtr *nsTab; /* array of ns in no particular order */
47 xmlNodePtr *nodeTab;/* array of nodes in no particular order */
48 } xmlC14NVisibleNsStack, *xmlC14NVisibleNsStackPtr;
50 typedef struct _xmlC14NCtx {
51 /* input parameters */
52 xmlDocPtr doc;
53 xmlC14NIsVisibleCallback is_visible_callback;
54 void* user_data;
55 int with_comments;
56 xmlOutputBufferPtr buf;
58 /* position in the XML document */
59 xmlC14NPosition pos;
60 int parent_is_doc;
61 xmlC14NVisibleNsStackPtr ns_rendered;
63 /* exclusive canonicalization */
64 int exclusive;
65 xmlChar **inclusive_ns_prefixes;
66 } xmlC14NCtx, *xmlC14NCtxPtr;
68 static xmlC14NVisibleNsStackPtr xmlC14NVisibleNsStackCreate (void);
69 static void xmlC14NVisibleNsStackDestroy (xmlC14NVisibleNsStackPtr cur);
70 static void xmlC14NVisibleNsStackAdd (xmlC14NVisibleNsStackPtr cur,
71 xmlNsPtr ns,
72 xmlNodePtr node);
73 static void xmlC14NVisibleNsStackSave (xmlC14NVisibleNsStackPtr cur,
74 xmlC14NVisibleNsStackPtr state);
75 static void xmlC14NVisibleNsStackRestore (xmlC14NVisibleNsStackPtr cur,
76 xmlC14NVisibleNsStackPtr state);
77 static void xmlC14NVisibleNsStackShift (xmlC14NVisibleNsStackPtr cur);
78 static int xmlC14NVisibleNsStackFind (xmlC14NVisibleNsStackPtr cur,
79 xmlNsPtr ns);
80 static int xmlExcC14NVisibleNsStackFind (xmlC14NVisibleNsStackPtr cur,
81 xmlNsPtr ns,
82 xmlC14NCtxPtr ctx);
84 static int xmlC14NIsNodeInNodeset (xmlNodeSetPtr nodes,
85 xmlNodePtr node,
86 xmlNodePtr parent);
90 static int xmlC14NProcessNode(xmlC14NCtxPtr ctx, xmlNodePtr cur);
91 static int xmlC14NProcessNodeList(xmlC14NCtxPtr ctx, xmlNodePtr cur);
92 typedef enum {
93 XMLC14N_NORMALIZE_ATTR = 0,
94 XMLC14N_NORMALIZE_COMMENT = 1,
95 XMLC14N_NORMALIZE_PI = 2,
96 XMLC14N_NORMALIZE_TEXT = 3
97 } xmlC14NNormalizationMode;
99 static xmlChar *xmlC11NNormalizeString(const xmlChar * input,
100 xmlC14NNormalizationMode mode);
102 #define xmlC11NNormalizeAttr( a ) \
103 xmlC11NNormalizeString((a), XMLC14N_NORMALIZE_ATTR)
104 #define xmlC11NNormalizeComment( a ) \
105 xmlC11NNormalizeString((a), XMLC14N_NORMALIZE_COMMENT)
106 #define xmlC11NNormalizePI( a ) \
107 xmlC11NNormalizeString((a), XMLC14N_NORMALIZE_PI)
108 #define xmlC11NNormalizeText( a ) \
109 xmlC11NNormalizeString((a), XMLC14N_NORMALIZE_TEXT)
111 #define xmlC14NIsVisible( ctx, node, parent ) \
112 (((ctx)->is_visible_callback != NULL) ? \
113 (ctx)->is_visible_callback((ctx)->user_data, \
114 (xmlNodePtr)(node), (xmlNodePtr)(parent)) : 1)
115 /************************************************************************
117 * The implementation internals *
119 ************************************************************************/
120 #define XML_NAMESPACES_DEFAULT 16
122 static int
123 xmlC14NIsNodeInNodeset(xmlNodeSetPtr nodes, xmlNodePtr node, xmlNodePtr parent) {
124 if((nodes != NULL) && (node != NULL)) {
125 if(node->type != XML_NAMESPACE_DECL) {
126 return(xmlXPathNodeSetContains(nodes, node));
127 } else {
128 xmlNs ns;
130 memcpy(&ns, node, sizeof(ns));
131 ns.next = (xmlNsPtr)parent; /* this is a libxml hack! check xpath.c for details */
134 * If the input is an XPath node-set, then the node-set must explicitly
135 * contain every node to be rendered to the canonical form.
137 return(xmlXPathNodeSetContains(nodes, (xmlNodePtr)&ns));
140 return(1);
143 static xmlC14NVisibleNsStackPtr
144 xmlC14NVisibleNsStackCreate(void) {
145 xmlC14NVisibleNsStackPtr ret;
147 ret = (xmlC14NVisibleNsStackPtr) xmlMalloc(sizeof(xmlC14NVisibleNsStack));
148 if (ret == NULL) {
149 xmlGenericError(xmlGenericErrorContext,
150 "xmlC14NVisibleNsStackCreate: out of memory\n");
151 return(NULL);
153 memset(ret, 0 , (size_t) sizeof(xmlC14NVisibleNsStack));
154 return(ret);
157 static void
158 xmlC14NVisibleNsStackDestroy(xmlC14NVisibleNsStackPtr cur) {
159 if(cur == NULL) {
160 #ifdef DEBUG_C14N
161 xmlGenericError(xmlGenericErrorContext,
162 "xmlC14NVisibleNsStackDestroy: cur is null.\n");
163 #endif
164 return;
166 if(cur->nsTab != NULL) {
167 memset(cur->nsTab, 0, cur->nsMax * sizeof(xmlNsPtr));
168 xmlFree(cur->nsTab);
170 if(cur->nodeTab != NULL) {
171 memset(cur->nodeTab, 0, cur->nsMax * sizeof(xmlNodePtr));
172 xmlFree(cur->nodeTab);
174 memset(cur, 0, sizeof(xmlC14NVisibleNsStack));
175 xmlFree(cur);
179 static void
180 xmlC14NVisibleNsStackAdd(xmlC14NVisibleNsStackPtr cur, xmlNsPtr ns, xmlNodePtr node) {
181 if((cur == NULL) ||
182 ((cur->nsTab == NULL) && (cur->nodeTab != NULL)) ||
183 ((cur->nsTab != NULL) && (cur->nodeTab == NULL))) {
184 #ifdef DEBUG_C14N
185 xmlGenericError(xmlGenericErrorContext,
186 "xmlC14NVisibleNsStackAdd: cur is null.\n");
187 #endif
188 return;
191 if ((cur->nsTab == NULL) && (cur->nodeTab == NULL)) {
192 cur->nsTab = (xmlNsPtr*) xmlMalloc(XML_NAMESPACES_DEFAULT * sizeof(xmlNsPtr));
193 cur->nodeTab = (xmlNodePtr*) xmlMalloc(XML_NAMESPACES_DEFAULT * sizeof(xmlNodePtr));
194 if ((cur->nsTab == NULL) || (cur->nodeTab == NULL)) {
195 xmlGenericError(xmlGenericErrorContext,
196 "xmlC14NVisibleNsStackAdd: out of memory\n");
197 return;
199 memset(cur->nsTab, 0 , XML_NAMESPACES_DEFAULT * sizeof(xmlNsPtr));
200 memset(cur->nodeTab, 0 , XML_NAMESPACES_DEFAULT * sizeof(xmlNodePtr));
201 cur->nsMax = XML_NAMESPACES_DEFAULT;
202 } else if(cur->nsMax == cur->nsCurEnd) {
203 void *tmp;
204 int tmpSize;
206 tmpSize = 2 * cur->nsMax;
207 tmp = xmlRealloc(cur->nsTab, tmpSize * sizeof(xmlNsPtr));
208 if (tmp == NULL) {
209 xmlGenericError(xmlGenericErrorContext,
210 "xmlC14NVisibleNsStackAdd: out of memory\n");
211 return;
213 cur->nsTab = (xmlNsPtr*)tmp;
215 tmp = xmlRealloc(cur->nodeTab, tmpSize * sizeof(xmlNodePtr));
216 if (tmp == NULL) {
217 xmlGenericError(xmlGenericErrorContext,
218 "xmlC14NVisibleNsStackAdd: out of memory\n");
219 return;
221 cur->nodeTab = (xmlNodePtr*)tmp;
223 cur->nsMax = tmpSize;
225 cur->nsTab[cur->nsCurEnd] = ns;
226 cur->nodeTab[cur->nsCurEnd] = node;
228 ++cur->nsCurEnd;
231 static void
232 xmlC14NVisibleNsStackSave(xmlC14NVisibleNsStackPtr cur, xmlC14NVisibleNsStackPtr state) {
233 if((cur == NULL) || (state == NULL)) {
234 #ifdef DEBUG_C14N
235 xmlGenericError(xmlGenericErrorContext,
236 "xmlC14NVisibleNsStackSave: cur or state is null.\n");
237 #endif
238 return;
241 state->nsCurEnd = cur->nsCurEnd;
242 state->nsPrevStart = cur->nsPrevStart;
243 state->nsPrevEnd = cur->nsPrevEnd;
246 static void
247 xmlC14NVisibleNsStackRestore(xmlC14NVisibleNsStackPtr cur, xmlC14NVisibleNsStackPtr state) {
248 if((cur == NULL) || (state == NULL)) {
249 #ifdef DEBUG_C14N
250 xmlGenericError(xmlGenericErrorContext,
251 "xmlC14NVisibleNsStackRestore: cur or state is null.\n");
252 #endif
253 return;
255 cur->nsCurEnd = state->nsCurEnd;
256 cur->nsPrevStart = state->nsPrevStart;
257 cur->nsPrevEnd = state->nsPrevEnd;
260 static void
261 xmlC14NVisibleNsStackShift(xmlC14NVisibleNsStackPtr cur) {
262 if(cur == NULL) {
263 #ifdef DEBUG_C14N
264 xmlGenericError(xmlGenericErrorContext,
265 "xmlC14NVisibleNsStackRestore: cur is null.\n");
266 #endif
267 return;
269 cur->nsPrevStart = cur->nsPrevEnd;
270 cur->nsPrevEnd = cur->nsCurEnd;
273 static int
274 xmlC14NStrEqual(const xmlChar *str1, const xmlChar *str2) {
275 if (str1 == str2) return(1);
276 if (str1 == NULL) return((*str2) == '\0');
277 if (str2 == NULL) return((*str1) == '\0');
278 do {
279 if (*str1++ != *str2) return(0);
280 } while (*str2++);
281 return(1);
285 * xmlC14NVisibleNsStackFind:
286 * @ctx the C14N context
287 * @ns the namespace to check
289 * Checks whether the given namespace was already rendered or not
291 * Returns 1 if we already wrote this namespace or 0 otherwise
293 static int
294 xmlC14NVisibleNsStackFind(xmlC14NVisibleNsStackPtr cur, xmlNsPtr ns)
296 int i;
297 const xmlChar *prefix;
298 const xmlChar *href;
299 int has_empty_ns;
301 if(cur == NULL) {
302 #ifdef DEBUG_C14N
303 xmlGenericError(xmlGenericErrorContext,
304 "xmlC14NVisibleNsStackFind: cur is null.\n");
305 #endif
306 return (0);
310 * if the default namespace xmlns="" is not defined yet then
311 * we do not want to print it out
313 prefix = ((ns == NULL) || (ns->prefix == NULL)) ? BAD_CAST "" : ns->prefix;
314 href = ((ns == NULL) || (ns->href == NULL)) ? BAD_CAST "" : ns->href;
315 has_empty_ns = (xmlC14NStrEqual(prefix, NULL) && xmlC14NStrEqual(href, NULL));
317 if (cur->nsTab != NULL) {
318 int start = (has_empty_ns) ? 0 : cur->nsPrevStart;
319 for (i = cur->nsCurEnd - 1; i >= start; --i) {
320 xmlNsPtr ns1 = cur->nsTab[i];
322 if(xmlC14NStrEqual(prefix, (ns1 != NULL) ? ns1->prefix : NULL)) {
323 return(xmlC14NStrEqual(href, (ns1 != NULL) ? ns1->href : NULL));
327 return(has_empty_ns);
330 static int
331 xmlExcC14NVisibleNsStackFind(xmlC14NVisibleNsStackPtr cur, xmlNsPtr ns, xmlC14NCtxPtr ctx) {
332 int i;
333 const xmlChar *prefix;
334 const xmlChar *href;
335 int has_empty_ns;
337 if(cur == NULL) {
338 #ifdef DEBUG_C14N
339 xmlGenericError(xmlGenericErrorContext,
340 "xmlExcC14NVisibleNsStackFind: cur is null.\n");
341 #endif
342 return (0);
346 * if the default namespace xmlns="" is not defined yet then
347 * we do not want to print it out
349 prefix = ((ns == NULL) || (ns->prefix == NULL)) ? BAD_CAST "" : ns->prefix;
350 href = ((ns == NULL) || (ns->href == NULL)) ? BAD_CAST "" : ns->href;
351 has_empty_ns = (xmlC14NStrEqual(prefix, NULL) && xmlC14NStrEqual(href, NULL));
353 if (cur->nsTab != NULL) {
354 int start = 0;
355 for (i = cur->nsCurEnd - 1; i >= start; --i) {
356 xmlNsPtr ns1 = cur->nsTab[i];
358 if(xmlC14NStrEqual(prefix, (ns1 != NULL) ? ns1->prefix : NULL)) {
359 if(xmlC14NStrEqual(href, (ns1 != NULL) ? ns1->href : NULL)) {
360 return(xmlC14NIsVisible(ctx, ns1, cur->nodeTab[i]));
361 } else {
362 return(0);
367 return(has_empty_ns);
374 * xmlC14NIsXmlNs:
375 * @ns: the namespace to check
377 * Checks whether the given namespace is a default "xml:" namespace
378 * with href="http://www.w3.org/XML/1998/namespace"
380 * Returns 1 if the node is default or 0 otherwise
383 /* todo: make it a define? */
384 static int
385 xmlC14NIsXmlNs(xmlNsPtr ns)
387 return ((ns != NULL) &&
388 (xmlStrEqual(ns->prefix, BAD_CAST "xml")) &&
389 (xmlStrEqual(ns->href,
390 BAD_CAST
391 "http://www.w3.org/XML/1998/namespace")));
396 * xmlC14NNsCompare:
397 * @ns1: the pointer to first namespace
398 * @ns2: the pointer to second namespace
400 * Compares the namespaces by names (prefixes).
402 * Returns -1 if ns1 < ns2, 0 if ns1 == ns2 or 1 if ns1 > ns2.
404 static int
405 xmlC14NNsCompare(xmlNsPtr ns1, xmlNsPtr ns2)
407 if (ns1 == ns2)
408 return (0);
409 if (ns1 == NULL)
410 return (-1);
411 if (ns2 == NULL)
412 return (1);
414 return (xmlStrcmp(ns1->prefix, ns2->prefix));
419 * xmlC14NPrintNamespaces:
420 * @ns: the pointer to namespace
421 * @ctx: the C14N context
423 * Prints the given namespace to the output buffer from C14N context.
425 * Returns 1 on success or 0 on fail.
427 static int
428 xmlC14NPrintNamespaces(const xmlNsPtr ns, xmlC14NCtxPtr ctx)
431 if ((ns == NULL) || (ctx == NULL)) {
432 #ifdef DEBUG_C14N
433 xmlGenericError(xmlGenericErrorContext,
434 "xmlC14NPrintNamespace: namespace or context pointer is null\n");
435 #endif
436 return 0;
439 if (ns->prefix != NULL) {
440 xmlOutputBufferWriteString(ctx->buf, " xmlns:");
441 xmlOutputBufferWriteString(ctx->buf, (const char *) ns->prefix);
442 xmlOutputBufferWriteString(ctx->buf, "=\"");
443 } else {
444 xmlOutputBufferWriteString(ctx->buf, " xmlns=\"");
446 if(ns->href != NULL) {
447 xmlOutputBufferWriteString(ctx->buf, (const char *) ns->href);
449 xmlOutputBufferWriteString(ctx->buf, "\"");
450 return (1);
454 * xmlC14NProcessNamespacesAxis:
455 * @ctx: the C14N context
456 * @node: the current node
458 * Prints out canonical namespace axis of the current node to the
459 * buffer from C14N context as follows
461 * Canonical XML v 1.0 (http://www.w3.org/TR/xml-c14n)
463 * Namespace Axis
464 * Consider a list L containing only namespace nodes in the
465 * axis and in the node-set in lexicographic order (ascending). To begin
466 * processing L, if the first node is not the default namespace node (a node
467 * with no namespace URI and no local name), then generate a space followed
468 * by xmlns="" if and only if the following conditions are met:
469 * - the element E that owns the axis is in the node-set
470 * - The nearest ancestor element of E in the node-set has a default
471 * namespace node in the node-set (default namespace nodes always
472 * have non-empty values in XPath)
473 * The latter condition eliminates unnecessary occurrences of xmlns="" in
474 * the canonical form since an element only receives an xmlns="" if its
475 * default namespace is empty and if it has an immediate parent in the
476 * canonical form that has a non-empty default namespace. To finish
477 * processing L, simply process every namespace node in L, except omit
478 * namespace node with local name xml, which defines the xml prefix,
479 * if its string value is http://www.w3.org/XML/1998/namespace.
481 * Exclusive XML Canonicalization v 1.0 (http://www.w3.org/TR/xml-exc-c14n)
482 * Canonical XML applied to a document subset requires the search of the
483 * ancestor nodes of each orphan element node for attributes in the xml
484 * namespace, such as xml:lang and xml:space. These are copied into the
485 * element node except if a declaration of the same attribute is already
486 * in the attribute axis of the element (whether or not it is included in
487 * the document subset). This search and copying are omitted from the
488 * Exclusive XML Canonicalization method.
490 * Returns 0 on success or -1 on fail.
492 static int
493 xmlC14NProcessNamespacesAxis(xmlC14NCtxPtr ctx, xmlNodePtr cur, int visible)
495 xmlNodePtr n;
496 xmlNsPtr ns, tmp;
497 xmlListPtr list;
498 int already_rendered;
499 int has_empty_ns = 0;
501 if ((ctx == NULL) || (cur == NULL) || (cur->type != XML_ELEMENT_NODE)) {
502 #ifdef DEBUG_C14N
503 xmlGenericError(xmlGenericErrorContext,
504 "xmlC14NProcessNamespacesAxis: Null context or node pointer or type != XML_ELEMENT_NODE.\n");
505 #endif
506 return (-1);
510 * Create a sorted list to store element namespaces
512 list = xmlListCreate(NULL, (xmlListDataCompare) xmlC14NNsCompare);
513 if (list == NULL) {
514 #ifdef DEBUG_C14N
515 xmlGenericError(xmlGenericErrorContext,
516 "xmlC14NProcessNamespacesAxis: list creation failed\n");
517 #endif
518 return (-1);
521 /* check all namespaces */
522 for(n = cur; n != NULL; n = n->parent) {
523 for(ns = n->nsDef; ns != NULL; ns = ns->next) {
524 tmp = xmlSearchNs(cur->doc, cur, ns->prefix);
526 if((tmp == ns) && !xmlC14NIsXmlNs(ns) && xmlC14NIsVisible(ctx, ns, cur)) {
527 already_rendered = xmlC14NVisibleNsStackFind(ctx->ns_rendered, ns);
528 if(visible) {
529 xmlC14NVisibleNsStackAdd(ctx->ns_rendered, ns, cur);
531 if(!already_rendered) {
532 xmlListInsert(list, ns);
534 if(xmlStrlen(ns->prefix) == 0) {
535 has_empty_ns = 1;
542 * if the first node is not the default namespace node (a node with no
543 * namespace URI and no local name), then generate a space followed by
544 * xmlns="" if and only if the following conditions are met:
545 * - the element E that owns the axis is in the node-set
546 * - the nearest ancestor element of E in the node-set has a default
547 * namespace node in the node-set (default namespace nodes always
548 * have non-empty values in XPath)
550 if(visible && !has_empty_ns) {
551 static xmlNs ns_default;
553 memset(&ns_default, 0, sizeof(ns_default));
554 if(!xmlC14NVisibleNsStackFind(ctx->ns_rendered, &ns_default)) {
555 xmlC14NPrintNamespaces(&ns_default, ctx);
561 * print out all elements from list
563 xmlListWalk(list, (xmlListWalker) xmlC14NPrintNamespaces, (const void *) ctx);
566 * Cleanup
568 xmlListDelete(list);
569 return (0);
574 * xmlExcC14NProcessNamespacesAxis:
575 * @ctx: the C14N context
576 * @node: the current node
578 * Prints out exclusive canonical namespace axis of the current node to the
579 * buffer from C14N context as follows
581 * Exclusive XML Canonicalization
582 * http://www.w3.org/TR/xml-exc-c14n
584 * If the element node is in the XPath subset then output the node in
585 * accordance with Canonical XML except for namespace nodes which are
586 * rendered as follows:
588 * 1. Render each namespace node iff:
589 * * it is visibly utilized by the immediate parent element or one of
590 * its attributes, or is present in InclusiveNamespaces PrefixList, and
591 * * its prefix and value do not appear in ns_rendered. ns_rendered is
592 * obtained by popping the state stack in order to obtain a list of
593 * prefixes and their values which have already been rendered by
594 * an output ancestor of the namespace node's parent element.
595 * 2. Append the rendered namespace node to the list ns_rendered of namespace
596 * nodes rendered by output ancestors. Push ns_rendered on state stack and
597 * recurse.
598 * 3. After the recursion returns, pop thestate stack.
601 * Returns 0 on success or -1 on fail.
603 static int
604 xmlExcC14NProcessNamespacesAxis(xmlC14NCtxPtr ctx, xmlNodePtr cur, int visible)
606 xmlNsPtr ns;
607 xmlListPtr list;
608 xmlAttrPtr attr;
609 int already_rendered;
610 int has_empty_ns = 0;
611 int has_visibly_utilized_empty_ns = 0;
612 int has_empty_ns_in_inclusive_list = 0;
614 if ((ctx == NULL) || (cur == NULL) || (cur->type != XML_ELEMENT_NODE)) {
615 #ifdef DEBUG_C14N
616 xmlGenericError(xmlGenericErrorContext,
617 "xmlExcC14NProcessNamespacesAxis: Null context or node pointer or type != XML_ELEMENT_NODE.\n");
618 #endif
619 return (-1);
622 if(!ctx->exclusive) {
623 #ifdef DEBUG_C14N
624 xmlGenericError(xmlGenericErrorContext,
625 "xmlExcC14NProcessNamespacesAxis: called for non-exclusive canonization or rendered stack is NULL.\n");
626 #endif
627 return (-1);
632 * Create a sorted list to store element namespaces
634 list = xmlListCreate(NULL, (xmlListDataCompare) xmlC14NNsCompare);
635 if (list == NULL) {
636 #ifdef DEBUG_C14N
637 xmlGenericError(xmlGenericErrorContext,
638 "xmlExcC14NProcessNamespacesAxis: list creation failed\n");
639 #endif
640 return (-1);
644 * process inclusive namespaces:
645 * All namespace nodes appearing on inclusive ns list are
646 * handled as provided in Canonical XML
648 if(ctx->inclusive_ns_prefixes != NULL) {
649 xmlChar *prefix;
650 int i;
652 for (i = 0; ctx->inclusive_ns_prefixes[i] != NULL; ++i) {
653 prefix = ctx->inclusive_ns_prefixes[i];
655 * Special values for namespace with empty prefix
657 if (xmlStrEqual(prefix, BAD_CAST "#default")
658 || xmlStrEqual(prefix, BAD_CAST "")) {
659 prefix = NULL;
660 has_empty_ns_in_inclusive_list = 1;
663 ns = xmlSearchNs(cur->doc, cur, prefix);
664 if((ns != NULL) && !xmlC14NIsXmlNs(ns) && xmlC14NIsVisible(ctx, ns, cur)) {
665 already_rendered = xmlC14NVisibleNsStackFind(ctx->ns_rendered, ns);
666 if(visible) {
667 xmlC14NVisibleNsStackAdd(ctx->ns_rendered, ns, cur);
669 if(!already_rendered) {
670 xmlListInsert(list, ns);
672 if(xmlStrlen(ns->prefix) == 0) {
673 has_empty_ns = 1;
679 /* add node namespace */
680 if(cur->ns != NULL) {
681 ns = cur->ns;
682 } else {
683 ns = xmlSearchNs(cur->doc, cur, NULL);
684 has_visibly_utilized_empty_ns = 1;
686 if((ns != NULL) && !xmlC14NIsXmlNs(ns)) {
687 if(visible && xmlC14NIsVisible(ctx, ns, cur)) {
688 if(!xmlExcC14NVisibleNsStackFind(ctx->ns_rendered, ns, ctx)) {
689 xmlListInsert(list, ns);
692 if(visible) {
693 xmlC14NVisibleNsStackAdd(ctx->ns_rendered, ns, cur);
695 if(xmlStrlen(ns->prefix) == 0) {
696 has_empty_ns = 1;
701 /* add attributes */
702 for(attr = cur->properties; attr != NULL; attr = attr->next) {
704 * we need to check that attribute is visible and has non
705 * default namespace (XML Namespaces: "default namespaces
706 * do not apply directly to attributes")
708 if((attr->ns != NULL) && xmlC14NIsVisible(ctx, attr, cur)) {
709 already_rendered = xmlExcC14NVisibleNsStackFind(ctx->ns_rendered, attr->ns, ctx);
710 xmlC14NVisibleNsStackAdd(ctx->ns_rendered, attr->ns, (xmlNodePtr)attr);
711 if(!already_rendered && visible) {
712 xmlListInsert(list, attr->ns);
714 if(xmlStrlen(attr->ns->prefix) == 0) {
715 has_empty_ns = 1;
717 } else if(attr->ns == NULL) {
718 has_visibly_utilized_empty_ns = 1;
723 * Process xmlns=""
725 if(visible && has_visibly_utilized_empty_ns &&
726 !has_empty_ns && !has_empty_ns_in_inclusive_list) {
727 static xmlNs ns_default;
729 memset(&ns_default, 0, sizeof(ns_default));
731 already_rendered = xmlExcC14NVisibleNsStackFind(ctx->ns_rendered, &ns_default, ctx);
732 if(!already_rendered) {
733 xmlC14NPrintNamespaces(&ns_default, ctx);
735 } else if(visible && !has_empty_ns && has_empty_ns_in_inclusive_list) {
736 static xmlNs ns_default;
738 memset(&ns_default, 0, sizeof(ns_default));
739 if(!xmlC14NVisibleNsStackFind(ctx->ns_rendered, &ns_default)) {
740 xmlC14NPrintNamespaces(&ns_default, ctx);
747 * print out all elements from list
749 xmlListWalk(list, (xmlListWalker) xmlC14NPrintNamespaces, (const void *) ctx);
752 * Cleanup
754 xmlListDelete(list);
755 return (0);
760 * xmlC14NAttrsCompare:
761 * @attr1: the pointer tls o first attr
762 * @attr2: the pointer to second attr
764 * Prints the given attribute to the output buffer from C14N context.
766 * Returns -1 if attr1 < attr2, 0 if attr1 == attr2 or 1 if attr1 > attr2.
768 static int
769 xmlC14NAttrsCompare(xmlAttrPtr attr1, xmlAttrPtr attr2)
771 int ret = 0;
774 * Simple cases
776 if (attr1 == attr2)
777 return (0);
778 if (attr1 == NULL)
779 return (-1);
780 if (attr2 == NULL)
781 return (1);
782 if (attr1->ns == attr2->ns) {
783 return (xmlStrcmp(attr1->name, attr2->name));
787 * Attributes in the default namespace are first
788 * because the default namespace is not applied to
789 * unqualified attributes
791 if (attr1->ns == NULL)
792 return (-1);
793 if (attr2->ns == NULL)
794 return (1);
795 if (attr1->ns->prefix == NULL)
796 return (-1);
797 if (attr2->ns->prefix == NULL)
798 return (1);
800 ret = xmlStrcmp(attr1->ns->href, attr2->ns->href);
801 if (ret == 0) {
802 ret = xmlStrcmp(attr1->name, attr2->name);
804 return (ret);
809 * xmlC14NPrintAttrs:
810 * @attr: the pointer to attr
811 * @ctx: the C14N context
813 * Prints out canonical attribute urrent node to the
814 * buffer from C14N context as follows
816 * Canonical XML v 1.0 (http://www.w3.org/TR/xml-c14n)
818 * Returns 1 on success or 0 on fail.
820 static int
821 xmlC14NPrintAttrs(const xmlAttrPtr attr, xmlC14NCtxPtr ctx)
823 xmlChar *value;
824 xmlChar *buffer;
826 if ((attr == NULL) || (ctx == NULL)) {
827 #ifdef DEBUG_C14N
828 xmlGenericError(xmlGenericErrorContext,
829 "xmlC14NPrintAttrs: attr == NULL or ctx == NULL\n");
830 #endif
831 return (0);
834 xmlOutputBufferWriteString(ctx->buf, " ");
835 if (attr->ns != NULL && xmlStrlen(attr->ns->prefix) > 0) {
836 xmlOutputBufferWriteString(ctx->buf,
837 (const char *) attr->ns->prefix);
838 xmlOutputBufferWriteString(ctx->buf, ":");
840 xmlOutputBufferWriteString(ctx->buf, (const char *) attr->name);
841 xmlOutputBufferWriteString(ctx->buf, "=\"");
843 value = xmlNodeListGetString(attr->doc, attr->children, 1);
844 /* todo: should we log an error if value==NULL ? */
845 if (value != NULL) {
846 buffer = xmlC11NNormalizeAttr(value);
847 xmlFree(value);
848 if (buffer != NULL) {
849 xmlOutputBufferWriteString(ctx->buf, (const char *) buffer);
850 xmlFree(buffer);
851 } else {
852 #ifdef DEBUG_C14N
853 xmlGenericError(xmlGenericErrorContext,
854 "xmlC14NPrintAttrs: xmlC11NNormalizeAttr failed\n");
855 #endif
856 return (0);
859 xmlOutputBufferWriteString(ctx->buf, "\"");
860 return (1);
864 * xmlC14NProcessAttrsAxis:
865 * @ctx: the C14N context
866 * @cur: the current node
868 * Prints out canonical attribute axis of the current node to the
869 * buffer from C14N context as follows
871 * Canonical XML v 1.0 (http://www.w3.org/TR/xml-c14n)
873 * Attribute Axis
874 * In lexicographic order (ascending), process each node that
875 * is in the element's attribute axis and in the node-set.
877 * The processing of an element node E MUST be modified slightly
878 * when an XPath node-set is given as input and the element's
879 * parent is omitted from the node-set.
882 * Exclusive XML Canonicalization v 1.0 (http://www.w3.org/TR/xml-exc-c14n)
884 * Canonical XML applied to a document subset requires the search of the
885 * ancestor nodes of each orphan element node for attributes in the xml
886 * namespace, such as xml:lang and xml:space. These are copied into the
887 * element node except if a declaration of the same attribute is already
888 * in the attribute axis of the element (whether or not it is included in
889 * the document subset). This search and copying are omitted from the
890 * Exclusive XML Canonicalization method.
892 * Returns 0 on success or -1 on fail.
894 static int
895 xmlC14NProcessAttrsAxis(xmlC14NCtxPtr ctx, xmlNodePtr cur)
897 xmlAttrPtr attr;
898 xmlListPtr list;
900 if ((ctx == NULL) || (cur == NULL) || (cur->type != XML_ELEMENT_NODE)) {
901 #ifdef DEBUG_C14N
902 xmlGenericError(xmlGenericErrorContext,
903 "xmlC14NProcessAttrsAxis: Null context or node pointer or type != XML_ELEMENT_NODE.\n");
904 #endif
905 return (-1);
909 * Create a sorted list to store element attributes
911 list = xmlListCreate(NULL, (xmlListDataCompare) xmlC14NAttrsCompare);
912 if (list == NULL) {
913 #ifdef DEBUG_C14N
914 xmlGenericError(xmlGenericErrorContext,
915 "xmlC14NProcessAttrsAxis: list creation failed\n");
916 #endif
917 return (-1);
921 * Add all visible attributes from current node.
923 attr = cur->properties;
924 while (attr != NULL) {
925 /* check that attribute is visible */
926 if (xmlC14NIsVisible(ctx, attr, cur)) {
927 xmlListInsert(list, attr);
929 attr = attr->next;
933 * include attributes in "xml" namespace defined in ancestors
934 * (only for non-exclusive XML Canonicalization)
936 if ((!ctx->exclusive) && (cur->parent != NULL)
937 && (!xmlC14NIsVisible(ctx, cur->parent, cur->parent->parent))) {
939 * If XPath node-set is not specified then the parent is always
940 * visible!
942 cur = cur->parent;
943 while (cur != NULL) {
944 attr = cur->properties;
945 while (attr != NULL) {
946 if ((attr->ns != NULL)
947 && (xmlStrEqual(attr->ns->prefix, BAD_CAST "xml"))) {
948 if (xmlListSearch(list, attr) == NULL) {
949 xmlListInsert(list, attr);
952 attr = attr->next;
954 cur = cur->parent;
959 * print out all elements from list
961 xmlListWalk(list, (xmlListWalker) xmlC14NPrintAttrs, (const void *) ctx);
964 * Cleanup
966 xmlListDelete(list);
967 return (0);
970 /**
971 * xmlC14NCheckForRelativeNamespaces:
972 * @ctx: the C14N context
973 * @cur: the current element node
975 * Checks that current element node has no relative namespaces defined
977 * Returns 0 if the node has no relative namespaces or -1 otherwise.
979 static int
980 xmlC14NCheckForRelativeNamespaces(xmlC14NCtxPtr ctx, xmlNodePtr cur)
982 xmlNsPtr ns;
984 if ((ctx == NULL) || (cur == NULL) || (cur->type != XML_ELEMENT_NODE)) {
985 #ifdef DEBUG_C14N
986 xmlGenericError(xmlGenericErrorContext,
987 "xmlC14NCheckForRelativeNamespaces: Null context or node pointer or type != XML_ELEMENT_NODE.\n");
988 #endif
989 return (-1);
992 ns = cur->nsDef;
993 while (ns != NULL) {
994 if (xmlStrlen(ns->href) > 0) {
995 xmlURIPtr uri;
997 uri = xmlParseURI((const char *) ns->href);
998 if (uri == NULL) {
999 #ifdef DEBUG_C14N
1000 xmlGenericError(xmlGenericErrorContext,
1001 "xmlC14NCheckForRelativeNamespaces: unable to parse uri=\"%s\".\n",
1002 ns->href);
1003 #endif
1004 return (-1);
1006 if (xmlStrlen((const xmlChar *) uri->scheme) == 0) {
1007 xmlFreeURI(uri);
1008 return (-1);
1010 if ((!xmlStrEqual
1011 ((const xmlChar *) uri->scheme, BAD_CAST "urn"))
1012 && (xmlStrlen((const xmlChar *) uri->server) == 0)) {
1013 xmlFreeURI(uri);
1014 return (-1);
1016 xmlFreeURI(uri);
1018 ns = ns->next;
1020 return (0);
1024 * xmlC14NProcessElementNode:
1025 * @ctx: the pointer to C14N context object
1026 * @cur: the node to process
1028 * Canonical XML v 1.0 (http://www.w3.org/TR/xml-c14n)
1030 * Element Nodes
1031 * If the element is not in the node-set, then the result is obtained
1032 * by processing the namespace axis, then the attribute axis, then
1033 * processing the child nodes of the element that are in the node-set
1034 * (in document order). If the element is in the node-set, then the result
1035 * is an open angle bracket (<), the element QName, the result of
1036 * processing the namespace axis, the result of processing the attribute
1037 * axis, a close angle bracket (>), the result of processing the child
1038 * nodes of the element that are in the node-set (in document order), an
1039 * open angle bracket, a forward slash (/), the element QName, and a close
1040 * angle bracket.
1042 * Returns non-negative value on success or negative value on fail
1044 static int
1045 xmlC14NProcessElementNode(xmlC14NCtxPtr ctx, xmlNodePtr cur, int visible)
1047 int ret;
1048 xmlC14NVisibleNsStack state;
1049 int parent_is_doc = 0;
1051 if ((ctx == NULL) || (cur == NULL) || (cur->type != XML_ELEMENT_NODE)) {
1052 #ifdef DEBUG_C14N
1053 xmlGenericError(xmlGenericErrorContext,
1054 "xmlC14NProcessElementNode: Null context or node pointer or type != XML_ELEMENT_NODE.\n");
1055 #endif
1056 return (-1);
1060 * Check relative relative namespaces:
1061 * implementations of XML canonicalization MUST report an operation
1062 * failure on documents containing relative namespace URIs.
1064 if (xmlC14NCheckForRelativeNamespaces(ctx, cur) < 0) {
1065 #ifdef DEBUG_C14N
1066 xmlGenericError(xmlGenericErrorContext,
1067 "xmlC14NProcessElementNode: xmlC14NCheckForRelativeNamespaces failed.\n");
1068 #endif
1069 return (-1);
1074 * Save ns_rendered stack position
1076 xmlC14NVisibleNsStackSave(ctx->ns_rendered, &state);
1078 if (visible) {
1079 if (ctx->parent_is_doc) {
1080 /* save this flag into the stack */
1081 parent_is_doc = ctx->parent_is_doc;
1082 ctx->parent_is_doc = 0;
1083 ctx->pos = XMLC14N_INSIDE_DOCUMENT_ELEMENT;
1085 xmlOutputBufferWriteString(ctx->buf, "<");
1087 if ((cur->ns != NULL) && (xmlStrlen(cur->ns->prefix) > 0)) {
1088 xmlOutputBufferWriteString(ctx->buf,
1089 (const char *) cur->ns->prefix);
1090 xmlOutputBufferWriteString(ctx->buf, ":");
1092 xmlOutputBufferWriteString(ctx->buf, (const char *) cur->name);
1095 if (!ctx->exclusive) {
1096 ret = xmlC14NProcessNamespacesAxis(ctx, cur, visible);
1097 } else {
1098 ret = xmlExcC14NProcessNamespacesAxis(ctx, cur, visible);
1100 if (ret < 0) {
1101 #ifdef DEBUG_C14N
1102 xmlGenericError(xmlGenericErrorContext,
1103 "xmlC14NProcessElementNode: xmlC14NProcessNamespacesAxis failed.\n");
1104 #endif
1105 return (-1);
1107 /* todo: shouldn't this go to "visible only"? */
1108 if(visible) {
1109 xmlC14NVisibleNsStackShift(ctx->ns_rendered);
1112 if(visible) {
1113 ret = xmlC14NProcessAttrsAxis(ctx, cur);
1114 if (ret < 0) {
1115 #ifdef DEBUG_C14N
1116 xmlGenericError(xmlGenericErrorContext,
1117 "xmlC14NProcessElementNode: xmlC14NProcessAttrsAxis failed.\n");
1118 #endif
1119 return (-1);
1123 if (visible) {
1124 xmlOutputBufferWriteString(ctx->buf, ">");
1126 if (cur->children != NULL) {
1127 ret = xmlC14NProcessNodeList(ctx, cur->children);
1128 if (ret < 0) {
1129 #ifdef DEBUG_C14N
1130 xmlGenericError(xmlGenericErrorContext,
1131 "xmlC14NProcessElementNode: xmlC14NProcessNodeList failed.\n");
1132 #endif
1133 return (-1);
1136 if (visible) {
1137 xmlOutputBufferWriteString(ctx->buf, "</");
1138 if ((cur->ns != NULL) && (xmlStrlen(cur->ns->prefix) > 0)) {
1139 xmlOutputBufferWriteString(ctx->buf,
1140 (const char *) cur->ns->prefix);
1141 xmlOutputBufferWriteString(ctx->buf, ":");
1143 xmlOutputBufferWriteString(ctx->buf, (const char *) cur->name);
1144 xmlOutputBufferWriteString(ctx->buf, ">");
1145 if (parent_is_doc) {
1146 /* restore this flag from the stack for next node */
1147 ctx->parent_is_doc = parent_is_doc;
1148 ctx->pos = XMLC14N_AFTER_DOCUMENT_ELEMENT;
1153 * Restore ns_rendered stack position
1155 xmlC14NVisibleNsStackRestore(ctx->ns_rendered, &state);
1156 return (0);
1160 * xmlC14NProcessNode:
1161 * @ctx: the pointer to C14N context object
1162 * @cur: the node to process
1164 * Processes the given node
1166 * Returns non-negative value on success or negative value on fail
1168 static int
1169 xmlC14NProcessNode(xmlC14NCtxPtr ctx, xmlNodePtr cur)
1171 int ret = 0;
1172 int visible;
1174 if ((ctx == NULL) || (cur == NULL)) {
1175 #ifdef DEBUG_C14N
1176 xmlGenericError(xmlGenericErrorContext,
1177 "xmlC14NProcessNode: Null context or node pointer.\n");
1178 #endif
1179 return (-1);
1182 visible = xmlC14NIsVisible(ctx, cur, cur->parent);
1183 switch (cur->type) {
1184 case XML_ELEMENT_NODE:
1185 ret = xmlC14NProcessElementNode(ctx, cur, visible);
1186 break;
1187 case XML_CDATA_SECTION_NODE:
1188 case XML_TEXT_NODE:
1190 * Text Nodes
1191 * the string value, except all ampersands are replaced
1192 * by &amp;, all open angle brackets (<) are replaced by &lt;, all closing
1193 * angle brackets (>) are replaced by &gt;, and all #xD characters are
1194 * replaced by &#xD;.
1196 /* cdata sections are processed as text nodes */
1197 /* todo: verify that cdata sections are included in XPath nodes set */
1198 if ((visible) && (cur->content != NULL)) {
1199 xmlChar *buffer;
1201 buffer = xmlC11NNormalizeText(cur->content);
1202 if (buffer != NULL) {
1203 xmlOutputBufferWriteString(ctx->buf,
1204 (const char *) buffer);
1205 xmlFree(buffer);
1206 } else {
1207 #ifdef DEBUG_C14N
1208 xmlGenericError(xmlGenericErrorContext,
1209 "xmlC14NProcessNode: xmlC11NNormalizeText() failed\n");
1210 #endif
1211 return (-1);
1214 break;
1215 case XML_PI_NODE:
1217 * Processing Instruction (PI) Nodes-
1218 * The opening PI symbol (<?), the PI target name of the node,
1219 * a leading space and the string value if it is not empty, and
1220 * the closing PI symbol (?>). If the string value is empty,
1221 * then the leading space is not added. Also, a trailing #xA is
1222 * rendered after the closing PI symbol for PI children of the
1223 * root node with a lesser document order than the document
1224 * element, and a leading #xA is rendered before the opening PI
1225 * symbol of PI children of the root node with a greater document
1226 * order than the document element.
1228 if (visible) {
1229 if (ctx->pos == XMLC14N_AFTER_DOCUMENT_ELEMENT) {
1230 xmlOutputBufferWriteString(ctx->buf, "\x0A<?");
1231 } else {
1232 xmlOutputBufferWriteString(ctx->buf, "<?");
1235 xmlOutputBufferWriteString(ctx->buf,
1236 (const char *) cur->name);
1237 if ((cur->content != NULL) && (*(cur->content) != '\0')) {
1238 xmlChar *buffer;
1240 xmlOutputBufferWriteString(ctx->buf, " ");
1242 /* todo: do we need to normalize pi? */
1243 buffer = xmlC11NNormalizePI(cur->content);
1244 if (buffer != NULL) {
1245 xmlOutputBufferWriteString(ctx->buf,
1246 (const char *) buffer);
1247 xmlFree(buffer);
1248 } else {
1249 #ifdef DEBUG_C14N
1250 xmlGenericError(xmlGenericErrorContext,
1251 "xmlC14NProcessNode: xmlC11NNormalizePI() failed\n");
1252 #endif
1253 return (-1);
1257 if (ctx->pos == XMLC14N_BEFORE_DOCUMENT_ELEMENT) {
1258 xmlOutputBufferWriteString(ctx->buf, "?>\x0A");
1259 } else {
1260 xmlOutputBufferWriteString(ctx->buf, "?>");
1263 break;
1264 case XML_COMMENT_NODE:
1266 * Comment Nodes
1267 * Nothing if generating canonical XML without comments. For
1268 * canonical XML with comments, generate the opening comment
1269 * symbol (<!--), the string value of the node, and the
1270 * closing comment symbol (-->). Also, a trailing #xA is rendered
1271 * after the closing comment symbol for comment children of the
1272 * root node with a lesser document order than the document
1273 * element, and a leading #xA is rendered before the opening
1274 * comment symbol of comment children of the root node with a
1275 * greater document order than the document element. (Comment
1276 * children of the root node represent comments outside of the
1277 * top-level document element and outside of the document type
1278 * declaration).
1280 if (visible && ctx->with_comments) {
1281 if (ctx->pos == XMLC14N_AFTER_DOCUMENT_ELEMENT) {
1282 xmlOutputBufferWriteString(ctx->buf, "\x0A<!--");
1283 } else {
1284 xmlOutputBufferWriteString(ctx->buf, "<!--");
1287 if (cur->content != NULL) {
1288 xmlChar *buffer;
1290 /* todo: do we need to normalize comment? */
1291 buffer = xmlC11NNormalizeComment(cur->content);
1292 if (buffer != NULL) {
1293 xmlOutputBufferWriteString(ctx->buf,
1294 (const char *) buffer);
1295 xmlFree(buffer);
1296 } else {
1297 #ifdef DEBUG_C14N
1298 xmlGenericError(xmlGenericErrorContext,
1299 "xmlC14NProcessNode: xmlC11NNormalizeComment() failed\n");
1300 #endif
1301 return (-1);
1305 if (ctx->pos == XMLC14N_BEFORE_DOCUMENT_ELEMENT) {
1306 xmlOutputBufferWriteString(ctx->buf, "-->\x0A");
1307 } else {
1308 xmlOutputBufferWriteString(ctx->buf, "-->");
1311 break;
1312 case XML_DOCUMENT_NODE:
1313 case XML_DOCUMENT_FRAG_NODE: /* should be processed as document? */
1314 #ifdef LIBXML_DOCB_ENABLED
1315 case XML_DOCB_DOCUMENT_NODE: /* should be processed as document? */
1316 #endif
1317 #ifdef LIBXML_HTML_ENABLED
1318 case XML_HTML_DOCUMENT_NODE: /* should be processed as document? */
1319 #endif
1320 if (cur->children != NULL) {
1321 ctx->pos = XMLC14N_BEFORE_DOCUMENT_ELEMENT;
1322 ctx->parent_is_doc = 1;
1323 ret = xmlC14NProcessNodeList(ctx, cur->children);
1325 break;
1327 case XML_ATTRIBUTE_NODE:
1328 xmlGenericError(xmlGenericErrorContext,
1329 "xmlC14NProcessNode: XML_ATTRIBUTE_NODE is illegal here\n");
1330 return (-1);
1331 case XML_NAMESPACE_DECL:
1332 xmlGenericError(xmlGenericErrorContext,
1333 "xmlC14NProcessNode: XML_NAMESPACE_DECL is illegal here\n");
1334 return (-1);
1335 case XML_ENTITY_REF_NODE:
1336 xmlGenericError(xmlGenericErrorContext,
1337 "xmlC14NProcessNode: XML_ENTITY_REF_NODE is illegal here\n");
1338 return (-1);
1339 case XML_ENTITY_NODE:
1340 xmlGenericError(xmlGenericErrorContext,
1341 "xmlC14NProcessNode: XML_ENTITY_NODE is illegal here\n");
1342 return (-1);
1344 case XML_DOCUMENT_TYPE_NODE:
1345 case XML_NOTATION_NODE:
1346 case XML_DTD_NODE:
1347 case XML_ELEMENT_DECL:
1348 case XML_ATTRIBUTE_DECL:
1349 case XML_ENTITY_DECL:
1350 #ifdef LIBXML_XINCLUDE_ENABLED
1351 case XML_XINCLUDE_START:
1352 case XML_XINCLUDE_END:
1353 #endif
1355 * should be ignored according to "W3C Canonical XML"
1357 break;
1358 default:
1359 #ifdef DEBUG_C14N
1360 xmlGenericError(xmlGenericErrorContext,
1361 "xmlC14NProcessNode: unknown node type = %d\n",
1362 cur->type);
1363 #endif
1364 return (-1);
1367 return (ret);
1371 * xmlC14NProcessNodeList:
1372 * @ctx: the pointer to C14N context object
1373 * @cur: the node to start from
1375 * Processes all nodes in the row starting from cur.
1377 * Returns non-negative value on success or negative value on fail
1379 static int
1380 xmlC14NProcessNodeList(xmlC14NCtxPtr ctx, xmlNodePtr cur)
1382 int ret;
1384 if (ctx == NULL) {
1385 #ifdef DEBUG_C14N
1386 xmlGenericError(xmlGenericErrorContext,
1387 "xmlC14NProcessNodeList: Null context pointer.\n");
1388 #endif
1389 return (-1);
1392 for (ret = 0; cur != NULL && ret >= 0; cur = cur->next) {
1393 ret = xmlC14NProcessNode(ctx, cur);
1395 return (ret);
1400 * xmlC14NFreeCtx:
1401 * @ctx: the pointer to C14N context object
1403 * Cleanups the C14N context object.
1406 static void
1407 xmlC14NFreeCtx(xmlC14NCtxPtr ctx)
1409 if (ctx == NULL) {
1410 #ifdef DEBUG_C14N
1411 xmlGenericError(xmlGenericErrorContext,
1412 "xmlC14NFreeCtx: ctx == NULL\n");
1413 #endif
1414 return;
1417 if (ctx->ns_rendered != NULL) {
1418 xmlC14NVisibleNsStackDestroy(ctx->ns_rendered);
1420 xmlFree(ctx);
1424 * xmlC14NNewCtx:
1425 * @doc: the XML document for canonization
1426 * @is_visible_callback:the function to use to determine is node visible
1427 * or not
1428 * @user_data: the first parameter for @is_visible_callback function
1429 * (in most cases, it is nodes set)
1430 * @inclusive_ns_prefixe the list of inclusive namespace prefixes
1431 * ended with a NULL or NULL if there is no
1432 * inclusive namespaces (only for exclusive
1433 * canonicalization)
1434 * @with_comments: include comments in the result (!=0) or not (==0)
1435 * @buf: the output buffer to store canonical XML; this
1436 * buffer MUST have encoder==NULL because C14N requires
1437 * UTF-8 output
1439 * Creates new C14N context object to store C14N parameters.
1441 * Returns pointer to newly created object (success) or NULL (fail)
1443 static xmlC14NCtxPtr
1444 xmlC14NNewCtx(xmlDocPtr doc,
1445 xmlC14NIsVisibleCallback is_visible_callback, void* user_data,
1446 int exclusive, xmlChar ** inclusive_ns_prefixes,
1447 int with_comments, xmlOutputBufferPtr buf)
1449 xmlC14NCtxPtr ctx;
1451 if ((doc == NULL) || (buf == NULL)) {
1452 #ifdef DEBUG_C14N
1453 xmlGenericError(xmlGenericErrorContext,
1454 "xmlC14NNewCtx: pointer to document or output buffer is NULL\n");
1455 #endif
1456 return (NULL);
1460 * Validate the encoding output buffer encoding
1462 if (buf->encoder != NULL) {
1463 xmlGenericError(xmlGenericErrorContext,
1464 "xmlC14NNewCtx: output buffer encoder != NULL but C14N requires UTF8 output\n");
1465 return (NULL);
1469 * Validate the XML document encoding value, if provided.
1471 if (doc->charset != XML_CHAR_ENCODING_UTF8) {
1472 xmlGenericError(xmlGenericErrorContext,
1473 "xmlC14NNewCtx: source document not in UTF8\n");
1474 return (NULL);
1478 * Allocate a new xmlC14NCtxPtr and fill the fields.
1480 ctx = (xmlC14NCtxPtr) xmlMalloc(sizeof(xmlC14NCtx));
1481 if (ctx == NULL) {
1482 xmlGenericError(xmlGenericErrorContext,
1483 "xmlC14NNewCtx: malloc failed\n");
1484 return (NULL);
1486 memset(ctx, 0, sizeof(xmlC14NCtx));
1489 * initialize C14N context
1491 ctx->doc = doc;
1492 ctx->with_comments = with_comments;
1493 ctx->is_visible_callback = is_visible_callback;
1494 ctx->user_data = user_data;
1495 ctx->buf = buf;
1496 ctx->parent_is_doc = 1;
1497 ctx->pos = XMLC14N_BEFORE_DOCUMENT_ELEMENT;
1498 ctx->ns_rendered = xmlC14NVisibleNsStackCreate();
1500 if(ctx->ns_rendered == NULL) {
1501 xmlGenericError(xmlGenericErrorContext,
1502 "xmlC14NNewCtx: xmlC14NVisibleNsStackCreate failed\n");
1503 xmlC14NFreeCtx(ctx);
1504 return (NULL);
1508 * Set "exclusive" flag, create a nodes set for namespaces
1509 * stack and remember list of incluseve prefixes
1511 if (exclusive) {
1512 ctx->exclusive = 1;
1513 ctx->inclusive_ns_prefixes = inclusive_ns_prefixes;
1515 return (ctx);
1519 * xmlC14NExecute:
1520 * @doc: the XML document for canonization
1521 * @is_visible_callback:the function to use to determine is node visible
1522 * or not
1523 * @user_data: the first parameter for @is_visible_callback function
1524 * (in most cases, it is nodes set)
1525 * @exclusive: the exclusive flag (0 - non-exclusive canonicalization;
1526 * otherwise - exclusive canonicalization)
1527 * @inclusive_ns_prefixes: the list of inclusive namespace prefixes
1528 * ended with a NULL or NULL if there is no
1529 * inclusive namespaces (only for exclusive
1530 * canonicalization, ignored otherwise)
1531 * @with_comments: include comments in the result (!=0) or not (==0)
1532 * @buf: the output buffer to store canonical XML; this
1533 * buffer MUST have encoder==NULL because C14N requires
1534 * UTF-8 output
1536 * Dumps the canonized image of given XML document into the provided buffer.
1537 * For details see "Canonical XML" (http://www.w3.org/TR/xml-c14n) or
1538 * "Exclusive XML Canonicalization" (http://www.w3.org/TR/xml-exc-c14n)
1540 * Returns non-negative value on success or a negative value on fail
1542 int
1543 xmlC14NExecute(xmlDocPtr doc, xmlC14NIsVisibleCallback is_visible_callback,
1544 void* user_data, int exclusive, xmlChar **inclusive_ns_prefixes,
1545 int with_comments, xmlOutputBufferPtr buf) {
1547 xmlC14NCtxPtr ctx;
1548 int ret;
1550 if ((buf == NULL) || (doc == NULL)) {
1551 #ifdef DEBUG_C14N
1552 xmlGenericError(xmlGenericErrorContext,
1553 "xmlC14NExecute: null return buffer or doc pointer\n");
1554 #endif
1555 return (-1);
1559 * Validate the encoding output buffer encoding
1561 if (buf->encoder != NULL) {
1562 xmlGenericError(xmlGenericErrorContext,
1563 "xmlC14NExecute: output buffer encoder != NULL but C14N requires UTF8 output\n");
1564 return (-1);
1567 ctx = xmlC14NNewCtx(doc, is_visible_callback, user_data,
1568 exclusive, inclusive_ns_prefixes,
1569 with_comments, buf);
1570 if (ctx == NULL) {
1571 xmlGenericError(xmlGenericErrorContext,
1572 "xmlC14NExecute: unable to create C14N context\n");
1573 return (-1);
1579 * Root Node
1580 * The root node is the parent of the top-level document element. The
1581 * result of processing each of its child nodes that is in the node-set
1582 * in document order. The root node does not generate a byte order mark,
1583 * XML declaration, nor anything from within the document type
1584 * declaration.
1586 if (doc->children != NULL) {
1587 ret = xmlC14NProcessNodeList(ctx, doc->children);
1588 if (ret < 0) {
1589 #ifdef DEBUG_C14N
1590 xmlGenericError(xmlGenericErrorContext,
1591 "xmlC14NExecute: process childrens' list failed.\n");
1592 #endif
1593 xmlC14NFreeCtx(ctx);
1594 return (-1);
1599 * Flush buffer to get number of bytes written
1601 ret = xmlOutputBufferFlush(buf);
1602 if (ret < 0) {
1603 #ifdef DEBUG_C14N
1604 xmlGenericError(xmlGenericErrorContext,
1605 "xmlC14NExecute: buffer flush failed.\n");
1606 #endif
1607 xmlC14NFreeCtx(ctx);
1608 return (-1);
1612 * Cleanup
1614 xmlC14NFreeCtx(ctx);
1615 return (ret);
1619 * xmlC14NDocSaveTo:
1620 * @doc: the XML document for canonization
1621 * @nodes: the nodes set to be included in the canonized image
1622 * or NULL if all document nodes should be included
1623 * @exclusive: the exclusive flag (0 - non-exclusive canonicalization;
1624 * otherwise - exclusive canonicalization)
1625 * @inclusive_ns_prefixes: the list of inclusive namespace prefixes
1626 * ended with a NULL or NULL if there is no
1627 * inclusive namespaces (only for exclusive
1628 * canonicalization, ignored otherwise)
1629 * @with_comments: include comments in the result (!=0) or not (==0)
1630 * @buf: the output buffer to store canonical XML; this
1631 * buffer MUST have encoder==NULL because C14N requires
1632 * UTF-8 output
1634 * Dumps the canonized image of given XML document into the provided buffer.
1635 * For details see "Canonical XML" (http://www.w3.org/TR/xml-c14n) or
1636 * "Exclusive XML Canonicalization" (http://www.w3.org/TR/xml-exc-c14n)
1638 * Returns non-negative value on success or a negative value on fail
1641 xmlC14NDocSaveTo(xmlDocPtr doc, xmlNodeSetPtr nodes,
1642 int exclusive, xmlChar ** inclusive_ns_prefixes,
1643 int with_comments, xmlOutputBufferPtr buf) {
1644 return(xmlC14NExecute(doc,
1645 (xmlC14NIsVisibleCallback)xmlC14NIsNodeInNodeset,
1646 nodes,
1647 exclusive,
1648 inclusive_ns_prefixes,
1649 with_comments,
1650 buf));
1655 * xmlC14NDocDumpMemory:
1656 * @doc: the XML document for canonization
1657 * @nodes: the nodes set to be included in the canonized image
1658 * or NULL if all document nodes should be included
1659 * @exclusive: the exclusive flag (0 - non-exclusive canonicalization;
1660 * otherwise - exclusive canonicalization)
1661 * @inclusive_ns_prefixes: the list of inclusive namespace prefixes
1662 * ended with a NULL or NULL if there is no
1663 * inclusive namespaces (only for exclusive
1664 * canonicalization, ignored otherwise)
1665 * @with_comments: include comments in the result (!=0) or not (==0)
1666 * @doc_txt_ptr: the memory pointer for allocated canonical XML text;
1667 * the caller of this functions is responsible for calling
1668 * xmlFree() to free allocated memory
1670 * Dumps the canonized image of given XML document into memory.
1671 * For details see "Canonical XML" (http://www.w3.org/TR/xml-c14n) or
1672 * "Exclusive XML Canonicalization" (http://www.w3.org/TR/xml-exc-c14n)
1674 * Returns the number of bytes written on success or a negative value on fail
1677 xmlC14NDocDumpMemory(xmlDocPtr doc, xmlNodeSetPtr nodes,
1678 int exclusive, xmlChar ** inclusive_ns_prefixes,
1679 int with_comments, xmlChar ** doc_txt_ptr)
1681 int ret;
1682 xmlOutputBufferPtr buf;
1684 if (doc_txt_ptr == NULL) {
1685 #ifdef DEBUG_C14N
1686 xmlGenericError(xmlGenericErrorContext,
1687 "xmlC14NDocDumpMemory: null return buffer pointer\n");
1688 #endif
1689 return (-1);
1692 *doc_txt_ptr = NULL;
1695 * create memory buffer with UTF8 (default) encoding
1697 buf = xmlAllocOutputBuffer(NULL);
1698 if (buf == NULL) {
1699 #ifdef DEBUG_C14N
1700 xmlGenericError(xmlGenericErrorContext,
1701 "xmlC14NDocDumpMemory: failed to allocate output buffer.\n");
1702 #endif
1703 return (-1);
1707 * canonize document and write to buffer
1709 ret = xmlC14NDocSaveTo(doc, nodes, exclusive, inclusive_ns_prefixes,
1710 with_comments, buf);
1711 if (ret < 0) {
1712 #ifdef DEBUG_C14N
1713 xmlGenericError(xmlGenericErrorContext,
1714 "xmlC14NDocDumpMemory: xmlC14NDocSaveTo failed.\n");
1715 #endif
1716 (void) xmlOutputBufferClose(buf);
1717 return (-1);
1720 ret = buf->buffer->use;
1721 if (ret > 0) {
1722 *doc_txt_ptr = xmlStrndup(buf->buffer->content, ret);
1724 (void) xmlOutputBufferClose(buf);
1726 if ((*doc_txt_ptr == NULL) && (ret > 0)) {
1727 #ifdef DEBUG_C14N
1728 xmlGenericError(xmlGenericErrorContext,
1729 "xmlC14NDocDumpMemory: failed to allocate memory for document text representation\n");
1730 #endif
1731 return (-1);
1733 return (ret);
1737 * xmlC14NDocSave:
1738 * @doc: the XML document for canonization
1739 * @nodes: the nodes set to be included in the canonized image
1740 * or NULL if all document nodes should be included
1741 * @exclusive: the exclusive flag (0 - non-exclusive canonicalization;
1742 * otherwise - exclusive canonicalization)
1743 * @inclusive_ns_prefixes: the list of inclusive namespace prefixes
1744 * ended with a NULL or NULL if there is no
1745 * inclusive namespaces (only for exclusive
1746 * canonicalization, ignored otherwise)
1747 * @with_comments: include comments in the result (!=0) or not (==0)
1748 * @filename: the filename to store canonical XML image
1749 * @compression: the compression level (zlib requred):
1750 * -1 - libxml default,
1751 * 0 - uncompressed,
1752 * >0 - compression level
1754 * Dumps the canonized image of given XML document into the file.
1755 * For details see "Canonical XML" (http://www.w3.org/TR/xml-c14n) or
1756 * "Exclusive XML Canonicalization" (http://www.w3.org/TR/xml-exc-c14n)
1758 * Returns the number of bytes written success or a negative value on fail
1761 xmlC14NDocSave(xmlDocPtr doc, xmlNodeSetPtr nodes,
1762 int exclusive, xmlChar ** inclusive_ns_prefixes,
1763 int with_comments, const char *filename, int compression)
1765 xmlOutputBufferPtr buf;
1766 int ret;
1768 if (filename == NULL) {
1769 #ifdef DEBUG_C14N
1770 xmlGenericError(xmlGenericErrorContext,
1771 "xmlC14NDocSave: filename is NULL\n");
1772 #endif
1773 return (-1);
1775 #ifdef HAVE_ZLIB_H
1776 if (compression < 0)
1777 compression = xmlGetCompressMode();
1778 #endif
1781 * save the content to a temp buffer, use default UTF8 encoding.
1783 buf = xmlOutputBufferCreateFilename(filename, NULL, compression);
1784 if (buf == NULL) {
1785 #ifdef DEBUG_C14N
1786 xmlGenericError(xmlGenericErrorContext,
1787 "xmlC14NDocSave: unable to create buffer for file=\"%s\" with compressin=%d\n",
1788 filename, compression);
1789 #endif
1790 return (-1);
1794 * canonize document and write to buffer
1796 ret = xmlC14NDocSaveTo(doc, nodes, exclusive, inclusive_ns_prefixes,
1797 with_comments, buf);
1798 if (ret < 0) {
1799 #ifdef DEBUG_C14N
1800 xmlGenericError(xmlGenericErrorContext,
1801 "xmlC14NDocSave: xmlC14NDocSaveTo failed.\n");
1802 #endif
1803 (void) xmlOutputBufferClose(buf);
1804 return (-1);
1808 * get the numbers of bytes written
1810 ret = xmlOutputBufferClose(buf);
1811 return (ret);
1817 * Macro used to grow the current buffer.
1819 #define growBufferReentrant() { \
1820 buffer_size *= 2; \
1821 buffer = (xmlChar *) \
1822 xmlRealloc(buffer, buffer_size * sizeof(xmlChar)); \
1823 if (buffer == NULL) { \
1824 xmlGenericError(xmlGenericErrorContext, "realloc failed"); \
1825 return(NULL); \
1829 /**
1830 * xmlC11NNormalizeString:
1831 * @input: the input string
1832 * @mode: the normalization mode (attribute, comment, PI or text)
1834 * Converts a string to a canonical (normalized) format. The code is stolen
1835 * from xmlEncodeEntitiesReentrant(). Added normalization of \x09, \x0a, \x0A
1836 * and the @mode parameter
1838 * Returns a normalized string (caller is responsible for calling xmlFree())
1839 * or NULL if an error occurs
1841 static xmlChar *
1842 xmlC11NNormalizeString(const xmlChar * input,
1843 xmlC14NNormalizationMode mode)
1845 const xmlChar *cur = input;
1846 xmlChar *buffer = NULL;
1847 xmlChar *out = NULL;
1848 int buffer_size = 0;
1850 if (input == NULL)
1851 return (NULL);
1854 * allocate an translation buffer.
1856 buffer_size = 1000;
1857 buffer = (xmlChar *) xmlMalloc(buffer_size * sizeof(xmlChar));
1858 if (buffer == NULL) {
1859 xmlGenericError(xmlGenericErrorContext, "malloc failed");
1860 return (NULL);
1862 out = buffer;
1864 while (*cur != '\0') {
1865 if ((out - buffer) > (buffer_size - 10)) {
1866 int indx = out - buffer;
1868 growBufferReentrant();
1869 out = &buffer[indx];
1872 if ((*cur == '<') && ((mode == XMLC14N_NORMALIZE_ATTR) ||
1873 (mode == XMLC14N_NORMALIZE_TEXT))) {
1874 *out++ = '&';
1875 *out++ = 'l';
1876 *out++ = 't';
1877 *out++ = ';';
1878 } else if ((*cur == '>') && (mode == XMLC14N_NORMALIZE_TEXT)) {
1879 *out++ = '&';
1880 *out++ = 'g';
1881 *out++ = 't';
1882 *out++ = ';';
1883 } else if ((*cur == '&') && ((mode == XMLC14N_NORMALIZE_ATTR) ||
1884 (mode == XMLC14N_NORMALIZE_TEXT))) {
1885 *out++ = '&';
1886 *out++ = 'a';
1887 *out++ = 'm';
1888 *out++ = 'p';
1889 *out++ = ';';
1890 } else if ((*cur == '"') && (mode == XMLC14N_NORMALIZE_ATTR)) {
1891 *out++ = '&';
1892 *out++ = 'q';
1893 *out++ = 'u';
1894 *out++ = 'o';
1895 *out++ = 't';
1896 *out++ = ';';
1897 } else if ((*cur == '\x09') && (mode == XMLC14N_NORMALIZE_ATTR)) {
1898 *out++ = '&';
1899 *out++ = '#';
1900 *out++ = 'x';
1901 *out++ = '9';
1902 *out++ = ';';
1903 } else if ((*cur == '\x0A') && (mode == XMLC14N_NORMALIZE_ATTR)) {
1904 *out++ = '&';
1905 *out++ = '#';
1906 *out++ = 'x';
1907 *out++ = 'A';
1908 *out++ = ';';
1909 } else if ((*cur == '\x0D') && ((mode == XMLC14N_NORMALIZE_ATTR) ||
1910 (mode == XMLC14N_NORMALIZE_TEXT) ||
1911 (mode == XMLC14N_NORMALIZE_COMMENT) ||
1912 (mode == XMLC14N_NORMALIZE_PI))) {
1913 *out++ = '&';
1914 *out++ = '#';
1915 *out++ = 'x';
1916 *out++ = 'D';
1917 *out++ = ';';
1918 } else {
1920 * Works because on UTF-8, all extended sequences cannot
1921 * result in bytes in the ASCII range.
1923 *out++ = *cur;
1925 cur++;
1927 *out++ = 0;
1928 return (buffer);
1930 #endif /* LIBXML_C14N_ENABLED */