blob: d8e42e013d4e458df16ea4338b61c4ecdcb07351 [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 */
44 xmlNsPtr *nsTab; /* array of nodes in no particular order */
45} xmlC14NNamespaces, *xmlC14NNamespacesPtr;
46
Daniel Veillard044fc6b2002-03-04 17:09:44 +000047typedef struct _xmlC14NCtx {
48 /* input parameters */
Daniel Veillard9ff88172002-03-11 09:15:32 +000049 xmlDocPtr doc;
50 xmlNodeSetPtr visible_nodes;
51 int with_comments;
52 xmlOutputBufferPtr buf;
Daniel Veillard044fc6b2002-03-04 17:09:44 +000053
54 /* position in the XML document */
Daniel Veillard9ff88172002-03-11 09:15:32 +000055 xmlC14NPosition pos;
56 int parent_is_doc;
Aleksey Sanindffd5c82002-05-31 04:24:13 +000057 xmlC14NNamespacesPtr ns_rendered;
Daniel Veillard044fc6b2002-03-04 17:09:44 +000058
59 /* exclusive canonicalization */
Daniel Veillard9ff88172002-03-11 09:15:32 +000060 int exclusive;
Daniel Veillard9ff88172002-03-11 09:15:32 +000061 xmlChar **inclusive_ns_prefixes;
Daniel Veillard044fc6b2002-03-04 17:09:44 +000062} xmlC14NCtx, *xmlC14NCtxPtr;
63
64
Daniel Veillard9ff88172002-03-11 09:15:32 +000065static int xmlC14NProcessNode(xmlC14NCtxPtr ctx, xmlNodePtr cur);
66static int xmlC14NProcessNodeList(xmlC14NCtxPtr ctx, xmlNodePtr cur);
Daniel Veillard044fc6b2002-03-04 17:09:44 +000067typedef enum {
Daniel Veillard9ff88172002-03-11 09:15:32 +000068 XMLC14N_NORMALIZE_ATTR = 0,
69 XMLC14N_NORMALIZE_COMMENT = 1,
70 XMLC14N_NORMALIZE_PI = 2,
71 XMLC14N_NORMALIZE_TEXT = 3
72} xmlC14NNormalizationMode;
Daniel Veillard044fc6b2002-03-04 17:09:44 +000073
Daniel Veillard9ff88172002-03-11 09:15:32 +000074static xmlChar *xmlC11NNormalizeString(const xmlChar * input,
75 xmlC14NNormalizationMode mode);
Daniel Veillard044fc6b2002-03-04 17:09:44 +000076
77#define xmlC11NNormalizeAttr( a ) \
Daniel Veillard9ff88172002-03-11 09:15:32 +000078 xmlC11NNormalizeString((a), XMLC14N_NORMALIZE_ATTR)
Daniel Veillard044fc6b2002-03-04 17:09:44 +000079#define xmlC11NNormalizeComment( a ) \
Daniel Veillard9ff88172002-03-11 09:15:32 +000080 xmlC11NNormalizeString((a), XMLC14N_NORMALIZE_COMMENT)
Daniel Veillard044fc6b2002-03-04 17:09:44 +000081#define xmlC11NNormalizePI( a ) \
Daniel Veillard9ff88172002-03-11 09:15:32 +000082 xmlC11NNormalizeString((a), XMLC14N_NORMALIZE_PI)
Daniel Veillard044fc6b2002-03-04 17:09:44 +000083#define xmlC11NNormalizeText( a ) \
Daniel Veillard9ff88172002-03-11 09:15:32 +000084 xmlC11NNormalizeString((a), XMLC14N_NORMALIZE_TEXT)
Daniel Veillard044fc6b2002-03-04 17:09:44 +000085
86/************************************************************************
87 * *
88 * The implementation internals *
89 * *
90 ************************************************************************/
Aleksey Sanindffd5c82002-05-31 04:24:13 +000091#define XML_NAMESPACES_DEFAULT 16
92
93static xmlC14NNamespacesPtr
94xmlC14NNamespacesCreate() {
95 xmlC14NNamespacesPtr ret;
96
97 ret = (xmlC14NNamespacesPtr) xmlMalloc(sizeof(xmlC14NNamespaces));
98 if (ret == NULL) {
99 xmlGenericError(xmlGenericErrorContext,
100 "xmlC14NNamespacesCreate: out of memory\n");
101 return(NULL);
102 }
103 memset(ret, 0 , (size_t) sizeof(xmlC14NNamespaces));
104 return(ret);
105}
106
107static void
108xmlC14NNamespacesDestroy(xmlC14NNamespacesPtr cur) {
109 if(cur == NULL) {
110 return;
111 }
112 if(cur->nsTab != NULL) {
113 memset(cur->nsTab, 0, cur->nsMax * sizeof(xmlNsPtr));
114 xmlFree(cur->nsTab);
115 }
116 memset(cur, 0, sizeof(xmlC14NNamespaces));
117 xmlFree(cur);
118
119}
120
121static void xmlC14NNamespacesAdd(xmlC14NNamespacesPtr cur, xmlNsPtr ns) {
122 if((cur == NULL) || (ns == NULL)) {
123 return;
124 }
125
126 if (cur->nsTab == NULL) {
127 cur->nsTab = (xmlNsPtr*) xmlMalloc(XML_NAMESPACES_DEFAULT * sizeof(xmlNsPtr));
128 if (cur->nsTab == NULL) {
129 xmlGenericError(xmlGenericErrorContext,
130 "xmlC14NNamespacesAdd: out of memory\n");
131 return;
132 }
133 memset(cur->nsTab, 0 , XML_NAMESPACES_DEFAULT * sizeof(xmlNsPtr));
134 cur->nsMax = XML_NAMESPACES_DEFAULT;
135 } else if(cur->nsMax == cur->nsNr) {
136 xmlNsPtr *tmp;
137 int tmpSize;
138
139 tmpSize = 2 * cur->nsMax;
140 tmp = (xmlNsPtr*) xmlRealloc(cur->nsTab, tmpSize * sizeof(xmlNsPtr));
141 if (tmp == NULL) {
142 xmlGenericError(xmlGenericErrorContext,
143 "xmlC14NNamespacesAdd: out of memory\n");
144 return;
145 }
146 cur->nsTab = tmp;
147 cur->nsMax = tmpSize;
148 }
149 cur->nsTab[cur->nsNr++] = ns;
150}
151
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000152
153/**
154 * xmlC14NIsVisible:
155 * @ctx: the C14N context
156 * @node: the node to check
157 *
158 * Checks whether the given node is visible. If the XML document normalization
159 * was called for the whole document then it is always "true".
160 *
161 * Returns 1 if the node is visible or 0 otherwise.
162 */
Daniel Veillard9ff88172002-03-11 09:15:32 +0000163
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000164/* todo: make it a define? */
Daniel Veillard9ff88172002-03-11 09:15:32 +0000165static int
166xmlC14NIsVisible(xmlC14NCtxPtr ctx, void *node)
167{
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000168 /*
169 * If the input is an XPath node-set, then the node-set must explicitly
170 * contain every node to be rendered to the canonical form.
171 */
Daniel Veillard9ff88172002-03-11 09:15:32 +0000172 if ((ctx->visible_nodes != NULL) &&
173 (!xmlXPathNodeSetContains(ctx->visible_nodes, (xmlNodePtr) node)))
174 {
175 return (0);
176 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000177
Daniel Veillard9ff88172002-03-11 09:15:32 +0000178 return (1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000179}
180
181/**
182 * xmlC14NIsXmlNs:
183 * @ns: the namespace to check
184 *
185 * Checks whether the given namespace is a default "xml:" namespace
186 * with href="http://www.w3.org/XML/1998/namespace"
187 *
188 * Returns 1 if the node is default or 0 otherwise
189 */
Daniel Veillard9ff88172002-03-11 09:15:32 +0000190
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000191/* todo: make it a define? */
192static int
Daniel Veillard9ff88172002-03-11 09:15:32 +0000193xmlC14NIsXmlNs(xmlNsPtr ns)
194{
195 return ((ns != NULL) &&
196 (xmlStrEqual(ns->prefix, BAD_CAST "xml")) &&
197 (xmlStrEqual(ns->href,
198 BAD_CAST
199 "http://www.w3.org/XML/1998/namespace")));
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000200}
201
202/**
203 * xmlExcC14NIsRendered:
204 * @ctx the C14N context
205 * @ns the namespace to check
206 *
207 * Checks whether the given namespace was already rendered or not
208 *
209 * Returns 1 if we already wrote this namespace or 0 otherwise
210 */
211static int
Daniel Veillard9ff88172002-03-11 09:15:32 +0000212xmlExcC14NIsRendered(xmlC14NCtxPtr ctx, xmlNsPtr ns)
213{
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000214 int i;
Daniel Veillard9ff88172002-03-11 09:15:32 +0000215
216 if ((ctx == NULL) || (ctx->ns_rendered == NULL) || (ns == NULL)) {
217 return (0);
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000218 }
219
Aleksey Sanindffd5c82002-05-31 04:24:13 +0000220 if (ctx->ns_rendered->nsTab != NULL) {
221 for (i = ctx->ns_rendered->nsNr - 1; i >= 0; --i) {
222 xmlNsPtr ns1 = (xmlNsPtr) ctx->ns_rendered->nsTab[i];
Daniel Veillard9ff88172002-03-11 09:15:32 +0000223
224 if (xmlStrEqual(ns1->prefix, ns->prefix)) {
225 return (xmlStrEqual(ns1->href, ns->href));
226 }
227 }
228 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000229 /*
230 * if the default namespace xmlns="" is not defined yet then
231 * we do not want to print it out
232 */
Daniel Veillard9ff88172002-03-11 09:15:32 +0000233 return ((xmlStrlen(ns->prefix) == 0) && (xmlStrlen(ns->href) == 0));
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000234}
235
236/**
237 * xmlC14NNamespacesCompare:
238 * @ns1: the pointer to first namespace
239 * @ns2: the pointer to second namespace
240 *
241 * Compares the namespaces by names (prefixes).
242 *
243 * Returns -1 if ns1 < ns2, 0 if ns1 == ns2 or 1 if ns1 > ns2.
244 */
Daniel Veillard9ff88172002-03-11 09:15:32 +0000245static int
246xmlC14NNamespacesCompare(xmlNsPtr ns1, xmlNsPtr ns2)
247{
248 if (ns1 == ns2)
249 return (0);
250 if (ns1 == NULL)
251 return (-1);
252 if (ns2 == NULL)
253 return (1);
254
255 return (xmlStrcmp(ns1->prefix, ns2->prefix));
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000256}
257
258
259/**
260 * xmlC14NPrintNamespaces:
261 * @ns: the pointer to namespace
262 * @ctx: the C14N context
263 *
264 * Prints the given namespace to the output buffer from C14N context.
265 *
266 * Returns 1 on success or 0 on fail.
267 */
268static int
Daniel Veillard9ff88172002-03-11 09:15:32 +0000269xmlC14NPrintNamespaces(const xmlNsPtr ns, xmlC14NCtxPtr ctx)
270{
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000271
Daniel Veillard9ff88172002-03-11 09:15:32 +0000272 if ((ns == NULL) || (ctx == NULL)) {
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000273#ifdef DEBUG_C14N
274 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +0000275 "xmlC14NPrintNamespace: namespace or context pointer is null\n");
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000276#endif
Daniel Veillard9ff88172002-03-11 09:15:32 +0000277 return 0;
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000278 }
279
Daniel Veillard9ff88172002-03-11 09:15:32 +0000280 if (ns->prefix != NULL) {
281 xmlOutputBufferWriteString(ctx->buf, " xmlns:");
282 xmlOutputBufferWriteString(ctx->buf, (const char *) ns->prefix);
283 xmlOutputBufferWriteString(ctx->buf, "=\"");
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000284 } else {
Daniel Veillard9ff88172002-03-11 09:15:32 +0000285 xmlOutputBufferWriteString(ctx->buf, " xmlns=\"");
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000286 }
287 xmlOutputBufferWriteString(ctx->buf, (const char *) ns->href);
288 xmlOutputBufferWriteString(ctx->buf, "\"");
Daniel Veillard9ff88172002-03-11 09:15:32 +0000289 return (1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000290}
291
292/**
293 * xmlC14NProcessNamespacesAxis:
294 * @ctx: the C14N context
295 * @node: the current node
296 *
297 * Prints out canonical namespace axis of the current node to the
298 * buffer from C14N context as follows
299 *
300 * Canonical XML v 1.0 (http://www.w3.org/TR/xml-c14n)
301 *
302 * Namespace Axis
303 * Consider a list L containing only namespace nodes in the
304 * axis and in the node-set in lexicographic order (ascending). To begin
305 * processing L, if the first node is not the default namespace node (a node
306 * with no namespace URI and no local name), then generate a space followed
307 * by xmlns="" if and only if the following conditions are met:
308 * - the element E that owns the axis is in the node-set
309 * - The nearest ancestor element of E in the node-set has a default
310 * namespace node in the node-set (default namespace nodes always
311 * have non-empty values in XPath)
312 * The latter condition eliminates unnecessary occurrences of xmlns="" in
313 * the canonical form since an element only receives an xmlns="" if its
314 * default namespace is empty and if it has an immediate parent in the
315 * canonical form that has a non-empty default namespace. To finish
316 * processing L, simply process every namespace node in L, except omit
317 * namespace node with local name xml, which defines the xml prefix,
318 * if its string value is http://www.w3.org/XML/1998/namespace.
319 *
320 * Exclusive XML Canonicalization v 1.0 (http://www.w3.org/TR/xml-exc-c14n)
321 * Canonical XML applied to a document subset requires the search of the
322 * ancestor nodes of each orphan element node for attributes in the xml
323 * namespace, such as xml:lang and xml:space. These are copied into the
324 * element node except if a declaration of the same attribute is already
325 * in the attribute axis of the element (whether or not it is included in
326 * the document subset). This search and copying are omitted from the
327 * Exclusive XML Canonicalization method.
328 *
329 * Returns 0 on success or -1 on fail.
330 */
331static int
Daniel Veillard9ff88172002-03-11 09:15:32 +0000332xmlC14NProcessNamespacesAxis(xmlC14NCtxPtr ctx, xmlNodePtr cur)
333{
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000334 xmlNsPtr ns;
335 xmlListPtr list;
336 xmlNodePtr visible_parent;
Daniel Veillard5c396542002-03-15 07:57:50 +0000337
Daniel Veillard9ff88172002-03-11 09:15:32 +0000338 if ((ctx == NULL) || (cur == NULL) || (cur->type != XML_ELEMENT_NODE)) {
339#ifdef DEBUG_C14N
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000340 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +0000341 "xmlC14NProcessNamespacesAxis: Null context or node pointer or type != XML_ELEMENT_NODE.\n");
342#endif
343 return (-1);
344 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000345
346 /*
347 * Create a sorted list to store element namespaces
348 */
Daniel Veillard9ff88172002-03-11 09:15:32 +0000349 list =
350 xmlListCreate(NULL, (xmlListDataCompare) xmlC14NNamespacesCompare);
351 if (list == NULL) {
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000352#ifdef DEBUG_C14N
353 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +0000354 "xmlC14NProcessNamespacesAxis: list creation failed\n");
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000355#endif
Daniel Veillard9ff88172002-03-11 09:15:32 +0000356 return (-1);
357 }
358
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000359 /* find nearest visible parent */
360 visible_parent = cur->parent;
Aleksey Sanindffd5c82002-05-31 04:24:13 +0000361 while ((visible_parent != NULL) && (!xmlC14NIsVisible(ctx, visible_parent))) {
Daniel Veillard9ff88172002-03-11 09:15:32 +0000362 visible_parent = visible_parent->parent;
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000363 }
364
Aleksey Sanindffd5c82002-05-31 04:24:13 +0000365 if(ctx->visible_nodes == NULL) {
366 xmlNodePtr node;
367
368 /*
369 * the libxml does not create nodes for all namespaces known
370 * to the node (i.e. for namespaces defined in node parents).
371 * By this we need to now walk thru all namespace in current
372 * node and all invisible ancesstors
373 */
374 node = cur;
375 while (cur != visible_parent) {
376 for (ns = cur->nsDef; ns != NULL; ns = ns->next) {
377 if(!xmlC14NIsXmlNs(ns) && !xmlExcC14NIsRendered(ctx, ns)) {
378 xmlListInsert(list, ns);
379 xmlC14NNamespacesAdd(ctx->ns_rendered, ns);
380 }
381 }
382 cur = cur->parent;
383 }
384 } else {
385 int i;
386
387 /*
388 * All visible namespace nodes are in the nodes set
389 */
390 for(i = 0; i < ctx->visible_nodes->nodeNr; i++) {
391 if(ctx->visible_nodes->nodeTab[i]->type == XML_NAMESPACE_DECL) {
392 ns = (xmlNsPtr) ctx->visible_nodes->nodeTab[i];
393
394 if((ns != NULL) && (ns->next == (xmlNsPtr)cur) &&
395 !xmlC14NIsXmlNs(ns) && !xmlExcC14NIsRendered(ctx, ns)) {
396 xmlListInsert(list, ns);
397 xmlC14NNamespacesAdd(ctx->ns_rendered, ns);
398 }
Daniel Veillard9ff88172002-03-11 09:15:32 +0000399 }
Aleksey Sanindffd5c82002-05-31 04:24:13 +0000400 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000401 }
402
403 /*
404 * print out all elements from list
405 */
Daniel Veillard9ff88172002-03-11 09:15:32 +0000406 xmlListWalk(list, (xmlListWalker) xmlC14NPrintNamespaces,
407 (const void *) ctx);
408
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000409 /*
410 * Cleanup
411 */
412 xmlListDelete(list);
Daniel Veillard9ff88172002-03-11 09:15:32 +0000413 return (0);
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000414}
415
Aleksey Sanindffd5c82002-05-31 04:24:13 +0000416
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000417/**
418 * xmlExcC14NProcessNamespacesAxis:
419 * @ctx: the C14N context
420 * @node: the current node
421 *
422 * Prints out exclusive canonical namespace axis of the current node to the
423 * buffer from C14N context as follows
424 *
425 * Exclusive XML Canonicalization
426 * http://www.w3.org/TR/xml-exc-c14n
427 *
428 * If the element node is in the XPath subset then output the node in
429 * accordance with Canonical XML except for namespace nodes which are
430 * rendered as follows:
431 *
432 * 1. Render each namespace node iff:
433 * * it is visibly utilized by the immediate parent element or one of
434 * its attributes, or is present in InclusiveNamespaces PrefixList, and
435 * * its prefix and value do not appear in ns_rendered. ns_rendered is
436 * obtained by popping the state stack in order to obtain a list of
437 * prefixes and their values which have already been rendered by
438 * an output ancestor of the namespace node's parent element.
439 * 2. Append the rendered namespace node to the list ns_rendered of namespace
440 * nodes rendered by output ancestors. Push ns_rendered on state stack and
441 * recurse.
442 * 3. After the recursion returns, pop thestate stack.
443 *
444 *
445 * Returns 0 on success or -1 on fail.
446 */
447static int
Daniel Veillard9ff88172002-03-11 09:15:32 +0000448xmlExcC14NProcessNamespacesAxis(xmlC14NCtxPtr ctx, xmlNodePtr cur)
449{
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000450 xmlListPtr list;
451 xmlAttrPtr attr;
452 xmlNsPtr ns;
Daniel Veillard9ff88172002-03-11 09:15:32 +0000453
454 if ((ctx == NULL) || (cur == NULL) || (cur->type != XML_ELEMENT_NODE)) {
455#ifdef DEBUG_C14N
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000456 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +0000457 "xmlExcC14NProcessNamespacesAxis: Null context or node pointer or type != XML_ELEMENT_NODE.\n");
458#endif
459 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000460 }
Daniel Veillard9ff88172002-03-11 09:15:32 +0000461
462 if ((!ctx->exclusive) || (ctx->ns_rendered == NULL)) {
463#ifdef DEBUG_C14N
464 xmlGenericError(xmlGenericErrorContext,
465 "xmlExcC14NProcessNamespacesAxis: called for non-exclusive canonization or rendered stack is NULL.\n");
466#endif
467 return (-1);
468
469 }
470
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000471 /*
472 * Create a sorted list to store element namespaces
473 */
Daniel Veillard9ff88172002-03-11 09:15:32 +0000474 list =
475 xmlListCreate(NULL, (xmlListDataCompare) xmlC14NNamespacesCompare);
476 if (list == NULL) {
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000477#ifdef DEBUG_C14N
478 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +0000479 "xmlExcC14NProcessNamespacesAxis: list creation failed\n");
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000480#endif
Daniel Veillard9ff88172002-03-11 09:15:32 +0000481 return (-1);
482 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000483
484 /*
485 * First of all, add all namespaces required by current node
486 * (i.e. node namespace and all attribute namespaces)
Daniel Veillard54761132002-04-18 21:00:44 +0000487 * we also need to check for default "xml:" namespace
Aleksey Sanindffd5c82002-05-31 04:24:13 +0000488 * todo: shouldn't we check for namespaces "visibility"?
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000489 */
490 ns = (cur->ns != NULL) ? cur->ns : xmlSearchNs(ctx->doc, cur, NULL);
Daniel Veillard9ff88172002-03-11 09:15:32 +0000491 if ((ns != NULL) && (!xmlC14NIsXmlNs(ns)) &&
Daniel Veillard54761132002-04-18 21:00:44 +0000492 (xmlListSearch(list, ns) == NULL) && !xmlExcC14NIsRendered(ctx, ns)) {
Daniel Veillard9ff88172002-03-11 09:15:32 +0000493 xmlListInsert(list, ns);
Aleksey Sanindffd5c82002-05-31 04:24:13 +0000494 xmlC14NNamespacesAdd(ctx->ns_rendered, ns);
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000495 }
496 attr = cur->properties;
Daniel Veillard9ff88172002-03-11 09:15:32 +0000497 while (attr != NULL) {
498 /*
Daniel Veillard2d347fa2002-03-17 10:34:11 +0000499 * we need to check that attribute is visible and has non
500 * default namespace (XML Namespaces: "default namespaces
501 * do not apply directly to attributes")
Daniel Veillard9ff88172002-03-11 09:15:32 +0000502 */
Daniel Veillard2d347fa2002-03-17 10:34:11 +0000503 if ((attr->ns != NULL) && xmlC14NIsVisible(ctx, attr) &&
Daniel Veillard54761132002-04-18 21:00:44 +0000504 (!xmlC14NIsXmlNs(attr->ns)) &&
505 (xmlListSearch(list, attr->ns) == NULL) && (!xmlExcC14NIsRendered(ctx, attr->ns))) {
506 xmlListInsert(list, attr->ns);
Aleksey Sanindffd5c82002-05-31 04:24:13 +0000507 xmlC14NNamespacesAdd(ctx->ns_rendered, attr->ns);
Daniel Veillard9ff88172002-03-11 09:15:32 +0000508 }
509 attr = attr->next;
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000510 }
Daniel Veillard9ff88172002-03-11 09:15:32 +0000511
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000512 /*
513 * Next add all inclusive namespaces if needed.
514 */
Daniel Veillard9ff88172002-03-11 09:15:32 +0000515 if (ctx->inclusive_ns_prefixes != NULL) {
516 int i;
517 xmlChar *prefix;
518
519 for (i = 0; ctx->inclusive_ns_prefixes[i] != NULL; ++i) {
520 prefix = ctx->inclusive_ns_prefixes[i];
521 /*
522 * Special values for namespace with empty prefix
523 */
524 if (xmlStrEqual(prefix, BAD_CAST "#default")
525 || xmlStrEqual(prefix, BAD_CAST "")) {
526 prefix = NULL;
527 }
528 ns = xmlSearchNs(ctx->doc, cur, prefix);
529 if ((ns != NULL) && (!xmlC14NIsXmlNs(ns))) {
530 if (xmlListSearch(list, ns) == NULL &&
531 !xmlExcC14NIsRendered(ctx, ns)) {
532 xmlListInsert(list, ns);
Aleksey Sanindffd5c82002-05-31 04:24:13 +0000533 xmlC14NNamespacesAdd(ctx->ns_rendered, ns);
Daniel Veillard9ff88172002-03-11 09:15:32 +0000534 }
535 }
536 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000537 }
Daniel Veillard9ff88172002-03-11 09:15:32 +0000538
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000539 /*
540 * print out all elements from list
541 */
Daniel Veillard9ff88172002-03-11 09:15:32 +0000542 xmlListWalk(list, (xmlListWalker) xmlC14NPrintNamespaces,
543 (const void *) ctx);
544
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000545 /*
546 * Cleanup
547 */
548 xmlListDelete(list);
Daniel Veillard9ff88172002-03-11 09:15:32 +0000549 return (0);
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000550}
551
552
553/**
554 * xmlC14NAttrsCompare:
555 * @attr1: the pointer to first attr
556 * @attr2: the pointer to second attr
557 *
558 * Prints the given attribute to the output buffer from C14N context.
559 *
560 * Returns -1 if attr1 < attr2, 0 if attr1 == attr2 or 1 if attr1 > attr2.
561 */
562static int
Daniel Veillard9ff88172002-03-11 09:15:32 +0000563xmlC14NAttrsCompare(xmlAttrPtr attr1, xmlAttrPtr attr2)
564{
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000565 int ret = 0;
566
567 /*
568 * Simple cases
569 */
Daniel Veillard9ff88172002-03-11 09:15:32 +0000570 if (attr1 == attr2)
571 return (0);
572 if (attr1 == NULL)
573 return (-1);
574 if (attr2 == NULL)
575 return (1);
576 if (attr1->ns == attr2->ns) {
577 return (xmlStrcmp(attr1->name, attr2->name));
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000578 }
579
580 /*
581 * Attributes in the default namespace are first
582 * because the default namespace is not applied to
583 * unqualified attributes
584 */
Daniel Veillard9ff88172002-03-11 09:15:32 +0000585 if (attr1->ns == NULL)
586 return (-1);
587 if (attr2->ns == NULL)
588 return (1);
589 if (attr1->ns->prefix == NULL)
590 return (-1);
591 if (attr2->ns->prefix == NULL)
592 return (1);
593
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000594 ret = xmlStrcmp(attr1->ns->href, attr2->ns->href);
Daniel Veillard9ff88172002-03-11 09:15:32 +0000595 if (ret == 0) {
596 ret = xmlStrcmp(attr1->name, attr2->name);
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000597 }
Daniel Veillard9ff88172002-03-11 09:15:32 +0000598 return (ret);
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000599}
600
601
602/**
603 * xmlC14NPrintAttrs:
604 * @attr: the pointer to attr
605 * @ctx: the C14N context
606 *
607 * Prints out canonical attribute urrent node to the
608 * buffer from C14N context as follows
609 *
610 * Canonical XML v 1.0 (http://www.w3.org/TR/xml-c14n)
611 *
612 * Returns 1 on success or 0 on fail.
613 */
614static int
Daniel Veillard9ff88172002-03-11 09:15:32 +0000615xmlC14NPrintAttrs(const xmlAttrPtr attr, xmlC14NCtxPtr ctx)
616{
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000617 xmlChar *value;
618 xmlChar *buffer;
619
Daniel Veillard9ff88172002-03-11 09:15:32 +0000620 if ((attr == NULL) || (ctx == NULL)) {
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000621#ifdef DEBUG_C14N
622 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +0000623 "xmlC14NPrintAttrs: attr == NULL or ctx == NULL\n");
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000624#endif
Daniel Veillard9ff88172002-03-11 09:15:32 +0000625 return (0);
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000626 }
627
628 xmlOutputBufferWriteString(ctx->buf, " ");
Daniel Veillard9ff88172002-03-11 09:15:32 +0000629 if (attr->ns != NULL && xmlStrlen(attr->ns->prefix) > 0) {
630 xmlOutputBufferWriteString(ctx->buf,
631 (const char *) attr->ns->prefix);
632 xmlOutputBufferWriteString(ctx->buf, ":");
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000633 }
634 xmlOutputBufferWriteString(ctx->buf, (const char *) attr->name);
635 xmlOutputBufferWriteString(ctx->buf, "=\"");
636
637 value = xmlNodeListGetString(attr->doc, attr->children, 1);
638 /* todo: should we log an error if value==NULL ? */
Daniel Veillard9ff88172002-03-11 09:15:32 +0000639 if (value != NULL) {
640 buffer = xmlC11NNormalizeAttr(value);
641 xmlFree(value);
642 if (buffer != NULL) {
643 xmlOutputBufferWriteString(ctx->buf, (const char *) buffer);
644 xmlFree(buffer);
645 } else {
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000646#ifdef DEBUG_C14N
Daniel Veillard9ff88172002-03-11 09:15:32 +0000647 xmlGenericError(xmlGenericErrorContext,
648 "xmlC14NPrintAttrs: xmlC11NNormalizeAttr failed\n");
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000649#endif
Daniel Veillard9ff88172002-03-11 09:15:32 +0000650 return (0);
651 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000652 }
653 xmlOutputBufferWriteString(ctx->buf, "\"");
Daniel Veillard9ff88172002-03-11 09:15:32 +0000654 return (1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000655}
656
657/**
658 * xmlC14NProcessAttrsAxis:
659 * @ctx: the C14N context
660 * @cur: the current node
661 *
662 * Prints out canonical attribute axis of the current node to the
663 * buffer from C14N context as follows
664 *
665 * Canonical XML v 1.0 (http://www.w3.org/TR/xml-c14n)
666 *
667 * Attribute Axis
668 * In lexicographic order (ascending), process each node that
669 * is in the element's attribute axis and in the node-set.
670 *
671 * The processing of an element node E MUST be modified slightly
672 * when an XPath node-set is given as input and the element's
673 * parent is omitted from the node-set.
674 *
675 *
676 * Exclusive XML Canonicalization v 1.0 (http://www.w3.org/TR/xml-exc-c14n)
677 *
678 * Canonical XML applied to a document subset requires the search of the
679 * ancestor nodes of each orphan element node for attributes in the xml
680 * namespace, such as xml:lang and xml:space. These are copied into the
681 * element node except if a declaration of the same attribute is already
682 * in the attribute axis of the element (whether or not it is included in
683 * the document subset). This search and copying are omitted from the
684 * Exclusive XML Canonicalization method.
685 *
686 * Returns 0 on success or -1 on fail.
687 */
688static int
Daniel Veillard9ff88172002-03-11 09:15:32 +0000689xmlC14NProcessAttrsAxis(xmlC14NCtxPtr ctx, xmlNodePtr cur)
690{
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000691 xmlAttrPtr attr;
692 xmlListPtr list;
Daniel Veillard9ff88172002-03-11 09:15:32 +0000693
694 if ((ctx == NULL) || (cur == NULL) || (cur->type != XML_ELEMENT_NODE)) {
695#ifdef DEBUG_C14N
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000696 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +0000697 "xmlC14NProcessAttrsAxis: Null context or node pointer or type != XML_ELEMENT_NODE.\n");
698#endif
699 return (-1);
700 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000701
702 /*
703 * Create a sorted list to store element attributes
704 */
Daniel Veillard9ff88172002-03-11 09:15:32 +0000705 list = xmlListCreate(NULL, (xmlListDataCompare) xmlC14NAttrsCompare);
706 if (list == NULL) {
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000707#ifdef DEBUG_C14N
708 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +0000709 "xmlC14NProcessAttrsAxis: list creation failed\n");
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000710#endif
Daniel Veillard9ff88172002-03-11 09:15:32 +0000711 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000712 }
713
714 /*
715 * Add all visible attributes from current node.
716 */
717 attr = cur->properties;
Daniel Veillard9ff88172002-03-11 09:15:32 +0000718 while (attr != NULL) {
719 /* check that attribute is visible */
720 if (xmlC14NIsVisible(ctx, attr)) {
721 xmlListInsert(list, attr);
722 }
723 attr = attr->next;
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000724 }
Daniel Veillard9ff88172002-03-11 09:15:32 +0000725
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000726 /*
727 * include attributes in "xml" namespace defined in ancestors
728 * (only for non-exclusive XML Canonicalization)
729 */
Daniel Veillard9ff88172002-03-11 09:15:32 +0000730 if ((!ctx->exclusive) && (cur->parent != NULL)
731 && (!xmlC14NIsVisible(ctx, cur->parent))) {
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000732 /*
Daniel Veillard9ff88172002-03-11 09:15:32 +0000733 * If XPath node-set is not specified then the parent is always
734 * visible!
735 */
736 cur = cur->parent;
737 while (cur != NULL) {
738 attr = cur->properties;
739 while (attr != NULL) {
740 if ((attr->ns != NULL)
741 && (xmlStrEqual(attr->ns->prefix, BAD_CAST "xml"))) {
742 if (xmlListSearch(list, attr) == NULL) {
743 xmlListInsert(list, attr);
744 }
745 }
746 attr = attr->next;
747 }
748 cur = cur->parent;
749 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000750 }
Daniel Veillard9ff88172002-03-11 09:15:32 +0000751
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000752 /*
753 * print out all elements from list
754 */
Daniel Veillard9ff88172002-03-11 09:15:32 +0000755 xmlListWalk(list, (xmlListWalker) xmlC14NPrintAttrs,
756 (const void *) ctx);
757
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000758 /*
759 * Cleanup
760 */
Daniel Veillard9ff88172002-03-11 09:15:32 +0000761 xmlListDelete(list);
762 return (0);
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000763}
764
765/**
766 * xmlC14NCheckForRelativeNamespaces:
767 * @ctx: the C14N context
768 * @cur: the current element node
769 *
770 * Checks that current element node has no relative namespaces defined
771 *
772 * Returns 0 if the node has no relative namespaces or -1 otherwise.
773 */
774static int
Daniel Veillard9ff88172002-03-11 09:15:32 +0000775xmlC14NCheckForRelativeNamespaces(xmlC14NCtxPtr ctx, xmlNodePtr cur)
776{
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000777 xmlNsPtr ns;
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000778
Daniel Veillard9ff88172002-03-11 09:15:32 +0000779 if ((ctx == NULL) || (cur == NULL) || (cur->type != XML_ELEMENT_NODE)) {
780#ifdef DEBUG_C14N
781 xmlGenericError(xmlGenericErrorContext,
782 "xmlC14NCheckForRelativeNamespaces: Null context or node pointer or type != XML_ELEMENT_NODE.\n");
783#endif
784 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000785 }
Daniel Veillard9ff88172002-03-11 09:15:32 +0000786
787 ns = cur->nsDef;
788 while (ns != NULL) {
789 if (xmlStrlen(ns->href) > 0) {
790 xmlURIPtr uri;
791
792 uri = xmlParseURI((const char *) ns->href);
793 if (uri == NULL) {
794#ifdef DEBUG_C14N
795 xmlGenericError(xmlGenericErrorContext,
796 "xmlC14NCheckForRelativeNamespaces: unable to parse uri=\"%s\".\n",
797 ns->href);
798#endif
799 return (-1);
800 }
801 if (xmlStrlen((const xmlChar *) uri->scheme) == 0) {
802 xmlFreeURI(uri);
803 return (-1);
804 }
805 if ((!xmlStrEqual
806 ((const xmlChar *) uri->scheme, BAD_CAST "urn"))
807 && (xmlStrlen((const xmlChar *) uri->server) == 0)) {
808 xmlFreeURI(uri);
809 return (-1);
810 }
811 xmlFreeURI(uri);
812 }
813 ns = ns->next;
814 }
815 return (0);
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000816}
817
818/**
819 * xmlC14NProcessElementNode:
820 * @ctx: the pointer to C14N context object
821 * @cur: the node to process
822 *
823 * Canonical XML v 1.0 (http://www.w3.org/TR/xml-c14n)
824 *
825 * Element Nodes
826 * If the element is not in the node-set, then the result is obtained
827 * by processing the namespace axis, then the attribute axis, then
828 * processing the child nodes of the element that are in the node-set
829 * (in document order). If the element is in the node-set, then the result
830 * is an open angle bracket (<), the element QName, the result of
831 * processing the namespace axis, the result of processing the attribute
832 * axis, a close angle bracket (>), the result of processing the child
833 * nodes of the element that are in the node-set (in document order), an
834 * open angle bracket, a forward slash (/), the element QName, and a close
835 * angle bracket.
836 *
837 * Returns non-negative value on success or negative value on fail
838 */
839static int
Daniel Veillard9ff88172002-03-11 09:15:32 +0000840xmlC14NProcessElementNode(xmlC14NCtxPtr ctx, xmlNodePtr cur, int visible)
841{
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000842 int ret;
843 int ns_rendered_pos = 0;
Daniel Veillard6f293b12002-03-15 09:42:33 +0000844 int parent_is_doc = 0;
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000845
Daniel Veillard9ff88172002-03-11 09:15:32 +0000846 if ((ctx == NULL) || (cur == NULL) || (cur->type != XML_ELEMENT_NODE)) {
847#ifdef DEBUG_C14N
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000848 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +0000849 "xmlC14NProcessElementNode: Null context or node pointer or type != XML_ELEMENT_NODE.\n");
850#endif
851 return (-1);
852 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000853
854 /*
855 * Check relative relative namespaces:
856 * implementations of XML canonicalization MUST report an operation
857 * failure on documents containing relative namespace URIs.
858 */
Daniel Veillard9ff88172002-03-11 09:15:32 +0000859 if (xmlC14NCheckForRelativeNamespaces(ctx, cur) < 0) {
860#ifdef DEBUG_C14N
861 xmlGenericError(xmlGenericErrorContext,
862 "xmlC14NProcessElementNode: xmlC14NCheckForRelativeNamespaces failed.\n");
863#endif
864 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000865 }
866
867
868 /*
869 * Save ns_rendered stack position for exclusive
870 * processing
871 */
Aleksey Sanindffd5c82002-05-31 04:24:13 +0000872 if (ctx->ns_rendered != NULL) {
873 ns_rendered_pos = ctx->ns_rendered->nsNr;
Daniel Veillard9ff88172002-03-11 09:15:32 +0000874 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000875
Daniel Veillard6f293b12002-03-15 09:42:33 +0000876 if (visible) {
Daniel Veillard9ff88172002-03-11 09:15:32 +0000877 if (ctx->parent_is_doc) {
Daniel Veillard6f293b12002-03-15 09:42:33 +0000878 /* save this flag into the stack */
879 parent_is_doc = ctx->parent_is_doc;
880 ctx->parent_is_doc = 0;
Daniel Veillard9ff88172002-03-11 09:15:32 +0000881 ctx->pos = XMLC14N_INSIDE_DOCUMENT_ELEMENT;
882 }
883 xmlOutputBufferWriteString(ctx->buf, "<");
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000884
Daniel Veillard9ff88172002-03-11 09:15:32 +0000885 if ((cur->ns != NULL) && (xmlStrlen(cur->ns->prefix) > 0)) {
886 xmlOutputBufferWriteString(ctx->buf,
887 (const char *) cur->ns->prefix);
888 xmlOutputBufferWriteString(ctx->buf, ":");
889 }
890 xmlOutputBufferWriteString(ctx->buf, (const char *) cur->name);
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000891
Daniel Veillard9ff88172002-03-11 09:15:32 +0000892 if (ctx->exclusive) {
893 ret = xmlExcC14NProcessNamespacesAxis(ctx, cur);
894 } else {
895 ret = xmlC14NProcessNamespacesAxis(ctx, cur);
896 }
897 if (ret < 0) {
898#ifdef DEBUG_C14N
899 xmlGenericError(xmlGenericErrorContext,
900 "xmlC14NProcessElementNode: xmlC14NProcessNamespacesAxis failed.\n");
901#endif
902 return (-1);
903 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000904
Daniel Veillard9ff88172002-03-11 09:15:32 +0000905 ret = xmlC14NProcessAttrsAxis(ctx, cur);
906 if (ret < 0) {
907#ifdef DEBUG_C14N
908 xmlGenericError(xmlGenericErrorContext,
909 "xmlC14NProcessElementNode: xmlC14NProcessAttrsAxis failed.\n");
910#endif
911 return (-1);
912 }
913
914 xmlOutputBufferWriteString(ctx->buf, ">");
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000915 }
916 if (cur->children != NULL) {
Daniel Veillard9ff88172002-03-11 09:15:32 +0000917 ret = xmlC14NProcessNodeList(ctx, cur->children);
918 if (ret < 0) {
919#ifdef DEBUG_C14N
920 xmlGenericError(xmlGenericErrorContext,
921 "xmlC14NProcessElementNode: xmlC14NProcessNodeList failed.\n");
922#endif
923 return (-1);
924 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000925 }
Daniel Veillard9ff88172002-03-11 09:15:32 +0000926 if (visible) {
927 xmlOutputBufferWriteString(ctx->buf, "</");
928 if ((cur->ns != NULL) && (xmlStrlen(cur->ns->prefix) > 0)) {
929 xmlOutputBufferWriteString(ctx->buf,
930 (const char *) cur->ns->prefix);
931 xmlOutputBufferWriteString(ctx->buf, ":");
932 }
933 xmlOutputBufferWriteString(ctx->buf, (const char *) cur->name);
934 xmlOutputBufferWriteString(ctx->buf, ">");
Daniel Veillard6f293b12002-03-15 09:42:33 +0000935 if (parent_is_doc) {
936 /* restore this flag from the stack for next node */
937 ctx->parent_is_doc = parent_is_doc;
938 ctx->pos = XMLC14N_AFTER_DOCUMENT_ELEMENT;
Daniel Veillard9ff88172002-03-11 09:15:32 +0000939 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000940 }
941
942 /*
943 * Restore ns_rendered stack position for exclusive
944 * processing
945 */
Aleksey Sanindffd5c82002-05-31 04:24:13 +0000946 if (ctx->ns_rendered != NULL) {
947 ctx->ns_rendered->nsNr = ns_rendered_pos;
Daniel Veillard9ff88172002-03-11 09:15:32 +0000948 }
949 return (0);
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000950}
951
952/**
953 * xmlC14NProcessNode:
954 * @ctx: the pointer to C14N context object
955 * @cur: the node to process
956 *
957 * Processes the given node
958 *
959 * Returns non-negative value on success or negative value on fail
960 */
961static int
Daniel Veillard9ff88172002-03-11 09:15:32 +0000962xmlC14NProcessNode(xmlC14NCtxPtr ctx, xmlNodePtr cur)
963{
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000964 int ret = 0;
965 int visible;
Daniel Veillard9ff88172002-03-11 09:15:32 +0000966
967 if ((ctx == NULL) || (cur == NULL)) {
968#ifdef DEBUG_C14N
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000969 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +0000970 "xmlC14NProcessNode: Null context or node pointer.\n");
971#endif
972 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000973 }
974
975 visible = xmlC14NIsVisible(ctx, cur);
Daniel Veillard9ff88172002-03-11 09:15:32 +0000976 switch (cur->type) {
977 case XML_ELEMENT_NODE:
978 ret = xmlC14NProcessElementNode(ctx, cur, visible);
979 break;
980 case XML_CDATA_SECTION_NODE:
981 case XML_TEXT_NODE:
982 /*
983 * Text Nodes
984 * the string value, except all ampersands are replaced
985 * by &amp;, all open angle brackets (<) are replaced by &lt;, all closing
986 * angle brackets (>) are replaced by &gt;, and all #xD characters are
987 * replaced by &#xD;.
988 */
989 /* cdata sections are processed as text nodes */
990 /* todo: verify that cdata sections are included in XPath nodes set */
991 if ((visible) && (cur->content != NULL)) {
992 xmlChar *buffer;
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000993
Daniel Veillard9ff88172002-03-11 09:15:32 +0000994 buffer = xmlC11NNormalizeText(cur->content);
995 if (buffer != NULL) {
996 xmlOutputBufferWriteString(ctx->buf,
997 (const char *) buffer);
998 xmlFree(buffer);
999 } else {
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001000#ifdef DEBUG_C14N
Daniel Veillard9ff88172002-03-11 09:15:32 +00001001 xmlGenericError(xmlGenericErrorContext,
1002 "xmlC14NProcessNode: xmlC11NNormalizeText() failed\n");
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001003#endif
Daniel Veillard9ff88172002-03-11 09:15:32 +00001004 return (-1);
1005 }
1006 }
1007 break;
1008 case XML_PI_NODE:
1009 /*
1010 * Processing Instruction (PI) Nodes-
1011 * The opening PI symbol (<?), the PI target name of the node,
1012 * a leading space and the string value if it is not empty, and
1013 * the closing PI symbol (?>). If the string value is empty,
1014 * then the leading space is not added. Also, a trailing #xA is
1015 * rendered after the closing PI symbol for PI children of the
1016 * root node with a lesser document order than the document
1017 * element, and a leading #xA is rendered before the opening PI
1018 * symbol of PI children of the root node with a greater document
1019 * order than the document element.
1020 */
1021 if (visible) {
1022 if (ctx->pos == XMLC14N_AFTER_DOCUMENT_ELEMENT) {
1023 xmlOutputBufferWriteString(ctx->buf, "\x0A<?");
1024 } else {
1025 xmlOutputBufferWriteString(ctx->buf, "<?");
1026 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001027
Daniel Veillard9ff88172002-03-11 09:15:32 +00001028 xmlOutputBufferWriteString(ctx->buf,
1029 (const char *) cur->name);
1030 if ((cur->content != NULL) && (*(cur->content) != '\0')) {
1031 xmlChar *buffer;
1032
1033 xmlOutputBufferWriteString(ctx->buf, " ");
1034
1035 /* todo: do we need to normalize pi? */
1036 buffer = xmlC11NNormalizePI(cur->content);
1037 if (buffer != NULL) {
1038 xmlOutputBufferWriteString(ctx->buf,
1039 (const char *) buffer);
1040 xmlFree(buffer);
1041 } else {
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001042#ifdef DEBUG_C14N
Daniel Veillard9ff88172002-03-11 09:15:32 +00001043 xmlGenericError(xmlGenericErrorContext,
1044 "xmlC14NProcessNode: xmlC11NNormalizePI() failed\n");
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001045#endif
Daniel Veillard9ff88172002-03-11 09:15:32 +00001046 return (-1);
1047 }
1048 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001049
Daniel Veillard9ff88172002-03-11 09:15:32 +00001050 if (ctx->pos == XMLC14N_BEFORE_DOCUMENT_ELEMENT) {
1051 xmlOutputBufferWriteString(ctx->buf, "?>\x0A");
1052 } else {
1053 xmlOutputBufferWriteString(ctx->buf, "?>");
1054 }
1055 }
1056 break;
1057 case XML_COMMENT_NODE:
1058 /*
1059 * Comment Nodes
1060 * Nothing if generating canonical XML without comments. For
1061 * canonical XML with comments, generate the opening comment
1062 * symbol (<!--), the string value of the node, and the
1063 * closing comment symbol (-->). Also, a trailing #xA is rendered
1064 * after the closing comment symbol for comment children of the
1065 * root node with a lesser document order than the document
1066 * element, and a leading #xA is rendered before the opening
1067 * comment symbol of comment children of the root node with a
1068 * greater document order than the document element. (Comment
1069 * children of the root node represent comments outside of the
1070 * top-level document element and outside of the document type
1071 * declaration).
1072 */
1073 if (visible && ctx->with_comments) {
1074 if (ctx->pos == XMLC14N_AFTER_DOCUMENT_ELEMENT) {
1075 xmlOutputBufferWriteString(ctx->buf, "\x0A<!--");
1076 } else {
1077 xmlOutputBufferWriteString(ctx->buf, "<!--");
1078 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001079
Daniel Veillard9ff88172002-03-11 09:15:32 +00001080 if (cur->content != NULL) {
1081 xmlChar *buffer;
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001082
Daniel Veillard9ff88172002-03-11 09:15:32 +00001083 /* todo: do we need to normalize comment? */
1084 buffer = xmlC11NNormalizeComment(cur->content);
1085 if (buffer != NULL) {
1086 xmlOutputBufferWriteString(ctx->buf,
1087 (const char *) buffer);
1088 xmlFree(buffer);
1089 } else {
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001090#ifdef DEBUG_C14N
Daniel Veillard9ff88172002-03-11 09:15:32 +00001091 xmlGenericError(xmlGenericErrorContext,
1092 "xmlC14NProcessNode: xmlC11NNormalizeComment() failed\n");
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001093#endif
Daniel Veillard9ff88172002-03-11 09:15:32 +00001094 return (-1);
1095 }
1096 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001097
Daniel Veillard9ff88172002-03-11 09:15:32 +00001098 if (ctx->pos == XMLC14N_BEFORE_DOCUMENT_ELEMENT) {
1099 xmlOutputBufferWriteString(ctx->buf, "-->\x0A");
1100 } else {
1101 xmlOutputBufferWriteString(ctx->buf, "-->");
1102 }
1103 }
1104 break;
1105 case XML_DOCUMENT_NODE:
1106 case XML_DOCUMENT_FRAG_NODE: /* should be processed as document? */
Daniel Veillard1840ef02002-03-21 08:05:23 +00001107#ifdef LIBXML_DOCB_ENABLED
Daniel Veillard9ff88172002-03-11 09:15:32 +00001108 case XML_DOCB_DOCUMENT_NODE: /* should be processed as document? */
Daniel Veillard1840ef02002-03-21 08:05:23 +00001109#endif
1110#ifdef LIBXML_HTML_ENABLED
Daniel Veillard9ff88172002-03-11 09:15:32 +00001111 case XML_HTML_DOCUMENT_NODE: /* should be processed as document? */
Daniel Veillard1840ef02002-03-21 08:05:23 +00001112#endif
Daniel Veillard9ff88172002-03-11 09:15:32 +00001113 if (cur->children != NULL) {
1114 ctx->pos = XMLC14N_BEFORE_DOCUMENT_ELEMENT;
1115 ctx->parent_is_doc = 1;
1116 ret = xmlC14NProcessNodeList(ctx, cur->children);
1117 }
1118 break;
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001119
Daniel Veillard9ff88172002-03-11 09:15:32 +00001120 case XML_ATTRIBUTE_NODE:
1121 xmlGenericError(xmlGenericErrorContext,
1122 "xmlC14NProcessNode: XML_ATTRIBUTE_NODE is illegal here\n");
1123 return (-1);
1124 case XML_NAMESPACE_DECL:
1125 xmlGenericError(xmlGenericErrorContext,
1126 "xmlC14NProcessNode: XML_NAMESPACE_DECL is illegal here\n");
1127 return (-1);
1128 case XML_ENTITY_REF_NODE:
1129 xmlGenericError(xmlGenericErrorContext,
1130 "xmlC14NProcessNode: XML_ENTITY_REF_NODE is illegal here\n");
1131 return (-1);
1132 case XML_ENTITY_NODE:
1133 xmlGenericError(xmlGenericErrorContext,
1134 "xmlC14NProcessNode: XML_ENTITY_NODE is illegal here\n");
1135 return (-1);
1136
1137 case XML_DOCUMENT_TYPE_NODE:
1138 case XML_NOTATION_NODE:
1139 case XML_DTD_NODE:
1140 case XML_ELEMENT_DECL:
1141 case XML_ATTRIBUTE_DECL:
1142 case XML_ENTITY_DECL:
Daniel Veillard1840ef02002-03-21 08:05:23 +00001143#ifdef LIBXML_XINCLUDE_ENABLED
Daniel Veillard9ff88172002-03-11 09:15:32 +00001144 case XML_XINCLUDE_START:
1145 case XML_XINCLUDE_END:
Daniel Veillard1840ef02002-03-21 08:05:23 +00001146#endif
Daniel Veillard9ff88172002-03-11 09:15:32 +00001147 /*
1148 * should be ignored according to "W3C Canonical XML"
1149 */
1150 break;
1151 default:
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001152#ifdef DEBUG_C14N
Daniel Veillard9ff88172002-03-11 09:15:32 +00001153 xmlGenericError(xmlGenericErrorContext,
1154 "xmlC14NProcessNode: unknown node type = %d\n",
1155 cur->type);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001156#endif
Daniel Veillard9ff88172002-03-11 09:15:32 +00001157 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001158 }
1159
Daniel Veillard9ff88172002-03-11 09:15:32 +00001160 return (ret);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001161}
1162
1163/**
1164 * xmlC14NProcessNodeList:
1165 * @ctx: the pointer to C14N context object
1166 * @cur: the node to start from
1167 *
1168 * Processes all nodes in the row starting from cur.
1169 *
1170 * Returns non-negative value on success or negative value on fail
1171 */
1172static int
Daniel Veillard9ff88172002-03-11 09:15:32 +00001173xmlC14NProcessNodeList(xmlC14NCtxPtr ctx, xmlNodePtr cur)
1174{
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001175 int ret;
Daniel Veillard9ff88172002-03-11 09:15:32 +00001176
1177 if (ctx == NULL) {
1178#ifdef DEBUG_C14N
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001179 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001180 "xmlC14NProcessNodeList: Null context pointer.\n");
1181#endif
1182 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001183 }
1184
Daniel Veillard9ff88172002-03-11 09:15:32 +00001185 for (ret = 0; cur != NULL && ret >= 0; cur = cur->next) {
1186 ret = xmlC14NProcessNode(ctx, cur);
1187 }
1188 return (ret);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001189}
1190
1191
1192/**
1193 * xmlC14NFreeCtx:
1194 * @ctx: the pointer to C14N context object
1195 *
1196 * Cleanups the C14N context object.
1197 */
1198
1199static void
Daniel Veillard9ff88172002-03-11 09:15:32 +00001200xmlC14NFreeCtx(xmlC14NCtxPtr ctx)
1201{
1202 if (ctx == NULL) {
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001203#ifdef DEBUG_C14N
1204 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001205 "xmlC14NFreeCtx: ctx == NULL\n");
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001206#endif
Daniel Veillard9ff88172002-03-11 09:15:32 +00001207 return;
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001208 }
Daniel Veillard9ff88172002-03-11 09:15:32 +00001209
1210 if (ctx->ns_rendered != NULL) {
Aleksey Sanindffd5c82002-05-31 04:24:13 +00001211 xmlC14NNamespacesDestroy(ctx->ns_rendered);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001212 }
1213 xmlFree(ctx);
1214}
1215
1216/**
1217 * xmlC14NNewCtx:
1218 * @doc: the XML document for canonization
1219 * @nodes: the nodes set to be included in the canonized image
1220 * or NULL if all document nodes should be included
1221 * @exclusive: the exclusive flag (0 - non-exclusive canonicalization;
1222 * otherwise - exclusive canonicalization)
1223 * @inclusive_ns_prefixe the list of inclusive namespace prefixes
1224 * ended with a NULL or NULL if there is no
1225 * inclusive namespaces (only for exclusive
1226 * canonicalization)
1227 * @with_comments: include comments in the result (!=0) or not (==0)
1228 * @buf: the output buffer to store canonical XML; this
1229 * buffer MUST have encoder==NULL because C14N requires
1230 * UTF-8 output
1231 *
1232 * Creates new C14N context object to store C14N parameters.
1233 *
1234 * Returns pointer to newly created object (success) or NULL (fail)
1235 */
1236static xmlC14NCtxPtr
Daniel Veillard9ff88172002-03-11 09:15:32 +00001237xmlC14NNewCtx(xmlDocPtr doc, xmlNodeSetPtr nodes,
1238 int exclusive, xmlChar ** inclusive_ns_prefixes,
1239 int with_comments, xmlOutputBufferPtr buf)
1240{
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001241 xmlC14NCtxPtr ctx;
1242
Daniel Veillard9ff88172002-03-11 09:15:32 +00001243 if ((doc == NULL) || (buf == NULL)) {
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001244#ifdef DEBUG_C14N
1245 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001246 "xmlC14NNewCtx: pointer to document or output buffer is NULL\n");
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001247#endif
Daniel Veillard9ff88172002-03-11 09:15:32 +00001248 return (NULL);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001249 }
1250
1251 /*
1252 * Validate the encoding output buffer encoding
1253 */
Daniel Veillard9ff88172002-03-11 09:15:32 +00001254 if (buf->encoder != NULL) {
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001255 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001256 "xmlC14NNewCtx: output buffer encoder != NULL but C14N requires UTF8 output\n");
1257 return (NULL);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001258 }
1259
1260 /*
1261 * Validate the XML document encoding value, if provided.
1262 */
Daniel Veillard9ff88172002-03-11 09:15:32 +00001263 if (doc->charset != XML_CHAR_ENCODING_UTF8) {
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001264 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001265 "xmlC14NNewCtx: source document not in UTF8\n");
1266 return (NULL);
1267 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001268
1269 /*
1270 * Allocate a new xmlC14NCtxPtr and fill the fields.
1271 */
1272 ctx = (xmlC14NCtxPtr) xmlMalloc(sizeof(xmlC14NCtx));
1273 if (ctx == NULL) {
1274 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001275 "xmlC14NNewCtx: malloc failed\n");
1276 return (NULL);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001277 }
1278 memset(ctx, 0, sizeof(xmlC14NCtx));
1279
1280 /*
1281 * initialize C14N context
Daniel Veillard9ff88172002-03-11 09:15:32 +00001282 */
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001283 ctx->doc = doc;
1284 ctx->with_comments = with_comments;
1285 ctx->visible_nodes = nodes;
1286 ctx->buf = buf;
1287 ctx->parent_is_doc = 1;
1288 ctx->pos = XMLC14N_BEFORE_DOCUMENT_ELEMENT;
Aleksey Sanindffd5c82002-05-31 04:24:13 +00001289 ctx->ns_rendered = xmlC14NNamespacesCreate();
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001290
1291 /*
1292 * Set "exclusive" flag, create a nodes set for namespaces
1293 * stack and remember list of incluseve prefixes
1294 */
Daniel Veillard9ff88172002-03-11 09:15:32 +00001295 if (exclusive) {
1296 ctx->exclusive = 1;
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001297 ctx->inclusive_ns_prefixes = inclusive_ns_prefixes;
1298 }
Daniel Veillard9ff88172002-03-11 09:15:32 +00001299 return (ctx);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001300}
1301
1302/**
1303 * xmlC14NDocSaveTo:
1304 * @doc: the XML document for canonization
1305 * @nodes: the nodes set to be included in the canonized image
1306 * or NULL if all document nodes should be included
1307 * @exclusive: the exclusive flag (0 - non-exclusive canonicalization;
1308 * otherwise - exclusive canonicalization)
Daniel Veillarddb1bdba2002-03-09 14:13:11 +00001309 * @inclusive_ns_prefixes: the list of inclusive namespace prefixes
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001310 * ended with a NULL or NULL if there is no
1311 * inclusive namespaces (only for exclusive
1312 * canonicalization, ignored otherwise)
1313 * @with_comments: include comments in the result (!=0) or not (==0)
1314 * @buf: the output buffer to store canonical XML; this
1315 * buffer MUST have encoder==NULL because C14N requires
1316 * UTF-8 output
1317 *
1318 * Dumps the canonized image of given XML document into the provided buffer.
1319 * For details see "Canonical XML" (http://www.w3.org/TR/xml-c14n) or
1320 * "Exclusive XML Canonicalization" (http://www.w3.org/TR/xml-exc-c14n)
1321 *
1322 * Returns non-negative value on success or a negative value on fail
1323 */
1324int
Daniel Veillard9ff88172002-03-11 09:15:32 +00001325xmlC14NDocSaveTo(xmlDocPtr doc, xmlNodeSetPtr nodes,
1326 int exclusive, xmlChar ** inclusive_ns_prefixes,
1327 int with_comments, xmlOutputBufferPtr buf)
1328{
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001329 xmlC14NCtxPtr ctx;
1330 int ret;
Daniel Veillard9ff88172002-03-11 09:15:32 +00001331
1332 if ((buf == NULL) || (doc == NULL)) {
1333#ifdef DEBUG_C14N
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001334 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001335 "xmlC14NDocSaveTo: null return buffer or doc pointer\n");
1336#endif
1337 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001338 }
1339
1340 /*
1341 * Validate the encoding output buffer encoding
1342 */
Daniel Veillard9ff88172002-03-11 09:15:32 +00001343 if (buf->encoder != NULL) {
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001344 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001345 "xmlC14NDocSaveTo: output buffer encoder != NULL but C14N requires UTF8 output\n");
1346 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001347 }
1348
1349 ctx = xmlC14NNewCtx(doc, nodes, exclusive, inclusive_ns_prefixes,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001350 with_comments, buf);
1351 if (ctx == NULL) {
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001352 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001353 "xmlC14NDocSaveTo: unable to create C14N context\n");
1354 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001355 }
1356
1357
1358
1359 /*
1360 * Root Node
1361 * The root node is the parent of the top-level document element. The
1362 * result of processing each of its child nodes that is in the node-set
1363 * in document order. The root node does not generate a byte order mark,
1364 * XML declaration, nor anything from within the document type
1365 * declaration.
1366 */
Daniel Veillard9ff88172002-03-11 09:15:32 +00001367 if (doc->children != NULL) {
1368 ret = xmlC14NProcessNodeList(ctx, doc->children);
1369 if (ret < 0) {
1370#ifdef DEBUG_C14N
1371 xmlGenericError(xmlGenericErrorContext,
1372 "xmlC14NDocSaveTo: process childrens' list failed.\n");
1373#endif
1374 xmlC14NFreeCtx(ctx);
1375 return (-1);
1376 }
1377 }
1378
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001379 /*
1380 * Flush buffer to get number of bytes written
1381 */
1382 ret = xmlOutputBufferFlush(buf);
Daniel Veillard9ff88172002-03-11 09:15:32 +00001383 if (ret < 0) {
1384#ifdef DEBUG_C14N
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001385 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001386 "xmlC14NDocSaveTo: buffer flush failed.\n");
1387#endif
1388 xmlC14NFreeCtx(ctx);
1389 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001390 }
1391
1392 /*
1393 * Cleanup
1394 */
Daniel Veillard9ff88172002-03-11 09:15:32 +00001395 xmlC14NFreeCtx(ctx);
1396 return (ret);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001397}
1398
1399/**
1400 * xmlC14NDocDumpMemory:
1401 * @doc: the XML document for canonization
1402 * @nodes: the nodes set to be included in the canonized image
1403 * or NULL if all document nodes should be included
1404 * @exclusive: the exclusive flag (0 - non-exclusive canonicalization;
1405 * otherwise - exclusive canonicalization)
Daniel Veillarddb1bdba2002-03-09 14:13:11 +00001406 * @inclusive_ns_prefixes: the list of inclusive namespace prefixes
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001407 * ended with a NULL or NULL if there is no
1408 * inclusive namespaces (only for exclusive
1409 * canonicalization, ignored otherwise)
1410 * @with_comments: include comments in the result (!=0) or not (==0)
1411 * @doc_txt_ptr: the memory pointer for allocated canonical XML text;
1412 * the caller of this functions is responsible for calling
1413 * xmlFree() to free allocated memory
1414 *
1415 * Dumps the canonized image of given XML document into memory.
1416 * For details see "Canonical XML" (http://www.w3.org/TR/xml-c14n) or
1417 * "Exclusive XML Canonicalization" (http://www.w3.org/TR/xml-exc-c14n)
1418 *
1419 * Returns the number of bytes written on success or a negative value on fail
1420 */
1421int
Daniel Veillard9ff88172002-03-11 09:15:32 +00001422xmlC14NDocDumpMemory(xmlDocPtr doc, xmlNodeSetPtr nodes,
1423 int exclusive, xmlChar ** inclusive_ns_prefixes,
1424 int with_comments, xmlChar ** doc_txt_ptr)
1425{
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001426 int ret;
1427 xmlOutputBufferPtr buf;
1428
1429 if (doc_txt_ptr == NULL) {
Daniel Veillard9ff88172002-03-11 09:15:32 +00001430#ifdef DEBUG_C14N
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001431 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001432 "xmlC14NDocDumpMemory: null return buffer pointer\n");
1433#endif
1434 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001435 }
1436
1437 *doc_txt_ptr = NULL;
Daniel Veillard9ff88172002-03-11 09:15:32 +00001438
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001439 /*
1440 * create memory buffer with UTF8 (default) encoding
1441 */
1442 buf = xmlAllocOutputBuffer(NULL);
Daniel Veillard9ff88172002-03-11 09:15:32 +00001443 if (buf == NULL) {
1444#ifdef DEBUG_C14N
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001445 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001446 "xmlC14NDocDumpMemory: failed to allocate output buffer.\n");
1447#endif
1448 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001449 }
1450
1451 /*
1452 * canonize document and write to buffer
1453 */
Daniel Veillard9ff88172002-03-11 09:15:32 +00001454 ret = xmlC14NDocSaveTo(doc, nodes, exclusive, inclusive_ns_prefixes,
1455 with_comments, buf);
1456 if (ret < 0) {
1457#ifdef DEBUG_C14N
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001458 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001459 "xmlC14NDocDumpMemory: xmlC14NDocSaveTo failed.\n");
1460#endif
1461 (void) xmlOutputBufferClose(buf);
1462 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001463 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001464
Daniel Veillard9ff88172002-03-11 09:15:32 +00001465 ret = buf->buffer->use;
1466 if (ret > 0) {
1467 *doc_txt_ptr = xmlStrndup(buf->buffer->content, ret);
1468 }
1469 (void) xmlOutputBufferClose(buf);
1470
1471 if ((*doc_txt_ptr == NULL) && (ret > 0)) {
1472#ifdef DEBUG_C14N
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001473 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001474 "xmlC14NDocDumpMemory: failed to allocate memory for document text representation\n");
1475#endif
1476 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001477 }
Daniel Veillard9ff88172002-03-11 09:15:32 +00001478 return (ret);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001479}
1480
1481/**
1482 * xmlC14NDocSave:
1483 * @doc: the XML document for canonization
1484 * @nodes: the nodes set to be included in the canonized image
1485 * or NULL if all document nodes should be included
1486 * @exclusive: the exclusive flag (0 - non-exclusive canonicalization;
1487 * otherwise - exclusive canonicalization)
Daniel Veillarddb1bdba2002-03-09 14:13:11 +00001488 * @inclusive_ns_prefixes: the list of inclusive namespace prefixes
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001489 * ended with a NULL or NULL if there is no
1490 * inclusive namespaces (only for exclusive
1491 * canonicalization, ignored otherwise)
1492 * @with_comments: include comments in the result (!=0) or not (==0)
1493 * @filename: the filename to store canonical XML image
1494 * @compression: the compression level (zlib requred):
1495 * -1 - libxml default,
1496 * 0 - uncompressed,
1497 * >0 - compression level
1498 *
1499 * Dumps the canonized image of given XML document into the file.
1500 * For details see "Canonical XML" (http://www.w3.org/TR/xml-c14n) or
1501 * "Exclusive XML Canonicalization" (http://www.w3.org/TR/xml-exc-c14n)
1502 *
1503 * Returns the number of bytes written success or a negative value on fail
1504 */
1505int
Daniel Veillard9ff88172002-03-11 09:15:32 +00001506xmlC14NDocSave(xmlDocPtr doc, xmlNodeSetPtr nodes,
1507 int exclusive, xmlChar ** inclusive_ns_prefixes,
1508 int with_comments, const char *filename, int compression)
1509{
1510 xmlOutputBufferPtr buf;
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001511 int ret;
1512
Daniel Veillard9ff88172002-03-11 09:15:32 +00001513 if (filename == NULL) {
1514#ifdef DEBUG_C14N
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001515 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001516 "xmlC14NDocSave: filename is NULL\n");
1517#endif
1518 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001519 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001520#ifdef HAVE_ZLIB_H
Daniel Veillard9ff88172002-03-11 09:15:32 +00001521 if (compression < 0)
1522 compression = xmlGetCompressMode();
1523#endif
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001524
1525 /*
1526 * save the content to a temp buffer, use default UTF8 encoding.
1527 */
1528 buf = xmlOutputBufferCreateFilename(filename, NULL, compression);
1529 if (buf == NULL) {
Daniel Veillard9ff88172002-03-11 09:15:32 +00001530#ifdef DEBUG_C14N
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001531 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001532 "xmlC14NDocSave: unable to create buffer for file=\"%s\" with compressin=%d\n",
1533 filename, compression);
1534#endif
1535 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001536 }
Daniel Veillard9ff88172002-03-11 09:15:32 +00001537
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001538 /*
1539 * canonize document and write to buffer
1540 */
Daniel Veillard9ff88172002-03-11 09:15:32 +00001541 ret = xmlC14NDocSaveTo(doc, nodes, exclusive, inclusive_ns_prefixes,
1542 with_comments, buf);
1543 if (ret < 0) {
1544#ifdef DEBUG_C14N
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001545 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001546 "xmlC14NDocSave: xmlC14NDocSaveTo failed.\n");
1547#endif
1548 (void) xmlOutputBufferClose(buf);
1549 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001550 }
Daniel Veillard9ff88172002-03-11 09:15:32 +00001551
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001552 /*
1553 * get the numbers of bytes written
1554 */
1555 ret = xmlOutputBufferClose(buf);
Daniel Veillard9ff88172002-03-11 09:15:32 +00001556 return (ret);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001557}
1558
1559
1560
1561/*
1562 * Macro used to grow the current buffer.
1563 */
1564#define growBufferReentrant() { \
1565 buffer_size *= 2; \
1566 buffer = (xmlChar *) \
1567 xmlRealloc(buffer, buffer_size * sizeof(xmlChar)); \
1568 if (buffer == NULL) { \
1569 perror("realloc failed"); \
1570 return(NULL); \
1571 } \
1572}
1573
1574/**
1575 * xmlC11NNormalizeString:
1576 * @input: the input string
1577 * @mode: the normalization mode (attribute, comment, PI or text)
1578 *
1579 * Converts a string to a canonical (normalized) format. The code is stolen
1580 * from xmlEncodeEntitiesReentrant(). Added normalization of \x09, \x0a, \x0A
1581 * and the @mode parameter
1582 *
1583 * Returns a normalized string (caller is responsible for calling xmlFree())
1584 * or NULL if an error occurs
1585 */
1586static xmlChar *
Daniel Veillard9ff88172002-03-11 09:15:32 +00001587xmlC11NNormalizeString(const xmlChar * input,
1588 xmlC14NNormalizationMode mode)
1589{
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001590 const xmlChar *cur = input;
1591 xmlChar *buffer = NULL;
1592 xmlChar *out = NULL;
1593 int buffer_size = 0;
1594
Daniel Veillard9ff88172002-03-11 09:15:32 +00001595 if (input == NULL)
1596 return (NULL);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001597
1598 /*
1599 * allocate an translation buffer.
1600 */
1601 buffer_size = 1000;
1602 buffer = (xmlChar *) xmlMalloc(buffer_size * sizeof(xmlChar));
1603 if (buffer == NULL) {
Daniel Veillard9ff88172002-03-11 09:15:32 +00001604 perror("malloc failed");
1605 return (NULL);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001606 }
1607 out = buffer;
1608
1609 while (*cur != '\0') {
Daniel Veillard9ff88172002-03-11 09:15:32 +00001610 if ((out - buffer) > (buffer_size - 10)) {
1611 int indx = out - buffer;
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001612
Daniel Veillard9ff88172002-03-11 09:15:32 +00001613 growBufferReentrant();
1614 out = &buffer[indx];
1615 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001616
Daniel Veillard9ff88172002-03-11 09:15:32 +00001617 if ((*cur == '<') && ((mode == XMLC14N_NORMALIZE_ATTR) ||
1618 (mode == XMLC14N_NORMALIZE_TEXT))) {
1619 *out++ = '&';
1620 *out++ = 'l';
1621 *out++ = 't';
1622 *out++ = ';';
1623 } else if ((*cur == '>') && (mode == XMLC14N_NORMALIZE_TEXT)) {
1624 *out++ = '&';
1625 *out++ = 'g';
1626 *out++ = 't';
1627 *out++ = ';';
1628 } else if ((*cur == '&') && ((mode == XMLC14N_NORMALIZE_ATTR) ||
1629 (mode == XMLC14N_NORMALIZE_TEXT))) {
1630 *out++ = '&';
1631 *out++ = 'a';
1632 *out++ = 'm';
1633 *out++ = 'p';
1634 *out++ = ';';
1635 } else if ((*cur == '"') && (mode == XMLC14N_NORMALIZE_ATTR)) {
1636 *out++ = '&';
1637 *out++ = 'q';
1638 *out++ = 'u';
1639 *out++ = 'o';
1640 *out++ = 't';
1641 *out++ = ';';
1642 } else if ((*cur == '\x09') && (mode == XMLC14N_NORMALIZE_ATTR)) {
1643 *out++ = '&';
1644 *out++ = '#';
1645 *out++ = 'x';
1646 *out++ = '9';
1647 *out++ = ';';
1648 } else if ((*cur == '\x0A') && (mode == XMLC14N_NORMALIZE_ATTR)) {
1649 *out++ = '&';
1650 *out++ = '#';
1651 *out++ = 'x';
1652 *out++ = 'A';
1653 *out++ = ';';
1654 } else if ((*cur == '\x0D') && ((mode == XMLC14N_NORMALIZE_ATTR) ||
1655 (mode == XMLC14N_NORMALIZE_TEXT) ||
1656 (mode == XMLC14N_NORMALIZE_COMMENT) ||
1657 (mode == XMLC14N_NORMALIZE_PI))) {
1658 *out++ = '&';
1659 *out++ = '#';
1660 *out++ = 'x';
1661 *out++ = 'D';
1662 *out++ = ';';
1663 } else {
1664 /*
1665 * Works because on UTF-8, all extended sequences cannot
1666 * result in bytes in the ASCII range.
1667 */
1668 *out++ = *cur;
1669 }
1670 cur++;
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001671 }
1672 *out++ = 0;
Daniel Veillard9ff88172002-03-11 09:15:32 +00001673 return (buffer);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001674}
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001675#endif /* LIBXML_C14N_ENABLED */