VirtualBox

source: vbox/trunk/src/libs/libxml2-2.12.6/c14n.c

Last change on this file was 104106, checked in by vboxsync, 8 weeks ago

libxml2-2.9.14: Applied and adjusted our libxml2 changes to 2.9.14. bugref:10640

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

© 2023 Oracle
ContactPrivacy policyTerms of Use