blob: 1db73fc8667f7dbb870e0cb895647846f07b335e [file] [log] [blame]
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001/*
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#include "libxml.h"
13#ifdef LIBXML_C14N_ENABLED
14
15#ifdef HAVE_STDLIB_H
16#include <stdlib.h>
17#endif
18#include <string.h>
19
20#include <libxml/tree.h>
21#include <libxml/parser.h>
22#include <libxml/uri.h>
23#include <libxml/xmlerror.h>
24#include <libxml/globals.h>
25#include <libxml/xpathInternals.h>
26#include <libxml/c14n.h>
27
28/************************************************************************
29 * *
30 * Some declaration better left private ATM *
31 * *
32 ************************************************************************/
33
34typedef enum {
35 XMLC14N_BEFORE_DOCUMENT_ELEMENT= 0,
36 XMLC14N_INSIDE_DOCUMENT_ELEMENT= 1,
37 XMLC14N_AFTER_DOCUMENT_ELEMENT= 2
38} xmlC14NPosition;
39
40typedef struct _xmlC14NCtx {
41 /* input parameters */
42 xmlDocPtr doc;
43 xmlNodeSetPtr visible_nodes;
44 int with_comments;
45 xmlOutputBufferPtr buf;
46
47 /* position in the XML document */
48 xmlC14NPosition pos;
49 int parent_is_doc;
50
51 /* exclusive canonicalization */
52 int exclusive;
53 xmlNodeSetPtr ns_rendered;
54 xmlChar **inclusive_ns_prefixes;
55} xmlC14NCtx, *xmlC14NCtxPtr;
56
57
58static int xmlC14NProcessNode (xmlC14NCtxPtr ctx,
59 xmlNodePtr cur);
60static int xmlC14NProcessNodeList (xmlC14NCtxPtr ctx,
61 xmlNodePtr cur);
62typedef enum {
63 XMLC14N_NORMALIZE_ATTR= 0,
64 XMLC14N_NORMALIZE_COMMENT= 1,
65 XMLC14N_NORMALIZE_PI= 2,
66 XMLC14N_NORMALIZE_TEXT= 3
67} xmlC14NNormalizationMode;
68
69static xmlChar* xmlC11NNormalizeString (const xmlChar *input,
70 xmlC14NNormalizationMode mode);
71
72#define xmlC11NNormalizeAttr( a ) \
73 xmlC11NNormalizeString((a), XMLC14N_NORMALIZE_ATTR)
74#define xmlC11NNormalizeComment( a ) \
75 xmlC11NNormalizeString((a), XMLC14N_NORMALIZE_COMMENT)
76#define xmlC11NNormalizePI( a ) \
77 xmlC11NNormalizeString((a), XMLC14N_NORMALIZE_PI)
78#define xmlC11NNormalizeText( a ) \
79 xmlC11NNormalizeString((a), XMLC14N_NORMALIZE_TEXT)
80
81/************************************************************************
82 * *
83 * The implementation internals *
84 * *
85 ************************************************************************/
86
87/**
88 * xmlC14NIsVisible:
89 * @ctx: the C14N context
90 * @node: the node to check
91 *
92 * Checks whether the given node is visible. If the XML document normalization
93 * was called for the whole document then it is always "true".
94 *
95 * Returns 1 if the node is visible or 0 otherwise.
96 */
97/* todo: make it a define? */
98static int
99xmlC14NIsVisible(xmlC14NCtxPtr ctx, void *node) {
100 /*
101 * If the input is an XPath node-set, then the node-set must explicitly
102 * contain every node to be rendered to the canonical form.
103 */
104 if(ctx->visible_nodes != NULL &&
105 !xmlXPathNodeSetContains(ctx->visible_nodes, (xmlNodePtr)node)) {
106 return(0);
107 }
108
109 return(1);
110}
111
112/**
113 * xmlC14NIsXmlNs:
114 * @ns: the namespace to check
115 *
116 * Checks whether the given namespace is a default "xml:" namespace
117 * with href="http://www.w3.org/XML/1998/namespace"
118 *
119 * Returns 1 if the node is default or 0 otherwise
120 */
121/* todo: make it a define? */
122static int
123xmlC14NIsXmlNs(xmlNsPtr ns) {
124 return (ns != NULL &&
125 xmlStrEqual(ns->prefix, BAD_CAST "xml") &&
126 xmlStrEqual(ns->href, BAD_CAST "http://www.w3.org/XML/1998/namespace"));
127}
128
129/**
130 * xmlExcC14NIsRendered:
131 * @ctx the C14N context
132 * @ns the namespace to check
133 *
134 * Checks whether the given namespace was already rendered or not
135 *
136 * Returns 1 if we already wrote this namespace or 0 otherwise
137 */
138static int
139xmlExcC14NIsRendered(xmlC14NCtxPtr ctx, xmlNsPtr ns) {
140 int i;
141
142 if(ctx == NULL || ctx->ns_rendered == NULL || ns == NULL) {
143 return(0);
144 }
145
146 if(ctx->ns_rendered->nodeTab != NULL) {
147 for(i = ctx->ns_rendered->nodeNr - 1; i >= 0; --i) {
148 xmlNsPtr ns1 = (xmlNsPtr)ctx->ns_rendered->nodeTab[i];
149 if(xmlStrEqual(ns1->prefix, ns->prefix)) {
150 return (xmlStrEqual(ns1->href, ns->href));
151 }
152 }
153 }
154 /*
155 * if the default namespace xmlns="" is not defined yet then
156 * we do not want to print it out
157 */
158 return(xmlStrlen(ns->prefix) == 0 && xmlStrlen(ns->href) == 0);
159}
160
161/**
162 * xmlC14NNamespacesCompare:
163 * @ns1: the pointer to first namespace
164 * @ns2: the pointer to second namespace
165 *
166 * Compares the namespaces by names (prefixes).
167 *
168 * Returns -1 if ns1 < ns2, 0 if ns1 == ns2 or 1 if ns1 > ns2.
169 */
170static int
171xmlC14NNamespacesCompare(xmlNsPtr ns1, xmlNsPtr ns2) {
172 if(ns1 == ns2) return(0);
173 if(ns1 == NULL) return(-1);
174 if(ns2 == NULL) return(1);
175
176 return(xmlStrcmp(ns1->prefix, ns2->prefix));
177}
178
179
180/**
181 * xmlC14NPrintNamespaces:
182 * @ns: the pointer to namespace
183 * @ctx: the C14N context
184 *
185 * Prints the given namespace to the output buffer from C14N context.
186 *
187 * Returns 1 on success or 0 on fail.
188 */
189static int
190xmlC14NPrintNamespaces(const xmlNsPtr ns, xmlC14NCtxPtr ctx) {
191
192 if(ns == NULL || ctx == NULL) {
193#ifdef DEBUG_C14N
194 xmlGenericError(xmlGenericErrorContext,
195 "xmlC14NPrintNamespace: namespace or context pointer is null\n");
196#endif
197 return 0;
198 }
199
200 if(ns->prefix != NULL) {
201 xmlOutputBufferWriteString(ctx->buf, " xmlns:");
202 xmlOutputBufferWriteString(ctx->buf, (const char *)ns->prefix);
203 xmlOutputBufferWriteString(ctx->buf, "=\"");
204 } else {
205 xmlOutputBufferWriteString(ctx->buf, " xmlns=\"");
206 }
207 xmlOutputBufferWriteString(ctx->buf, (const char *) ns->href);
208 xmlOutputBufferWriteString(ctx->buf, "\"");
209 return(1);
210}
211
212/**
213 * xmlC14NProcessNamespacesAxis:
214 * @ctx: the C14N context
215 * @node: the current node
216 *
217 * Prints out canonical namespace axis of the current node to the
218 * buffer from C14N context as follows
219 *
220 * Canonical XML v 1.0 (http://www.w3.org/TR/xml-c14n)
221 *
222 * Namespace Axis
223 * Consider a list L containing only namespace nodes in the
224 * axis and in the node-set in lexicographic order (ascending). To begin
225 * processing L, if the first node is not the default namespace node (a node
226 * with no namespace URI and no local name), then generate a space followed
227 * by xmlns="" if and only if the following conditions are met:
228 * - the element E that owns the axis is in the node-set
229 * - The nearest ancestor element of E in the node-set has a default
230 * namespace node in the node-set (default namespace nodes always
231 * have non-empty values in XPath)
232 * The latter condition eliminates unnecessary occurrences of xmlns="" in
233 * the canonical form since an element only receives an xmlns="" if its
234 * default namespace is empty and if it has an immediate parent in the
235 * canonical form that has a non-empty default namespace. To finish
236 * processing L, simply process every namespace node in L, except omit
237 * namespace node with local name xml, which defines the xml prefix,
238 * if its string value is http://www.w3.org/XML/1998/namespace.
239 *
240 * Exclusive XML Canonicalization v 1.0 (http://www.w3.org/TR/xml-exc-c14n)
241 * Canonical XML applied to a document subset requires the search of the
242 * ancestor nodes of each orphan element node for attributes in the xml
243 * namespace, such as xml:lang and xml:space. These are copied into the
244 * element node except if a declaration of the same attribute is already
245 * in the attribute axis of the element (whether or not it is included in
246 * the document subset). This search and copying are omitted from the
247 * Exclusive XML Canonicalization method.
248 *
249 * Returns 0 on success or -1 on fail.
250 */
251static int
252xmlC14NProcessNamespacesAxis(xmlC14NCtxPtr ctx, xmlNodePtr cur) {
253 xmlNsPtr ns;
254 xmlListPtr list;
255 xmlNodePtr visible_parent;
256 xmlNsPtr prev;
257
258 if(ctx == NULL || cur == NULL || cur->type != XML_ELEMENT_NODE) {
259#ifdef DEBUG_C14N
260 xmlGenericError(xmlGenericErrorContext,
261 "xmlC14NProcessNamespacesAxis: Null context or node pointer or type != XML_ELEMENT_NODE.\n");
262#endif
263 return(-1);
264 }
265
266 /*
267 * Create a sorted list to store element namespaces
268 */
269 list = xmlListCreate(NULL, (xmlListDataCompare)xmlC14NNamespacesCompare);
270 if(list == NULL) {
271#ifdef DEBUG_C14N
272 xmlGenericError(xmlGenericErrorContext,
273 "xmlC14NProcessNamespacesAxis: list creation failed\n");
274#endif
275 return(-1);
276 }
277
278 /* find nearest visible parent */
279 visible_parent = cur->parent;
280 while(visible_parent != NULL && !xmlC14NIsVisible(ctx, visible_parent)) {
281 visible_parent = visible_parent->parent;
282 }
283
284 /*
285 * todo: the libxml XPath implementation does not create
286 * nodes for all namespaces known to the node (i.e. for namespaces
287 * defined in node parents). By this we need to now walk thru
288 * all namespace in current node and all invisible ancesstors
289 */
290 while(cur != visible_parent) {
291 for(ns = cur->nsDef; ns != NULL; ns = ns->next) {
292 /*
293 * first of all ignore default "xml" namespace and
294 * already included namespace
295 */
296 if(xmlC14NIsXmlNs(ns) || xmlListSearch(list, ns) != NULL) {
297 continue;
298 }
299
300 /*
301 * Lookup nearest namespace after visible parent having
302 * the same prefix. Namespace included if and only if one of
303 * the following:
304 * - another namespace having the same prefix but
305 * different value found or
306 * - there is no namespaces having the same prefix and
307 * it is not a default xmlns="" namespace (empty prefix
308 * and empty href)
309 */
310 prev = xmlSearchNs(ctx->doc, visible_parent, ns->prefix);
311 if(prev == NULL && (xmlStrlen(ns->prefix) > 0 || xmlStrlen(ns->href) > 0)) {
312 xmlListInsert(list, ns);
313 } else if(prev != NULL && !xmlStrEqual(ns->href, prev->href)) {
314 xmlListInsert(list, ns);
315 }
316 }
317 cur = cur->parent;
318 }
319
320 /*
321 * print out all elements from list
322 */
323 xmlListWalk(list, (xmlListWalker)xmlC14NPrintNamespaces, (const void*)ctx);
324
325 /*
326 * Cleanup
327 */
328 xmlListDelete(list);
329 return(0);
330}
331
332/**
333 * xmlExcC14NProcessNamespacesAxis:
334 * @ctx: the C14N context
335 * @node: the current node
336 *
337 * Prints out exclusive canonical namespace axis of the current node to the
338 * buffer from C14N context as follows
339 *
340 * Exclusive XML Canonicalization
341 * http://www.w3.org/TR/xml-exc-c14n
342 *
343 * If the element node is in the XPath subset then output the node in
344 * accordance with Canonical XML except for namespace nodes which are
345 * rendered as follows:
346 *
347 * 1. Render each namespace node iff:
348 * * it is visibly utilized by the immediate parent element or one of
349 * its attributes, or is present in InclusiveNamespaces PrefixList, and
350 * * its prefix and value do not appear in ns_rendered. ns_rendered is
351 * obtained by popping the state stack in order to obtain a list of
352 * prefixes and their values which have already been rendered by
353 * an output ancestor of the namespace node's parent element.
354 * 2. Append the rendered namespace node to the list ns_rendered of namespace
355 * nodes rendered by output ancestors. Push ns_rendered on state stack and
356 * recurse.
357 * 3. After the recursion returns, pop thestate stack.
358 *
359 *
360 * Returns 0 on success or -1 on fail.
361 */
362static int
363xmlExcC14NProcessNamespacesAxis(xmlC14NCtxPtr ctx, xmlNodePtr cur) {
364 xmlListPtr list;
365 xmlAttrPtr attr;
366 xmlNsPtr ns;
367
368 if(ctx == NULL || cur == NULL || cur->type != XML_ELEMENT_NODE) {
369#ifdef DEBUG_C14N
370 xmlGenericError(xmlGenericErrorContext,
371 "xmlExcC14NProcessNamespacesAxis: Null context or node pointer or type != XML_ELEMENT_NODE.\n");
372#endif
373 return(-1);
374 }
375
376 if(!ctx->exclusive || ctx->ns_rendered == NULL) {
377#ifdef DEBUG_C14N
378 xmlGenericError(xmlGenericErrorContext,
379 "xmlExcC14NProcessNamespacesAxis: called for non-exclusive canonization or rendered stack is NULL.\n");
380#endif
381 return(-1);
382
383 }
384
385 /*
386 * Create a sorted list to store element namespaces
387 */
388 list = xmlListCreate(NULL, (xmlListDataCompare)xmlC14NNamespacesCompare);
389 if(list == NULL) {
390#ifdef DEBUG_C14N
391 xmlGenericError(xmlGenericErrorContext,
392 "xmlExcC14NProcessNamespacesAxis: list creation failed\n");
393#endif
394 return(-1);
395 }
396
397 /*
398 * First of all, add all namespaces required by current node
399 * (i.e. node namespace and all attribute namespaces)
400 * todo: do we need to check for default "xml:" namespace
401 */
402 ns = (cur->ns != NULL) ? cur->ns : xmlSearchNs(ctx->doc, cur, NULL);
403 if(ns != NULL && !xmlC14NIsXmlNs(ns) && xmlListSearch(list, ns) == NULL) {
404 if(!xmlExcC14NIsRendered(ctx, ns)) {
405 xmlListInsert(list, ns);
406 xmlXPathNodeSetAdd(ctx->ns_rendered, (xmlNodePtr)ns);
407 }
408 }
409 attr = cur->properties;
410 while (attr != NULL) {
411 /*
412 * todo: do we need to check that attribute is visible and has non
413 * default namespace
414 */
415 if(xmlC14NIsVisible(ctx, attr)) {
416 ns = (attr->ns != NULL) ? attr->ns : xmlSearchNs(ctx->doc, cur, NULL);
417 if(ns != NULL && xmlC14NIsVisible(ctx, attr) && !xmlC14NIsXmlNs(ns)) {
418 if(xmlListSearch(list, ns) == NULL &&
419 !xmlExcC14NIsRendered(ctx, ns)) {
420 xmlListInsert(list, ns);
421 xmlXPathNodeSetAdd(ctx->ns_rendered, (xmlNodePtr)ns);
422 }
423 }
424 }
425 attr = attr->next;
426 }
427
428 /*
429 * Next add all inclusive namespaces if needed.
430 */
431 if(ctx->inclusive_ns_prefixes != NULL) {
432 int i;
433 xmlChar *prefix;
434
435 for(i = 0; ctx->inclusive_ns_prefixes[i] != NULL; ++i) {
436 prefix = ctx->inclusive_ns_prefixes[i];
437 /*
438 * Special values for namespace with empty prefix
439 */
440 if(xmlStrEqual(prefix, BAD_CAST "#default") || xmlStrEqual(prefix, BAD_CAST "")) {
441 prefix = NULL;
442 }
443 ns = xmlSearchNs(ctx->doc, cur, prefix);
444 if(ns != NULL && !xmlC14NIsXmlNs(ns)) {
445 if(xmlListSearch(list, ns) == NULL &&
446 !xmlExcC14NIsRendered(ctx, ns)) {
447 xmlListInsert(list, ns);
448 xmlXPathNodeSetAdd(ctx->ns_rendered, (xmlNodePtr)ns);
449 }
450 }
451 }
452 }
453
454 /*
455 * print out all elements from list
456 */
457 xmlListWalk(list, (xmlListWalker)xmlC14NPrintNamespaces, (const void*)ctx);
458
459 /*
460 * Cleanup
461 */
462 xmlListDelete(list);
463 return(0);
464}
465
466
467/**
468 * xmlC14NAttrsCompare:
469 * @attr1: the pointer to first attr
470 * @attr2: the pointer to second attr
471 *
472 * Prints the given attribute to the output buffer from C14N context.
473 *
474 * Returns -1 if attr1 < attr2, 0 if attr1 == attr2 or 1 if attr1 > attr2.
475 */
476static int
477xmlC14NAttrsCompare(xmlAttrPtr attr1, xmlAttrPtr attr2) {
478 int ret = 0;
479
480 /*
481 * Simple cases
482 */
483 if(attr1 == attr2) return(0);
484 if(attr1 == NULL) return(-1);
485 if(attr2 == NULL) return(1);
486 if(attr1->ns == attr2->ns) {
487 return(xmlStrcmp(attr1->name, attr2->name));
488 }
489
490 /*
491 * Attributes in the default namespace are first
492 * because the default namespace is not applied to
493 * unqualified attributes
494 */
495 if(attr1->ns == NULL) return(-1);
496 if(attr2->ns == NULL) return(1);
497 if(attr1->ns->prefix == NULL) return(-1);
498 if(attr2->ns->prefix == NULL) return(1);
499
500 ret = xmlStrcmp(attr1->ns->href, attr2->ns->href);
501 if(ret == 0) {
502 ret = xmlStrcmp(attr1->name, attr2->name);
503 }
504 return(ret);
505}
506
507
508/**
509 * xmlC14NPrintAttrs:
510 * @attr: the pointer to attr
511 * @ctx: the C14N context
512 *
513 * Prints out canonical attribute urrent node to the
514 * buffer from C14N context as follows
515 *
516 * Canonical XML v 1.0 (http://www.w3.org/TR/xml-c14n)
517 *
518 * Returns 1 on success or 0 on fail.
519 */
520static int
521xmlC14NPrintAttrs(const xmlAttrPtr attr, xmlC14NCtxPtr ctx) {
522 xmlChar *value;
523 xmlChar *buffer;
524
525 if(attr == NULL || ctx == NULL) {
526#ifdef DEBUG_C14N
527 xmlGenericError(xmlGenericErrorContext,
528 "xmlC14NPrintAttrs: attr == NULL or ctx == NULL\n");
529#endif
530 return(0);
531 }
532
533 xmlOutputBufferWriteString(ctx->buf, " ");
534 if(attr->ns != NULL && xmlStrlen(attr->ns->prefix) > 0) {
535 xmlOutputBufferWriteString(ctx->buf, (const char *) attr->ns->prefix);
536 xmlOutputBufferWriteString(ctx->buf, ":");
537 }
538 xmlOutputBufferWriteString(ctx->buf, (const char *) attr->name);
539 xmlOutputBufferWriteString(ctx->buf, "=\"");
540
541 value = xmlNodeListGetString(attr->doc, attr->children, 1);
542 /* todo: should we log an error if value==NULL ? */
543 if(value != NULL) {
544 buffer = xmlC11NNormalizeAttr(value);
545 xmlFree(value);
546 if (buffer != NULL) {
547 xmlOutputBufferWriteString(ctx->buf, (const char *)buffer);
548 xmlFree(buffer);
549 } else {
550#ifdef DEBUG_C14N
551 xmlGenericError(xmlGenericErrorContext,
552 "xmlC14NPrintAttrs: xmlC11NNormalizeAttr failed\n");
553#endif
554 return(0);
555 }
556 }
557 xmlOutputBufferWriteString(ctx->buf, "\"");
558 return(1);
559}
560
561/**
562 * xmlC14NProcessAttrsAxis:
563 * @ctx: the C14N context
564 * @cur: the current node
565 *
566 * Prints out canonical attribute 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 * Attribute Axis
572 * In lexicographic order (ascending), process each node that
573 * is in the element's attribute axis and in the node-set.
574 *
575 * The processing of an element node E MUST be modified slightly
576 * when an XPath node-set is given as input and the element's
577 * parent is omitted from the node-set.
578 *
579 *
580 * Exclusive XML Canonicalization v 1.0 (http://www.w3.org/TR/xml-exc-c14n)
581 *
582 * Canonical XML applied to a document subset requires the search of the
583 * ancestor nodes of each orphan element node for attributes in the xml
584 * namespace, such as xml:lang and xml:space. These are copied into the
585 * element node except if a declaration of the same attribute is already
586 * in the attribute axis of the element (whether or not it is included in
587 * the document subset). This search and copying are omitted from the
588 * Exclusive XML Canonicalization method.
589 *
590 * Returns 0 on success or -1 on fail.
591 */
592static int
593xmlC14NProcessAttrsAxis(xmlC14NCtxPtr ctx, xmlNodePtr cur) {
594 xmlAttrPtr attr;
595 xmlListPtr list;
596
597 if(ctx == NULL || cur == NULL || cur->type != XML_ELEMENT_NODE) {
598#ifdef DEBUG_C14N
599 xmlGenericError(xmlGenericErrorContext,
600 "xmlC14NProcessAttrsAxis: Null context or node pointer or type != XML_ELEMENT_NODE.\n");
601#endif
602 return(-1);
603 }
604
605 /*
606 * Create a sorted list to store element attributes
607 */
608 list = xmlListCreate(NULL, (xmlListDataCompare)xmlC14NAttrsCompare);
609 if(list == NULL) {
610#ifdef DEBUG_C14N
611 xmlGenericError(xmlGenericErrorContext,
612 "xmlC14NProcessAttrsAxis: list creation failed\n");
613#endif
614 return(-1);
615 }
616
617 /*
618 * Add all visible attributes from current node.
619 */
620 attr = cur->properties;
621 while (attr != NULL) {
622 /* check that attribute is visible */
623 if(xmlC14NIsVisible(ctx, attr)) {
624 xmlListInsert(list, attr);
625 }
626 attr = attr->next;
627 }
628
629 /*
630 * include attributes in "xml" namespace defined in ancestors
631 * (only for non-exclusive XML Canonicalization)
632 */
633 if(!ctx->exclusive && cur->parent != NULL && !xmlC14NIsVisible(ctx, cur->parent)) {
634 /*
635 * If XPath node-set is not specified then the parent is always
636 * visible!
637 */
638 cur = cur->parent;
639 while(cur != NULL) {
640 attr = cur->properties;
641 while (attr != NULL) {
642 if(attr->ns != NULL && xmlStrEqual(attr->ns->prefix, BAD_CAST "xml")) {
643 if(xmlListSearch(list, attr) == NULL) {
644 xmlListInsert(list, attr);
645 }
646 }
647 attr = attr->next;
648 }
649 cur = cur->parent;
650 }
651 }
652
653 /*
654 * print out all elements from list
655 */
656 xmlListWalk(list, (xmlListWalker)xmlC14NPrintAttrs, (const void*)ctx);
657
658 /*
659 * Cleanup
660 */
661 xmlListDelete(list);
662 return(0);
663}
664
665/**
666 * xmlC14NCheckForRelativeNamespaces:
667 * @ctx: the C14N context
668 * @cur: the current element node
669 *
670 * Checks that current element node has no relative namespaces defined
671 *
672 * Returns 0 if the node has no relative namespaces or -1 otherwise.
673 */
674static int
675xmlC14NCheckForRelativeNamespaces(xmlC14NCtxPtr ctx, xmlNodePtr cur) {
676 xmlNsPtr ns;
677
678 if(ctx == NULL || cur == NULL || cur->type != XML_ELEMENT_NODE) {
679#ifdef DEBUG_C14N
680 xmlGenericError(xmlGenericErrorContext,
681 "xmlC14NCheckForRelativeNamespaces: Null context or node pointer or type != XML_ELEMENT_NODE.\n");
682#endif
683 return(-1);
684 }
685
686 ns = cur->nsDef;
687 while(ns != NULL) {
688 if(xmlStrlen(ns->href) > 0) {
689 xmlURIPtr uri;
690
691 uri = xmlParseURI((const char *) ns->href);
692 if(uri == NULL) {
693#ifdef DEBUG_C14N
694 xmlGenericError(xmlGenericErrorContext,
695 "xmlC14NCheckForRelativeNamespaces: unable to parse uri=\"%s\".\n", ns->href);
696#endif
697 return(-1);
698 }
699 if(xmlStrlen((const xmlChar *) uri->scheme) == 0) {
700 xmlFreeURI(uri);
701 return(-1);
702 }
703 if(!xmlStrEqual((const xmlChar *) uri->scheme, BAD_CAST "urn") && xmlStrlen((const xmlChar *) uri->server) == 0) {
704 xmlFreeURI(uri);
705 return(-1);
706 }
707 xmlFreeURI(uri);
708 }
709 ns = ns->next;
710 }
711 return(0);
712}
713
714/**
715 * xmlC14NProcessElementNode:
716 * @ctx: the pointer to C14N context object
717 * @cur: the node to process
718 *
719 * Canonical XML v 1.0 (http://www.w3.org/TR/xml-c14n)
720 *
721 * Element Nodes
722 * If the element is not in the node-set, then the result is obtained
723 * by processing the namespace axis, then the attribute axis, then
724 * processing the child nodes of the element that are in the node-set
725 * (in document order). If the element is in the node-set, then the result
726 * is an open angle bracket (<), the element QName, the result of
727 * processing the namespace axis, the result of processing the attribute
728 * axis, a close angle bracket (>), the result of processing the child
729 * nodes of the element that are in the node-set (in document order), an
730 * open angle bracket, a forward slash (/), the element QName, and a close
731 * angle bracket.
732 *
733 * Returns non-negative value on success or negative value on fail
734 */
735static int
736xmlC14NProcessElementNode(xmlC14NCtxPtr ctx, xmlNodePtr cur, int visible) {
737 int ret;
738 int ns_rendered_pos = 0;
739
740 if(ctx == NULL || cur == NULL || cur->type != XML_ELEMENT_NODE) {
741#ifdef DEBUG_C14N
742 xmlGenericError(xmlGenericErrorContext,
743 "xmlC14NProcessElementNode: Null context or node pointer or type != XML_ELEMENT_NODE.\n");
744#endif
745 return(-1);
746 }
747
748 /*
749 * Check relative relative namespaces:
750 * implementations of XML canonicalization MUST report an operation
751 * failure on documents containing relative namespace URIs.
752 */
753 if(xmlC14NCheckForRelativeNamespaces(ctx, cur) < 0) {
754#ifdef DEBUG_C14N
755 xmlGenericError(xmlGenericErrorContext,
756 "xmlC14NProcessElementNode: xmlC14NCheckForRelativeNamespaces failed.\n");
757#endif
758 return(-1);
759 }
760
761
762 /*
763 * Save ns_rendered stack position for exclusive
764 * processing
765 */
766 if(ctx->exclusive && ctx->ns_rendered != NULL) {
767 ns_rendered_pos = ctx->ns_rendered->nodeNr;
768 }
769
770 if(visible) {
771 if(ctx->parent_is_doc) {
772 ctx->pos = XMLC14N_INSIDE_DOCUMENT_ELEMENT;
773 }
774 xmlOutputBufferWriteString(ctx->buf, "<");
775
776 if(cur->ns != NULL && xmlStrlen(cur->ns->prefix) > 0) {
777 xmlOutputBufferWriteString(ctx->buf, (const char *)cur->ns->prefix);
778 xmlOutputBufferWriteString(ctx->buf, ":");
779 }
780 xmlOutputBufferWriteString(ctx->buf, (const char *)cur->name);
781
782 if(ctx->exclusive) {
783 ret = xmlExcC14NProcessNamespacesAxis(ctx, cur);
784 } else {
785 ret = xmlC14NProcessNamespacesAxis(ctx, cur);
786 }
787 if(ret < 0) {
788#ifdef DEBUG_C14N
789 xmlGenericError(xmlGenericErrorContext,
790 "xmlC14NProcessElementNode: xmlC14NProcessNamespacesAxis failed.\n");
791#endif
792 return(-1);
793 }
794
795 ret = xmlC14NProcessAttrsAxis(ctx, cur);
796 if(ret < 0) {
797#ifdef DEBUG_C14N
798 xmlGenericError(xmlGenericErrorContext,
799 "xmlC14NProcessElementNode: xmlC14NProcessAttrsAxis failed.\n");
800#endif
801 return(-1);
802 }
803
804 xmlOutputBufferWriteString(ctx->buf, ">");
805 }
806 if (cur->children != NULL) {
807 ret = xmlC14NProcessNodeList(ctx, cur->children);
808 if(ret < 0) {
809#ifdef DEBUG_C14N
810 xmlGenericError(xmlGenericErrorContext,
811 "xmlC14NProcessElementNode: xmlC14NProcessNodeList failed.\n");
812#endif
813 return(-1);
814 }
815 }
816 if(visible) {
817 xmlOutputBufferWriteString(ctx->buf, "</");
818 if(cur->ns != NULL && xmlStrlen(cur->ns->prefix) > 0) {
819 xmlOutputBufferWriteString(ctx->buf, (const char *)cur->ns->prefix);
820 xmlOutputBufferWriteString(ctx->buf, ":");
821 }
822 xmlOutputBufferWriteString(ctx->buf, (const char *)cur->name);
823 xmlOutputBufferWriteString(ctx->buf, ">");
824 if(ctx->parent_is_doc) {
825 ctx->pos = XMLC14N_AFTER_DOCUMENT_ELEMENT;
826 }
827 }
828
829 /*
830 * Restore ns_rendered stack position for exclusive
831 * processing
832 */
833 if(ctx->exclusive && ctx->ns_rendered != NULL) {
834 ctx->ns_rendered->nodeNr = ns_rendered_pos;
835 }
836 return(0);
837}
838
839/**
840 * xmlC14NProcessNode:
841 * @ctx: the pointer to C14N context object
842 * @cur: the node to process
843 *
844 * Processes the given node
845 *
846 * Returns non-negative value on success or negative value on fail
847 */
848static int
849xmlC14NProcessNode(xmlC14NCtxPtr ctx, xmlNodePtr cur) {
850 int ret = 0;
851 int visible;
852
853 if(ctx == NULL || cur == NULL) {
854#ifdef DEBUG_C14N
855 xmlGenericError(xmlGenericErrorContext,
856 "xmlC14NProcessNode: Null context or node pointer.\n");
857#endif
858 return(-1);
859 }
860
861 visible = xmlC14NIsVisible(ctx, cur);
862 switch(cur->type) {
863 case XML_ELEMENT_NODE:
864 ret = xmlC14NProcessElementNode(ctx, cur, visible);
865 break;
866 case XML_CDATA_SECTION_NODE:
867 case XML_TEXT_NODE:
868 /*
869 * Text Nodes
870 * the string value, except all ampersands are replaced
871 * by &amp;, all open angle brackets (<) are replaced by &lt;, all closing
872 * angle brackets (>) are replaced by &gt;, and all #xD characters are
873 * replaced by &#xD;.
874 */
875 /* cdata sections are processed as text nodes */
876 /* todo: verify that cdata sections are included in XPath nodes set */
877 if(visible && cur->content != NULL) {
878 xmlChar *buffer;
879
880#ifndef XML_USE_BUFFER_CONTENT
881 buffer = xmlC11NNormalizeText(cur->content);
882#else
883 buffer = xmlC11NNormalizeText(xmlBufferContent(cur->content));
884#endif
885 if(buffer != NULL) {
886 xmlOutputBufferWriteString(ctx->buf, (const char *)buffer);
887 xmlFree(buffer);
888 } else {
889#ifdef DEBUG_C14N
890 xmlGenericError(xmlGenericErrorContext,
891 "xmlC14NProcessNode: xmlC11NNormalizeText() failed\n");
892#endif
893 return(-1);
894 }
895 }
896 break;
897 case XML_PI_NODE:
898 /*
899 * Processing Instruction (PI) Nodes-
900 * The opening PI symbol (<?), the PI target name of the node,
901 * a leading space and the string value if it is not empty, and
902 * the closing PI symbol (?>). If the string value is empty,
903 * then the leading space is not added. Also, a trailing #xA is
904 * rendered after the closing PI symbol for PI children of the
905 * root node with a lesser document order than the document
906 * element, and a leading #xA is rendered before the opening PI
907 * symbol of PI children of the root node with a greater document
908 * order than the document element.
909 */
910 if(visible) {
911 if(ctx->pos == XMLC14N_AFTER_DOCUMENT_ELEMENT) {
912 xmlOutputBufferWriteString(ctx->buf, "\x0A<?");
913 } else {
914 xmlOutputBufferWriteString(ctx->buf, "<?");
915 }
916
917 xmlOutputBufferWriteString(ctx->buf, (const char *)cur->name);
918 if(cur->content != NULL && *(cur->content) != '\0') {
919 xmlChar *buffer;
920 xmlOutputBufferWriteString(ctx->buf, " ");
921
922 /* todo: do we need to normalize pi? */
923#ifndef XML_USE_BUFFER_CONTENT
924 buffer = xmlC11NNormalizePI(cur->content);
925#else
926 buffer = xmlC11NNormalizePI(xmlBufferContent(cur->content));
927#endif
928 if (buffer != NULL) {
929 xmlOutputBufferWriteString(ctx->buf, (const char *)buffer);
930 xmlFree(buffer);
931 } else {
932#ifdef DEBUG_C14N
933 xmlGenericError(xmlGenericErrorContext,
934 "xmlC14NProcessNode: xmlC11NNormalizePI() failed\n");
935#endif
936 return(-1);
937 }
938 }
939
940 if(ctx->pos == XMLC14N_BEFORE_DOCUMENT_ELEMENT) {
941 xmlOutputBufferWriteString(ctx->buf, "?>\x0A");
942 } else {
943 xmlOutputBufferWriteString(ctx->buf, "?>");
944 }
945 }
946 break;
947 case XML_COMMENT_NODE:
948 /*
949 * Comment Nodes
950 * Nothing if generating canonical XML without comments. For
951 * canonical XML with comments, generate the opening comment
952 * symbol (<!--), the string value of the node, and the
953 * closing comment symbol (-->). Also, a trailing #xA is rendered
954 * after the closing comment symbol for comment children of the
955 * root node with a lesser document order than the document
956 * element, and a leading #xA is rendered before the opening
957 * comment symbol of comment children of the root node with a
958 * greater document order than the document element. (Comment
959 * children of the root node represent comments outside of the
960 * top-level document element and outside of the document type
961 * declaration).
962 */
963 if(visible && ctx->with_comments) {
964 if(ctx->pos == XMLC14N_AFTER_DOCUMENT_ELEMENT) {
965 xmlOutputBufferWriteString(ctx->buf, "\x0A<!--");
966 } else {
967 xmlOutputBufferWriteString(ctx->buf, "<!--");
968 }
969
970 if(cur->content != NULL) {
971 xmlChar *buffer;
972
973 /* todo: do we need to normalize comment? */
974#ifndef XML_USE_BUFFER_CONTENT
975 buffer = xmlC11NNormalizeComment(cur->content);
976#else
977 buffer = xmlC11NNormalizeString(xmlBufferContent(cur->content));
978#endif
979 if (buffer != NULL) {
980 xmlOutputBufferWriteString(ctx->buf, (const char *)buffer);
981 xmlFree(buffer);
982 } else {
983#ifdef DEBUG_C14N
984 xmlGenericError(xmlGenericErrorContext,
985 "xmlC14NProcessNode: xmlC11NNormalizeComment() failed\n");
986#endif
987 return(-1);
988 }
989 }
990
991 if(ctx->pos == XMLC14N_BEFORE_DOCUMENT_ELEMENT) {
992 xmlOutputBufferWriteString(ctx->buf, "-->\x0A");
993 } else {
994 xmlOutputBufferWriteString(ctx->buf, "-->");
995 }
996 }
997 break;
998 case XML_DOCUMENT_NODE:
999 case XML_DOCUMENT_FRAG_NODE: /* should be processed as document? */
1000 case XML_DOCB_DOCUMENT_NODE: /* should be processed as document? */
1001 case XML_HTML_DOCUMENT_NODE: /* should be processed as document? */
1002 if (cur->children != NULL) {
1003 ctx->pos = XMLC14N_BEFORE_DOCUMENT_ELEMENT;
1004 ctx->parent_is_doc = 1;
1005 ret = xmlC14NProcessNodeList(ctx, cur->children);
1006 }
1007 break;
1008
1009 case XML_ATTRIBUTE_NODE:
1010 xmlGenericError(xmlGenericErrorContext,
1011 "xmlC14NProcessNode: XML_ATTRIBUTE_NODE is illegal here\n");
1012 return(-1);
1013 case XML_NAMESPACE_DECL:
1014 xmlGenericError(xmlGenericErrorContext,
1015 "xmlC14NProcessNode: XML_NAMESPACE_DECL is illegal here\n");
1016 return(-1);
1017 case XML_ENTITY_REF_NODE:
1018 xmlGenericError(xmlGenericErrorContext,
1019 "xmlC14NProcessNode: XML_ENTITY_REF_NODE is illegal here\n");
1020 return(-1);
1021 case XML_ENTITY_NODE:
1022 xmlGenericError(xmlGenericErrorContext,
1023 "xmlC14NProcessNode: XML_ENTITY_NODE is illegal here\n");
1024 return(-1);
1025
1026 case XML_DOCUMENT_TYPE_NODE:
1027 case XML_NOTATION_NODE:
1028 case XML_DTD_NODE:
1029 case XML_ELEMENT_DECL:
1030 case XML_ATTRIBUTE_DECL:
1031 case XML_ENTITY_DECL:
1032 case XML_XINCLUDE_START:
1033 case XML_XINCLUDE_END:
1034 /*
1035 * should be ignored according to "W3C Canonical XML"
1036 */
1037 break;
1038 default:
1039#ifdef DEBUG_C14N
1040 xmlGenericError(xmlGenericErrorContext,
1041 "xmlC14NProcessNode: unknown node type = %d\n", cur->type);
1042#endif
1043 return(-1);
1044 }
1045
1046 return(ret);
1047}
1048
1049/**
1050 * xmlC14NProcessNodeList:
1051 * @ctx: the pointer to C14N context object
1052 * @cur: the node to start from
1053 *
1054 * Processes all nodes in the row starting from cur.
1055 *
1056 * Returns non-negative value on success or negative value on fail
1057 */
1058static int
1059xmlC14NProcessNodeList(xmlC14NCtxPtr ctx, xmlNodePtr cur) {
1060 int ret;
1061
1062 if(ctx == NULL) {
1063#ifdef DEBUG_C14N
1064 xmlGenericError(xmlGenericErrorContext,
1065 "xmlC14NProcessNodeList: Null context pointer.\n");
1066#endif
1067 return(-1);
1068 }
1069
1070 for(ret = 0; cur != NULL && ret >= 0; cur = cur->next) {
1071 ret = xmlC14NProcessNode(ctx, cur);
1072 }
1073 return(ret);
1074}
1075
1076
1077/**
1078 * xmlC14NFreeCtx:
1079 * @ctx: the pointer to C14N context object
1080 *
1081 * Cleanups the C14N context object.
1082 */
1083
1084static void
1085xmlC14NFreeCtx(xmlC14NCtxPtr ctx) {
1086 if(ctx == NULL) {
1087#ifdef DEBUG_C14N
1088 xmlGenericError(xmlGenericErrorContext,
1089 "xmlC14NFreeCtx: ctx == NULL\n");
1090#endif
1091 return;
1092 }
1093
1094 if(ctx->ns_rendered != NULL) {
1095 xmlXPathFreeNodeSet(ctx->ns_rendered);
1096 }
1097 xmlFree(ctx);
1098}
1099
1100/**
1101 * xmlC14NNewCtx:
1102 * @doc: the XML document for canonization
1103 * @nodes: the nodes set to be included in the canonized image
1104 * or NULL if all document nodes should be included
1105 * @exclusive: the exclusive flag (0 - non-exclusive canonicalization;
1106 * otherwise - exclusive canonicalization)
1107 * @inclusive_ns_prefixe the list of inclusive namespace prefixes
1108 * ended with a NULL or NULL if there is no
1109 * inclusive namespaces (only for exclusive
1110 * canonicalization)
1111 * @with_comments: include comments in the result (!=0) or not (==0)
1112 * @buf: the output buffer to store canonical XML; this
1113 * buffer MUST have encoder==NULL because C14N requires
1114 * UTF-8 output
1115 *
1116 * Creates new C14N context object to store C14N parameters.
1117 *
1118 * Returns pointer to newly created object (success) or NULL (fail)
1119 */
1120static xmlC14NCtxPtr
1121xmlC14NNewCtx(xmlDocPtr doc, xmlNodeSetPtr nodes,
1122 int exclusive, xmlChar **inclusive_ns_prefixes,
1123 int with_comments,
1124 xmlOutputBufferPtr buf) {
1125 xmlC14NCtxPtr ctx;
1126
1127 if(doc == NULL || buf == NULL) {
1128#ifdef DEBUG_C14N
1129 xmlGenericError(xmlGenericErrorContext,
1130 "xmlC14NNewCtx: pointer to document or output buffer is NULL\n");
1131#endif
1132 return(NULL);
1133 }
1134
1135 /*
1136 * Validate the encoding output buffer encoding
1137 */
1138 if(buf->encoder != NULL) {
1139 xmlGenericError(xmlGenericErrorContext,
1140 "xmlC14NNewCtx: output buffer encoder != NULL but C14N requires UTF8 output\n");
1141 return(NULL);
1142 }
1143
1144 /*
1145 * Validate the XML document encoding value, if provided.
1146 */
1147 if(doc->charset != XML_CHAR_ENCODING_UTF8) {
1148 xmlGenericError(xmlGenericErrorContext,
1149 "xmlC14NNewCtx: source document not in UTF8\n");
1150 return(NULL);
1151 }
1152
1153 /*
1154 * Allocate a new xmlC14NCtxPtr and fill the fields.
1155 */
1156 ctx = (xmlC14NCtxPtr) xmlMalloc(sizeof(xmlC14NCtx));
1157 if (ctx == NULL) {
1158 xmlGenericError(xmlGenericErrorContext,
1159 "xmlC14NNewCtx: malloc failed\n");
1160 return(NULL);
1161 }
1162 memset(ctx, 0, sizeof(xmlC14NCtx));
1163
1164 /*
1165 * initialize C14N context
1166 */
1167 ctx->doc = doc;
1168 ctx->with_comments = with_comments;
1169 ctx->visible_nodes = nodes;
1170 ctx->buf = buf;
1171 ctx->parent_is_doc = 1;
1172 ctx->pos = XMLC14N_BEFORE_DOCUMENT_ELEMENT;
1173
1174 /*
1175 * Set "exclusive" flag, create a nodes set for namespaces
1176 * stack and remember list of incluseve prefixes
1177 */
1178 if(exclusive) {
1179 ctx->exclusive = 1;
1180 ctx->ns_rendered = xmlXPathNodeSetCreate(NULL);
1181 ctx->inclusive_ns_prefixes = inclusive_ns_prefixes;
1182 }
1183 return(ctx);
1184}
1185
1186/**
1187 * xmlC14NDocSaveTo:
1188 * @doc: the XML document for canonization
1189 * @nodes: the nodes set to be included in the canonized image
1190 * or NULL if all document nodes should be included
1191 * @exclusive: the exclusive flag (0 - non-exclusive canonicalization;
1192 * otherwise - exclusive canonicalization)
1193 * @inclusive_ns_prefixe the list of inclusive namespace prefixes
1194 * ended with a NULL or NULL if there is no
1195 * inclusive namespaces (only for exclusive
1196 * canonicalization, ignored otherwise)
1197 * @with_comments: include comments in the result (!=0) or not (==0)
1198 * @buf: the output buffer to store canonical XML; this
1199 * buffer MUST have encoder==NULL because C14N requires
1200 * UTF-8 output
1201 *
1202 * Dumps the canonized image of given XML document into the provided buffer.
1203 * For details see "Canonical XML" (http://www.w3.org/TR/xml-c14n) or
1204 * "Exclusive XML Canonicalization" (http://www.w3.org/TR/xml-exc-c14n)
1205 *
1206 * Returns non-negative value on success or a negative value on fail
1207 */
1208int
1209xmlC14NDocSaveTo(xmlDocPtr doc, xmlNodeSetPtr nodes,
1210 int exclusive, xmlChar **inclusive_ns_prefixes,
1211 int with_comments,
1212 xmlOutputBufferPtr buf) {
1213 xmlC14NCtxPtr ctx;
1214 int ret;
1215
1216 if(buf == NULL || doc == NULL) {
1217#ifdef DEBUG_C14N
1218 xmlGenericError(xmlGenericErrorContext,
1219 "xmlC14NDocSaveTo: null return buffer or doc pointer\n");
1220#endif
1221 return(-1);
1222 }
1223
1224 /*
1225 * Validate the encoding output buffer encoding
1226 */
1227 if(buf->encoder != NULL) {
1228 xmlGenericError(xmlGenericErrorContext,
1229 "xmlC14NDocSaveTo: output buffer encoder != NULL but C14N requires UTF8 output\n");
1230 return(-1);
1231 }
1232
1233 ctx = xmlC14NNewCtx(doc, nodes, exclusive, inclusive_ns_prefixes,
1234 with_comments, buf);
1235 if(ctx == NULL) {
1236 xmlGenericError(xmlGenericErrorContext,
1237 "xmlC14NDocSaveTo: unable to create C14N context\n");
1238 return(-1);
1239 }
1240
1241
1242
1243 /*
1244 * Root Node
1245 * The root node is the parent of the top-level document element. The
1246 * result of processing each of its child nodes that is in the node-set
1247 * in document order. The root node does not generate a byte order mark,
1248 * XML declaration, nor anything from within the document type
1249 * declaration.
1250 */
1251 if(doc->children != NULL) {
1252 ret = xmlC14NProcessNodeList(ctx, doc->children);
1253 if(ret < 0) {
1254#ifdef DEBUG_C14N
1255 xmlGenericError(xmlGenericErrorContext,
1256 "xmlC14NDocSaveTo: process childrens' list failed.\n");
1257#endif
1258 xmlC14NFreeCtx(ctx);
1259 return(-1);
1260 }
1261 }
1262
1263 /*
1264 * Flush buffer to get number of bytes written
1265 */
1266 ret = xmlOutputBufferFlush(buf);
1267 if(ret < 0) {
1268#ifdef DEBUG_C14N
1269 xmlGenericError(xmlGenericErrorContext,
1270 "xmlC14NDocSaveTo: buffer flush failed.\n");
1271#endif
1272 xmlC14NFreeCtx(ctx);
1273 return(-1);
1274 }
1275
1276 /*
1277 * Cleanup
1278 */
1279 xmlC14NFreeCtx(ctx);
1280 return(ret);
1281}
1282
1283/**
1284 * xmlC14NDocDumpMemory:
1285 * @doc: the XML document for canonization
1286 * @nodes: the nodes set to be included in the canonized image
1287 * or NULL if all document nodes should be included
1288 * @exclusive: the exclusive flag (0 - non-exclusive canonicalization;
1289 * otherwise - exclusive canonicalization)
1290 * @inclusive_ns_prefixe the list of inclusive namespace prefixes
1291 * ended with a NULL or NULL if there is no
1292 * inclusive namespaces (only for exclusive
1293 * canonicalization, ignored otherwise)
1294 * @with_comments: include comments in the result (!=0) or not (==0)
1295 * @doc_txt_ptr: the memory pointer for allocated canonical XML text;
1296 * the caller of this functions is responsible for calling
1297 * xmlFree() to free allocated memory
1298 *
1299 * Dumps the canonized image of given XML document into memory.
1300 * For details see "Canonical XML" (http://www.w3.org/TR/xml-c14n) or
1301 * "Exclusive XML Canonicalization" (http://www.w3.org/TR/xml-exc-c14n)
1302 *
1303 * Returns the number of bytes written on success or a negative value on fail
1304 */
1305int
1306xmlC14NDocDumpMemory(xmlDocPtr doc, xmlNodeSetPtr nodes,
1307 int exclusive, xmlChar **inclusive_ns_prefixes,
1308 int with_comments,
1309 xmlChar **doc_txt_ptr) {
1310 int ret;
1311 xmlOutputBufferPtr buf;
1312
1313 if (doc_txt_ptr == NULL) {
1314#ifdef DEBUG_C14N
1315 xmlGenericError(xmlGenericErrorContext,
1316 "xmlC14NDocDumpMemory: null return buffer pointer\n");
1317#endif
1318 return(-1);
1319 }
1320
1321 *doc_txt_ptr = NULL;
1322
1323 /*
1324 * create memory buffer with UTF8 (default) encoding
1325 */
1326 buf = xmlAllocOutputBuffer(NULL);
1327 if(buf == NULL ) {
1328#ifdef DEBUG_C14N
1329 xmlGenericError(xmlGenericErrorContext,
1330 "xmlC14NDocDumpMemory: failed to allocate output buffer.\n");
1331#endif
1332 return(-1);
1333 }
1334
1335 /*
1336 * canonize document and write to buffer
1337 */
1338 ret = xmlC14NDocSaveTo(doc, nodes, exclusive, inclusive_ns_prefixes,
1339 with_comments, buf);
1340 if(ret < 0) {
1341#ifdef DEBUG_C14N
1342 xmlGenericError(xmlGenericErrorContext,
1343 "xmlC14NDocDumpMemory: xmlC14NDocSaveTo failed.\n");
1344#endif
1345 (void)xmlOutputBufferClose(buf);
1346 return(-1);
1347 }
1348
1349 ret = buf->buffer->use;
1350 if(ret > 0) {
1351 *doc_txt_ptr = xmlStrndup(buf->buffer->content, ret);
1352 }
1353 (void)xmlOutputBufferClose(buf);
1354
1355 if(*doc_txt_ptr == NULL && ret > 0) {
1356#ifdef DEBUG_C14N
1357 xmlGenericError(xmlGenericErrorContext,
1358 "xmlC14NDocDumpMemory: failed to allocate memory for document text representation\n");
1359#endif
1360 return(-1);
1361 }
1362 return(ret);
1363}
1364
1365/**
1366 * xmlC14NDocSave:
1367 * @doc: the XML document for canonization
1368 * @nodes: the nodes set to be included in the canonized image
1369 * or NULL if all document nodes should be included
1370 * @exclusive: the exclusive flag (0 - non-exclusive canonicalization;
1371 * otherwise - exclusive canonicalization)
1372 * @inclusive_ns_prefixe the list of inclusive namespace prefixes
1373 * ended with a NULL or NULL if there is no
1374 * inclusive namespaces (only for exclusive
1375 * canonicalization, ignored otherwise)
1376 * @with_comments: include comments in the result (!=0) or not (==0)
1377 * @filename: the filename to store canonical XML image
1378 * @compression: the compression level (zlib requred):
1379 * -1 - libxml default,
1380 * 0 - uncompressed,
1381 * >0 - compression level
1382 *
1383 * Dumps the canonized image of given XML document into the file.
1384 * For details see "Canonical XML" (http://www.w3.org/TR/xml-c14n) or
1385 * "Exclusive XML Canonicalization" (http://www.w3.org/TR/xml-exc-c14n)
1386 *
1387 * Returns the number of bytes written success or a negative value on fail
1388 */
1389int
1390xmlC14NDocSave(xmlDocPtr doc, xmlNodeSetPtr nodes,
1391 int exclusive, xmlChar **inclusive_ns_prefixes,
1392 int with_comments,
1393 const char* filename, int compression) {
1394 xmlOutputBufferPtr buf;
1395 int ret;
1396
1397 if(filename == NULL) {
1398#ifdef DEBUG_C14N
1399 xmlGenericError(xmlGenericErrorContext,
1400 "xmlC14NDocSave: filename is NULL\n");
1401#endif
1402 return(-1);
1403 }
1404
1405#ifdef HAVE_ZLIB_H
1406 if(compression < 0) compression = xmlGetCompressMode();
1407#endif
1408
1409 /*
1410 * save the content to a temp buffer, use default UTF8 encoding.
1411 */
1412 buf = xmlOutputBufferCreateFilename(filename, NULL, compression);
1413 if (buf == NULL) {
1414#ifdef DEBUG_C14N
1415 xmlGenericError(xmlGenericErrorContext,
1416 "xmlC14NDocSave: unable to create buffer for file=\"%s\" with compressin=%d\n", filename, compression);
1417#endif
1418 return(-1);
1419 }
1420
1421 /*
1422 * canonize document and write to buffer
1423 */
1424 ret = xmlC14NDocSaveTo(doc, nodes, exclusive, inclusive_ns_prefixes,
1425 with_comments, buf);
1426 if(ret < 0) {
1427#ifdef DEBUG_C14N
1428 xmlGenericError(xmlGenericErrorContext,
1429 "xmlC14NDocSave: xmlC14NDocSaveTo failed.\n");
1430#endif
1431 (void)xmlOutputBufferClose(buf);
1432 return(-1);
1433 }
1434
1435 /*
1436 * get the numbers of bytes written
1437 */
1438 ret = xmlOutputBufferClose(buf);
1439 return(ret);
1440}
1441
1442
1443
1444/*
1445 * Macro used to grow the current buffer.
1446 */
1447#define growBufferReentrant() { \
1448 buffer_size *= 2; \
1449 buffer = (xmlChar *) \
1450 xmlRealloc(buffer, buffer_size * sizeof(xmlChar)); \
1451 if (buffer == NULL) { \
1452 perror("realloc failed"); \
1453 return(NULL); \
1454 } \
1455}
1456
1457/**
1458 * xmlC11NNormalizeString:
1459 * @input: the input string
1460 * @mode: the normalization mode (attribute, comment, PI or text)
1461 *
1462 * Converts a string to a canonical (normalized) format. The code is stolen
1463 * from xmlEncodeEntitiesReentrant(). Added normalization of \x09, \x0a, \x0A
1464 * and the @mode parameter
1465 *
1466 * Returns a normalized string (caller is responsible for calling xmlFree())
1467 * or NULL if an error occurs
1468 */
1469static xmlChar *
1470xmlC11NNormalizeString(const xmlChar *input, xmlC14NNormalizationMode mode) {
1471 const xmlChar *cur = input;
1472 xmlChar *buffer = NULL;
1473 xmlChar *out = NULL;
1474 int buffer_size = 0;
1475
1476 if (input == NULL) return(NULL);
1477
1478 /*
1479 * allocate an translation buffer.
1480 */
1481 buffer_size = 1000;
1482 buffer = (xmlChar *) xmlMalloc(buffer_size * sizeof(xmlChar));
1483 if (buffer == NULL) {
1484 perror("malloc failed");
1485 return(NULL);
1486 }
1487 out = buffer;
1488
1489 while (*cur != '\0') {
1490 if (out - buffer > buffer_size - 10) {
1491 int indx = out - buffer;
1492
1493 growBufferReentrant();
1494 out = &buffer[indx];
1495 }
1496
1497 if (*cur == '<' && (mode == XMLC14N_NORMALIZE_ATTR ||
1498 mode == XMLC14N_NORMALIZE_TEXT)) {
1499 *out++ = '&';
1500 *out++ = 'l';
1501 *out++ = 't';
1502 *out++ = ';';
1503 } else if (*cur == '>' && (mode == XMLC14N_NORMALIZE_TEXT)) {
1504 *out++ = '&';
1505 *out++ = 'g';
1506 *out++ = 't';
1507 *out++ = ';';
1508 } else if (*cur == '&' && (mode == XMLC14N_NORMALIZE_ATTR ||
1509 mode == XMLC14N_NORMALIZE_TEXT)) {
1510 *out++ = '&';
1511 *out++ = 'a';
1512 *out++ = 'm';
1513 *out++ = 'p';
1514 *out++ = ';';
1515 } else if (*cur == '"' && (mode == XMLC14N_NORMALIZE_ATTR)) {
1516 *out++ = '&';
1517 *out++ = 'q';
1518 *out++ = 'u';
1519 *out++ = 'o';
1520 *out++ = 't';
1521 *out++ = ';';
1522 } else if (*cur == '\x09' && (mode == XMLC14N_NORMALIZE_ATTR)) {
1523 *out++ = '&';
1524 *out++ = '#';
1525 *out++ = 'x';
1526 *out++ = '9';
1527 *out++ = ';';
1528 } else if (*cur == '\x0A' && (mode == XMLC14N_NORMALIZE_ATTR)) {
1529 *out++ = '&';
1530 *out++ = '#';
1531 *out++ = 'x';
1532 *out++ = 'A';
1533 *out++ = ';';
1534 } else if (*cur == '\x0D' && (mode == XMLC14N_NORMALIZE_ATTR ||
1535 mode == XMLC14N_NORMALIZE_TEXT ||
1536 mode == XMLC14N_NORMALIZE_COMMENT ||
1537 mode == XMLC14N_NORMALIZE_PI)) {
1538 *out++ = '&';
1539 *out++ = '#';
1540 *out++ = 'x';
1541 *out++ = 'D';
1542 *out++ = ';';
1543 } else {
1544 /*
1545 * Works because on UTF-8, all extended sequences cannot
1546 * result in bytes in the ASCII range.
1547 */
1548 *out++ = *cur;
1549 }
1550 cur++;
1551 }
1552 *out++ = 0;
1553 return(buffer);
1554}
1555
1556#endif /* LIBXML_C14N_ENABLED */