blob: 3ab4a2f20e293267c599bc34109db05dba44b613 [file] [log] [blame]
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001/*
2 * "Canonical XML" implementation
3 * http://www.w3.org/TR/xml-c14n
4 *
5 * "Exclusive XML Canonicalization" implementation
6 * http://www.w3.org/TR/xml-exc-c14n
7 *
8 * See Copyright for the status of this software.
9 *
10 * Author: Aleksey Sanin <aleksey@aleksey.com>
11 */
Daniel Veillard34ce8be2002-03-18 19:37:11 +000012#define IN_LIBXML
Daniel Veillard044fc6b2002-03-04 17:09:44 +000013#include "libxml.h"
14#ifdef LIBXML_C14N_ENABLED
15
16#ifdef HAVE_STDLIB_H
17#include <stdlib.h>
18#endif
19#include <string.h>
20
21#include <libxml/tree.h>
22#include <libxml/parser.h>
Daniel Veillard9ff88172002-03-11 09:15:32 +000023#include <libxml/uri.h>
Daniel Veillard044fc6b2002-03-04 17:09:44 +000024#include <libxml/xmlerror.h>
25#include <libxml/globals.h>
26#include <libxml/xpathInternals.h>
27#include <libxml/c14n.h>
28
29/************************************************************************
30 * *
31 * Some declaration better left private ATM *
32 * *
33 ************************************************************************/
34
Daniel Veillard9ff88172002-03-11 09:15:32 +000035typedef enum {
36 XMLC14N_BEFORE_DOCUMENT_ELEMENT = 0,
37 XMLC14N_INSIDE_DOCUMENT_ELEMENT = 1,
38 XMLC14N_AFTER_DOCUMENT_ELEMENT = 2
Daniel Veillard044fc6b2002-03-04 17:09:44 +000039} xmlC14NPosition;
40
Aleksey Sanindffd5c82002-05-31 04:24:13 +000041typedef struct _xmlC14NNamespaces {
42 int nsNr; /* number of nodes in the set */
43 int nsMax; /* size of the array as allocated */
Aleksey Saninc57f9c12002-05-31 19:14:57 +000044 int nsPos; /* the begginning of the stack for this node */
Aleksey Sanindffd5c82002-05-31 04:24:13 +000045 xmlNsPtr *nsTab; /* array of nodes in no particular order */
46} xmlC14NNamespaces, *xmlC14NNamespacesPtr;
47
Daniel Veillard044fc6b2002-03-04 17:09:44 +000048typedef struct _xmlC14NCtx {
49 /* input parameters */
Daniel Veillard9ff88172002-03-11 09:15:32 +000050 xmlDocPtr doc;
51 xmlNodeSetPtr visible_nodes;
52 int with_comments;
53 xmlOutputBufferPtr buf;
Daniel Veillard044fc6b2002-03-04 17:09:44 +000054
55 /* position in the XML document */
Daniel Veillard9ff88172002-03-11 09:15:32 +000056 xmlC14NPosition pos;
57 int parent_is_doc;
Aleksey Sanindffd5c82002-05-31 04:24:13 +000058 xmlC14NNamespacesPtr ns_rendered;
Daniel Veillard044fc6b2002-03-04 17:09:44 +000059
60 /* exclusive canonicalization */
Daniel Veillard9ff88172002-03-11 09:15:32 +000061 int exclusive;
Daniel Veillard9ff88172002-03-11 09:15:32 +000062 xmlChar **inclusive_ns_prefixes;
Daniel Veillard044fc6b2002-03-04 17:09:44 +000063} xmlC14NCtx, *xmlC14NCtxPtr;
64
65
Daniel Veillard9ff88172002-03-11 09:15:32 +000066static int xmlC14NProcessNode(xmlC14NCtxPtr ctx, xmlNodePtr cur);
67static int xmlC14NProcessNodeList(xmlC14NCtxPtr ctx, xmlNodePtr cur);
Daniel Veillard044fc6b2002-03-04 17:09:44 +000068typedef enum {
Daniel Veillard9ff88172002-03-11 09:15:32 +000069 XMLC14N_NORMALIZE_ATTR = 0,
70 XMLC14N_NORMALIZE_COMMENT = 1,
71 XMLC14N_NORMALIZE_PI = 2,
72 XMLC14N_NORMALIZE_TEXT = 3
73} xmlC14NNormalizationMode;
Daniel Veillard044fc6b2002-03-04 17:09:44 +000074
Daniel Veillard9ff88172002-03-11 09:15:32 +000075static xmlChar *xmlC11NNormalizeString(const xmlChar * input,
76 xmlC14NNormalizationMode mode);
Daniel Veillard044fc6b2002-03-04 17:09:44 +000077
78#define xmlC11NNormalizeAttr( a ) \
Daniel Veillard9ff88172002-03-11 09:15:32 +000079 xmlC11NNormalizeString((a), XMLC14N_NORMALIZE_ATTR)
Daniel Veillard044fc6b2002-03-04 17:09:44 +000080#define xmlC11NNormalizeComment( a ) \
Daniel Veillard9ff88172002-03-11 09:15:32 +000081 xmlC11NNormalizeString((a), XMLC14N_NORMALIZE_COMMENT)
Daniel Veillard044fc6b2002-03-04 17:09:44 +000082#define xmlC11NNormalizePI( a ) \
Daniel Veillard9ff88172002-03-11 09:15:32 +000083 xmlC11NNormalizeString((a), XMLC14N_NORMALIZE_PI)
Daniel Veillard044fc6b2002-03-04 17:09:44 +000084#define xmlC11NNormalizeText( a ) \
Daniel Veillard9ff88172002-03-11 09:15:32 +000085 xmlC11NNormalizeString((a), XMLC14N_NORMALIZE_TEXT)
Daniel Veillard044fc6b2002-03-04 17:09:44 +000086
87/************************************************************************
88 * *
89 * The implementation internals *
90 * *
91 ************************************************************************/
Aleksey Sanindffd5c82002-05-31 04:24:13 +000092#define XML_NAMESPACES_DEFAULT 16
93
94static xmlC14NNamespacesPtr
95xmlC14NNamespacesCreate() {
96 xmlC14NNamespacesPtr ret;
97
98 ret = (xmlC14NNamespacesPtr) xmlMalloc(sizeof(xmlC14NNamespaces));
99 if (ret == NULL) {
100 xmlGenericError(xmlGenericErrorContext,
101 "xmlC14NNamespacesCreate: out of memory\n");
102 return(NULL);
103 }
104 memset(ret, 0 , (size_t) sizeof(xmlC14NNamespaces));
105 return(ret);
106}
107
108static void
109xmlC14NNamespacesDestroy(xmlC14NNamespacesPtr cur) {
110 if(cur == NULL) {
111 return;
112 }
113 if(cur->nsTab != NULL) {
114 memset(cur->nsTab, 0, cur->nsMax * sizeof(xmlNsPtr));
115 xmlFree(cur->nsTab);
116 }
117 memset(cur, 0, sizeof(xmlC14NNamespaces));
118 xmlFree(cur);
119
120}
121
122static void xmlC14NNamespacesAdd(xmlC14NNamespacesPtr cur, xmlNsPtr ns) {
123 if((cur == NULL) || (ns == NULL)) {
124 return;
125 }
126
127 if (cur->nsTab == NULL) {
128 cur->nsTab = (xmlNsPtr*) xmlMalloc(XML_NAMESPACES_DEFAULT * sizeof(xmlNsPtr));
129 if (cur->nsTab == NULL) {
130 xmlGenericError(xmlGenericErrorContext,
131 "xmlC14NNamespacesAdd: out of memory\n");
132 return;
133 }
134 memset(cur->nsTab, 0 , XML_NAMESPACES_DEFAULT * sizeof(xmlNsPtr));
135 cur->nsMax = XML_NAMESPACES_DEFAULT;
136 } else if(cur->nsMax == cur->nsNr) {
137 xmlNsPtr *tmp;
138 int tmpSize;
139
140 tmpSize = 2 * cur->nsMax;
141 tmp = (xmlNsPtr*) xmlRealloc(cur->nsTab, tmpSize * sizeof(xmlNsPtr));
142 if (tmp == NULL) {
143 xmlGenericError(xmlGenericErrorContext,
144 "xmlC14NNamespacesAdd: out of memory\n");
145 return;
146 }
147 cur->nsTab = tmp;
148 cur->nsMax = tmpSize;
149 }
150 cur->nsTab[cur->nsNr++] = ns;
151}
152
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000153
154/**
155 * xmlC14NIsVisible:
156 * @ctx: the C14N context
157 * @node: the node to check
158 *
159 * Checks whether the given node is visible. If the XML document normalization
160 * was called for the whole document then it is always "true".
161 *
162 * Returns 1 if the node is visible or 0 otherwise.
163 */
Daniel Veillard9ff88172002-03-11 09:15:32 +0000164
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000165/* todo: make it a define? */
Daniel Veillard9ff88172002-03-11 09:15:32 +0000166static int
167xmlC14NIsVisible(xmlC14NCtxPtr ctx, void *node)
168{
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000169 /*
170 * If the input is an XPath node-set, then the node-set must explicitly
171 * contain every node to be rendered to the canonical form.
172 */
Daniel Veillard9ff88172002-03-11 09:15:32 +0000173 if ((ctx->visible_nodes != NULL) &&
174 (!xmlXPathNodeSetContains(ctx->visible_nodes, (xmlNodePtr) node)))
175 {
176 return (0);
177 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000178
Daniel Veillard9ff88172002-03-11 09:15:32 +0000179 return (1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000180}
181
182/**
183 * xmlC14NIsXmlNs:
184 * @ns: the namespace to check
185 *
186 * Checks whether the given namespace is a default "xml:" namespace
187 * with href="http://www.w3.org/XML/1998/namespace"
188 *
189 * Returns 1 if the node is default or 0 otherwise
190 */
Daniel Veillard9ff88172002-03-11 09:15:32 +0000191
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000192/* todo: make it a define? */
193static int
Daniel Veillard9ff88172002-03-11 09:15:32 +0000194xmlC14NIsXmlNs(xmlNsPtr ns)
195{
196 return ((ns != NULL) &&
197 (xmlStrEqual(ns->prefix, BAD_CAST "xml")) &&
198 (xmlStrEqual(ns->href,
199 BAD_CAST
200 "http://www.w3.org/XML/1998/namespace")));
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000201}
202
203/**
204 * xmlExcC14NIsRendered:
205 * @ctx the C14N context
206 * @ns the namespace to check
207 *
208 * Checks whether the given namespace was already rendered or not
209 *
210 * Returns 1 if we already wrote this namespace or 0 otherwise
211 */
212static int
Daniel Veillard9ff88172002-03-11 09:15:32 +0000213xmlExcC14NIsRendered(xmlC14NCtxPtr ctx, xmlNsPtr ns)
214{
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000215 int i;
Aleksey Saninc57f9c12002-05-31 19:14:57 +0000216 int emptyNs;
217
Daniel Veillard9ff88172002-03-11 09:15:32 +0000218 if ((ctx == NULL) || (ctx->ns_rendered == NULL) || (ns == NULL)) {
219 return (0);
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000220 }
221
Aleksey Saninc57f9c12002-05-31 19:14:57 +0000222 /*
223 * if the default namespace xmlns="" is not defined yet then
224 * we do not want to print it out
225 */
226 emptyNs = ((xmlStrlen(ns->prefix) == 0) && (xmlStrlen(ns->href) == 0));
Aleksey Sanindffd5c82002-05-31 04:24:13 +0000227 if (ctx->ns_rendered->nsTab != NULL) {
Aleksey Saninc57f9c12002-05-31 19:14:57 +0000228 int pos = (emptyNs) ? 0 : ctx->ns_rendered->nsPos;
229 for (i = ctx->ns_rendered->nsNr - 1; i >= pos; --i) {
230 xmlNsPtr ns1 = ctx->ns_rendered->nsTab[i];
Daniel Veillard9ff88172002-03-11 09:15:32 +0000231
232 if (xmlStrEqual(ns1->prefix, ns->prefix)) {
233 return (xmlStrEqual(ns1->href, ns->href));
234 }
235 }
236 }
Aleksey Saninc57f9c12002-05-31 19:14:57 +0000237 return(emptyNs);
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000238}
239
240/**
241 * xmlC14NNamespacesCompare:
242 * @ns1: the pointer to first namespace
243 * @ns2: the pointer to second namespace
244 *
245 * Compares the namespaces by names (prefixes).
246 *
247 * Returns -1 if ns1 < ns2, 0 if ns1 == ns2 or 1 if ns1 > ns2.
248 */
Daniel Veillard9ff88172002-03-11 09:15:32 +0000249static int
250xmlC14NNamespacesCompare(xmlNsPtr ns1, xmlNsPtr ns2)
251{
252 if (ns1 == ns2)
253 return (0);
254 if (ns1 == NULL)
255 return (-1);
256 if (ns2 == NULL)
257 return (1);
258
259 return (xmlStrcmp(ns1->prefix, ns2->prefix));
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000260}
261
262
263/**
264 * xmlC14NPrintNamespaces:
265 * @ns: the pointer to namespace
266 * @ctx: the C14N context
267 *
268 * Prints the given namespace to the output buffer from C14N context.
269 *
270 * Returns 1 on success or 0 on fail.
271 */
272static int
Daniel Veillard9ff88172002-03-11 09:15:32 +0000273xmlC14NPrintNamespaces(const xmlNsPtr ns, xmlC14NCtxPtr ctx)
274{
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000275
Daniel Veillard9ff88172002-03-11 09:15:32 +0000276 if ((ns == NULL) || (ctx == NULL)) {
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000277#ifdef DEBUG_C14N
278 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +0000279 "xmlC14NPrintNamespace: namespace or context pointer is null\n");
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000280#endif
Daniel Veillard9ff88172002-03-11 09:15:32 +0000281 return 0;
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000282 }
283
Daniel Veillard9ff88172002-03-11 09:15:32 +0000284 if (ns->prefix != NULL) {
285 xmlOutputBufferWriteString(ctx->buf, " xmlns:");
286 xmlOutputBufferWriteString(ctx->buf, (const char *) ns->prefix);
287 xmlOutputBufferWriteString(ctx->buf, "=\"");
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000288 } else {
Daniel Veillard9ff88172002-03-11 09:15:32 +0000289 xmlOutputBufferWriteString(ctx->buf, " xmlns=\"");
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000290 }
291 xmlOutputBufferWriteString(ctx->buf, (const char *) ns->href);
292 xmlOutputBufferWriteString(ctx->buf, "\"");
Daniel Veillard9ff88172002-03-11 09:15:32 +0000293 return (1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000294}
295
296/**
297 * xmlC14NProcessNamespacesAxis:
298 * @ctx: the C14N context
299 * @node: the current node
300 *
301 * Prints out canonical namespace axis of the current node to the
302 * buffer from C14N context as follows
303 *
304 * Canonical XML v 1.0 (http://www.w3.org/TR/xml-c14n)
305 *
306 * Namespace Axis
307 * Consider a list L containing only namespace nodes in the
308 * axis and in the node-set in lexicographic order (ascending). To begin
309 * processing L, if the first node is not the default namespace node (a node
310 * with no namespace URI and no local name), then generate a space followed
311 * by xmlns="" if and only if the following conditions are met:
312 * - the element E that owns the axis is in the node-set
313 * - The nearest ancestor element of E in the node-set has a default
314 * namespace node in the node-set (default namespace nodes always
315 * have non-empty values in XPath)
316 * The latter condition eliminates unnecessary occurrences of xmlns="" in
317 * the canonical form since an element only receives an xmlns="" if its
318 * default namespace is empty and if it has an immediate parent in the
319 * canonical form that has a non-empty default namespace. To finish
320 * processing L, simply process every namespace node in L, except omit
321 * namespace node with local name xml, which defines the xml prefix,
322 * if its string value is http://www.w3.org/XML/1998/namespace.
323 *
324 * Exclusive XML Canonicalization v 1.0 (http://www.w3.org/TR/xml-exc-c14n)
325 * Canonical XML applied to a document subset requires the search of the
326 * ancestor nodes of each orphan element node for attributes in the xml
327 * namespace, such as xml:lang and xml:space. These are copied into the
328 * element node except if a declaration of the same attribute is already
329 * in the attribute axis of the element (whether or not it is included in
330 * the document subset). This search and copying are omitted from the
331 * Exclusive XML Canonicalization method.
332 *
333 * Returns 0 on success or -1 on fail.
334 */
335static int
Daniel Veillard9ff88172002-03-11 09:15:32 +0000336xmlC14NProcessNamespacesAxis(xmlC14NCtxPtr ctx, xmlNodePtr cur)
337{
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000338 xmlNsPtr ns;
339 xmlListPtr list;
Daniel Veillard5c396542002-03-15 07:57:50 +0000340
Daniel Veillard9ff88172002-03-11 09:15:32 +0000341 if ((ctx == NULL) || (cur == NULL) || (cur->type != XML_ELEMENT_NODE)) {
342#ifdef DEBUG_C14N
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000343 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +0000344 "xmlC14NProcessNamespacesAxis: Null context or node pointer or type != XML_ELEMENT_NODE.\n");
345#endif
346 return (-1);
347 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000348
349 /*
350 * Create a sorted list to store element namespaces
351 */
Daniel Veillard9ff88172002-03-11 09:15:32 +0000352 list =
353 xmlListCreate(NULL, (xmlListDataCompare) xmlC14NNamespacesCompare);
354 if (list == NULL) {
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000355#ifdef DEBUG_C14N
356 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +0000357 "xmlC14NProcessNamespacesAxis: list creation failed\n");
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000358#endif
Daniel Veillard9ff88172002-03-11 09:15:32 +0000359 return (-1);
360 }
361
Aleksey Sanindffd5c82002-05-31 04:24:13 +0000362 if(ctx->visible_nodes == NULL) {
Aleksey Saninc57f9c12002-05-31 19:14:57 +0000363 xmlNsPtr tmp;
364
365 for (ns = cur->nsDef; ns != NULL; ns = ns->next) {
366 if(!xmlC14NIsXmlNs(ns)) {
367 tmp = (cur->parent != NULL) ?
368 xmlSearchNs(ctx->doc, cur->parent, ns->prefix) :
369 NULL;
370 if(((tmp == NULL) && (xmlStrlen(ns->href) > 0)) ||
371 ((tmp != NULL) && !xmlStrEqual(ns->href, tmp->href))) {
372 xmlListInsert(list, ns);
373 }
374 }
Aleksey Sanindffd5c82002-05-31 04:24:13 +0000375 }
376 } else {
377 int i;
378
379 /*
380 * All visible namespace nodes are in the nodes set
381 */
382 for(i = 0; i < ctx->visible_nodes->nodeNr; i++) {
383 if(ctx->visible_nodes->nodeTab[i]->type == XML_NAMESPACE_DECL) {
384 ns = (xmlNsPtr) ctx->visible_nodes->nodeTab[i];
385
Aleksey Saninc57f9c12002-05-31 19:14:57 +0000386 if((ns != NULL) && (ns->next == (xmlNsPtr)cur) && !xmlC14NIsXmlNs(ns)) {
387 if(!xmlExcC14NIsRendered(ctx, ns)) {
388 xmlListInsert(list, ns);
389 }
Aleksey Sanindffd5c82002-05-31 04:24:13 +0000390 xmlC14NNamespacesAdd(ctx->ns_rendered, ns);
391 }
Daniel Veillard9ff88172002-03-11 09:15:32 +0000392 }
Aleksey Sanindffd5c82002-05-31 04:24:13 +0000393 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000394 }
395
396 /*
397 * print out all elements from list
398 */
Daniel Veillard9ff88172002-03-11 09:15:32 +0000399 xmlListWalk(list, (xmlListWalker) xmlC14NPrintNamespaces,
400 (const void *) ctx);
401
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000402 /*
403 * Cleanup
404 */
405 xmlListDelete(list);
Daniel Veillard9ff88172002-03-11 09:15:32 +0000406 return (0);
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000407}
408
Aleksey Sanindffd5c82002-05-31 04:24:13 +0000409
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000410/**
411 * xmlExcC14NProcessNamespacesAxis:
412 * @ctx: the C14N context
413 * @node: the current node
414 *
415 * Prints out exclusive canonical namespace axis of the current node to the
416 * buffer from C14N context as follows
417 *
418 * Exclusive XML Canonicalization
419 * http://www.w3.org/TR/xml-exc-c14n
420 *
421 * If the element node is in the XPath subset then output the node in
422 * accordance with Canonical XML except for namespace nodes which are
423 * rendered as follows:
424 *
425 * 1. Render each namespace node iff:
426 * * it is visibly utilized by the immediate parent element or one of
427 * its attributes, or is present in InclusiveNamespaces PrefixList, and
428 * * its prefix and value do not appear in ns_rendered. ns_rendered is
429 * obtained by popping the state stack in order to obtain a list of
430 * prefixes and their values which have already been rendered by
431 * an output ancestor of the namespace node's parent element.
432 * 2. Append the rendered namespace node to the list ns_rendered of namespace
433 * nodes rendered by output ancestors. Push ns_rendered on state stack and
434 * recurse.
435 * 3. After the recursion returns, pop thestate stack.
436 *
437 *
438 * Returns 0 on success or -1 on fail.
439 */
440static int
Daniel Veillard9ff88172002-03-11 09:15:32 +0000441xmlExcC14NProcessNamespacesAxis(xmlC14NCtxPtr ctx, xmlNodePtr cur)
442{
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000443 xmlListPtr list;
444 xmlAttrPtr attr;
445 xmlNsPtr ns;
Daniel Veillard9ff88172002-03-11 09:15:32 +0000446
447 if ((ctx == NULL) || (cur == NULL) || (cur->type != XML_ELEMENT_NODE)) {
448#ifdef DEBUG_C14N
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000449 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +0000450 "xmlExcC14NProcessNamespacesAxis: Null context or node pointer or type != XML_ELEMENT_NODE.\n");
451#endif
452 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000453 }
Daniel Veillard9ff88172002-03-11 09:15:32 +0000454
455 if ((!ctx->exclusive) || (ctx->ns_rendered == NULL)) {
456#ifdef DEBUG_C14N
457 xmlGenericError(xmlGenericErrorContext,
458 "xmlExcC14NProcessNamespacesAxis: called for non-exclusive canonization or rendered stack is NULL.\n");
459#endif
460 return (-1);
461
462 }
463
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000464 /*
465 * Create a sorted list to store element namespaces
466 */
Daniel Veillard9ff88172002-03-11 09:15:32 +0000467 list =
468 xmlListCreate(NULL, (xmlListDataCompare) xmlC14NNamespacesCompare);
469 if (list == NULL) {
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000470#ifdef DEBUG_C14N
471 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +0000472 "xmlExcC14NProcessNamespacesAxis: list creation failed\n");
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000473#endif
Daniel Veillard9ff88172002-03-11 09:15:32 +0000474 return (-1);
475 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000476
477 /*
478 * First of all, add all namespaces required by current node
479 * (i.e. node namespace and all attribute namespaces)
Daniel Veillard54761132002-04-18 21:00:44 +0000480 * we also need to check for default "xml:" namespace
Aleksey Sanindffd5c82002-05-31 04:24:13 +0000481 * todo: shouldn't we check for namespaces "visibility"?
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000482 */
483 ns = (cur->ns != NULL) ? cur->ns : xmlSearchNs(ctx->doc, cur, NULL);
Aleksey Saninc57f9c12002-05-31 19:14:57 +0000484 if ((ns != NULL) && (!xmlC14NIsXmlNs(ns)) && (xmlListSearch(list, ns) == NULL)) {
485 if(!xmlExcC14NIsRendered(ctx, ns)) {
Daniel Veillard9ff88172002-03-11 09:15:32 +0000486 xmlListInsert(list, ns);
Aleksey Saninc57f9c12002-05-31 19:14:57 +0000487 }
488 xmlC14NNamespacesAdd(ctx->ns_rendered, ns);
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000489 }
490 attr = cur->properties;
Daniel Veillard9ff88172002-03-11 09:15:32 +0000491 while (attr != NULL) {
492 /*
Daniel Veillard2d347fa2002-03-17 10:34:11 +0000493 * we need to check that attribute is visible and has non
494 * default namespace (XML Namespaces: "default namespaces
495 * do not apply directly to attributes")
Daniel Veillard9ff88172002-03-11 09:15:32 +0000496 */
Daniel Veillard2d347fa2002-03-17 10:34:11 +0000497 if ((attr->ns != NULL) && xmlC14NIsVisible(ctx, attr) &&
Aleksey Saninc57f9c12002-05-31 19:14:57 +0000498 (!xmlC14NIsXmlNs(attr->ns)) && (xmlListSearch(list, attr->ns) == NULL)) {
499 if(!xmlExcC14NIsRendered(ctx, attr->ns)) {
500 xmlListInsert(list, attr->ns);
501 }
Aleksey Sanindffd5c82002-05-31 04:24:13 +0000502 xmlC14NNamespacesAdd(ctx->ns_rendered, attr->ns);
Daniel Veillard9ff88172002-03-11 09:15:32 +0000503 }
504 attr = attr->next;
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000505 }
Daniel Veillard9ff88172002-03-11 09:15:32 +0000506
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000507 /*
508 * Next add all inclusive namespaces if needed.
509 */
Daniel Veillard9ff88172002-03-11 09:15:32 +0000510 if (ctx->inclusive_ns_prefixes != NULL) {
511 int i;
512 xmlChar *prefix;
513
514 for (i = 0; ctx->inclusive_ns_prefixes[i] != NULL; ++i) {
515 prefix = ctx->inclusive_ns_prefixes[i];
516 /*
517 * Special values for namespace with empty prefix
518 */
519 if (xmlStrEqual(prefix, BAD_CAST "#default")
520 || xmlStrEqual(prefix, BAD_CAST "")) {
521 prefix = NULL;
522 }
523 ns = xmlSearchNs(ctx->doc, cur, prefix);
524 if ((ns != NULL) && (!xmlC14NIsXmlNs(ns))) {
Aleksey Saninc57f9c12002-05-31 19:14:57 +0000525 if (xmlListSearch(list, ns) == NULL) {
526 if(!xmlExcC14NIsRendered(ctx, ns)) {
527 xmlListInsert(list, ns);
528 }
Aleksey Sanindffd5c82002-05-31 04:24:13 +0000529 xmlC14NNamespacesAdd(ctx->ns_rendered, ns);
Daniel Veillard9ff88172002-03-11 09:15:32 +0000530 }
531 }
532 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000533 }
Daniel Veillard9ff88172002-03-11 09:15:32 +0000534
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000535 /*
536 * print out all elements from list
537 */
Daniel Veillard9ff88172002-03-11 09:15:32 +0000538 xmlListWalk(list, (xmlListWalker) xmlC14NPrintNamespaces,
539 (const void *) ctx);
540
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000541 /*
542 * Cleanup
543 */
544 xmlListDelete(list);
Daniel Veillard9ff88172002-03-11 09:15:32 +0000545 return (0);
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000546}
547
548
549/**
550 * xmlC14NAttrsCompare:
551 * @attr1: the pointer to first attr
552 * @attr2: the pointer to second attr
553 *
554 * Prints the given attribute to the output buffer from C14N context.
555 *
556 * Returns -1 if attr1 < attr2, 0 if attr1 == attr2 or 1 if attr1 > attr2.
557 */
558static int
Daniel Veillard9ff88172002-03-11 09:15:32 +0000559xmlC14NAttrsCompare(xmlAttrPtr attr1, xmlAttrPtr attr2)
560{
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000561 int ret = 0;
562
563 /*
564 * Simple cases
565 */
Daniel Veillard9ff88172002-03-11 09:15:32 +0000566 if (attr1 == attr2)
567 return (0);
568 if (attr1 == NULL)
569 return (-1);
570 if (attr2 == NULL)
571 return (1);
572 if (attr1->ns == attr2->ns) {
573 return (xmlStrcmp(attr1->name, attr2->name));
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000574 }
575
576 /*
577 * Attributes in the default namespace are first
578 * because the default namespace is not applied to
579 * unqualified attributes
580 */
Daniel Veillard9ff88172002-03-11 09:15:32 +0000581 if (attr1->ns == NULL)
582 return (-1);
583 if (attr2->ns == NULL)
584 return (1);
585 if (attr1->ns->prefix == NULL)
586 return (-1);
587 if (attr2->ns->prefix == NULL)
588 return (1);
589
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000590 ret = xmlStrcmp(attr1->ns->href, attr2->ns->href);
Daniel Veillard9ff88172002-03-11 09:15:32 +0000591 if (ret == 0) {
592 ret = xmlStrcmp(attr1->name, attr2->name);
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000593 }
Daniel Veillard9ff88172002-03-11 09:15:32 +0000594 return (ret);
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000595}
596
597
598/**
599 * xmlC14NPrintAttrs:
600 * @attr: the pointer to attr
601 * @ctx: the C14N context
602 *
603 * Prints out canonical attribute urrent node to the
604 * buffer from C14N context as follows
605 *
606 * Canonical XML v 1.0 (http://www.w3.org/TR/xml-c14n)
607 *
608 * Returns 1 on success or 0 on fail.
609 */
610static int
Daniel Veillard9ff88172002-03-11 09:15:32 +0000611xmlC14NPrintAttrs(const xmlAttrPtr attr, xmlC14NCtxPtr ctx)
612{
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000613 xmlChar *value;
614 xmlChar *buffer;
615
Daniel Veillard9ff88172002-03-11 09:15:32 +0000616 if ((attr == NULL) || (ctx == NULL)) {
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000617#ifdef DEBUG_C14N
618 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +0000619 "xmlC14NPrintAttrs: attr == NULL or ctx == NULL\n");
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000620#endif
Daniel Veillard9ff88172002-03-11 09:15:32 +0000621 return (0);
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000622 }
623
624 xmlOutputBufferWriteString(ctx->buf, " ");
Daniel Veillard9ff88172002-03-11 09:15:32 +0000625 if (attr->ns != NULL && xmlStrlen(attr->ns->prefix) > 0) {
626 xmlOutputBufferWriteString(ctx->buf,
627 (const char *) attr->ns->prefix);
628 xmlOutputBufferWriteString(ctx->buf, ":");
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000629 }
630 xmlOutputBufferWriteString(ctx->buf, (const char *) attr->name);
631 xmlOutputBufferWriteString(ctx->buf, "=\"");
632
633 value = xmlNodeListGetString(attr->doc, attr->children, 1);
634 /* todo: should we log an error if value==NULL ? */
Daniel Veillard9ff88172002-03-11 09:15:32 +0000635 if (value != NULL) {
636 buffer = xmlC11NNormalizeAttr(value);
637 xmlFree(value);
638 if (buffer != NULL) {
639 xmlOutputBufferWriteString(ctx->buf, (const char *) buffer);
640 xmlFree(buffer);
641 } else {
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000642#ifdef DEBUG_C14N
Daniel Veillard9ff88172002-03-11 09:15:32 +0000643 xmlGenericError(xmlGenericErrorContext,
644 "xmlC14NPrintAttrs: xmlC11NNormalizeAttr failed\n");
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000645#endif
Daniel Veillard9ff88172002-03-11 09:15:32 +0000646 return (0);
647 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000648 }
649 xmlOutputBufferWriteString(ctx->buf, "\"");
Daniel Veillard9ff88172002-03-11 09:15:32 +0000650 return (1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000651}
652
653/**
654 * xmlC14NProcessAttrsAxis:
655 * @ctx: the C14N context
656 * @cur: the current node
657 *
658 * Prints out canonical attribute axis of the current node to the
659 * buffer from C14N context as follows
660 *
661 * Canonical XML v 1.0 (http://www.w3.org/TR/xml-c14n)
662 *
663 * Attribute Axis
664 * In lexicographic order (ascending), process each node that
665 * is in the element's attribute axis and in the node-set.
666 *
667 * The processing of an element node E MUST be modified slightly
668 * when an XPath node-set is given as input and the element's
669 * parent is omitted from the node-set.
670 *
671 *
672 * Exclusive XML Canonicalization v 1.0 (http://www.w3.org/TR/xml-exc-c14n)
673 *
674 * Canonical XML applied to a document subset requires the search of the
675 * ancestor nodes of each orphan element node for attributes in the xml
676 * namespace, such as xml:lang and xml:space. These are copied into the
677 * element node except if a declaration of the same attribute is already
678 * in the attribute axis of the element (whether or not it is included in
679 * the document subset). This search and copying are omitted from the
680 * Exclusive XML Canonicalization method.
681 *
682 * Returns 0 on success or -1 on fail.
683 */
684static int
Daniel Veillard9ff88172002-03-11 09:15:32 +0000685xmlC14NProcessAttrsAxis(xmlC14NCtxPtr ctx, xmlNodePtr cur)
686{
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000687 xmlAttrPtr attr;
688 xmlListPtr list;
Daniel Veillard9ff88172002-03-11 09:15:32 +0000689
690 if ((ctx == NULL) || (cur == NULL) || (cur->type != XML_ELEMENT_NODE)) {
691#ifdef DEBUG_C14N
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000692 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +0000693 "xmlC14NProcessAttrsAxis: Null context or node pointer or type != XML_ELEMENT_NODE.\n");
694#endif
695 return (-1);
696 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000697
698 /*
699 * Create a sorted list to store element attributes
700 */
Daniel Veillard9ff88172002-03-11 09:15:32 +0000701 list = xmlListCreate(NULL, (xmlListDataCompare) xmlC14NAttrsCompare);
702 if (list == NULL) {
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000703#ifdef DEBUG_C14N
704 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +0000705 "xmlC14NProcessAttrsAxis: list creation failed\n");
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000706#endif
Daniel Veillard9ff88172002-03-11 09:15:32 +0000707 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000708 }
709
710 /*
711 * Add all visible attributes from current node.
712 */
713 attr = cur->properties;
Daniel Veillard9ff88172002-03-11 09:15:32 +0000714 while (attr != NULL) {
715 /* check that attribute is visible */
716 if (xmlC14NIsVisible(ctx, attr)) {
717 xmlListInsert(list, attr);
718 }
719 attr = attr->next;
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000720 }
Daniel Veillard9ff88172002-03-11 09:15:32 +0000721
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000722 /*
723 * include attributes in "xml" namespace defined in ancestors
724 * (only for non-exclusive XML Canonicalization)
725 */
Daniel Veillard9ff88172002-03-11 09:15:32 +0000726 if ((!ctx->exclusive) && (cur->parent != NULL)
727 && (!xmlC14NIsVisible(ctx, cur->parent))) {
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000728 /*
Daniel Veillard9ff88172002-03-11 09:15:32 +0000729 * If XPath node-set is not specified then the parent is always
730 * visible!
731 */
732 cur = cur->parent;
733 while (cur != NULL) {
734 attr = cur->properties;
735 while (attr != NULL) {
736 if ((attr->ns != NULL)
737 && (xmlStrEqual(attr->ns->prefix, BAD_CAST "xml"))) {
738 if (xmlListSearch(list, attr) == NULL) {
739 xmlListInsert(list, attr);
740 }
741 }
742 attr = attr->next;
743 }
744 cur = cur->parent;
745 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000746 }
Daniel Veillard9ff88172002-03-11 09:15:32 +0000747
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000748 /*
749 * print out all elements from list
750 */
Daniel Veillard9ff88172002-03-11 09:15:32 +0000751 xmlListWalk(list, (xmlListWalker) xmlC14NPrintAttrs,
752 (const void *) ctx);
753
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000754 /*
755 * Cleanup
756 */
Daniel Veillard9ff88172002-03-11 09:15:32 +0000757 xmlListDelete(list);
758 return (0);
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000759}
760
761/**
762 * xmlC14NCheckForRelativeNamespaces:
763 * @ctx: the C14N context
764 * @cur: the current element node
765 *
766 * Checks that current element node has no relative namespaces defined
767 *
768 * Returns 0 if the node has no relative namespaces or -1 otherwise.
769 */
770static int
Daniel Veillard9ff88172002-03-11 09:15:32 +0000771xmlC14NCheckForRelativeNamespaces(xmlC14NCtxPtr ctx, xmlNodePtr cur)
772{
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000773 xmlNsPtr ns;
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000774
Daniel Veillard9ff88172002-03-11 09:15:32 +0000775 if ((ctx == NULL) || (cur == NULL) || (cur->type != XML_ELEMENT_NODE)) {
776#ifdef DEBUG_C14N
777 xmlGenericError(xmlGenericErrorContext,
778 "xmlC14NCheckForRelativeNamespaces: Null context or node pointer or type != XML_ELEMENT_NODE.\n");
779#endif
780 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000781 }
Daniel Veillard9ff88172002-03-11 09:15:32 +0000782
783 ns = cur->nsDef;
784 while (ns != NULL) {
785 if (xmlStrlen(ns->href) > 0) {
786 xmlURIPtr uri;
787
788 uri = xmlParseURI((const char *) ns->href);
789 if (uri == NULL) {
790#ifdef DEBUG_C14N
791 xmlGenericError(xmlGenericErrorContext,
792 "xmlC14NCheckForRelativeNamespaces: unable to parse uri=\"%s\".\n",
793 ns->href);
794#endif
795 return (-1);
796 }
797 if (xmlStrlen((const xmlChar *) uri->scheme) == 0) {
798 xmlFreeURI(uri);
799 return (-1);
800 }
801 if ((!xmlStrEqual
802 ((const xmlChar *) uri->scheme, BAD_CAST "urn"))
803 && (xmlStrlen((const xmlChar *) uri->server) == 0)) {
804 xmlFreeURI(uri);
805 return (-1);
806 }
807 xmlFreeURI(uri);
808 }
809 ns = ns->next;
810 }
811 return (0);
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000812}
813
814/**
815 * xmlC14NProcessElementNode:
816 * @ctx: the pointer to C14N context object
817 * @cur: the node to process
818 *
819 * Canonical XML v 1.0 (http://www.w3.org/TR/xml-c14n)
820 *
821 * Element Nodes
822 * If the element is not in the node-set, then the result is obtained
823 * by processing the namespace axis, then the attribute axis, then
824 * processing the child nodes of the element that are in the node-set
825 * (in document order). If the element is in the node-set, then the result
826 * is an open angle bracket (<), the element QName, the result of
827 * processing the namespace axis, the result of processing the attribute
828 * axis, a close angle bracket (>), the result of processing the child
829 * nodes of the element that are in the node-set (in document order), an
830 * open angle bracket, a forward slash (/), the element QName, and a close
831 * angle bracket.
832 *
833 * Returns non-negative value on success or negative value on fail
834 */
835static int
Daniel Veillard9ff88172002-03-11 09:15:32 +0000836xmlC14NProcessElementNode(xmlC14NCtxPtr ctx, xmlNodePtr cur, int visible)
837{
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000838 int ret;
839 int ns_rendered_pos = 0;
Aleksey Saninc57f9c12002-05-31 19:14:57 +0000840 int ns_rendered_nr = 0;
Daniel Veillard6f293b12002-03-15 09:42:33 +0000841 int parent_is_doc = 0;
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000842
Daniel Veillard9ff88172002-03-11 09:15:32 +0000843 if ((ctx == NULL) || (cur == NULL) || (cur->type != XML_ELEMENT_NODE)) {
844#ifdef DEBUG_C14N
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000845 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +0000846 "xmlC14NProcessElementNode: Null context or node pointer or type != XML_ELEMENT_NODE.\n");
847#endif
848 return (-1);
849 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000850
851 /*
852 * Check relative relative namespaces:
853 * implementations of XML canonicalization MUST report an operation
854 * failure on documents containing relative namespace URIs.
855 */
Daniel Veillard9ff88172002-03-11 09:15:32 +0000856 if (xmlC14NCheckForRelativeNamespaces(ctx, cur) < 0) {
857#ifdef DEBUG_C14N
858 xmlGenericError(xmlGenericErrorContext,
859 "xmlC14NProcessElementNode: xmlC14NCheckForRelativeNamespaces failed.\n");
860#endif
861 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000862 }
863
864
865 /*
866 * Save ns_rendered stack position for exclusive
867 * processing
868 */
Aleksey Saninc57f9c12002-05-31 19:14:57 +0000869 if (ctx->ns_rendered != NULL) {
870 ns_rendered_pos = ctx->ns_rendered->nsPos;
871 ns_rendered_nr = ctx->ns_rendered->nsNr;
Daniel Veillard9ff88172002-03-11 09:15:32 +0000872 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000873
Daniel Veillard6f293b12002-03-15 09:42:33 +0000874 if (visible) {
Daniel Veillard9ff88172002-03-11 09:15:32 +0000875 if (ctx->parent_is_doc) {
Daniel Veillard6f293b12002-03-15 09:42:33 +0000876 /* save this flag into the stack */
877 parent_is_doc = ctx->parent_is_doc;
878 ctx->parent_is_doc = 0;
Daniel Veillard9ff88172002-03-11 09:15:32 +0000879 ctx->pos = XMLC14N_INSIDE_DOCUMENT_ELEMENT;
880 }
881 xmlOutputBufferWriteString(ctx->buf, "<");
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000882
Daniel Veillard9ff88172002-03-11 09:15:32 +0000883 if ((cur->ns != NULL) && (xmlStrlen(cur->ns->prefix) > 0)) {
884 xmlOutputBufferWriteString(ctx->buf,
885 (const char *) cur->ns->prefix);
886 xmlOutputBufferWriteString(ctx->buf, ":");
887 }
888 xmlOutputBufferWriteString(ctx->buf, (const char *) cur->name);
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000889
Daniel Veillard9ff88172002-03-11 09:15:32 +0000890 if (ctx->exclusive) {
891 ret = xmlExcC14NProcessNamespacesAxis(ctx, cur);
892 } else {
893 ret = xmlC14NProcessNamespacesAxis(ctx, cur);
894 }
895 if (ret < 0) {
896#ifdef DEBUG_C14N
897 xmlGenericError(xmlGenericErrorContext,
898 "xmlC14NProcessElementNode: xmlC14NProcessNamespacesAxis failed.\n");
899#endif
900 return (-1);
901 }
Aleksey Saninc57f9c12002-05-31 19:14:57 +0000902 if (ctx->ns_rendered != NULL) {
903 ctx->ns_rendered->nsPos = ns_rendered_nr;
904 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000905
Daniel Veillard9ff88172002-03-11 09:15:32 +0000906 ret = xmlC14NProcessAttrsAxis(ctx, cur);
907 if (ret < 0) {
908#ifdef DEBUG_C14N
909 xmlGenericError(xmlGenericErrorContext,
910 "xmlC14NProcessElementNode: xmlC14NProcessAttrsAxis failed.\n");
911#endif
912 return (-1);
913 }
914
915 xmlOutputBufferWriteString(ctx->buf, ">");
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000916 }
917 if (cur->children != NULL) {
Daniel Veillard9ff88172002-03-11 09:15:32 +0000918 ret = xmlC14NProcessNodeList(ctx, cur->children);
919 if (ret < 0) {
920#ifdef DEBUG_C14N
921 xmlGenericError(xmlGenericErrorContext,
922 "xmlC14NProcessElementNode: xmlC14NProcessNodeList failed.\n");
923#endif
924 return (-1);
925 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000926 }
Daniel Veillard9ff88172002-03-11 09:15:32 +0000927 if (visible) {
928 xmlOutputBufferWriteString(ctx->buf, "</");
929 if ((cur->ns != NULL) && (xmlStrlen(cur->ns->prefix) > 0)) {
930 xmlOutputBufferWriteString(ctx->buf,
931 (const char *) cur->ns->prefix);
932 xmlOutputBufferWriteString(ctx->buf, ":");
933 }
934 xmlOutputBufferWriteString(ctx->buf, (const char *) cur->name);
935 xmlOutputBufferWriteString(ctx->buf, ">");
Daniel Veillard6f293b12002-03-15 09:42:33 +0000936 if (parent_is_doc) {
937 /* restore this flag from the stack for next node */
938 ctx->parent_is_doc = parent_is_doc;
939 ctx->pos = XMLC14N_AFTER_DOCUMENT_ELEMENT;
Daniel Veillard9ff88172002-03-11 09:15:32 +0000940 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000941 }
942
943 /*
944 * Restore ns_rendered stack position for exclusive
945 * processing
946 */
Aleksey Sanindffd5c82002-05-31 04:24:13 +0000947 if (ctx->ns_rendered != NULL) {
Aleksey Saninc57f9c12002-05-31 19:14:57 +0000948 ctx->ns_rendered->nsPos = ns_rendered_pos;
949 ctx->ns_rendered->nsNr = ns_rendered_nr;
Daniel Veillard9ff88172002-03-11 09:15:32 +0000950 }
951 return (0);
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000952}
953
954/**
955 * xmlC14NProcessNode:
956 * @ctx: the pointer to C14N context object
957 * @cur: the node to process
958 *
959 * Processes the given node
960 *
961 * Returns non-negative value on success or negative value on fail
962 */
963static int
Daniel Veillard9ff88172002-03-11 09:15:32 +0000964xmlC14NProcessNode(xmlC14NCtxPtr ctx, xmlNodePtr cur)
965{
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000966 int ret = 0;
967 int visible;
Daniel Veillard9ff88172002-03-11 09:15:32 +0000968
969 if ((ctx == NULL) || (cur == NULL)) {
970#ifdef DEBUG_C14N
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000971 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +0000972 "xmlC14NProcessNode: Null context or node pointer.\n");
973#endif
974 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000975 }
976
977 visible = xmlC14NIsVisible(ctx, cur);
Daniel Veillard9ff88172002-03-11 09:15:32 +0000978 switch (cur->type) {
979 case XML_ELEMENT_NODE:
980 ret = xmlC14NProcessElementNode(ctx, cur, visible);
981 break;
982 case XML_CDATA_SECTION_NODE:
983 case XML_TEXT_NODE:
984 /*
985 * Text Nodes
986 * the string value, except all ampersands are replaced
987 * by &amp;, all open angle brackets (<) are replaced by &lt;, all closing
988 * angle brackets (>) are replaced by &gt;, and all #xD characters are
989 * replaced by &#xD;.
990 */
991 /* cdata sections are processed as text nodes */
992 /* todo: verify that cdata sections are included in XPath nodes set */
993 if ((visible) && (cur->content != NULL)) {
994 xmlChar *buffer;
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000995
Daniel Veillard9ff88172002-03-11 09:15:32 +0000996 buffer = xmlC11NNormalizeText(cur->content);
997 if (buffer != NULL) {
998 xmlOutputBufferWriteString(ctx->buf,
999 (const char *) buffer);
1000 xmlFree(buffer);
1001 } else {
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001002#ifdef DEBUG_C14N
Daniel Veillard9ff88172002-03-11 09:15:32 +00001003 xmlGenericError(xmlGenericErrorContext,
1004 "xmlC14NProcessNode: xmlC11NNormalizeText() failed\n");
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001005#endif
Daniel Veillard9ff88172002-03-11 09:15:32 +00001006 return (-1);
1007 }
1008 }
1009 break;
1010 case XML_PI_NODE:
1011 /*
1012 * Processing Instruction (PI) Nodes-
1013 * The opening PI symbol (<?), the PI target name of the node,
1014 * a leading space and the string value if it is not empty, and
1015 * the closing PI symbol (?>). If the string value is empty,
1016 * then the leading space is not added. Also, a trailing #xA is
1017 * rendered after the closing PI symbol for PI children of the
1018 * root node with a lesser document order than the document
1019 * element, and a leading #xA is rendered before the opening PI
1020 * symbol of PI children of the root node with a greater document
1021 * order than the document element.
1022 */
1023 if (visible) {
1024 if (ctx->pos == XMLC14N_AFTER_DOCUMENT_ELEMENT) {
1025 xmlOutputBufferWriteString(ctx->buf, "\x0A<?");
1026 } else {
1027 xmlOutputBufferWriteString(ctx->buf, "<?");
1028 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001029
Daniel Veillard9ff88172002-03-11 09:15:32 +00001030 xmlOutputBufferWriteString(ctx->buf,
1031 (const char *) cur->name);
1032 if ((cur->content != NULL) && (*(cur->content) != '\0')) {
1033 xmlChar *buffer;
1034
1035 xmlOutputBufferWriteString(ctx->buf, " ");
1036
1037 /* todo: do we need to normalize pi? */
1038 buffer = xmlC11NNormalizePI(cur->content);
1039 if (buffer != NULL) {
1040 xmlOutputBufferWriteString(ctx->buf,
1041 (const char *) buffer);
1042 xmlFree(buffer);
1043 } else {
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001044#ifdef DEBUG_C14N
Daniel Veillard9ff88172002-03-11 09:15:32 +00001045 xmlGenericError(xmlGenericErrorContext,
1046 "xmlC14NProcessNode: xmlC11NNormalizePI() failed\n");
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001047#endif
Daniel Veillard9ff88172002-03-11 09:15:32 +00001048 return (-1);
1049 }
1050 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001051
Daniel Veillard9ff88172002-03-11 09:15:32 +00001052 if (ctx->pos == XMLC14N_BEFORE_DOCUMENT_ELEMENT) {
1053 xmlOutputBufferWriteString(ctx->buf, "?>\x0A");
1054 } else {
1055 xmlOutputBufferWriteString(ctx->buf, "?>");
1056 }
1057 }
1058 break;
1059 case XML_COMMENT_NODE:
1060 /*
1061 * Comment Nodes
1062 * Nothing if generating canonical XML without comments. For
1063 * canonical XML with comments, generate the opening comment
1064 * symbol (<!--), the string value of the node, and the
1065 * closing comment symbol (-->). Also, a trailing #xA is rendered
1066 * after the closing comment symbol for comment children of the
1067 * root node with a lesser document order than the document
1068 * element, and a leading #xA is rendered before the opening
1069 * comment symbol of comment children of the root node with a
1070 * greater document order than the document element. (Comment
1071 * children of the root node represent comments outside of the
1072 * top-level document element and outside of the document type
1073 * declaration).
1074 */
1075 if (visible && ctx->with_comments) {
1076 if (ctx->pos == XMLC14N_AFTER_DOCUMENT_ELEMENT) {
1077 xmlOutputBufferWriteString(ctx->buf, "\x0A<!--");
1078 } else {
1079 xmlOutputBufferWriteString(ctx->buf, "<!--");
1080 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001081
Daniel Veillard9ff88172002-03-11 09:15:32 +00001082 if (cur->content != NULL) {
1083 xmlChar *buffer;
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001084
Daniel Veillard9ff88172002-03-11 09:15:32 +00001085 /* todo: do we need to normalize comment? */
1086 buffer = xmlC11NNormalizeComment(cur->content);
1087 if (buffer != NULL) {
1088 xmlOutputBufferWriteString(ctx->buf,
1089 (const char *) buffer);
1090 xmlFree(buffer);
1091 } else {
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001092#ifdef DEBUG_C14N
Daniel Veillard9ff88172002-03-11 09:15:32 +00001093 xmlGenericError(xmlGenericErrorContext,
1094 "xmlC14NProcessNode: xmlC11NNormalizeComment() failed\n");
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001095#endif
Daniel Veillard9ff88172002-03-11 09:15:32 +00001096 return (-1);
1097 }
1098 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001099
Daniel Veillard9ff88172002-03-11 09:15:32 +00001100 if (ctx->pos == XMLC14N_BEFORE_DOCUMENT_ELEMENT) {
1101 xmlOutputBufferWriteString(ctx->buf, "-->\x0A");
1102 } else {
1103 xmlOutputBufferWriteString(ctx->buf, "-->");
1104 }
1105 }
1106 break;
1107 case XML_DOCUMENT_NODE:
1108 case XML_DOCUMENT_FRAG_NODE: /* should be processed as document? */
Daniel Veillard1840ef02002-03-21 08:05:23 +00001109#ifdef LIBXML_DOCB_ENABLED
Daniel Veillard9ff88172002-03-11 09:15:32 +00001110 case XML_DOCB_DOCUMENT_NODE: /* should be processed as document? */
Daniel Veillard1840ef02002-03-21 08:05:23 +00001111#endif
1112#ifdef LIBXML_HTML_ENABLED
Daniel Veillard9ff88172002-03-11 09:15:32 +00001113 case XML_HTML_DOCUMENT_NODE: /* should be processed as document? */
Daniel Veillard1840ef02002-03-21 08:05:23 +00001114#endif
Daniel Veillard9ff88172002-03-11 09:15:32 +00001115 if (cur->children != NULL) {
1116 ctx->pos = XMLC14N_BEFORE_DOCUMENT_ELEMENT;
1117 ctx->parent_is_doc = 1;
1118 ret = xmlC14NProcessNodeList(ctx, cur->children);
1119 }
1120 break;
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001121
Daniel Veillard9ff88172002-03-11 09:15:32 +00001122 case XML_ATTRIBUTE_NODE:
1123 xmlGenericError(xmlGenericErrorContext,
1124 "xmlC14NProcessNode: XML_ATTRIBUTE_NODE is illegal here\n");
1125 return (-1);
1126 case XML_NAMESPACE_DECL:
1127 xmlGenericError(xmlGenericErrorContext,
1128 "xmlC14NProcessNode: XML_NAMESPACE_DECL is illegal here\n");
1129 return (-1);
1130 case XML_ENTITY_REF_NODE:
1131 xmlGenericError(xmlGenericErrorContext,
1132 "xmlC14NProcessNode: XML_ENTITY_REF_NODE is illegal here\n");
1133 return (-1);
1134 case XML_ENTITY_NODE:
1135 xmlGenericError(xmlGenericErrorContext,
1136 "xmlC14NProcessNode: XML_ENTITY_NODE is illegal here\n");
1137 return (-1);
1138
1139 case XML_DOCUMENT_TYPE_NODE:
1140 case XML_NOTATION_NODE:
1141 case XML_DTD_NODE:
1142 case XML_ELEMENT_DECL:
1143 case XML_ATTRIBUTE_DECL:
1144 case XML_ENTITY_DECL:
Daniel Veillard1840ef02002-03-21 08:05:23 +00001145#ifdef LIBXML_XINCLUDE_ENABLED
Daniel Veillard9ff88172002-03-11 09:15:32 +00001146 case XML_XINCLUDE_START:
1147 case XML_XINCLUDE_END:
Daniel Veillard1840ef02002-03-21 08:05:23 +00001148#endif
Daniel Veillard9ff88172002-03-11 09:15:32 +00001149 /*
1150 * should be ignored according to "W3C Canonical XML"
1151 */
1152 break;
1153 default:
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001154#ifdef DEBUG_C14N
Daniel Veillard9ff88172002-03-11 09:15:32 +00001155 xmlGenericError(xmlGenericErrorContext,
1156 "xmlC14NProcessNode: unknown node type = %d\n",
1157 cur->type);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001158#endif
Daniel Veillard9ff88172002-03-11 09:15:32 +00001159 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001160 }
1161
Daniel Veillard9ff88172002-03-11 09:15:32 +00001162 return (ret);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001163}
1164
1165/**
1166 * xmlC14NProcessNodeList:
1167 * @ctx: the pointer to C14N context object
1168 * @cur: the node to start from
1169 *
1170 * Processes all nodes in the row starting from cur.
1171 *
1172 * Returns non-negative value on success or negative value on fail
1173 */
1174static int
Daniel Veillard9ff88172002-03-11 09:15:32 +00001175xmlC14NProcessNodeList(xmlC14NCtxPtr ctx, xmlNodePtr cur)
1176{
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001177 int ret;
Daniel Veillard9ff88172002-03-11 09:15:32 +00001178
1179 if (ctx == NULL) {
1180#ifdef DEBUG_C14N
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001181 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001182 "xmlC14NProcessNodeList: Null context pointer.\n");
1183#endif
1184 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001185 }
1186
Daniel Veillard9ff88172002-03-11 09:15:32 +00001187 for (ret = 0; cur != NULL && ret >= 0; cur = cur->next) {
1188 ret = xmlC14NProcessNode(ctx, cur);
1189 }
1190 return (ret);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001191}
1192
1193
1194/**
1195 * xmlC14NFreeCtx:
1196 * @ctx: the pointer to C14N context object
1197 *
1198 * Cleanups the C14N context object.
1199 */
1200
1201static void
Daniel Veillard9ff88172002-03-11 09:15:32 +00001202xmlC14NFreeCtx(xmlC14NCtxPtr ctx)
1203{
1204 if (ctx == NULL) {
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001205#ifdef DEBUG_C14N
1206 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001207 "xmlC14NFreeCtx: ctx == NULL\n");
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001208#endif
Daniel Veillard9ff88172002-03-11 09:15:32 +00001209 return;
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001210 }
Daniel Veillard9ff88172002-03-11 09:15:32 +00001211
1212 if (ctx->ns_rendered != NULL) {
Aleksey Sanindffd5c82002-05-31 04:24:13 +00001213 xmlC14NNamespacesDestroy(ctx->ns_rendered);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001214 }
1215 xmlFree(ctx);
1216}
1217
1218/**
1219 * xmlC14NNewCtx:
1220 * @doc: the XML document for canonization
1221 * @nodes: the nodes set to be included in the canonized image
1222 * or NULL if all document nodes should be included
1223 * @exclusive: the exclusive flag (0 - non-exclusive canonicalization;
1224 * otherwise - exclusive canonicalization)
1225 * @inclusive_ns_prefixe the list of inclusive namespace prefixes
1226 * ended with a NULL or NULL if there is no
1227 * inclusive namespaces (only for exclusive
1228 * canonicalization)
1229 * @with_comments: include comments in the result (!=0) or not (==0)
1230 * @buf: the output buffer to store canonical XML; this
1231 * buffer MUST have encoder==NULL because C14N requires
1232 * UTF-8 output
1233 *
1234 * Creates new C14N context object to store C14N parameters.
1235 *
1236 * Returns pointer to newly created object (success) or NULL (fail)
1237 */
1238static xmlC14NCtxPtr
Daniel Veillard9ff88172002-03-11 09:15:32 +00001239xmlC14NNewCtx(xmlDocPtr doc, xmlNodeSetPtr nodes,
1240 int exclusive, xmlChar ** inclusive_ns_prefixes,
1241 int with_comments, xmlOutputBufferPtr buf)
1242{
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001243 xmlC14NCtxPtr ctx;
1244
Daniel Veillard9ff88172002-03-11 09:15:32 +00001245 if ((doc == NULL) || (buf == NULL)) {
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001246#ifdef DEBUG_C14N
1247 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001248 "xmlC14NNewCtx: pointer to document or output buffer is NULL\n");
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001249#endif
Daniel Veillard9ff88172002-03-11 09:15:32 +00001250 return (NULL);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001251 }
1252
1253 /*
1254 * Validate the encoding output buffer encoding
1255 */
Daniel Veillard9ff88172002-03-11 09:15:32 +00001256 if (buf->encoder != NULL) {
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001257 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001258 "xmlC14NNewCtx: output buffer encoder != NULL but C14N requires UTF8 output\n");
1259 return (NULL);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001260 }
1261
1262 /*
1263 * Validate the XML document encoding value, if provided.
1264 */
Daniel Veillard9ff88172002-03-11 09:15:32 +00001265 if (doc->charset != XML_CHAR_ENCODING_UTF8) {
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001266 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001267 "xmlC14NNewCtx: source document not in UTF8\n");
1268 return (NULL);
1269 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001270
1271 /*
1272 * Allocate a new xmlC14NCtxPtr and fill the fields.
1273 */
1274 ctx = (xmlC14NCtxPtr) xmlMalloc(sizeof(xmlC14NCtx));
1275 if (ctx == NULL) {
1276 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001277 "xmlC14NNewCtx: malloc failed\n");
1278 return (NULL);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001279 }
1280 memset(ctx, 0, sizeof(xmlC14NCtx));
1281
1282 /*
1283 * initialize C14N context
Daniel Veillard9ff88172002-03-11 09:15:32 +00001284 */
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001285 ctx->doc = doc;
1286 ctx->with_comments = with_comments;
1287 ctx->visible_nodes = nodes;
1288 ctx->buf = buf;
1289 ctx->parent_is_doc = 1;
1290 ctx->pos = XMLC14N_BEFORE_DOCUMENT_ELEMENT;
Aleksey Sanindffd5c82002-05-31 04:24:13 +00001291 ctx->ns_rendered = xmlC14NNamespacesCreate();
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001292
1293 /*
1294 * Set "exclusive" flag, create a nodes set for namespaces
1295 * stack and remember list of incluseve prefixes
1296 */
Daniel Veillard9ff88172002-03-11 09:15:32 +00001297 if (exclusive) {
1298 ctx->exclusive = 1;
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001299 ctx->inclusive_ns_prefixes = inclusive_ns_prefixes;
1300 }
Daniel Veillard9ff88172002-03-11 09:15:32 +00001301 return (ctx);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001302}
1303
1304/**
1305 * xmlC14NDocSaveTo:
1306 * @doc: the XML document for canonization
1307 * @nodes: the nodes set to be included in the canonized image
1308 * or NULL if all document nodes should be included
1309 * @exclusive: the exclusive flag (0 - non-exclusive canonicalization;
1310 * otherwise - exclusive canonicalization)
Daniel Veillarddb1bdba2002-03-09 14:13:11 +00001311 * @inclusive_ns_prefixes: the list of inclusive namespace prefixes
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001312 * ended with a NULL or NULL if there is no
1313 * inclusive namespaces (only for exclusive
1314 * canonicalization, ignored otherwise)
1315 * @with_comments: include comments in the result (!=0) or not (==0)
1316 * @buf: the output buffer to store canonical XML; this
1317 * buffer MUST have encoder==NULL because C14N requires
1318 * UTF-8 output
1319 *
1320 * Dumps the canonized image of given XML document into the provided buffer.
1321 * For details see "Canonical XML" (http://www.w3.org/TR/xml-c14n) or
1322 * "Exclusive XML Canonicalization" (http://www.w3.org/TR/xml-exc-c14n)
1323 *
1324 * Returns non-negative value on success or a negative value on fail
1325 */
1326int
Daniel Veillard9ff88172002-03-11 09:15:32 +00001327xmlC14NDocSaveTo(xmlDocPtr doc, xmlNodeSetPtr nodes,
1328 int exclusive, xmlChar ** inclusive_ns_prefixes,
1329 int with_comments, xmlOutputBufferPtr buf)
1330{
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001331 xmlC14NCtxPtr ctx;
1332 int ret;
Daniel Veillard9ff88172002-03-11 09:15:32 +00001333
1334 if ((buf == NULL) || (doc == NULL)) {
1335#ifdef DEBUG_C14N
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001336 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001337 "xmlC14NDocSaveTo: null return buffer or doc pointer\n");
1338#endif
1339 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001340 }
1341
1342 /*
1343 * Validate the encoding output buffer encoding
1344 */
Daniel Veillard9ff88172002-03-11 09:15:32 +00001345 if (buf->encoder != NULL) {
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001346 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001347 "xmlC14NDocSaveTo: output buffer encoder != NULL but C14N requires UTF8 output\n");
1348 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001349 }
1350
1351 ctx = xmlC14NNewCtx(doc, nodes, exclusive, inclusive_ns_prefixes,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001352 with_comments, buf);
1353 if (ctx == NULL) {
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001354 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001355 "xmlC14NDocSaveTo: unable to create C14N context\n");
1356 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001357 }
1358
1359
1360
1361 /*
1362 * Root Node
1363 * The root node is the parent of the top-level document element. The
1364 * result of processing each of its child nodes that is in the node-set
1365 * in document order. The root node does not generate a byte order mark,
1366 * XML declaration, nor anything from within the document type
1367 * declaration.
1368 */
Daniel Veillard9ff88172002-03-11 09:15:32 +00001369 if (doc->children != NULL) {
1370 ret = xmlC14NProcessNodeList(ctx, doc->children);
1371 if (ret < 0) {
1372#ifdef DEBUG_C14N
1373 xmlGenericError(xmlGenericErrorContext,
1374 "xmlC14NDocSaveTo: process childrens' list failed.\n");
1375#endif
1376 xmlC14NFreeCtx(ctx);
1377 return (-1);
1378 }
1379 }
1380
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001381 /*
1382 * Flush buffer to get number of bytes written
1383 */
1384 ret = xmlOutputBufferFlush(buf);
Daniel Veillard9ff88172002-03-11 09:15:32 +00001385 if (ret < 0) {
1386#ifdef DEBUG_C14N
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001387 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001388 "xmlC14NDocSaveTo: buffer flush failed.\n");
1389#endif
1390 xmlC14NFreeCtx(ctx);
1391 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001392 }
1393
1394 /*
1395 * Cleanup
1396 */
Daniel Veillard9ff88172002-03-11 09:15:32 +00001397 xmlC14NFreeCtx(ctx);
1398 return (ret);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001399}
1400
1401/**
1402 * xmlC14NDocDumpMemory:
1403 * @doc: the XML document for canonization
1404 * @nodes: the nodes set to be included in the canonized image
1405 * or NULL if all document nodes should be included
1406 * @exclusive: the exclusive flag (0 - non-exclusive canonicalization;
1407 * otherwise - exclusive canonicalization)
Daniel Veillarddb1bdba2002-03-09 14:13:11 +00001408 * @inclusive_ns_prefixes: the list of inclusive namespace prefixes
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001409 * ended with a NULL or NULL if there is no
1410 * inclusive namespaces (only for exclusive
1411 * canonicalization, ignored otherwise)
1412 * @with_comments: include comments in the result (!=0) or not (==0)
1413 * @doc_txt_ptr: the memory pointer for allocated canonical XML text;
1414 * the caller of this functions is responsible for calling
1415 * xmlFree() to free allocated memory
1416 *
1417 * Dumps the canonized image of given XML document into memory.
1418 * For details see "Canonical XML" (http://www.w3.org/TR/xml-c14n) or
1419 * "Exclusive XML Canonicalization" (http://www.w3.org/TR/xml-exc-c14n)
1420 *
1421 * Returns the number of bytes written on success or a negative value on fail
1422 */
1423int
Daniel Veillard9ff88172002-03-11 09:15:32 +00001424xmlC14NDocDumpMemory(xmlDocPtr doc, xmlNodeSetPtr nodes,
1425 int exclusive, xmlChar ** inclusive_ns_prefixes,
1426 int with_comments, xmlChar ** doc_txt_ptr)
1427{
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001428 int ret;
1429 xmlOutputBufferPtr buf;
1430
1431 if (doc_txt_ptr == NULL) {
Daniel Veillard9ff88172002-03-11 09:15:32 +00001432#ifdef DEBUG_C14N
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001433 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001434 "xmlC14NDocDumpMemory: null return buffer pointer\n");
1435#endif
1436 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001437 }
1438
1439 *doc_txt_ptr = NULL;
Daniel Veillard9ff88172002-03-11 09:15:32 +00001440
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001441 /*
1442 * create memory buffer with UTF8 (default) encoding
1443 */
1444 buf = xmlAllocOutputBuffer(NULL);
Daniel Veillard9ff88172002-03-11 09:15:32 +00001445 if (buf == NULL) {
1446#ifdef DEBUG_C14N
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001447 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001448 "xmlC14NDocDumpMemory: failed to allocate output buffer.\n");
1449#endif
1450 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001451 }
1452
1453 /*
1454 * canonize document and write to buffer
1455 */
Daniel Veillard9ff88172002-03-11 09:15:32 +00001456 ret = xmlC14NDocSaveTo(doc, nodes, exclusive, inclusive_ns_prefixes,
1457 with_comments, buf);
1458 if (ret < 0) {
1459#ifdef DEBUG_C14N
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001460 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001461 "xmlC14NDocDumpMemory: xmlC14NDocSaveTo failed.\n");
1462#endif
1463 (void) xmlOutputBufferClose(buf);
1464 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001465 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001466
Daniel Veillard9ff88172002-03-11 09:15:32 +00001467 ret = buf->buffer->use;
1468 if (ret > 0) {
1469 *doc_txt_ptr = xmlStrndup(buf->buffer->content, ret);
1470 }
1471 (void) xmlOutputBufferClose(buf);
1472
1473 if ((*doc_txt_ptr == NULL) && (ret > 0)) {
1474#ifdef DEBUG_C14N
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001475 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001476 "xmlC14NDocDumpMemory: failed to allocate memory for document text representation\n");
1477#endif
1478 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001479 }
Daniel Veillard9ff88172002-03-11 09:15:32 +00001480 return (ret);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001481}
1482
1483/**
1484 * xmlC14NDocSave:
1485 * @doc: the XML document for canonization
1486 * @nodes: the nodes set to be included in the canonized image
1487 * or NULL if all document nodes should be included
1488 * @exclusive: the exclusive flag (0 - non-exclusive canonicalization;
1489 * otherwise - exclusive canonicalization)
Daniel Veillarddb1bdba2002-03-09 14:13:11 +00001490 * @inclusive_ns_prefixes: the list of inclusive namespace prefixes
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001491 * ended with a NULL or NULL if there is no
1492 * inclusive namespaces (only for exclusive
1493 * canonicalization, ignored otherwise)
1494 * @with_comments: include comments in the result (!=0) or not (==0)
1495 * @filename: the filename to store canonical XML image
1496 * @compression: the compression level (zlib requred):
1497 * -1 - libxml default,
1498 * 0 - uncompressed,
1499 * >0 - compression level
1500 *
1501 * Dumps the canonized image of given XML document into the file.
1502 * For details see "Canonical XML" (http://www.w3.org/TR/xml-c14n) or
1503 * "Exclusive XML Canonicalization" (http://www.w3.org/TR/xml-exc-c14n)
1504 *
1505 * Returns the number of bytes written success or a negative value on fail
1506 */
1507int
Daniel Veillard9ff88172002-03-11 09:15:32 +00001508xmlC14NDocSave(xmlDocPtr doc, xmlNodeSetPtr nodes,
1509 int exclusive, xmlChar ** inclusive_ns_prefixes,
1510 int with_comments, const char *filename, int compression)
1511{
1512 xmlOutputBufferPtr buf;
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001513 int ret;
1514
Daniel Veillard9ff88172002-03-11 09:15:32 +00001515 if (filename == NULL) {
1516#ifdef DEBUG_C14N
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001517 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001518 "xmlC14NDocSave: filename is NULL\n");
1519#endif
1520 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001521 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001522#ifdef HAVE_ZLIB_H
Daniel Veillard9ff88172002-03-11 09:15:32 +00001523 if (compression < 0)
1524 compression = xmlGetCompressMode();
1525#endif
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001526
1527 /*
1528 * save the content to a temp buffer, use default UTF8 encoding.
1529 */
1530 buf = xmlOutputBufferCreateFilename(filename, NULL, compression);
1531 if (buf == NULL) {
Daniel Veillard9ff88172002-03-11 09:15:32 +00001532#ifdef DEBUG_C14N
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001533 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001534 "xmlC14NDocSave: unable to create buffer for file=\"%s\" with compressin=%d\n",
1535 filename, compression);
1536#endif
1537 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001538 }
Daniel Veillard9ff88172002-03-11 09:15:32 +00001539
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001540 /*
1541 * canonize document and write to buffer
1542 */
Daniel Veillard9ff88172002-03-11 09:15:32 +00001543 ret = xmlC14NDocSaveTo(doc, nodes, exclusive, inclusive_ns_prefixes,
1544 with_comments, buf);
1545 if (ret < 0) {
1546#ifdef DEBUG_C14N
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001547 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001548 "xmlC14NDocSave: xmlC14NDocSaveTo failed.\n");
1549#endif
1550 (void) xmlOutputBufferClose(buf);
1551 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001552 }
Daniel Veillard9ff88172002-03-11 09:15:32 +00001553
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001554 /*
1555 * get the numbers of bytes written
1556 */
1557 ret = xmlOutputBufferClose(buf);
Daniel Veillard9ff88172002-03-11 09:15:32 +00001558 return (ret);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001559}
1560
1561
1562
1563/*
1564 * Macro used to grow the current buffer.
1565 */
1566#define growBufferReentrant() { \
1567 buffer_size *= 2; \
1568 buffer = (xmlChar *) \
1569 xmlRealloc(buffer, buffer_size * sizeof(xmlChar)); \
1570 if (buffer == NULL) { \
1571 perror("realloc failed"); \
1572 return(NULL); \
1573 } \
1574}
1575
1576/**
1577 * xmlC11NNormalizeString:
1578 * @input: the input string
1579 * @mode: the normalization mode (attribute, comment, PI or text)
1580 *
1581 * Converts a string to a canonical (normalized) format. The code is stolen
1582 * from xmlEncodeEntitiesReentrant(). Added normalization of \x09, \x0a, \x0A
1583 * and the @mode parameter
1584 *
1585 * Returns a normalized string (caller is responsible for calling xmlFree())
1586 * or NULL if an error occurs
1587 */
1588static xmlChar *
Daniel Veillard9ff88172002-03-11 09:15:32 +00001589xmlC11NNormalizeString(const xmlChar * input,
1590 xmlC14NNormalizationMode mode)
1591{
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001592 const xmlChar *cur = input;
1593 xmlChar *buffer = NULL;
1594 xmlChar *out = NULL;
1595 int buffer_size = 0;
1596
Daniel Veillard9ff88172002-03-11 09:15:32 +00001597 if (input == NULL)
1598 return (NULL);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001599
1600 /*
1601 * allocate an translation buffer.
1602 */
1603 buffer_size = 1000;
1604 buffer = (xmlChar *) xmlMalloc(buffer_size * sizeof(xmlChar));
1605 if (buffer == NULL) {
Daniel Veillard9ff88172002-03-11 09:15:32 +00001606 perror("malloc failed");
1607 return (NULL);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001608 }
1609 out = buffer;
1610
1611 while (*cur != '\0') {
Daniel Veillard9ff88172002-03-11 09:15:32 +00001612 if ((out - buffer) > (buffer_size - 10)) {
1613 int indx = out - buffer;
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001614
Daniel Veillard9ff88172002-03-11 09:15:32 +00001615 growBufferReentrant();
1616 out = &buffer[indx];
1617 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001618
Daniel Veillard9ff88172002-03-11 09:15:32 +00001619 if ((*cur == '<') && ((mode == XMLC14N_NORMALIZE_ATTR) ||
1620 (mode == XMLC14N_NORMALIZE_TEXT))) {
1621 *out++ = '&';
1622 *out++ = 'l';
1623 *out++ = 't';
1624 *out++ = ';';
1625 } else if ((*cur == '>') && (mode == XMLC14N_NORMALIZE_TEXT)) {
1626 *out++ = '&';
1627 *out++ = 'g';
1628 *out++ = 't';
1629 *out++ = ';';
1630 } else if ((*cur == '&') && ((mode == XMLC14N_NORMALIZE_ATTR) ||
1631 (mode == XMLC14N_NORMALIZE_TEXT))) {
1632 *out++ = '&';
1633 *out++ = 'a';
1634 *out++ = 'm';
1635 *out++ = 'p';
1636 *out++ = ';';
1637 } else if ((*cur == '"') && (mode == XMLC14N_NORMALIZE_ATTR)) {
1638 *out++ = '&';
1639 *out++ = 'q';
1640 *out++ = 'u';
1641 *out++ = 'o';
1642 *out++ = 't';
1643 *out++ = ';';
1644 } else if ((*cur == '\x09') && (mode == XMLC14N_NORMALIZE_ATTR)) {
1645 *out++ = '&';
1646 *out++ = '#';
1647 *out++ = 'x';
1648 *out++ = '9';
1649 *out++ = ';';
1650 } else if ((*cur == '\x0A') && (mode == XMLC14N_NORMALIZE_ATTR)) {
1651 *out++ = '&';
1652 *out++ = '#';
1653 *out++ = 'x';
1654 *out++ = 'A';
1655 *out++ = ';';
1656 } else if ((*cur == '\x0D') && ((mode == XMLC14N_NORMALIZE_ATTR) ||
1657 (mode == XMLC14N_NORMALIZE_TEXT) ||
1658 (mode == XMLC14N_NORMALIZE_COMMENT) ||
1659 (mode == XMLC14N_NORMALIZE_PI))) {
1660 *out++ = '&';
1661 *out++ = '#';
1662 *out++ = 'x';
1663 *out++ = 'D';
1664 *out++ = ';';
1665 } else {
1666 /*
1667 * Works because on UTF-8, all extended sequences cannot
1668 * result in bytes in the ASCII range.
1669 */
1670 *out++ = *cur;
1671 }
1672 cur++;
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001673 }
1674 *out++ = 0;
Daniel Veillard9ff88172002-03-11 09:15:32 +00001675 return (buffer);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001676}
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001677#endif /* LIBXML_C14N_ENABLED */