blob: 7aab5394469a30a38cc43a78111d2ef2ed35d9ce [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 */
12#include "libxml.h"
13#ifdef LIBXML_C14N_ENABLED
14
15#ifdef HAVE_STDLIB_H
16#include <stdlib.h>
17#endif
18#include <string.h>
19
20#include <libxml/tree.h>
21#include <libxml/parser.h>
Daniel Veillard9ff88172002-03-11 09:15:32 +000022#include <libxml/uri.h>
Daniel Veillard044fc6b2002-03-04 17:09:44 +000023#include <libxml/xmlerror.h>
24#include <libxml/globals.h>
25#include <libxml/xpathInternals.h>
26#include <libxml/c14n.h>
27
28/************************************************************************
29 * *
30 * Some declaration better left private ATM *
31 * *
32 ************************************************************************/
33
Daniel Veillard9ff88172002-03-11 09:15:32 +000034typedef enum {
35 XMLC14N_BEFORE_DOCUMENT_ELEMENT = 0,
36 XMLC14N_INSIDE_DOCUMENT_ELEMENT = 1,
37 XMLC14N_AFTER_DOCUMENT_ELEMENT = 2
Daniel Veillard044fc6b2002-03-04 17:09:44 +000038} xmlC14NPosition;
39
40typedef struct _xmlC14NCtx {
41 /* input parameters */
Daniel Veillard9ff88172002-03-11 09:15:32 +000042 xmlDocPtr doc;
43 xmlNodeSetPtr visible_nodes;
44 int with_comments;
45 xmlOutputBufferPtr buf;
Daniel Veillard044fc6b2002-03-04 17:09:44 +000046
47 /* position in the XML document */
Daniel Veillard9ff88172002-03-11 09:15:32 +000048 xmlC14NPosition pos;
49 int parent_is_doc;
Daniel Veillard044fc6b2002-03-04 17:09:44 +000050
51 /* exclusive canonicalization */
Daniel Veillard9ff88172002-03-11 09:15:32 +000052 int exclusive;
53 xmlNodeSetPtr ns_rendered;
54 xmlChar **inclusive_ns_prefixes;
Daniel Veillard044fc6b2002-03-04 17:09:44 +000055} xmlC14NCtx, *xmlC14NCtxPtr;
56
57
Daniel Veillard9ff88172002-03-11 09:15:32 +000058static int xmlC14NProcessNode(xmlC14NCtxPtr ctx, xmlNodePtr cur);
59static int xmlC14NProcessNodeList(xmlC14NCtxPtr ctx, xmlNodePtr cur);
Daniel Veillard044fc6b2002-03-04 17:09:44 +000060typedef enum {
Daniel Veillard9ff88172002-03-11 09:15:32 +000061 XMLC14N_NORMALIZE_ATTR = 0,
62 XMLC14N_NORMALIZE_COMMENT = 1,
63 XMLC14N_NORMALIZE_PI = 2,
64 XMLC14N_NORMALIZE_TEXT = 3
65} xmlC14NNormalizationMode;
Daniel Veillard044fc6b2002-03-04 17:09:44 +000066
Daniel Veillard9ff88172002-03-11 09:15:32 +000067static xmlChar *xmlC11NNormalizeString(const xmlChar * input,
68 xmlC14NNormalizationMode mode);
Daniel Veillard044fc6b2002-03-04 17:09:44 +000069
70#define xmlC11NNormalizeAttr( a ) \
Daniel Veillard9ff88172002-03-11 09:15:32 +000071 xmlC11NNormalizeString((a), XMLC14N_NORMALIZE_ATTR)
Daniel Veillard044fc6b2002-03-04 17:09:44 +000072#define xmlC11NNormalizeComment( a ) \
Daniel Veillard9ff88172002-03-11 09:15:32 +000073 xmlC11NNormalizeString((a), XMLC14N_NORMALIZE_COMMENT)
Daniel Veillard044fc6b2002-03-04 17:09:44 +000074#define xmlC11NNormalizePI( a ) \
Daniel Veillard9ff88172002-03-11 09:15:32 +000075 xmlC11NNormalizeString((a), XMLC14N_NORMALIZE_PI)
Daniel Veillard044fc6b2002-03-04 17:09:44 +000076#define xmlC11NNormalizeText( a ) \
Daniel Veillard9ff88172002-03-11 09:15:32 +000077 xmlC11NNormalizeString((a), XMLC14N_NORMALIZE_TEXT)
Daniel Veillard044fc6b2002-03-04 17:09:44 +000078
79/************************************************************************
80 * *
81 * The implementation internals *
82 * *
83 ************************************************************************/
84
85/**
86 * xmlC14NIsVisible:
87 * @ctx: the C14N context
88 * @node: the node to check
89 *
90 * Checks whether the given node is visible. If the XML document normalization
91 * was called for the whole document then it is always "true".
92 *
93 * Returns 1 if the node is visible or 0 otherwise.
94 */
Daniel Veillard9ff88172002-03-11 09:15:32 +000095
Daniel Veillard044fc6b2002-03-04 17:09:44 +000096/* todo: make it a define? */
Daniel Veillard9ff88172002-03-11 09:15:32 +000097static int
98xmlC14NIsVisible(xmlC14NCtxPtr ctx, void *node)
99{
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000100 /*
101 * If the input is an XPath node-set, then the node-set must explicitly
102 * contain every node to be rendered to the canonical form.
103 */
Daniel Veillard9ff88172002-03-11 09:15:32 +0000104 if ((ctx->visible_nodes != NULL) &&
105 (!xmlXPathNodeSetContains(ctx->visible_nodes, (xmlNodePtr) node)))
106 {
107 return (0);
108 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000109
Daniel Veillard9ff88172002-03-11 09:15:32 +0000110 return (1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000111}
112
113/**
114 * xmlC14NIsXmlNs:
115 * @ns: the namespace to check
116 *
117 * Checks whether the given namespace is a default "xml:" namespace
118 * with href="http://www.w3.org/XML/1998/namespace"
119 *
120 * Returns 1 if the node is default or 0 otherwise
121 */
Daniel Veillard9ff88172002-03-11 09:15:32 +0000122
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000123/* todo: make it a define? */
124static int
Daniel Veillard9ff88172002-03-11 09:15:32 +0000125xmlC14NIsXmlNs(xmlNsPtr ns)
126{
127 return ((ns != NULL) &&
128 (xmlStrEqual(ns->prefix, BAD_CAST "xml")) &&
129 (xmlStrEqual(ns->href,
130 BAD_CAST
131 "http://www.w3.org/XML/1998/namespace")));
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000132}
133
134/**
135 * xmlExcC14NIsRendered:
136 * @ctx the C14N context
137 * @ns the namespace to check
138 *
139 * Checks whether the given namespace was already rendered or not
140 *
141 * Returns 1 if we already wrote this namespace or 0 otherwise
142 */
143static int
Daniel Veillard9ff88172002-03-11 09:15:32 +0000144xmlExcC14NIsRendered(xmlC14NCtxPtr ctx, xmlNsPtr ns)
145{
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000146 int i;
Daniel Veillard9ff88172002-03-11 09:15:32 +0000147
148 if ((ctx == NULL) || (ctx->ns_rendered == NULL) || (ns == NULL)) {
149 return (0);
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000150 }
151
Daniel Veillard9ff88172002-03-11 09:15:32 +0000152 if (ctx->ns_rendered->nodeTab != NULL) {
153 for (i = ctx->ns_rendered->nodeNr - 1; i >= 0; --i) {
154 xmlNsPtr ns1 = (xmlNsPtr) ctx->ns_rendered->nodeTab[i];
155
156 if (xmlStrEqual(ns1->prefix, ns->prefix)) {
157 return (xmlStrEqual(ns1->href, ns->href));
158 }
159 }
160 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000161 /*
162 * if the default namespace xmlns="" is not defined yet then
163 * we do not want to print it out
164 */
Daniel Veillard9ff88172002-03-11 09:15:32 +0000165 return ((xmlStrlen(ns->prefix) == 0) && (xmlStrlen(ns->href) == 0));
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000166}
167
168/**
169 * xmlC14NNamespacesCompare:
170 * @ns1: the pointer to first namespace
171 * @ns2: the pointer to second namespace
172 *
173 * Compares the namespaces by names (prefixes).
174 *
175 * Returns -1 if ns1 < ns2, 0 if ns1 == ns2 or 1 if ns1 > ns2.
176 */
Daniel Veillard9ff88172002-03-11 09:15:32 +0000177static int
178xmlC14NNamespacesCompare(xmlNsPtr ns1, xmlNsPtr ns2)
179{
180 if (ns1 == ns2)
181 return (0);
182 if (ns1 == NULL)
183 return (-1);
184 if (ns2 == NULL)
185 return (1);
186
187 return (xmlStrcmp(ns1->prefix, ns2->prefix));
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000188}
189
190
191/**
192 * xmlC14NPrintNamespaces:
193 * @ns: the pointer to namespace
194 * @ctx: the C14N context
195 *
196 * Prints the given namespace to the output buffer from C14N context.
197 *
198 * Returns 1 on success or 0 on fail.
199 */
200static int
Daniel Veillard9ff88172002-03-11 09:15:32 +0000201xmlC14NPrintNamespaces(const xmlNsPtr ns, xmlC14NCtxPtr ctx)
202{
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000203
Daniel Veillard9ff88172002-03-11 09:15:32 +0000204 if ((ns == NULL) || (ctx == NULL)) {
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000205#ifdef DEBUG_C14N
206 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +0000207 "xmlC14NPrintNamespace: namespace or context pointer is null\n");
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000208#endif
Daniel Veillard9ff88172002-03-11 09:15:32 +0000209 return 0;
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000210 }
211
Daniel Veillard9ff88172002-03-11 09:15:32 +0000212 if (ns->prefix != NULL) {
213 xmlOutputBufferWriteString(ctx->buf, " xmlns:");
214 xmlOutputBufferWriteString(ctx->buf, (const char *) ns->prefix);
215 xmlOutputBufferWriteString(ctx->buf, "=\"");
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000216 } else {
Daniel Veillard9ff88172002-03-11 09:15:32 +0000217 xmlOutputBufferWriteString(ctx->buf, " xmlns=\"");
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000218 }
219 xmlOutputBufferWriteString(ctx->buf, (const char *) ns->href);
220 xmlOutputBufferWriteString(ctx->buf, "\"");
Daniel Veillard9ff88172002-03-11 09:15:32 +0000221 return (1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000222}
223
224/**
225 * xmlC14NProcessNamespacesAxis:
226 * @ctx: the C14N context
227 * @node: the current node
228 *
229 * Prints out canonical namespace axis of the current node to the
230 * buffer from C14N context as follows
231 *
232 * Canonical XML v 1.0 (http://www.w3.org/TR/xml-c14n)
233 *
234 * Namespace Axis
235 * Consider a list L containing only namespace nodes in the
236 * axis and in the node-set in lexicographic order (ascending). To begin
237 * processing L, if the first node is not the default namespace node (a node
238 * with no namespace URI and no local name), then generate a space followed
239 * by xmlns="" if and only if the following conditions are met:
240 * - the element E that owns the axis is in the node-set
241 * - The nearest ancestor element of E in the node-set has a default
242 * namespace node in the node-set (default namespace nodes always
243 * have non-empty values in XPath)
244 * The latter condition eliminates unnecessary occurrences of xmlns="" in
245 * the canonical form since an element only receives an xmlns="" if its
246 * default namespace is empty and if it has an immediate parent in the
247 * canonical form that has a non-empty default namespace. To finish
248 * processing L, simply process every namespace node in L, except omit
249 * namespace node with local name xml, which defines the xml prefix,
250 * if its string value is http://www.w3.org/XML/1998/namespace.
251 *
252 * Exclusive XML Canonicalization v 1.0 (http://www.w3.org/TR/xml-exc-c14n)
253 * Canonical XML applied to a document subset requires the search of the
254 * ancestor nodes of each orphan element node for attributes in the xml
255 * namespace, such as xml:lang and xml:space. These are copied into the
256 * element node except if a declaration of the same attribute is already
257 * in the attribute axis of the element (whether or not it is included in
258 * the document subset). This search and copying are omitted from the
259 * Exclusive XML Canonicalization method.
260 *
261 * Returns 0 on success or -1 on fail.
262 */
263static int
Daniel Veillard9ff88172002-03-11 09:15:32 +0000264xmlC14NProcessNamespacesAxis(xmlC14NCtxPtr ctx, xmlNodePtr cur)
265{
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000266 xmlNsPtr ns;
267 xmlListPtr list;
268 xmlNodePtr visible_parent;
269 xmlNsPtr prev;
Daniel Veillard9ff88172002-03-11 09:15:32 +0000270
271 if ((ctx == NULL) || (cur == NULL) || (cur->type != XML_ELEMENT_NODE)) {
272#ifdef DEBUG_C14N
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000273 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +0000274 "xmlC14NProcessNamespacesAxis: Null context or node pointer or type != XML_ELEMENT_NODE.\n");
275#endif
276 return (-1);
277 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000278
279 /*
280 * Create a sorted list to store element namespaces
281 */
Daniel Veillard9ff88172002-03-11 09:15:32 +0000282 list =
283 xmlListCreate(NULL, (xmlListDataCompare) xmlC14NNamespacesCompare);
284 if (list == NULL) {
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000285#ifdef DEBUG_C14N
286 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +0000287 "xmlC14NProcessNamespacesAxis: list creation failed\n");
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000288#endif
Daniel Veillard9ff88172002-03-11 09:15:32 +0000289 return (-1);
290 }
291
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000292 /* find nearest visible parent */
293 visible_parent = cur->parent;
Daniel Veillard9ff88172002-03-11 09:15:32 +0000294 while ((visible_parent != NULL) &&
295 (!xmlC14NIsVisible(ctx, visible_parent))) {
296 visible_parent = visible_parent->parent;
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000297 }
298
299 /*
300 * todo: the libxml XPath implementation does not create
301 * nodes for all namespaces known to the node (i.e. for namespaces
302 * defined in node parents). By this we need to now walk thru
303 * all namespace in current node and all invisible ancesstors
304 */
Daniel Veillard9ff88172002-03-11 09:15:32 +0000305 while (cur != visible_parent) {
306 for (ns = cur->nsDef; ns != NULL; ns = ns->next) {
307 /*
308 * first of all ignore default "xml" namespace and
309 * already included namespace
310 */
311 if ((xmlC14NIsXmlNs(ns)) || (xmlListSearch(list, ns) != NULL)) {
312 continue;
313 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000314
Daniel Veillard9ff88172002-03-11 09:15:32 +0000315 /*
316 * Lookup nearest namespace after visible parent having
317 * the same prefix. Namespace included if and only if one of
318 * the following:
319 * - another namespace having the same prefix but
320 * different value found or
321 * - there is no namespaces having the same prefix and
322 * it is not a default xmlns="" namespace (empty prefix
323 * and empty href)
324 */
325 prev = xmlSearchNs(ctx->doc, visible_parent, ns->prefix);
326 if ((prev == NULL) && ((xmlStrlen(ns->prefix) > 0) ||
327 (xmlStrlen(ns->href) > 0))) {
328 xmlListInsert(list, ns);
329 } else if ((prev != NULL)
330 && (!xmlStrEqual(ns->href, prev->href))) {
331 xmlListInsert(list, ns);
332 }
333 }
334 cur = cur->parent;
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000335 }
336
337 /*
338 * print out all elements from list
339 */
Daniel Veillard9ff88172002-03-11 09:15:32 +0000340 xmlListWalk(list, (xmlListWalker) xmlC14NPrintNamespaces,
341 (const void *) ctx);
342
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000343 /*
344 * Cleanup
345 */
346 xmlListDelete(list);
Daniel Veillard9ff88172002-03-11 09:15:32 +0000347 return (0);
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000348}
349
350/**
351 * xmlExcC14NProcessNamespacesAxis:
352 * @ctx: the C14N context
353 * @node: the current node
354 *
355 * Prints out exclusive canonical namespace axis of the current node to the
356 * buffer from C14N context as follows
357 *
358 * Exclusive XML Canonicalization
359 * http://www.w3.org/TR/xml-exc-c14n
360 *
361 * If the element node is in the XPath subset then output the node in
362 * accordance with Canonical XML except for namespace nodes which are
363 * rendered as follows:
364 *
365 * 1. Render each namespace node iff:
366 * * it is visibly utilized by the immediate parent element or one of
367 * its attributes, or is present in InclusiveNamespaces PrefixList, and
368 * * its prefix and value do not appear in ns_rendered. ns_rendered is
369 * obtained by popping the state stack in order to obtain a list of
370 * prefixes and their values which have already been rendered by
371 * an output ancestor of the namespace node's parent element.
372 * 2. Append the rendered namespace node to the list ns_rendered of namespace
373 * nodes rendered by output ancestors. Push ns_rendered on state stack and
374 * recurse.
375 * 3. After the recursion returns, pop thestate stack.
376 *
377 *
378 * Returns 0 on success or -1 on fail.
379 */
380static int
Daniel Veillard9ff88172002-03-11 09:15:32 +0000381xmlExcC14NProcessNamespacesAxis(xmlC14NCtxPtr ctx, xmlNodePtr cur)
382{
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000383 xmlListPtr list;
384 xmlAttrPtr attr;
385 xmlNsPtr ns;
Daniel Veillard9ff88172002-03-11 09:15:32 +0000386
387 if ((ctx == NULL) || (cur == NULL) || (cur->type != XML_ELEMENT_NODE)) {
388#ifdef DEBUG_C14N
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000389 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +0000390 "xmlExcC14NProcessNamespacesAxis: Null context or node pointer or type != XML_ELEMENT_NODE.\n");
391#endif
392 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000393 }
Daniel Veillard9ff88172002-03-11 09:15:32 +0000394
395 if ((!ctx->exclusive) || (ctx->ns_rendered == NULL)) {
396#ifdef DEBUG_C14N
397 xmlGenericError(xmlGenericErrorContext,
398 "xmlExcC14NProcessNamespacesAxis: called for non-exclusive canonization or rendered stack is NULL.\n");
399#endif
400 return (-1);
401
402 }
403
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000404 /*
405 * Create a sorted list to store element namespaces
406 */
Daniel Veillard9ff88172002-03-11 09:15:32 +0000407 list =
408 xmlListCreate(NULL, (xmlListDataCompare) xmlC14NNamespacesCompare);
409 if (list == NULL) {
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000410#ifdef DEBUG_C14N
411 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +0000412 "xmlExcC14NProcessNamespacesAxis: list creation failed\n");
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000413#endif
Daniel Veillard9ff88172002-03-11 09:15:32 +0000414 return (-1);
415 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000416
417 /*
418 * First of all, add all namespaces required by current node
419 * (i.e. node namespace and all attribute namespaces)
420 * todo: do we need to check for default "xml:" namespace
421 */
422 ns = (cur->ns != NULL) ? cur->ns : xmlSearchNs(ctx->doc, cur, NULL);
Daniel Veillard9ff88172002-03-11 09:15:32 +0000423 if ((ns != NULL) && (!xmlC14NIsXmlNs(ns)) &&
424 (xmlListSearch(list, ns) == NULL)) {
425 if (!xmlExcC14NIsRendered(ctx, ns)) {
426 xmlListInsert(list, ns);
427 xmlXPathNodeSetAdd(ctx->ns_rendered, (xmlNodePtr) ns);
428 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000429 }
430 attr = cur->properties;
Daniel Veillard9ff88172002-03-11 09:15:32 +0000431 while (attr != NULL) {
432 /*
433 * todo: do we need to check that attribute is visible and has non
434 * default namespace
435 */
436 if (xmlC14NIsVisible(ctx, attr)) {
437 ns = (attr->ns != NULL) ? attr->ns : xmlSearchNs(ctx->doc, cur,
438 NULL);
439 if ((ns != NULL) && (xmlC14NIsVisible(ctx, attr)) &&
440 (!xmlC14NIsXmlNs(ns))) {
441 if ((xmlListSearch(list, ns) == NULL)
442 && (!xmlExcC14NIsRendered(ctx, ns))) {
443 xmlListInsert(list, ns);
444 xmlXPathNodeSetAdd(ctx->ns_rendered, (xmlNodePtr) ns);
445 }
446 }
447 }
448 attr = attr->next;
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000449 }
Daniel Veillard9ff88172002-03-11 09:15:32 +0000450
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000451 /*
452 * Next add all inclusive namespaces if needed.
453 */
Daniel Veillard9ff88172002-03-11 09:15:32 +0000454 if (ctx->inclusive_ns_prefixes != NULL) {
455 int i;
456 xmlChar *prefix;
457
458 for (i = 0; ctx->inclusive_ns_prefixes[i] != NULL; ++i) {
459 prefix = ctx->inclusive_ns_prefixes[i];
460 /*
461 * Special values for namespace with empty prefix
462 */
463 if (xmlStrEqual(prefix, BAD_CAST "#default")
464 || xmlStrEqual(prefix, BAD_CAST "")) {
465 prefix = NULL;
466 }
467 ns = xmlSearchNs(ctx->doc, cur, prefix);
468 if ((ns != NULL) && (!xmlC14NIsXmlNs(ns))) {
469 if (xmlListSearch(list, ns) == NULL &&
470 !xmlExcC14NIsRendered(ctx, ns)) {
471 xmlListInsert(list, ns);
472 xmlXPathNodeSetAdd(ctx->ns_rendered, (xmlNodePtr) ns);
473 }
474 }
475 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000476 }
Daniel Veillard9ff88172002-03-11 09:15:32 +0000477
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000478 /*
479 * print out all elements from list
480 */
Daniel Veillard9ff88172002-03-11 09:15:32 +0000481 xmlListWalk(list, (xmlListWalker) xmlC14NPrintNamespaces,
482 (const void *) ctx);
483
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000484 /*
485 * Cleanup
486 */
487 xmlListDelete(list);
Daniel Veillard9ff88172002-03-11 09:15:32 +0000488 return (0);
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000489}
490
491
492/**
493 * xmlC14NAttrsCompare:
494 * @attr1: the pointer to first attr
495 * @attr2: the pointer to second attr
496 *
497 * Prints the given attribute to the output buffer from C14N context.
498 *
499 * Returns -1 if attr1 < attr2, 0 if attr1 == attr2 or 1 if attr1 > attr2.
500 */
501static int
Daniel Veillard9ff88172002-03-11 09:15:32 +0000502xmlC14NAttrsCompare(xmlAttrPtr attr1, xmlAttrPtr attr2)
503{
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000504 int ret = 0;
505
506 /*
507 * Simple cases
508 */
Daniel Veillard9ff88172002-03-11 09:15:32 +0000509 if (attr1 == attr2)
510 return (0);
511 if (attr1 == NULL)
512 return (-1);
513 if (attr2 == NULL)
514 return (1);
515 if (attr1->ns == attr2->ns) {
516 return (xmlStrcmp(attr1->name, attr2->name));
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000517 }
518
519 /*
520 * Attributes in the default namespace are first
521 * because the default namespace is not applied to
522 * unqualified attributes
523 */
Daniel Veillard9ff88172002-03-11 09:15:32 +0000524 if (attr1->ns == NULL)
525 return (-1);
526 if (attr2->ns == NULL)
527 return (1);
528 if (attr1->ns->prefix == NULL)
529 return (-1);
530 if (attr2->ns->prefix == NULL)
531 return (1);
532
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000533 ret = xmlStrcmp(attr1->ns->href, attr2->ns->href);
Daniel Veillard9ff88172002-03-11 09:15:32 +0000534 if (ret == 0) {
535 ret = xmlStrcmp(attr1->name, attr2->name);
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000536 }
Daniel Veillard9ff88172002-03-11 09:15:32 +0000537 return (ret);
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000538}
539
540
541/**
542 * xmlC14NPrintAttrs:
543 * @attr: the pointer to attr
544 * @ctx: the C14N context
545 *
546 * Prints out canonical attribute urrent node to the
547 * buffer from C14N context as follows
548 *
549 * Canonical XML v 1.0 (http://www.w3.org/TR/xml-c14n)
550 *
551 * Returns 1 on success or 0 on fail.
552 */
553static int
Daniel Veillard9ff88172002-03-11 09:15:32 +0000554xmlC14NPrintAttrs(const xmlAttrPtr attr, xmlC14NCtxPtr ctx)
555{
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000556 xmlChar *value;
557 xmlChar *buffer;
558
Daniel Veillard9ff88172002-03-11 09:15:32 +0000559 if ((attr == NULL) || (ctx == NULL)) {
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000560#ifdef DEBUG_C14N
561 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +0000562 "xmlC14NPrintAttrs: attr == NULL or ctx == NULL\n");
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000563#endif
Daniel Veillard9ff88172002-03-11 09:15:32 +0000564 return (0);
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000565 }
566
567 xmlOutputBufferWriteString(ctx->buf, " ");
Daniel Veillard9ff88172002-03-11 09:15:32 +0000568 if (attr->ns != NULL && xmlStrlen(attr->ns->prefix) > 0) {
569 xmlOutputBufferWriteString(ctx->buf,
570 (const char *) attr->ns->prefix);
571 xmlOutputBufferWriteString(ctx->buf, ":");
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000572 }
573 xmlOutputBufferWriteString(ctx->buf, (const char *) attr->name);
574 xmlOutputBufferWriteString(ctx->buf, "=\"");
575
576 value = xmlNodeListGetString(attr->doc, attr->children, 1);
577 /* todo: should we log an error if value==NULL ? */
Daniel Veillard9ff88172002-03-11 09:15:32 +0000578 if (value != NULL) {
579 buffer = xmlC11NNormalizeAttr(value);
580 xmlFree(value);
581 if (buffer != NULL) {
582 xmlOutputBufferWriteString(ctx->buf, (const char *) buffer);
583 xmlFree(buffer);
584 } else {
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000585#ifdef DEBUG_C14N
Daniel Veillard9ff88172002-03-11 09:15:32 +0000586 xmlGenericError(xmlGenericErrorContext,
587 "xmlC14NPrintAttrs: xmlC11NNormalizeAttr failed\n");
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000588#endif
Daniel Veillard9ff88172002-03-11 09:15:32 +0000589 return (0);
590 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000591 }
592 xmlOutputBufferWriteString(ctx->buf, "\"");
Daniel Veillard9ff88172002-03-11 09:15:32 +0000593 return (1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000594}
595
596/**
597 * xmlC14NProcessAttrsAxis:
598 * @ctx: the C14N context
599 * @cur: the current node
600 *
601 * Prints out canonical attribute axis of the current node to the
602 * buffer from C14N context as follows
603 *
604 * Canonical XML v 1.0 (http://www.w3.org/TR/xml-c14n)
605 *
606 * Attribute Axis
607 * In lexicographic order (ascending), process each node that
608 * is in the element's attribute axis and in the node-set.
609 *
610 * The processing of an element node E MUST be modified slightly
611 * when an XPath node-set is given as input and the element's
612 * parent is omitted from the node-set.
613 *
614 *
615 * Exclusive XML Canonicalization v 1.0 (http://www.w3.org/TR/xml-exc-c14n)
616 *
617 * Canonical XML applied to a document subset requires the search of the
618 * ancestor nodes of each orphan element node for attributes in the xml
619 * namespace, such as xml:lang and xml:space. These are copied into the
620 * element node except if a declaration of the same attribute is already
621 * in the attribute axis of the element (whether or not it is included in
622 * the document subset). This search and copying are omitted from the
623 * Exclusive XML Canonicalization method.
624 *
625 * Returns 0 on success or -1 on fail.
626 */
627static int
Daniel Veillard9ff88172002-03-11 09:15:32 +0000628xmlC14NProcessAttrsAxis(xmlC14NCtxPtr ctx, xmlNodePtr cur)
629{
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000630 xmlAttrPtr attr;
631 xmlListPtr list;
Daniel Veillard9ff88172002-03-11 09:15:32 +0000632
633 if ((ctx == NULL) || (cur == NULL) || (cur->type != XML_ELEMENT_NODE)) {
634#ifdef DEBUG_C14N
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000635 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +0000636 "xmlC14NProcessAttrsAxis: Null context or node pointer or type != XML_ELEMENT_NODE.\n");
637#endif
638 return (-1);
639 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000640
641 /*
642 * Create a sorted list to store element attributes
643 */
Daniel Veillard9ff88172002-03-11 09:15:32 +0000644 list = xmlListCreate(NULL, (xmlListDataCompare) xmlC14NAttrsCompare);
645 if (list == NULL) {
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000646#ifdef DEBUG_C14N
647 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +0000648 "xmlC14NProcessAttrsAxis: list creation failed\n");
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000649#endif
Daniel Veillard9ff88172002-03-11 09:15:32 +0000650 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000651 }
652
653 /*
654 * Add all visible attributes from current node.
655 */
656 attr = cur->properties;
Daniel Veillard9ff88172002-03-11 09:15:32 +0000657 while (attr != NULL) {
658 /* check that attribute is visible */
659 if (xmlC14NIsVisible(ctx, attr)) {
660 xmlListInsert(list, attr);
661 }
662 attr = attr->next;
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000663 }
Daniel Veillard9ff88172002-03-11 09:15:32 +0000664
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000665 /*
666 * include attributes in "xml" namespace defined in ancestors
667 * (only for non-exclusive XML Canonicalization)
668 */
Daniel Veillard9ff88172002-03-11 09:15:32 +0000669 if ((!ctx->exclusive) && (cur->parent != NULL)
670 && (!xmlC14NIsVisible(ctx, cur->parent))) {
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000671 /*
Daniel Veillard9ff88172002-03-11 09:15:32 +0000672 * If XPath node-set is not specified then the parent is always
673 * visible!
674 */
675 cur = cur->parent;
676 while (cur != NULL) {
677 attr = cur->properties;
678 while (attr != NULL) {
679 if ((attr->ns != NULL)
680 && (xmlStrEqual(attr->ns->prefix, BAD_CAST "xml"))) {
681 if (xmlListSearch(list, attr) == NULL) {
682 xmlListInsert(list, attr);
683 }
684 }
685 attr = attr->next;
686 }
687 cur = cur->parent;
688 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000689 }
Daniel Veillard9ff88172002-03-11 09:15:32 +0000690
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000691 /*
692 * print out all elements from list
693 */
Daniel Veillard9ff88172002-03-11 09:15:32 +0000694 xmlListWalk(list, (xmlListWalker) xmlC14NPrintAttrs,
695 (const void *) ctx);
696
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000697 /*
698 * Cleanup
699 */
Daniel Veillard9ff88172002-03-11 09:15:32 +0000700 xmlListDelete(list);
701 return (0);
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000702}
703
704/**
705 * xmlC14NCheckForRelativeNamespaces:
706 * @ctx: the C14N context
707 * @cur: the current element node
708 *
709 * Checks that current element node has no relative namespaces defined
710 *
711 * Returns 0 if the node has no relative namespaces or -1 otherwise.
712 */
713static int
Daniel Veillard9ff88172002-03-11 09:15:32 +0000714xmlC14NCheckForRelativeNamespaces(xmlC14NCtxPtr ctx, xmlNodePtr cur)
715{
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000716 xmlNsPtr ns;
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000717
Daniel Veillard9ff88172002-03-11 09:15:32 +0000718 if ((ctx == NULL) || (cur == NULL) || (cur->type != XML_ELEMENT_NODE)) {
719#ifdef DEBUG_C14N
720 xmlGenericError(xmlGenericErrorContext,
721 "xmlC14NCheckForRelativeNamespaces: Null context or node pointer or type != XML_ELEMENT_NODE.\n");
722#endif
723 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000724 }
Daniel Veillard9ff88172002-03-11 09:15:32 +0000725
726 ns = cur->nsDef;
727 while (ns != NULL) {
728 if (xmlStrlen(ns->href) > 0) {
729 xmlURIPtr uri;
730
731 uri = xmlParseURI((const char *) ns->href);
732 if (uri == NULL) {
733#ifdef DEBUG_C14N
734 xmlGenericError(xmlGenericErrorContext,
735 "xmlC14NCheckForRelativeNamespaces: unable to parse uri=\"%s\".\n",
736 ns->href);
737#endif
738 return (-1);
739 }
740 if (xmlStrlen((const xmlChar *) uri->scheme) == 0) {
741 xmlFreeURI(uri);
742 return (-1);
743 }
744 if ((!xmlStrEqual
745 ((const xmlChar *) uri->scheme, BAD_CAST "urn"))
746 && (xmlStrlen((const xmlChar *) uri->server) == 0)) {
747 xmlFreeURI(uri);
748 return (-1);
749 }
750 xmlFreeURI(uri);
751 }
752 ns = ns->next;
753 }
754 return (0);
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000755}
756
757/**
758 * xmlC14NProcessElementNode:
759 * @ctx: the pointer to C14N context object
760 * @cur: the node to process
761 *
762 * Canonical XML v 1.0 (http://www.w3.org/TR/xml-c14n)
763 *
764 * Element Nodes
765 * If the element is not in the node-set, then the result is obtained
766 * by processing the namespace axis, then the attribute axis, then
767 * processing the child nodes of the element that are in the node-set
768 * (in document order). If the element is in the node-set, then the result
769 * is an open angle bracket (<), the element QName, the result of
770 * processing the namespace axis, the result of processing the attribute
771 * axis, a close angle bracket (>), the result of processing the child
772 * nodes of the element that are in the node-set (in document order), an
773 * open angle bracket, a forward slash (/), the element QName, and a close
774 * angle bracket.
775 *
776 * Returns non-negative value on success or negative value on fail
777 */
778static int
Daniel Veillard9ff88172002-03-11 09:15:32 +0000779xmlC14NProcessElementNode(xmlC14NCtxPtr ctx, xmlNodePtr cur, int visible)
780{
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000781 int ret;
782 int ns_rendered_pos = 0;
783
Daniel Veillard9ff88172002-03-11 09:15:32 +0000784 if ((ctx == NULL) || (cur == NULL) || (cur->type != XML_ELEMENT_NODE)) {
785#ifdef DEBUG_C14N
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000786 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +0000787 "xmlC14NProcessElementNode: Null context or node pointer or type != XML_ELEMENT_NODE.\n");
788#endif
789 return (-1);
790 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000791
792 /*
793 * Check relative relative namespaces:
794 * implementations of XML canonicalization MUST report an operation
795 * failure on documents containing relative namespace URIs.
796 */
Daniel Veillard9ff88172002-03-11 09:15:32 +0000797 if (xmlC14NCheckForRelativeNamespaces(ctx, cur) < 0) {
798#ifdef DEBUG_C14N
799 xmlGenericError(xmlGenericErrorContext,
800 "xmlC14NProcessElementNode: xmlC14NCheckForRelativeNamespaces failed.\n");
801#endif
802 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000803 }
804
805
806 /*
807 * Save ns_rendered stack position for exclusive
808 * processing
809 */
Daniel Veillard9ff88172002-03-11 09:15:32 +0000810 if ((ctx->exclusive) && (ctx->ns_rendered != NULL)) {
811 ns_rendered_pos = ctx->ns_rendered->nodeNr;
812 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000813
Daniel Veillard9ff88172002-03-11 09:15:32 +0000814 if (visible) {
815 if (ctx->parent_is_doc) {
816 ctx->pos = XMLC14N_INSIDE_DOCUMENT_ELEMENT;
817 }
818 xmlOutputBufferWriteString(ctx->buf, "<");
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000819
Daniel Veillard9ff88172002-03-11 09:15:32 +0000820 if ((cur->ns != NULL) && (xmlStrlen(cur->ns->prefix) > 0)) {
821 xmlOutputBufferWriteString(ctx->buf,
822 (const char *) cur->ns->prefix);
823 xmlOutputBufferWriteString(ctx->buf, ":");
824 }
825 xmlOutputBufferWriteString(ctx->buf, (const char *) cur->name);
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000826
Daniel Veillard9ff88172002-03-11 09:15:32 +0000827 if (ctx->exclusive) {
828 ret = xmlExcC14NProcessNamespacesAxis(ctx, cur);
829 } else {
830 ret = xmlC14NProcessNamespacesAxis(ctx, cur);
831 }
832 if (ret < 0) {
833#ifdef DEBUG_C14N
834 xmlGenericError(xmlGenericErrorContext,
835 "xmlC14NProcessElementNode: xmlC14NProcessNamespacesAxis failed.\n");
836#endif
837 return (-1);
838 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000839
Daniel Veillard9ff88172002-03-11 09:15:32 +0000840 ret = xmlC14NProcessAttrsAxis(ctx, cur);
841 if (ret < 0) {
842#ifdef DEBUG_C14N
843 xmlGenericError(xmlGenericErrorContext,
844 "xmlC14NProcessElementNode: xmlC14NProcessAttrsAxis failed.\n");
845#endif
846 return (-1);
847 }
848
849 xmlOutputBufferWriteString(ctx->buf, ">");
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000850 }
851 if (cur->children != NULL) {
Daniel Veillard9ff88172002-03-11 09:15:32 +0000852 ret = xmlC14NProcessNodeList(ctx, cur->children);
853 if (ret < 0) {
854#ifdef DEBUG_C14N
855 xmlGenericError(xmlGenericErrorContext,
856 "xmlC14NProcessElementNode: xmlC14NProcessNodeList failed.\n");
857#endif
858 return (-1);
859 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000860 }
Daniel Veillard9ff88172002-03-11 09:15:32 +0000861 if (visible) {
862 xmlOutputBufferWriteString(ctx->buf, "</");
863 if ((cur->ns != NULL) && (xmlStrlen(cur->ns->prefix) > 0)) {
864 xmlOutputBufferWriteString(ctx->buf,
865 (const char *) cur->ns->prefix);
866 xmlOutputBufferWriteString(ctx->buf, ":");
867 }
868 xmlOutputBufferWriteString(ctx->buf, (const char *) cur->name);
869 xmlOutputBufferWriteString(ctx->buf, ">");
870 if (ctx->parent_is_doc) {
871 ctx->pos = XMLC14N_AFTER_DOCUMENT_ELEMENT;
872 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000873 }
874
875 /*
876 * Restore ns_rendered stack position for exclusive
877 * processing
878 */
Daniel Veillard9ff88172002-03-11 09:15:32 +0000879 if ((ctx->exclusive) && (ctx->ns_rendered != NULL)) {
880 ctx->ns_rendered->nodeNr = ns_rendered_pos;
881 }
882 return (0);
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000883}
884
885/**
886 * xmlC14NProcessNode:
887 * @ctx: the pointer to C14N context object
888 * @cur: the node to process
889 *
890 * Processes the given node
891 *
892 * Returns non-negative value on success or negative value on fail
893 */
894static int
Daniel Veillard9ff88172002-03-11 09:15:32 +0000895xmlC14NProcessNode(xmlC14NCtxPtr ctx, xmlNodePtr cur)
896{
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000897 int ret = 0;
898 int visible;
Daniel Veillard9ff88172002-03-11 09:15:32 +0000899
900 if ((ctx == NULL) || (cur == NULL)) {
901#ifdef DEBUG_C14N
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000902 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +0000903 "xmlC14NProcessNode: Null context or node pointer.\n");
904#endif
905 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000906 }
907
908 visible = xmlC14NIsVisible(ctx, cur);
Daniel Veillard9ff88172002-03-11 09:15:32 +0000909 switch (cur->type) {
910 case XML_ELEMENT_NODE:
911 ret = xmlC14NProcessElementNode(ctx, cur, visible);
912 break;
913 case XML_CDATA_SECTION_NODE:
914 case XML_TEXT_NODE:
915 /*
916 * Text Nodes
917 * the string value, except all ampersands are replaced
918 * by &amp;, all open angle brackets (<) are replaced by &lt;, all closing
919 * angle brackets (>) are replaced by &gt;, and all #xD characters are
920 * replaced by &#xD;.
921 */
922 /* cdata sections are processed as text nodes */
923 /* todo: verify that cdata sections are included in XPath nodes set */
924 if ((visible) && (cur->content != NULL)) {
925 xmlChar *buffer;
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000926
Daniel Veillard9ff88172002-03-11 09:15:32 +0000927 buffer = xmlC11NNormalizeText(cur->content);
928 if (buffer != NULL) {
929 xmlOutputBufferWriteString(ctx->buf,
930 (const char *) buffer);
931 xmlFree(buffer);
932 } else {
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000933#ifdef DEBUG_C14N
Daniel Veillard9ff88172002-03-11 09:15:32 +0000934 xmlGenericError(xmlGenericErrorContext,
935 "xmlC14NProcessNode: xmlC11NNormalizeText() failed\n");
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000936#endif
Daniel Veillard9ff88172002-03-11 09:15:32 +0000937 return (-1);
938 }
939 }
940 break;
941 case XML_PI_NODE:
942 /*
943 * Processing Instruction (PI) Nodes-
944 * The opening PI symbol (<?), the PI target name of the node,
945 * a leading space and the string value if it is not empty, and
946 * the closing PI symbol (?>). If the string value is empty,
947 * then the leading space is not added. Also, a trailing #xA is
948 * rendered after the closing PI symbol for PI children of the
949 * root node with a lesser document order than the document
950 * element, and a leading #xA is rendered before the opening PI
951 * symbol of PI children of the root node with a greater document
952 * order than the document element.
953 */
954 if (visible) {
955 if (ctx->pos == XMLC14N_AFTER_DOCUMENT_ELEMENT) {
956 xmlOutputBufferWriteString(ctx->buf, "\x0A<?");
957 } else {
958 xmlOutputBufferWriteString(ctx->buf, "<?");
959 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000960
Daniel Veillard9ff88172002-03-11 09:15:32 +0000961 xmlOutputBufferWriteString(ctx->buf,
962 (const char *) cur->name);
963 if ((cur->content != NULL) && (*(cur->content) != '\0')) {
964 xmlChar *buffer;
965
966 xmlOutputBufferWriteString(ctx->buf, " ");
967
968 /* todo: do we need to normalize pi? */
969 buffer = xmlC11NNormalizePI(cur->content);
970 if (buffer != NULL) {
971 xmlOutputBufferWriteString(ctx->buf,
972 (const char *) buffer);
973 xmlFree(buffer);
974 } else {
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000975#ifdef DEBUG_C14N
Daniel Veillard9ff88172002-03-11 09:15:32 +0000976 xmlGenericError(xmlGenericErrorContext,
977 "xmlC14NProcessNode: xmlC11NNormalizePI() failed\n");
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000978#endif
Daniel Veillard9ff88172002-03-11 09:15:32 +0000979 return (-1);
980 }
981 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000982
Daniel Veillard9ff88172002-03-11 09:15:32 +0000983 if (ctx->pos == XMLC14N_BEFORE_DOCUMENT_ELEMENT) {
984 xmlOutputBufferWriteString(ctx->buf, "?>\x0A");
985 } else {
986 xmlOutputBufferWriteString(ctx->buf, "?>");
987 }
988 }
989 break;
990 case XML_COMMENT_NODE:
991 /*
992 * Comment Nodes
993 * Nothing if generating canonical XML without comments. For
994 * canonical XML with comments, generate the opening comment
995 * symbol (<!--), the string value of the node, and the
996 * closing comment symbol (-->). Also, a trailing #xA is rendered
997 * after the closing comment symbol for comment children of the
998 * root node with a lesser document order than the document
999 * element, and a leading #xA is rendered before the opening
1000 * comment symbol of comment children of the root node with a
1001 * greater document order than the document element. (Comment
1002 * children of the root node represent comments outside of the
1003 * top-level document element and outside of the document type
1004 * declaration).
1005 */
1006 if (visible && ctx->with_comments) {
1007 if (ctx->pos == XMLC14N_AFTER_DOCUMENT_ELEMENT) {
1008 xmlOutputBufferWriteString(ctx->buf, "\x0A<!--");
1009 } else {
1010 xmlOutputBufferWriteString(ctx->buf, "<!--");
1011 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001012
Daniel Veillard9ff88172002-03-11 09:15:32 +00001013 if (cur->content != NULL) {
1014 xmlChar *buffer;
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001015
Daniel Veillard9ff88172002-03-11 09:15:32 +00001016 /* todo: do we need to normalize comment? */
1017 buffer = xmlC11NNormalizeComment(cur->content);
1018 if (buffer != NULL) {
1019 xmlOutputBufferWriteString(ctx->buf,
1020 (const char *) buffer);
1021 xmlFree(buffer);
1022 } else {
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001023#ifdef DEBUG_C14N
Daniel Veillard9ff88172002-03-11 09:15:32 +00001024 xmlGenericError(xmlGenericErrorContext,
1025 "xmlC14NProcessNode: xmlC11NNormalizeComment() failed\n");
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001026#endif
Daniel Veillard9ff88172002-03-11 09:15:32 +00001027 return (-1);
1028 }
1029 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001030
Daniel Veillard9ff88172002-03-11 09:15:32 +00001031 if (ctx->pos == XMLC14N_BEFORE_DOCUMENT_ELEMENT) {
1032 xmlOutputBufferWriteString(ctx->buf, "-->\x0A");
1033 } else {
1034 xmlOutputBufferWriteString(ctx->buf, "-->");
1035 }
1036 }
1037 break;
1038 case XML_DOCUMENT_NODE:
1039 case XML_DOCUMENT_FRAG_NODE: /* should be processed as document? */
1040 case XML_DOCB_DOCUMENT_NODE: /* should be processed as document? */
1041 case XML_HTML_DOCUMENT_NODE: /* should be processed as document? */
1042 if (cur->children != NULL) {
1043 ctx->pos = XMLC14N_BEFORE_DOCUMENT_ELEMENT;
1044 ctx->parent_is_doc = 1;
1045 ret = xmlC14NProcessNodeList(ctx, cur->children);
1046 }
1047 break;
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001048
Daniel Veillard9ff88172002-03-11 09:15:32 +00001049 case XML_ATTRIBUTE_NODE:
1050 xmlGenericError(xmlGenericErrorContext,
1051 "xmlC14NProcessNode: XML_ATTRIBUTE_NODE is illegal here\n");
1052 return (-1);
1053 case XML_NAMESPACE_DECL:
1054 xmlGenericError(xmlGenericErrorContext,
1055 "xmlC14NProcessNode: XML_NAMESPACE_DECL is illegal here\n");
1056 return (-1);
1057 case XML_ENTITY_REF_NODE:
1058 xmlGenericError(xmlGenericErrorContext,
1059 "xmlC14NProcessNode: XML_ENTITY_REF_NODE is illegal here\n");
1060 return (-1);
1061 case XML_ENTITY_NODE:
1062 xmlGenericError(xmlGenericErrorContext,
1063 "xmlC14NProcessNode: XML_ENTITY_NODE is illegal here\n");
1064 return (-1);
1065
1066 case XML_DOCUMENT_TYPE_NODE:
1067 case XML_NOTATION_NODE:
1068 case XML_DTD_NODE:
1069 case XML_ELEMENT_DECL:
1070 case XML_ATTRIBUTE_DECL:
1071 case XML_ENTITY_DECL:
1072 case XML_XINCLUDE_START:
1073 case XML_XINCLUDE_END:
1074 /*
1075 * should be ignored according to "W3C Canonical XML"
1076 */
1077 break;
1078 default:
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001079#ifdef DEBUG_C14N
Daniel Veillard9ff88172002-03-11 09:15:32 +00001080 xmlGenericError(xmlGenericErrorContext,
1081 "xmlC14NProcessNode: unknown node type = %d\n",
1082 cur->type);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001083#endif
Daniel Veillard9ff88172002-03-11 09:15:32 +00001084 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001085 }
1086
Daniel Veillard9ff88172002-03-11 09:15:32 +00001087 return (ret);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001088}
1089
1090/**
1091 * xmlC14NProcessNodeList:
1092 * @ctx: the pointer to C14N context object
1093 * @cur: the node to start from
1094 *
1095 * Processes all nodes in the row starting from cur.
1096 *
1097 * Returns non-negative value on success or negative value on fail
1098 */
1099static int
Daniel Veillard9ff88172002-03-11 09:15:32 +00001100xmlC14NProcessNodeList(xmlC14NCtxPtr ctx, xmlNodePtr cur)
1101{
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001102 int ret;
Daniel Veillard9ff88172002-03-11 09:15:32 +00001103
1104 if (ctx == NULL) {
1105#ifdef DEBUG_C14N
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001106 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001107 "xmlC14NProcessNodeList: Null context pointer.\n");
1108#endif
1109 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001110 }
1111
Daniel Veillard9ff88172002-03-11 09:15:32 +00001112 for (ret = 0; cur != NULL && ret >= 0; cur = cur->next) {
1113 ret = xmlC14NProcessNode(ctx, cur);
1114 }
1115 return (ret);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001116}
1117
1118
1119/**
1120 * xmlC14NFreeCtx:
1121 * @ctx: the pointer to C14N context object
1122 *
1123 * Cleanups the C14N context object.
1124 */
1125
1126static void
Daniel Veillard9ff88172002-03-11 09:15:32 +00001127xmlC14NFreeCtx(xmlC14NCtxPtr ctx)
1128{
1129 if (ctx == NULL) {
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001130#ifdef DEBUG_C14N
1131 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001132 "xmlC14NFreeCtx: ctx == NULL\n");
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001133#endif
Daniel Veillard9ff88172002-03-11 09:15:32 +00001134 return;
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001135 }
Daniel Veillard9ff88172002-03-11 09:15:32 +00001136
1137 if (ctx->ns_rendered != NULL) {
1138 xmlXPathFreeNodeSet(ctx->ns_rendered);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001139 }
1140 xmlFree(ctx);
1141}
1142
1143/**
1144 * xmlC14NNewCtx:
1145 * @doc: the XML document for canonization
1146 * @nodes: the nodes set to be included in the canonized image
1147 * or NULL if all document nodes should be included
1148 * @exclusive: the exclusive flag (0 - non-exclusive canonicalization;
1149 * otherwise - exclusive canonicalization)
1150 * @inclusive_ns_prefixe the list of inclusive namespace prefixes
1151 * ended with a NULL or NULL if there is no
1152 * inclusive namespaces (only for exclusive
1153 * canonicalization)
1154 * @with_comments: include comments in the result (!=0) or not (==0)
1155 * @buf: the output buffer to store canonical XML; this
1156 * buffer MUST have encoder==NULL because C14N requires
1157 * UTF-8 output
1158 *
1159 * Creates new C14N context object to store C14N parameters.
1160 *
1161 * Returns pointer to newly created object (success) or NULL (fail)
1162 */
1163static xmlC14NCtxPtr
Daniel Veillard9ff88172002-03-11 09:15:32 +00001164xmlC14NNewCtx(xmlDocPtr doc, xmlNodeSetPtr nodes,
1165 int exclusive, xmlChar ** inclusive_ns_prefixes,
1166 int with_comments, xmlOutputBufferPtr buf)
1167{
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001168 xmlC14NCtxPtr ctx;
1169
Daniel Veillard9ff88172002-03-11 09:15:32 +00001170 if ((doc == NULL) || (buf == NULL)) {
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001171#ifdef DEBUG_C14N
1172 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001173 "xmlC14NNewCtx: pointer to document or output buffer is NULL\n");
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001174#endif
Daniel Veillard9ff88172002-03-11 09:15:32 +00001175 return (NULL);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001176 }
1177
1178 /*
1179 * Validate the encoding output buffer encoding
1180 */
Daniel Veillard9ff88172002-03-11 09:15:32 +00001181 if (buf->encoder != NULL) {
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001182 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001183 "xmlC14NNewCtx: output buffer encoder != NULL but C14N requires UTF8 output\n");
1184 return (NULL);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001185 }
1186
1187 /*
1188 * Validate the XML document encoding value, if provided.
1189 */
Daniel Veillard9ff88172002-03-11 09:15:32 +00001190 if (doc->charset != XML_CHAR_ENCODING_UTF8) {
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001191 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001192 "xmlC14NNewCtx: source document not in UTF8\n");
1193 return (NULL);
1194 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001195
1196 /*
1197 * Allocate a new xmlC14NCtxPtr and fill the fields.
1198 */
1199 ctx = (xmlC14NCtxPtr) xmlMalloc(sizeof(xmlC14NCtx));
1200 if (ctx == NULL) {
1201 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001202 "xmlC14NNewCtx: malloc failed\n");
1203 return (NULL);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001204 }
1205 memset(ctx, 0, sizeof(xmlC14NCtx));
1206
1207 /*
1208 * initialize C14N context
Daniel Veillard9ff88172002-03-11 09:15:32 +00001209 */
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001210 ctx->doc = doc;
1211 ctx->with_comments = with_comments;
1212 ctx->visible_nodes = nodes;
1213 ctx->buf = buf;
1214 ctx->parent_is_doc = 1;
1215 ctx->pos = XMLC14N_BEFORE_DOCUMENT_ELEMENT;
1216
1217 /*
1218 * Set "exclusive" flag, create a nodes set for namespaces
1219 * stack and remember list of incluseve prefixes
1220 */
Daniel Veillard9ff88172002-03-11 09:15:32 +00001221 if (exclusive) {
1222 ctx->exclusive = 1;
1223 ctx->ns_rendered = xmlXPathNodeSetCreate(NULL);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001224 ctx->inclusive_ns_prefixes = inclusive_ns_prefixes;
1225 }
Daniel Veillard9ff88172002-03-11 09:15:32 +00001226 return (ctx);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001227}
1228
1229/**
1230 * xmlC14NDocSaveTo:
1231 * @doc: the XML document for canonization
1232 * @nodes: the nodes set to be included in the canonized image
1233 * or NULL if all document nodes should be included
1234 * @exclusive: the exclusive flag (0 - non-exclusive canonicalization;
1235 * otherwise - exclusive canonicalization)
Daniel Veillarddb1bdba2002-03-09 14:13:11 +00001236 * @inclusive_ns_prefixes: the list of inclusive namespace prefixes
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001237 * ended with a NULL or NULL if there is no
1238 * inclusive namespaces (only for exclusive
1239 * canonicalization, ignored otherwise)
1240 * @with_comments: include comments in the result (!=0) or not (==0)
1241 * @buf: the output buffer to store canonical XML; this
1242 * buffer MUST have encoder==NULL because C14N requires
1243 * UTF-8 output
1244 *
1245 * Dumps the canonized image of given XML document into the provided buffer.
1246 * For details see "Canonical XML" (http://www.w3.org/TR/xml-c14n) or
1247 * "Exclusive XML Canonicalization" (http://www.w3.org/TR/xml-exc-c14n)
1248 *
1249 * Returns non-negative value on success or a negative value on fail
1250 */
1251int
Daniel Veillard9ff88172002-03-11 09:15:32 +00001252xmlC14NDocSaveTo(xmlDocPtr doc, xmlNodeSetPtr nodes,
1253 int exclusive, xmlChar ** inclusive_ns_prefixes,
1254 int with_comments, xmlOutputBufferPtr buf)
1255{
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001256 xmlC14NCtxPtr ctx;
1257 int ret;
Daniel Veillard9ff88172002-03-11 09:15:32 +00001258
1259 if ((buf == NULL) || (doc == NULL)) {
1260#ifdef DEBUG_C14N
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001261 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001262 "xmlC14NDocSaveTo: null return buffer or doc pointer\n");
1263#endif
1264 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001265 }
1266
1267 /*
1268 * Validate the encoding output buffer encoding
1269 */
Daniel Veillard9ff88172002-03-11 09:15:32 +00001270 if (buf->encoder != NULL) {
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001271 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001272 "xmlC14NDocSaveTo: output buffer encoder != NULL but C14N requires UTF8 output\n");
1273 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001274 }
1275
1276 ctx = xmlC14NNewCtx(doc, nodes, exclusive, inclusive_ns_prefixes,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001277 with_comments, buf);
1278 if (ctx == NULL) {
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001279 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001280 "xmlC14NDocSaveTo: unable to create C14N context\n");
1281 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001282 }
1283
1284
1285
1286 /*
1287 * Root Node
1288 * The root node is the parent of the top-level document element. The
1289 * result of processing each of its child nodes that is in the node-set
1290 * in document order. The root node does not generate a byte order mark,
1291 * XML declaration, nor anything from within the document type
1292 * declaration.
1293 */
Daniel Veillard9ff88172002-03-11 09:15:32 +00001294 if (doc->children != NULL) {
1295 ret = xmlC14NProcessNodeList(ctx, doc->children);
1296 if (ret < 0) {
1297#ifdef DEBUG_C14N
1298 xmlGenericError(xmlGenericErrorContext,
1299 "xmlC14NDocSaveTo: process childrens' list failed.\n");
1300#endif
1301 xmlC14NFreeCtx(ctx);
1302 return (-1);
1303 }
1304 }
1305
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001306 /*
1307 * Flush buffer to get number of bytes written
1308 */
1309 ret = xmlOutputBufferFlush(buf);
Daniel Veillard9ff88172002-03-11 09:15:32 +00001310 if (ret < 0) {
1311#ifdef DEBUG_C14N
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001312 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001313 "xmlC14NDocSaveTo: buffer flush failed.\n");
1314#endif
1315 xmlC14NFreeCtx(ctx);
1316 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001317 }
1318
1319 /*
1320 * Cleanup
1321 */
Daniel Veillard9ff88172002-03-11 09:15:32 +00001322 xmlC14NFreeCtx(ctx);
1323 return (ret);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001324}
1325
1326/**
1327 * xmlC14NDocDumpMemory:
1328 * @doc: the XML document for canonization
1329 * @nodes: the nodes set to be included in the canonized image
1330 * or NULL if all document nodes should be included
1331 * @exclusive: the exclusive flag (0 - non-exclusive canonicalization;
1332 * otherwise - exclusive canonicalization)
Daniel Veillarddb1bdba2002-03-09 14:13:11 +00001333 * @inclusive_ns_prefixes: the list of inclusive namespace prefixes
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001334 * ended with a NULL or NULL if there is no
1335 * inclusive namespaces (only for exclusive
1336 * canonicalization, ignored otherwise)
1337 * @with_comments: include comments in the result (!=0) or not (==0)
1338 * @doc_txt_ptr: the memory pointer for allocated canonical XML text;
1339 * the caller of this functions is responsible for calling
1340 * xmlFree() to free allocated memory
1341 *
1342 * Dumps the canonized image of given XML document into memory.
1343 * For details see "Canonical XML" (http://www.w3.org/TR/xml-c14n) or
1344 * "Exclusive XML Canonicalization" (http://www.w3.org/TR/xml-exc-c14n)
1345 *
1346 * Returns the number of bytes written on success or a negative value on fail
1347 */
1348int
Daniel Veillard9ff88172002-03-11 09:15:32 +00001349xmlC14NDocDumpMemory(xmlDocPtr doc, xmlNodeSetPtr nodes,
1350 int exclusive, xmlChar ** inclusive_ns_prefixes,
1351 int with_comments, xmlChar ** doc_txt_ptr)
1352{
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001353 int ret;
1354 xmlOutputBufferPtr buf;
1355
1356 if (doc_txt_ptr == NULL) {
Daniel Veillard9ff88172002-03-11 09:15:32 +00001357#ifdef DEBUG_C14N
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001358 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001359 "xmlC14NDocDumpMemory: null return buffer pointer\n");
1360#endif
1361 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001362 }
1363
1364 *doc_txt_ptr = NULL;
Daniel Veillard9ff88172002-03-11 09:15:32 +00001365
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001366 /*
1367 * create memory buffer with UTF8 (default) encoding
1368 */
1369 buf = xmlAllocOutputBuffer(NULL);
Daniel Veillard9ff88172002-03-11 09:15:32 +00001370 if (buf == NULL) {
1371#ifdef DEBUG_C14N
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001372 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001373 "xmlC14NDocDumpMemory: failed to allocate output buffer.\n");
1374#endif
1375 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001376 }
1377
1378 /*
1379 * canonize document and write to buffer
1380 */
Daniel Veillard9ff88172002-03-11 09:15:32 +00001381 ret = xmlC14NDocSaveTo(doc, nodes, exclusive, inclusive_ns_prefixes,
1382 with_comments, buf);
1383 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 "xmlC14NDocDumpMemory: xmlC14NDocSaveTo failed.\n");
1387#endif
1388 (void) xmlOutputBufferClose(buf);
1389 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001390 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001391
Daniel Veillard9ff88172002-03-11 09:15:32 +00001392 ret = buf->buffer->use;
1393 if (ret > 0) {
1394 *doc_txt_ptr = xmlStrndup(buf->buffer->content, ret);
1395 }
1396 (void) xmlOutputBufferClose(buf);
1397
1398 if ((*doc_txt_ptr == NULL) && (ret > 0)) {
1399#ifdef DEBUG_C14N
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001400 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001401 "xmlC14NDocDumpMemory: failed to allocate memory for document text representation\n");
1402#endif
1403 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001404 }
Daniel Veillard9ff88172002-03-11 09:15:32 +00001405 return (ret);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001406}
1407
1408/**
1409 * xmlC14NDocSave:
1410 * @doc: the XML document for canonization
1411 * @nodes: the nodes set to be included in the canonized image
1412 * or NULL if all document nodes should be included
1413 * @exclusive: the exclusive flag (0 - non-exclusive canonicalization;
1414 * otherwise - exclusive canonicalization)
Daniel Veillarddb1bdba2002-03-09 14:13:11 +00001415 * @inclusive_ns_prefixes: the list of inclusive namespace prefixes
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001416 * ended with a NULL or NULL if there is no
1417 * inclusive namespaces (only for exclusive
1418 * canonicalization, ignored otherwise)
1419 * @with_comments: include comments in the result (!=0) or not (==0)
1420 * @filename: the filename to store canonical XML image
1421 * @compression: the compression level (zlib requred):
1422 * -1 - libxml default,
1423 * 0 - uncompressed,
1424 * >0 - compression level
1425 *
1426 * Dumps the canonized image of given XML document into the file.
1427 * For details see "Canonical XML" (http://www.w3.org/TR/xml-c14n) or
1428 * "Exclusive XML Canonicalization" (http://www.w3.org/TR/xml-exc-c14n)
1429 *
1430 * Returns the number of bytes written success or a negative value on fail
1431 */
1432int
Daniel Veillard9ff88172002-03-11 09:15:32 +00001433xmlC14NDocSave(xmlDocPtr doc, xmlNodeSetPtr nodes,
1434 int exclusive, xmlChar ** inclusive_ns_prefixes,
1435 int with_comments, const char *filename, int compression)
1436{
1437 xmlOutputBufferPtr buf;
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001438 int ret;
1439
Daniel Veillard9ff88172002-03-11 09:15:32 +00001440 if (filename == NULL) {
1441#ifdef DEBUG_C14N
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001442 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001443 "xmlC14NDocSave: filename is NULL\n");
1444#endif
1445 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001446 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001447#ifdef HAVE_ZLIB_H
Daniel Veillard9ff88172002-03-11 09:15:32 +00001448 if (compression < 0)
1449 compression = xmlGetCompressMode();
1450#endif
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001451
1452 /*
1453 * save the content to a temp buffer, use default UTF8 encoding.
1454 */
1455 buf = xmlOutputBufferCreateFilename(filename, NULL, compression);
1456 if (buf == NULL) {
Daniel Veillard9ff88172002-03-11 09:15:32 +00001457#ifdef DEBUG_C14N
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001458 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001459 "xmlC14NDocSave: unable to create buffer for file=\"%s\" with compressin=%d\n",
1460 filename, compression);
1461#endif
1462 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001463 }
Daniel Veillard9ff88172002-03-11 09:15:32 +00001464
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001465 /*
1466 * canonize document and write to buffer
1467 */
Daniel Veillard9ff88172002-03-11 09:15:32 +00001468 ret = xmlC14NDocSaveTo(doc, nodes, exclusive, inclusive_ns_prefixes,
1469 with_comments, buf);
1470 if (ret < 0) {
1471#ifdef DEBUG_C14N
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001472 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001473 "xmlC14NDocSave: xmlC14NDocSaveTo failed.\n");
1474#endif
1475 (void) xmlOutputBufferClose(buf);
1476 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001477 }
Daniel Veillard9ff88172002-03-11 09:15:32 +00001478
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001479 /*
1480 * get the numbers of bytes written
1481 */
1482 ret = xmlOutputBufferClose(buf);
Daniel Veillard9ff88172002-03-11 09:15:32 +00001483 return (ret);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001484}
1485
1486
1487
1488/*
1489 * Macro used to grow the current buffer.
1490 */
1491#define growBufferReentrant() { \
1492 buffer_size *= 2; \
1493 buffer = (xmlChar *) \
1494 xmlRealloc(buffer, buffer_size * sizeof(xmlChar)); \
1495 if (buffer == NULL) { \
1496 perror("realloc failed"); \
1497 return(NULL); \
1498 } \
1499}
1500
1501/**
1502 * xmlC11NNormalizeString:
1503 * @input: the input string
1504 * @mode: the normalization mode (attribute, comment, PI or text)
1505 *
1506 * Converts a string to a canonical (normalized) format. The code is stolen
1507 * from xmlEncodeEntitiesReentrant(). Added normalization of \x09, \x0a, \x0A
1508 * and the @mode parameter
1509 *
1510 * Returns a normalized string (caller is responsible for calling xmlFree())
1511 * or NULL if an error occurs
1512 */
1513static xmlChar *
Daniel Veillard9ff88172002-03-11 09:15:32 +00001514xmlC11NNormalizeString(const xmlChar * input,
1515 xmlC14NNormalizationMode mode)
1516{
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001517 const xmlChar *cur = input;
1518 xmlChar *buffer = NULL;
1519 xmlChar *out = NULL;
1520 int buffer_size = 0;
1521
Daniel Veillard9ff88172002-03-11 09:15:32 +00001522 if (input == NULL)
1523 return (NULL);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001524
1525 /*
1526 * allocate an translation buffer.
1527 */
1528 buffer_size = 1000;
1529 buffer = (xmlChar *) xmlMalloc(buffer_size * sizeof(xmlChar));
1530 if (buffer == NULL) {
Daniel Veillard9ff88172002-03-11 09:15:32 +00001531 perror("malloc failed");
1532 return (NULL);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001533 }
1534 out = buffer;
1535
1536 while (*cur != '\0') {
Daniel Veillard9ff88172002-03-11 09:15:32 +00001537 if ((out - buffer) > (buffer_size - 10)) {
1538 int indx = out - buffer;
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001539
Daniel Veillard9ff88172002-03-11 09:15:32 +00001540 growBufferReentrant();
1541 out = &buffer[indx];
1542 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001543
Daniel Veillard9ff88172002-03-11 09:15:32 +00001544 if ((*cur == '<') && ((mode == XMLC14N_NORMALIZE_ATTR) ||
1545 (mode == XMLC14N_NORMALIZE_TEXT))) {
1546 *out++ = '&';
1547 *out++ = 'l';
1548 *out++ = 't';
1549 *out++ = ';';
1550 } else if ((*cur == '>') && (mode == XMLC14N_NORMALIZE_TEXT)) {
1551 *out++ = '&';
1552 *out++ = 'g';
1553 *out++ = 't';
1554 *out++ = ';';
1555 } else if ((*cur == '&') && ((mode == XMLC14N_NORMALIZE_ATTR) ||
1556 (mode == XMLC14N_NORMALIZE_TEXT))) {
1557 *out++ = '&';
1558 *out++ = 'a';
1559 *out++ = 'm';
1560 *out++ = 'p';
1561 *out++ = ';';
1562 } else if ((*cur == '"') && (mode == XMLC14N_NORMALIZE_ATTR)) {
1563 *out++ = '&';
1564 *out++ = 'q';
1565 *out++ = 'u';
1566 *out++ = 'o';
1567 *out++ = 't';
1568 *out++ = ';';
1569 } else if ((*cur == '\x09') && (mode == XMLC14N_NORMALIZE_ATTR)) {
1570 *out++ = '&';
1571 *out++ = '#';
1572 *out++ = 'x';
1573 *out++ = '9';
1574 *out++ = ';';
1575 } else if ((*cur == '\x0A') && (mode == XMLC14N_NORMALIZE_ATTR)) {
1576 *out++ = '&';
1577 *out++ = '#';
1578 *out++ = 'x';
1579 *out++ = 'A';
1580 *out++ = ';';
1581 } else if ((*cur == '\x0D') && ((mode == XMLC14N_NORMALIZE_ATTR) ||
1582 (mode == XMLC14N_NORMALIZE_TEXT) ||
1583 (mode == XMLC14N_NORMALIZE_COMMENT) ||
1584 (mode == XMLC14N_NORMALIZE_PI))) {
1585 *out++ = '&';
1586 *out++ = '#';
1587 *out++ = 'x';
1588 *out++ = 'D';
1589 *out++ = ';';
1590 } else {
1591 /*
1592 * Works because on UTF-8, all extended sequences cannot
1593 * result in bytes in the ASCII range.
1594 */
1595 *out++ = *cur;
1596 }
1597 cur++;
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001598 }
1599 *out++ = 0;
Daniel Veillard9ff88172002-03-11 09:15:32 +00001600 return (buffer);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001601}
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001602#endif /* LIBXML_C14N_ENABLED */