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