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