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