blob: 0a58a61e5f0a87660b9df63e3ec87ff85f2b9fbe [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;
Daniel Veillard5c396542002-03-15 07:57:50 +0000269 xmlNodePtr node;
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000270 xmlNsPtr prev;
Daniel Veillard5c396542002-03-15 07:57:50 +0000271
Daniel Veillard9ff88172002-03-11 09:15:32 +0000272 if ((ctx == NULL) || (cur == NULL) || (cur->type != XML_ELEMENT_NODE)) {
273#ifdef DEBUG_C14N
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000274 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +0000275 "xmlC14NProcessNamespacesAxis: Null context or node pointer or type != XML_ELEMENT_NODE.\n");
276#endif
277 return (-1);
278 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000279
280 /*
281 * Create a sorted list to store element namespaces
282 */
Daniel Veillard9ff88172002-03-11 09:15:32 +0000283 list =
284 xmlListCreate(NULL, (xmlListDataCompare) xmlC14NNamespacesCompare);
285 if (list == NULL) {
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000286#ifdef DEBUG_C14N
287 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +0000288 "xmlC14NProcessNamespacesAxis: list creation failed\n");
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000289#endif
Daniel Veillard9ff88172002-03-11 09:15:32 +0000290 return (-1);
291 }
292
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000293 /* find nearest visible parent */
294 visible_parent = cur->parent;
Daniel Veillard9ff88172002-03-11 09:15:32 +0000295 while ((visible_parent != NULL) &&
296 (!xmlC14NIsVisible(ctx, visible_parent))) {
297 visible_parent = visible_parent->parent;
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000298 }
299
300 /*
301 * todo: the libxml XPath implementation does not create
302 * nodes for all namespaces known to the node (i.e. for namespaces
303 * defined in node parents). By this we need to now walk thru
304 * all namespace in current node and all invisible ancesstors
305 */
Daniel Veillard5c396542002-03-15 07:57:50 +0000306 node = cur;
Daniel Veillard9ff88172002-03-11 09:15:32 +0000307 while (cur != visible_parent) {
308 for (ns = cur->nsDef; ns != NULL; ns = ns->next) {
309 /*
310 * first of all ignore default "xml" namespace and
311 * already included namespace
312 */
313 if ((xmlC14NIsXmlNs(ns)) || (xmlListSearch(list, ns) != NULL)) {
314 continue;
315 }
Daniel Veillard5c396542002-03-15 07:57:50 +0000316 prev = xmlSearchNs(ctx->doc, node, ns->prefix);
317 if(prev != ns) {
318 /* we already processed a namespace with this name */
319 continue;
320 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000321
Daniel Veillard9ff88172002-03-11 09:15:32 +0000322 /*
323 * Lookup nearest namespace after visible parent having
324 * the same prefix. Namespace included if and only if one of
325 * the following:
326 * - another namespace having the same prefix but
327 * different value found or
328 * - there is no namespaces having the same prefix and
329 * it is not a default xmlns="" namespace (empty prefix
330 * and empty href)
331 */
332 prev = xmlSearchNs(ctx->doc, visible_parent, ns->prefix);
333 if ((prev == NULL) && ((xmlStrlen(ns->prefix) > 0) ||
334 (xmlStrlen(ns->href) > 0))) {
335 xmlListInsert(list, ns);
336 } else if ((prev != NULL)
337 && (!xmlStrEqual(ns->href, prev->href))) {
338 xmlListInsert(list, ns);
339 }
340 }
341 cur = cur->parent;
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000342 }
343
344 /*
345 * print out all elements from list
346 */
Daniel Veillard9ff88172002-03-11 09:15:32 +0000347 xmlListWalk(list, (xmlListWalker) xmlC14NPrintNamespaces,
348 (const void *) ctx);
349
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000350 /*
351 * Cleanup
352 */
353 xmlListDelete(list);
Daniel Veillard9ff88172002-03-11 09:15:32 +0000354 return (0);
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000355}
356
357/**
358 * xmlExcC14NProcessNamespacesAxis:
359 * @ctx: the C14N context
360 * @node: the current node
361 *
362 * Prints out exclusive canonical namespace axis of the current node to the
363 * buffer from C14N context as follows
364 *
365 * Exclusive XML Canonicalization
366 * http://www.w3.org/TR/xml-exc-c14n
367 *
368 * If the element node is in the XPath subset then output the node in
369 * accordance with Canonical XML except for namespace nodes which are
370 * rendered as follows:
371 *
372 * 1. Render each namespace node iff:
373 * * it is visibly utilized by the immediate parent element or one of
374 * its attributes, or is present in InclusiveNamespaces PrefixList, and
375 * * its prefix and value do not appear in ns_rendered. ns_rendered is
376 * obtained by popping the state stack in order to obtain a list of
377 * prefixes and their values which have already been rendered by
378 * an output ancestor of the namespace node's parent element.
379 * 2. Append the rendered namespace node to the list ns_rendered of namespace
380 * nodes rendered by output ancestors. Push ns_rendered on state stack and
381 * recurse.
382 * 3. After the recursion returns, pop thestate stack.
383 *
384 *
385 * Returns 0 on success or -1 on fail.
386 */
387static int
Daniel Veillard9ff88172002-03-11 09:15:32 +0000388xmlExcC14NProcessNamespacesAxis(xmlC14NCtxPtr ctx, xmlNodePtr cur)
389{
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000390 xmlListPtr list;
391 xmlAttrPtr attr;
392 xmlNsPtr ns;
Daniel Veillard9ff88172002-03-11 09:15:32 +0000393
394 if ((ctx == NULL) || (cur == NULL) || (cur->type != XML_ELEMENT_NODE)) {
395#ifdef DEBUG_C14N
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000396 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +0000397 "xmlExcC14NProcessNamespacesAxis: Null context or node pointer or type != XML_ELEMENT_NODE.\n");
398#endif
399 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000400 }
Daniel Veillard9ff88172002-03-11 09:15:32 +0000401
402 if ((!ctx->exclusive) || (ctx->ns_rendered == NULL)) {
403#ifdef DEBUG_C14N
404 xmlGenericError(xmlGenericErrorContext,
405 "xmlExcC14NProcessNamespacesAxis: called for non-exclusive canonization or rendered stack is NULL.\n");
406#endif
407 return (-1);
408
409 }
410
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000411 /*
412 * Create a sorted list to store element namespaces
413 */
Daniel Veillard9ff88172002-03-11 09:15:32 +0000414 list =
415 xmlListCreate(NULL, (xmlListDataCompare) xmlC14NNamespacesCompare);
416 if (list == NULL) {
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000417#ifdef DEBUG_C14N
418 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +0000419 "xmlExcC14NProcessNamespacesAxis: list creation failed\n");
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000420#endif
Daniel Veillard9ff88172002-03-11 09:15:32 +0000421 return (-1);
422 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000423
424 /*
425 * First of all, add all namespaces required by current node
426 * (i.e. node namespace and all attribute namespaces)
427 * todo: do we need to check for default "xml:" namespace
428 */
429 ns = (cur->ns != NULL) ? cur->ns : xmlSearchNs(ctx->doc, cur, NULL);
Daniel Veillard9ff88172002-03-11 09:15:32 +0000430 if ((ns != NULL) && (!xmlC14NIsXmlNs(ns)) &&
431 (xmlListSearch(list, ns) == NULL)) {
432 if (!xmlExcC14NIsRendered(ctx, ns)) {
433 xmlListInsert(list, ns);
434 xmlXPathNodeSetAdd(ctx->ns_rendered, (xmlNodePtr) ns);
435 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000436 }
437 attr = cur->properties;
Daniel Veillard9ff88172002-03-11 09:15:32 +0000438 while (attr != NULL) {
439 /*
440 * todo: do we need to check that attribute is visible and has non
441 * default namespace
442 */
443 if (xmlC14NIsVisible(ctx, attr)) {
444 ns = (attr->ns != NULL) ? attr->ns : xmlSearchNs(ctx->doc, cur,
445 NULL);
446 if ((ns != NULL) && (xmlC14NIsVisible(ctx, attr)) &&
447 (!xmlC14NIsXmlNs(ns))) {
448 if ((xmlListSearch(list, ns) == NULL)
449 && (!xmlExcC14NIsRendered(ctx, ns))) {
450 xmlListInsert(list, ns);
451 xmlXPathNodeSetAdd(ctx->ns_rendered, (xmlNodePtr) ns);
452 }
453 }
454 }
455 attr = attr->next;
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000456 }
Daniel Veillard9ff88172002-03-11 09:15:32 +0000457
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000458 /*
459 * Next add all inclusive namespaces if needed.
460 */
Daniel Veillard9ff88172002-03-11 09:15:32 +0000461 if (ctx->inclusive_ns_prefixes != NULL) {
462 int i;
463 xmlChar *prefix;
464
465 for (i = 0; ctx->inclusive_ns_prefixes[i] != NULL; ++i) {
466 prefix = ctx->inclusive_ns_prefixes[i];
467 /*
468 * Special values for namespace with empty prefix
469 */
470 if (xmlStrEqual(prefix, BAD_CAST "#default")
471 || xmlStrEqual(prefix, BAD_CAST "")) {
472 prefix = NULL;
473 }
474 ns = xmlSearchNs(ctx->doc, cur, prefix);
475 if ((ns != NULL) && (!xmlC14NIsXmlNs(ns))) {
476 if (xmlListSearch(list, ns) == NULL &&
477 !xmlExcC14NIsRendered(ctx, ns)) {
478 xmlListInsert(list, ns);
479 xmlXPathNodeSetAdd(ctx->ns_rendered, (xmlNodePtr) ns);
480 }
481 }
482 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000483 }
Daniel Veillard9ff88172002-03-11 09:15:32 +0000484
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000485 /*
486 * print out all elements from list
487 */
Daniel Veillard9ff88172002-03-11 09:15:32 +0000488 xmlListWalk(list, (xmlListWalker) xmlC14NPrintNamespaces,
489 (const void *) ctx);
490
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000491 /*
492 * Cleanup
493 */
494 xmlListDelete(list);
Daniel Veillard9ff88172002-03-11 09:15:32 +0000495 return (0);
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000496}
497
498
499/**
500 * xmlC14NAttrsCompare:
501 * @attr1: the pointer to first attr
502 * @attr2: the pointer to second attr
503 *
504 * Prints the given attribute to the output buffer from C14N context.
505 *
506 * Returns -1 if attr1 < attr2, 0 if attr1 == attr2 or 1 if attr1 > attr2.
507 */
508static int
Daniel Veillard9ff88172002-03-11 09:15:32 +0000509xmlC14NAttrsCompare(xmlAttrPtr attr1, xmlAttrPtr attr2)
510{
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000511 int ret = 0;
512
513 /*
514 * Simple cases
515 */
Daniel Veillard9ff88172002-03-11 09:15:32 +0000516 if (attr1 == attr2)
517 return (0);
518 if (attr1 == NULL)
519 return (-1);
520 if (attr2 == NULL)
521 return (1);
522 if (attr1->ns == attr2->ns) {
523 return (xmlStrcmp(attr1->name, attr2->name));
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000524 }
525
526 /*
527 * Attributes in the default namespace are first
528 * because the default namespace is not applied to
529 * unqualified attributes
530 */
Daniel Veillard9ff88172002-03-11 09:15:32 +0000531 if (attr1->ns == NULL)
532 return (-1);
533 if (attr2->ns == NULL)
534 return (1);
535 if (attr1->ns->prefix == NULL)
536 return (-1);
537 if (attr2->ns->prefix == NULL)
538 return (1);
539
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000540 ret = xmlStrcmp(attr1->ns->href, attr2->ns->href);
Daniel Veillard9ff88172002-03-11 09:15:32 +0000541 if (ret == 0) {
542 ret = xmlStrcmp(attr1->name, attr2->name);
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000543 }
Daniel Veillard9ff88172002-03-11 09:15:32 +0000544 return (ret);
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000545}
546
547
548/**
549 * xmlC14NPrintAttrs:
550 * @attr: the pointer to attr
551 * @ctx: the C14N context
552 *
553 * Prints out canonical attribute urrent node to the
554 * buffer from C14N context as follows
555 *
556 * Canonical XML v 1.0 (http://www.w3.org/TR/xml-c14n)
557 *
558 * Returns 1 on success or 0 on fail.
559 */
560static int
Daniel Veillard9ff88172002-03-11 09:15:32 +0000561xmlC14NPrintAttrs(const xmlAttrPtr attr, xmlC14NCtxPtr ctx)
562{
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000563 xmlChar *value;
564 xmlChar *buffer;
565
Daniel Veillard9ff88172002-03-11 09:15:32 +0000566 if ((attr == NULL) || (ctx == NULL)) {
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000567#ifdef DEBUG_C14N
568 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +0000569 "xmlC14NPrintAttrs: attr == NULL or ctx == NULL\n");
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000570#endif
Daniel Veillard9ff88172002-03-11 09:15:32 +0000571 return (0);
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000572 }
573
574 xmlOutputBufferWriteString(ctx->buf, " ");
Daniel Veillard9ff88172002-03-11 09:15:32 +0000575 if (attr->ns != NULL && xmlStrlen(attr->ns->prefix) > 0) {
576 xmlOutputBufferWriteString(ctx->buf,
577 (const char *) attr->ns->prefix);
578 xmlOutputBufferWriteString(ctx->buf, ":");
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000579 }
580 xmlOutputBufferWriteString(ctx->buf, (const char *) attr->name);
581 xmlOutputBufferWriteString(ctx->buf, "=\"");
582
583 value = xmlNodeListGetString(attr->doc, attr->children, 1);
584 /* todo: should we log an error if value==NULL ? */
Daniel Veillard9ff88172002-03-11 09:15:32 +0000585 if (value != NULL) {
586 buffer = xmlC11NNormalizeAttr(value);
587 xmlFree(value);
588 if (buffer != NULL) {
589 xmlOutputBufferWriteString(ctx->buf, (const char *) buffer);
590 xmlFree(buffer);
591 } else {
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000592#ifdef DEBUG_C14N
Daniel Veillard9ff88172002-03-11 09:15:32 +0000593 xmlGenericError(xmlGenericErrorContext,
594 "xmlC14NPrintAttrs: xmlC11NNormalizeAttr failed\n");
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000595#endif
Daniel Veillard9ff88172002-03-11 09:15:32 +0000596 return (0);
597 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000598 }
599 xmlOutputBufferWriteString(ctx->buf, "\"");
Daniel Veillard9ff88172002-03-11 09:15:32 +0000600 return (1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000601}
602
603/**
604 * xmlC14NProcessAttrsAxis:
605 * @ctx: the C14N context
606 * @cur: the current node
607 *
608 * Prints out canonical attribute axis of the current node to the
609 * buffer from C14N context as follows
610 *
611 * Canonical XML v 1.0 (http://www.w3.org/TR/xml-c14n)
612 *
613 * Attribute Axis
614 * In lexicographic order (ascending), process each node that
615 * is in the element's attribute axis and in the node-set.
616 *
617 * The processing of an element node E MUST be modified slightly
618 * when an XPath node-set is given as input and the element's
619 * parent is omitted from the node-set.
620 *
621 *
622 * Exclusive XML Canonicalization v 1.0 (http://www.w3.org/TR/xml-exc-c14n)
623 *
624 * Canonical XML applied to a document subset requires the search of the
625 * ancestor nodes of each orphan element node for attributes in the xml
626 * namespace, such as xml:lang and xml:space. These are copied into the
627 * element node except if a declaration of the same attribute is already
628 * in the attribute axis of the element (whether or not it is included in
629 * the document subset). This search and copying are omitted from the
630 * Exclusive XML Canonicalization method.
631 *
632 * Returns 0 on success or -1 on fail.
633 */
634static int
Daniel Veillard9ff88172002-03-11 09:15:32 +0000635xmlC14NProcessAttrsAxis(xmlC14NCtxPtr ctx, xmlNodePtr cur)
636{
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000637 xmlAttrPtr attr;
638 xmlListPtr list;
Daniel Veillard9ff88172002-03-11 09:15:32 +0000639
640 if ((ctx == NULL) || (cur == NULL) || (cur->type != XML_ELEMENT_NODE)) {
641#ifdef DEBUG_C14N
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000642 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +0000643 "xmlC14NProcessAttrsAxis: Null context or node pointer or type != XML_ELEMENT_NODE.\n");
644#endif
645 return (-1);
646 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000647
648 /*
649 * Create a sorted list to store element attributes
650 */
Daniel Veillard9ff88172002-03-11 09:15:32 +0000651 list = xmlListCreate(NULL, (xmlListDataCompare) xmlC14NAttrsCompare);
652 if (list == NULL) {
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000653#ifdef DEBUG_C14N
654 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +0000655 "xmlC14NProcessAttrsAxis: list creation failed\n");
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000656#endif
Daniel Veillard9ff88172002-03-11 09:15:32 +0000657 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000658 }
659
660 /*
661 * Add all visible attributes from current node.
662 */
663 attr = cur->properties;
Daniel Veillard9ff88172002-03-11 09:15:32 +0000664 while (attr != NULL) {
665 /* check that attribute is visible */
666 if (xmlC14NIsVisible(ctx, attr)) {
667 xmlListInsert(list, attr);
668 }
669 attr = attr->next;
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000670 }
Daniel Veillard9ff88172002-03-11 09:15:32 +0000671
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000672 /*
673 * include attributes in "xml" namespace defined in ancestors
674 * (only for non-exclusive XML Canonicalization)
675 */
Daniel Veillard9ff88172002-03-11 09:15:32 +0000676 if ((!ctx->exclusive) && (cur->parent != NULL)
677 && (!xmlC14NIsVisible(ctx, cur->parent))) {
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000678 /*
Daniel Veillard9ff88172002-03-11 09:15:32 +0000679 * If XPath node-set is not specified then the parent is always
680 * visible!
681 */
682 cur = cur->parent;
683 while (cur != NULL) {
684 attr = cur->properties;
685 while (attr != NULL) {
686 if ((attr->ns != NULL)
687 && (xmlStrEqual(attr->ns->prefix, BAD_CAST "xml"))) {
688 if (xmlListSearch(list, attr) == NULL) {
689 xmlListInsert(list, attr);
690 }
691 }
692 attr = attr->next;
693 }
694 cur = cur->parent;
695 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000696 }
Daniel Veillard9ff88172002-03-11 09:15:32 +0000697
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000698 /*
699 * print out all elements from list
700 */
Daniel Veillard9ff88172002-03-11 09:15:32 +0000701 xmlListWalk(list, (xmlListWalker) xmlC14NPrintAttrs,
702 (const void *) ctx);
703
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000704 /*
705 * Cleanup
706 */
Daniel Veillard9ff88172002-03-11 09:15:32 +0000707 xmlListDelete(list);
708 return (0);
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000709}
710
711/**
712 * xmlC14NCheckForRelativeNamespaces:
713 * @ctx: the C14N context
714 * @cur: the current element node
715 *
716 * Checks that current element node has no relative namespaces defined
717 *
718 * Returns 0 if the node has no relative namespaces or -1 otherwise.
719 */
720static int
Daniel Veillard9ff88172002-03-11 09:15:32 +0000721xmlC14NCheckForRelativeNamespaces(xmlC14NCtxPtr ctx, xmlNodePtr cur)
722{
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000723 xmlNsPtr ns;
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000724
Daniel Veillard9ff88172002-03-11 09:15:32 +0000725 if ((ctx == NULL) || (cur == NULL) || (cur->type != XML_ELEMENT_NODE)) {
726#ifdef DEBUG_C14N
727 xmlGenericError(xmlGenericErrorContext,
728 "xmlC14NCheckForRelativeNamespaces: Null context or node pointer or type != XML_ELEMENT_NODE.\n");
729#endif
730 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000731 }
Daniel Veillard9ff88172002-03-11 09:15:32 +0000732
733 ns = cur->nsDef;
734 while (ns != NULL) {
735 if (xmlStrlen(ns->href) > 0) {
736 xmlURIPtr uri;
737
738 uri = xmlParseURI((const char *) ns->href);
739 if (uri == NULL) {
740#ifdef DEBUG_C14N
741 xmlGenericError(xmlGenericErrorContext,
742 "xmlC14NCheckForRelativeNamespaces: unable to parse uri=\"%s\".\n",
743 ns->href);
744#endif
745 return (-1);
746 }
747 if (xmlStrlen((const xmlChar *) uri->scheme) == 0) {
748 xmlFreeURI(uri);
749 return (-1);
750 }
751 if ((!xmlStrEqual
752 ((const xmlChar *) uri->scheme, BAD_CAST "urn"))
753 && (xmlStrlen((const xmlChar *) uri->server) == 0)) {
754 xmlFreeURI(uri);
755 return (-1);
756 }
757 xmlFreeURI(uri);
758 }
759 ns = ns->next;
760 }
761 return (0);
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000762}
763
764/**
765 * xmlC14NProcessElementNode:
766 * @ctx: the pointer to C14N context object
767 * @cur: the node to process
768 *
769 * Canonical XML v 1.0 (http://www.w3.org/TR/xml-c14n)
770 *
771 * Element Nodes
772 * If the element is not in the node-set, then the result is obtained
773 * by processing the namespace axis, then the attribute axis, then
774 * processing the child nodes of the element that are in the node-set
775 * (in document order). If the element is in the node-set, then the result
776 * is an open angle bracket (<), the element QName, the result of
777 * processing the namespace axis, the result of processing the attribute
778 * axis, a close angle bracket (>), the result of processing the child
779 * nodes of the element that are in the node-set (in document order), an
780 * open angle bracket, a forward slash (/), the element QName, and a close
781 * angle bracket.
782 *
783 * Returns non-negative value on success or negative value on fail
784 */
785static int
Daniel Veillard9ff88172002-03-11 09:15:32 +0000786xmlC14NProcessElementNode(xmlC14NCtxPtr ctx, xmlNodePtr cur, int visible)
787{
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000788 int ret;
789 int ns_rendered_pos = 0;
Daniel Veillard6f293b12002-03-15 09:42:33 +0000790 int parent_is_doc = 0;
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000791
Daniel Veillard9ff88172002-03-11 09:15:32 +0000792 if ((ctx == NULL) || (cur == NULL) || (cur->type != XML_ELEMENT_NODE)) {
793#ifdef DEBUG_C14N
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000794 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +0000795 "xmlC14NProcessElementNode: Null context or node pointer or type != XML_ELEMENT_NODE.\n");
796#endif
797 return (-1);
798 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000799
800 /*
801 * Check relative relative namespaces:
802 * implementations of XML canonicalization MUST report an operation
803 * failure on documents containing relative namespace URIs.
804 */
Daniel Veillard9ff88172002-03-11 09:15:32 +0000805 if (xmlC14NCheckForRelativeNamespaces(ctx, cur) < 0) {
806#ifdef DEBUG_C14N
807 xmlGenericError(xmlGenericErrorContext,
808 "xmlC14NProcessElementNode: xmlC14NCheckForRelativeNamespaces failed.\n");
809#endif
810 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000811 }
812
813
814 /*
815 * Save ns_rendered stack position for exclusive
816 * processing
817 */
Daniel Veillard9ff88172002-03-11 09:15:32 +0000818 if ((ctx->exclusive) && (ctx->ns_rendered != NULL)) {
819 ns_rendered_pos = ctx->ns_rendered->nodeNr;
820 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000821
Daniel Veillard6f293b12002-03-15 09:42:33 +0000822 if (visible) {
Daniel Veillard9ff88172002-03-11 09:15:32 +0000823 if (ctx->parent_is_doc) {
Daniel Veillard6f293b12002-03-15 09:42:33 +0000824 /* save this flag into the stack */
825 parent_is_doc = ctx->parent_is_doc;
826 ctx->parent_is_doc = 0;
Daniel Veillard9ff88172002-03-11 09:15:32 +0000827 ctx->pos = XMLC14N_INSIDE_DOCUMENT_ELEMENT;
828 }
829 xmlOutputBufferWriteString(ctx->buf, "<");
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000830
Daniel Veillard9ff88172002-03-11 09:15:32 +0000831 if ((cur->ns != NULL) && (xmlStrlen(cur->ns->prefix) > 0)) {
832 xmlOutputBufferWriteString(ctx->buf,
833 (const char *) cur->ns->prefix);
834 xmlOutputBufferWriteString(ctx->buf, ":");
835 }
836 xmlOutputBufferWriteString(ctx->buf, (const char *) cur->name);
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000837
Daniel Veillard9ff88172002-03-11 09:15:32 +0000838 if (ctx->exclusive) {
839 ret = xmlExcC14NProcessNamespacesAxis(ctx, cur);
840 } else {
841 ret = xmlC14NProcessNamespacesAxis(ctx, cur);
842 }
843 if (ret < 0) {
844#ifdef DEBUG_C14N
845 xmlGenericError(xmlGenericErrorContext,
846 "xmlC14NProcessElementNode: xmlC14NProcessNamespacesAxis failed.\n");
847#endif
848 return (-1);
849 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000850
Daniel Veillard9ff88172002-03-11 09:15:32 +0000851 ret = xmlC14NProcessAttrsAxis(ctx, cur);
852 if (ret < 0) {
853#ifdef DEBUG_C14N
854 xmlGenericError(xmlGenericErrorContext,
855 "xmlC14NProcessElementNode: xmlC14NProcessAttrsAxis failed.\n");
856#endif
857 return (-1);
858 }
859
860 xmlOutputBufferWriteString(ctx->buf, ">");
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000861 }
862 if (cur->children != NULL) {
Daniel Veillard9ff88172002-03-11 09:15:32 +0000863 ret = xmlC14NProcessNodeList(ctx, cur->children);
864 if (ret < 0) {
865#ifdef DEBUG_C14N
866 xmlGenericError(xmlGenericErrorContext,
867 "xmlC14NProcessElementNode: xmlC14NProcessNodeList failed.\n");
868#endif
869 return (-1);
870 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000871 }
Daniel Veillard9ff88172002-03-11 09:15:32 +0000872 if (visible) {
873 xmlOutputBufferWriteString(ctx->buf, "</");
874 if ((cur->ns != NULL) && (xmlStrlen(cur->ns->prefix) > 0)) {
875 xmlOutputBufferWriteString(ctx->buf,
876 (const char *) cur->ns->prefix);
877 xmlOutputBufferWriteString(ctx->buf, ":");
878 }
879 xmlOutputBufferWriteString(ctx->buf, (const char *) cur->name);
880 xmlOutputBufferWriteString(ctx->buf, ">");
Daniel Veillard6f293b12002-03-15 09:42:33 +0000881 if (parent_is_doc) {
882 /* restore this flag from the stack for next node */
883 ctx->parent_is_doc = parent_is_doc;
884 ctx->pos = XMLC14N_AFTER_DOCUMENT_ELEMENT;
Daniel Veillard9ff88172002-03-11 09:15:32 +0000885 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000886 }
887
888 /*
889 * Restore ns_rendered stack position for exclusive
890 * processing
891 */
Daniel Veillard9ff88172002-03-11 09:15:32 +0000892 if ((ctx->exclusive) && (ctx->ns_rendered != NULL)) {
893 ctx->ns_rendered->nodeNr = ns_rendered_pos;
894 }
895 return (0);
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000896}
897
898/**
899 * xmlC14NProcessNode:
900 * @ctx: the pointer to C14N context object
901 * @cur: the node to process
902 *
903 * Processes the given node
904 *
905 * Returns non-negative value on success or negative value on fail
906 */
907static int
Daniel Veillard9ff88172002-03-11 09:15:32 +0000908xmlC14NProcessNode(xmlC14NCtxPtr ctx, xmlNodePtr cur)
909{
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000910 int ret = 0;
911 int visible;
Daniel Veillard9ff88172002-03-11 09:15:32 +0000912
913 if ((ctx == NULL) || (cur == NULL)) {
914#ifdef DEBUG_C14N
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000915 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +0000916 "xmlC14NProcessNode: Null context or node pointer.\n");
917#endif
918 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000919 }
920
921 visible = xmlC14NIsVisible(ctx, cur);
Daniel Veillard9ff88172002-03-11 09:15:32 +0000922 switch (cur->type) {
923 case XML_ELEMENT_NODE:
924 ret = xmlC14NProcessElementNode(ctx, cur, visible);
925 break;
926 case XML_CDATA_SECTION_NODE:
927 case XML_TEXT_NODE:
928 /*
929 * Text Nodes
930 * the string value, except all ampersands are replaced
931 * by &amp;, all open angle brackets (<) are replaced by &lt;, all closing
932 * angle brackets (>) are replaced by &gt;, and all #xD characters are
933 * replaced by &#xD;.
934 */
935 /* cdata sections are processed as text nodes */
936 /* todo: verify that cdata sections are included in XPath nodes set */
937 if ((visible) && (cur->content != NULL)) {
938 xmlChar *buffer;
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000939
Daniel Veillard9ff88172002-03-11 09:15:32 +0000940 buffer = xmlC11NNormalizeText(cur->content);
941 if (buffer != NULL) {
942 xmlOutputBufferWriteString(ctx->buf,
943 (const char *) buffer);
944 xmlFree(buffer);
945 } else {
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000946#ifdef DEBUG_C14N
Daniel Veillard9ff88172002-03-11 09:15:32 +0000947 xmlGenericError(xmlGenericErrorContext,
948 "xmlC14NProcessNode: xmlC11NNormalizeText() failed\n");
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000949#endif
Daniel Veillard9ff88172002-03-11 09:15:32 +0000950 return (-1);
951 }
952 }
953 break;
954 case XML_PI_NODE:
955 /*
956 * Processing Instruction (PI) Nodes-
957 * The opening PI symbol (<?), the PI target name of the node,
958 * a leading space and the string value if it is not empty, and
959 * the closing PI symbol (?>). If the string value is empty,
960 * then the leading space is not added. Also, a trailing #xA is
961 * rendered after the closing PI symbol for PI children of the
962 * root node with a lesser document order than the document
963 * element, and a leading #xA is rendered before the opening PI
964 * symbol of PI children of the root node with a greater document
965 * order than the document element.
966 */
967 if (visible) {
968 if (ctx->pos == XMLC14N_AFTER_DOCUMENT_ELEMENT) {
969 xmlOutputBufferWriteString(ctx->buf, "\x0A<?");
970 } else {
971 xmlOutputBufferWriteString(ctx->buf, "<?");
972 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000973
Daniel Veillard9ff88172002-03-11 09:15:32 +0000974 xmlOutputBufferWriteString(ctx->buf,
975 (const char *) cur->name);
976 if ((cur->content != NULL) && (*(cur->content) != '\0')) {
977 xmlChar *buffer;
978
979 xmlOutputBufferWriteString(ctx->buf, " ");
980
981 /* todo: do we need to normalize pi? */
982 buffer = xmlC11NNormalizePI(cur->content);
983 if (buffer != NULL) {
984 xmlOutputBufferWriteString(ctx->buf,
985 (const char *) buffer);
986 xmlFree(buffer);
987 } else {
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000988#ifdef DEBUG_C14N
Daniel Veillard9ff88172002-03-11 09:15:32 +0000989 xmlGenericError(xmlGenericErrorContext,
990 "xmlC14NProcessNode: xmlC11NNormalizePI() failed\n");
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000991#endif
Daniel Veillard9ff88172002-03-11 09:15:32 +0000992 return (-1);
993 }
994 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000995
Daniel Veillard9ff88172002-03-11 09:15:32 +0000996 if (ctx->pos == XMLC14N_BEFORE_DOCUMENT_ELEMENT) {
997 xmlOutputBufferWriteString(ctx->buf, "?>\x0A");
998 } else {
999 xmlOutputBufferWriteString(ctx->buf, "?>");
1000 }
1001 }
1002 break;
1003 case XML_COMMENT_NODE:
1004 /*
1005 * Comment Nodes
1006 * Nothing if generating canonical XML without comments. For
1007 * canonical XML with comments, generate the opening comment
1008 * symbol (<!--), the string value of the node, and the
1009 * closing comment symbol (-->). Also, a trailing #xA is rendered
1010 * after the closing comment symbol for comment children of the
1011 * root node with a lesser document order than the document
1012 * element, and a leading #xA is rendered before the opening
1013 * comment symbol of comment children of the root node with a
1014 * greater document order than the document element. (Comment
1015 * children of the root node represent comments outside of the
1016 * top-level document element and outside of the document type
1017 * declaration).
1018 */
1019 if (visible && ctx->with_comments) {
1020 if (ctx->pos == XMLC14N_AFTER_DOCUMENT_ELEMENT) {
1021 xmlOutputBufferWriteString(ctx->buf, "\x0A<!--");
1022 } else {
1023 xmlOutputBufferWriteString(ctx->buf, "<!--");
1024 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001025
Daniel Veillard9ff88172002-03-11 09:15:32 +00001026 if (cur->content != NULL) {
1027 xmlChar *buffer;
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001028
Daniel Veillard9ff88172002-03-11 09:15:32 +00001029 /* todo: do we need to normalize comment? */
1030 buffer = xmlC11NNormalizeComment(cur->content);
1031 if (buffer != NULL) {
1032 xmlOutputBufferWriteString(ctx->buf,
1033 (const char *) buffer);
1034 xmlFree(buffer);
1035 } else {
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001036#ifdef DEBUG_C14N
Daniel Veillard9ff88172002-03-11 09:15:32 +00001037 xmlGenericError(xmlGenericErrorContext,
1038 "xmlC14NProcessNode: xmlC11NNormalizeComment() failed\n");
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001039#endif
Daniel Veillard9ff88172002-03-11 09:15:32 +00001040 return (-1);
1041 }
1042 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001043
Daniel Veillard9ff88172002-03-11 09:15:32 +00001044 if (ctx->pos == XMLC14N_BEFORE_DOCUMENT_ELEMENT) {
1045 xmlOutputBufferWriteString(ctx->buf, "-->\x0A");
1046 } else {
1047 xmlOutputBufferWriteString(ctx->buf, "-->");
1048 }
1049 }
1050 break;
1051 case XML_DOCUMENT_NODE:
1052 case XML_DOCUMENT_FRAG_NODE: /* should be processed as document? */
1053 case XML_DOCB_DOCUMENT_NODE: /* should be processed as document? */
1054 case XML_HTML_DOCUMENT_NODE: /* should be processed as document? */
1055 if (cur->children != NULL) {
1056 ctx->pos = XMLC14N_BEFORE_DOCUMENT_ELEMENT;
1057 ctx->parent_is_doc = 1;
1058 ret = xmlC14NProcessNodeList(ctx, cur->children);
1059 }
1060 break;
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001061
Daniel Veillard9ff88172002-03-11 09:15:32 +00001062 case XML_ATTRIBUTE_NODE:
1063 xmlGenericError(xmlGenericErrorContext,
1064 "xmlC14NProcessNode: XML_ATTRIBUTE_NODE is illegal here\n");
1065 return (-1);
1066 case XML_NAMESPACE_DECL:
1067 xmlGenericError(xmlGenericErrorContext,
1068 "xmlC14NProcessNode: XML_NAMESPACE_DECL is illegal here\n");
1069 return (-1);
1070 case XML_ENTITY_REF_NODE:
1071 xmlGenericError(xmlGenericErrorContext,
1072 "xmlC14NProcessNode: XML_ENTITY_REF_NODE is illegal here\n");
1073 return (-1);
1074 case XML_ENTITY_NODE:
1075 xmlGenericError(xmlGenericErrorContext,
1076 "xmlC14NProcessNode: XML_ENTITY_NODE is illegal here\n");
1077 return (-1);
1078
1079 case XML_DOCUMENT_TYPE_NODE:
1080 case XML_NOTATION_NODE:
1081 case XML_DTD_NODE:
1082 case XML_ELEMENT_DECL:
1083 case XML_ATTRIBUTE_DECL:
1084 case XML_ENTITY_DECL:
1085 case XML_XINCLUDE_START:
1086 case XML_XINCLUDE_END:
1087 /*
1088 * should be ignored according to "W3C Canonical XML"
1089 */
1090 break;
1091 default:
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001092#ifdef DEBUG_C14N
Daniel Veillard9ff88172002-03-11 09:15:32 +00001093 xmlGenericError(xmlGenericErrorContext,
1094 "xmlC14NProcessNode: unknown node type = %d\n",
1095 cur->type);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001096#endif
Daniel Veillard9ff88172002-03-11 09:15:32 +00001097 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001098 }
1099
Daniel Veillard9ff88172002-03-11 09:15:32 +00001100 return (ret);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001101}
1102
1103/**
1104 * xmlC14NProcessNodeList:
1105 * @ctx: the pointer to C14N context object
1106 * @cur: the node to start from
1107 *
1108 * Processes all nodes in the row starting from cur.
1109 *
1110 * Returns non-negative value on success or negative value on fail
1111 */
1112static int
Daniel Veillard9ff88172002-03-11 09:15:32 +00001113xmlC14NProcessNodeList(xmlC14NCtxPtr ctx, xmlNodePtr cur)
1114{
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001115 int ret;
Daniel Veillard9ff88172002-03-11 09:15:32 +00001116
1117 if (ctx == NULL) {
1118#ifdef DEBUG_C14N
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001119 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001120 "xmlC14NProcessNodeList: Null context pointer.\n");
1121#endif
1122 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001123 }
1124
Daniel Veillard9ff88172002-03-11 09:15:32 +00001125 for (ret = 0; cur != NULL && ret >= 0; cur = cur->next) {
1126 ret = xmlC14NProcessNode(ctx, cur);
1127 }
1128 return (ret);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001129}
1130
1131
1132/**
1133 * xmlC14NFreeCtx:
1134 * @ctx: the pointer to C14N context object
1135 *
1136 * Cleanups the C14N context object.
1137 */
1138
1139static void
Daniel Veillard9ff88172002-03-11 09:15:32 +00001140xmlC14NFreeCtx(xmlC14NCtxPtr ctx)
1141{
1142 if (ctx == NULL) {
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001143#ifdef DEBUG_C14N
1144 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001145 "xmlC14NFreeCtx: ctx == NULL\n");
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001146#endif
Daniel Veillard9ff88172002-03-11 09:15:32 +00001147 return;
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001148 }
Daniel Veillard9ff88172002-03-11 09:15:32 +00001149
1150 if (ctx->ns_rendered != NULL) {
1151 xmlXPathFreeNodeSet(ctx->ns_rendered);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001152 }
1153 xmlFree(ctx);
1154}
1155
1156/**
1157 * xmlC14NNewCtx:
1158 * @doc: the XML document for canonization
1159 * @nodes: the nodes set to be included in the canonized image
1160 * or NULL if all document nodes should be included
1161 * @exclusive: the exclusive flag (0 - non-exclusive canonicalization;
1162 * otherwise - exclusive canonicalization)
1163 * @inclusive_ns_prefixe the list of inclusive namespace prefixes
1164 * ended with a NULL or NULL if there is no
1165 * inclusive namespaces (only for exclusive
1166 * canonicalization)
1167 * @with_comments: include comments in the result (!=0) or not (==0)
1168 * @buf: the output buffer to store canonical XML; this
1169 * buffer MUST have encoder==NULL because C14N requires
1170 * UTF-8 output
1171 *
1172 * Creates new C14N context object to store C14N parameters.
1173 *
1174 * Returns pointer to newly created object (success) or NULL (fail)
1175 */
1176static xmlC14NCtxPtr
Daniel Veillard9ff88172002-03-11 09:15:32 +00001177xmlC14NNewCtx(xmlDocPtr doc, xmlNodeSetPtr nodes,
1178 int exclusive, xmlChar ** inclusive_ns_prefixes,
1179 int with_comments, xmlOutputBufferPtr buf)
1180{
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001181 xmlC14NCtxPtr ctx;
1182
Daniel Veillard9ff88172002-03-11 09:15:32 +00001183 if ((doc == NULL) || (buf == NULL)) {
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001184#ifdef DEBUG_C14N
1185 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001186 "xmlC14NNewCtx: pointer to document or output buffer is NULL\n");
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001187#endif
Daniel Veillard9ff88172002-03-11 09:15:32 +00001188 return (NULL);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001189 }
1190
1191 /*
1192 * Validate the encoding output buffer encoding
1193 */
Daniel Veillard9ff88172002-03-11 09:15:32 +00001194 if (buf->encoder != NULL) {
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001195 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001196 "xmlC14NNewCtx: output buffer encoder != NULL but C14N requires UTF8 output\n");
1197 return (NULL);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001198 }
1199
1200 /*
1201 * Validate the XML document encoding value, if provided.
1202 */
Daniel Veillard9ff88172002-03-11 09:15:32 +00001203 if (doc->charset != XML_CHAR_ENCODING_UTF8) {
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001204 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001205 "xmlC14NNewCtx: source document not in UTF8\n");
1206 return (NULL);
1207 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001208
1209 /*
1210 * Allocate a new xmlC14NCtxPtr and fill the fields.
1211 */
1212 ctx = (xmlC14NCtxPtr) xmlMalloc(sizeof(xmlC14NCtx));
1213 if (ctx == NULL) {
1214 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001215 "xmlC14NNewCtx: malloc failed\n");
1216 return (NULL);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001217 }
1218 memset(ctx, 0, sizeof(xmlC14NCtx));
1219
1220 /*
1221 * initialize C14N context
Daniel Veillard9ff88172002-03-11 09:15:32 +00001222 */
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001223 ctx->doc = doc;
1224 ctx->with_comments = with_comments;
1225 ctx->visible_nodes = nodes;
1226 ctx->buf = buf;
1227 ctx->parent_is_doc = 1;
1228 ctx->pos = XMLC14N_BEFORE_DOCUMENT_ELEMENT;
1229
1230 /*
1231 * Set "exclusive" flag, create a nodes set for namespaces
1232 * stack and remember list of incluseve prefixes
1233 */
Daniel Veillard9ff88172002-03-11 09:15:32 +00001234 if (exclusive) {
1235 ctx->exclusive = 1;
1236 ctx->ns_rendered = xmlXPathNodeSetCreate(NULL);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001237 ctx->inclusive_ns_prefixes = inclusive_ns_prefixes;
1238 }
Daniel Veillard9ff88172002-03-11 09:15:32 +00001239 return (ctx);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001240}
1241
1242/**
1243 * xmlC14NDocSaveTo:
1244 * @doc: the XML document for canonization
1245 * @nodes: the nodes set to be included in the canonized image
1246 * or NULL if all document nodes should be included
1247 * @exclusive: the exclusive flag (0 - non-exclusive canonicalization;
1248 * otherwise - exclusive canonicalization)
Daniel Veillarddb1bdba2002-03-09 14:13:11 +00001249 * @inclusive_ns_prefixes: the list of inclusive namespace prefixes
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001250 * ended with a NULL or NULL if there is no
1251 * inclusive namespaces (only for exclusive
1252 * canonicalization, ignored otherwise)
1253 * @with_comments: include comments in the result (!=0) or not (==0)
1254 * @buf: the output buffer to store canonical XML; this
1255 * buffer MUST have encoder==NULL because C14N requires
1256 * UTF-8 output
1257 *
1258 * Dumps the canonized image of given XML document into the provided buffer.
1259 * For details see "Canonical XML" (http://www.w3.org/TR/xml-c14n) or
1260 * "Exclusive XML Canonicalization" (http://www.w3.org/TR/xml-exc-c14n)
1261 *
1262 * Returns non-negative value on success or a negative value on fail
1263 */
1264int
Daniel Veillard9ff88172002-03-11 09:15:32 +00001265xmlC14NDocSaveTo(xmlDocPtr doc, xmlNodeSetPtr nodes,
1266 int exclusive, xmlChar ** inclusive_ns_prefixes,
1267 int with_comments, xmlOutputBufferPtr buf)
1268{
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001269 xmlC14NCtxPtr ctx;
1270 int ret;
Daniel Veillard9ff88172002-03-11 09:15:32 +00001271
1272 if ((buf == NULL) || (doc == NULL)) {
1273#ifdef DEBUG_C14N
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001274 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001275 "xmlC14NDocSaveTo: null return buffer or doc pointer\n");
1276#endif
1277 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001278 }
1279
1280 /*
1281 * Validate the encoding output buffer encoding
1282 */
Daniel Veillard9ff88172002-03-11 09:15:32 +00001283 if (buf->encoder != NULL) {
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001284 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001285 "xmlC14NDocSaveTo: output buffer encoder != NULL but C14N requires UTF8 output\n");
1286 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001287 }
1288
1289 ctx = xmlC14NNewCtx(doc, nodes, exclusive, inclusive_ns_prefixes,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001290 with_comments, buf);
1291 if (ctx == NULL) {
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001292 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001293 "xmlC14NDocSaveTo: unable to create C14N context\n");
1294 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001295 }
1296
1297
1298
1299 /*
1300 * Root Node
1301 * The root node is the parent of the top-level document element. The
1302 * result of processing each of its child nodes that is in the node-set
1303 * in document order. The root node does not generate a byte order mark,
1304 * XML declaration, nor anything from within the document type
1305 * declaration.
1306 */
Daniel Veillard9ff88172002-03-11 09:15:32 +00001307 if (doc->children != NULL) {
1308 ret = xmlC14NProcessNodeList(ctx, doc->children);
1309 if (ret < 0) {
1310#ifdef DEBUG_C14N
1311 xmlGenericError(xmlGenericErrorContext,
1312 "xmlC14NDocSaveTo: process childrens' list failed.\n");
1313#endif
1314 xmlC14NFreeCtx(ctx);
1315 return (-1);
1316 }
1317 }
1318
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001319 /*
1320 * Flush buffer to get number of bytes written
1321 */
1322 ret = xmlOutputBufferFlush(buf);
Daniel Veillard9ff88172002-03-11 09:15:32 +00001323 if (ret < 0) {
1324#ifdef DEBUG_C14N
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001325 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001326 "xmlC14NDocSaveTo: buffer flush failed.\n");
1327#endif
1328 xmlC14NFreeCtx(ctx);
1329 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001330 }
1331
1332 /*
1333 * Cleanup
1334 */
Daniel Veillard9ff88172002-03-11 09:15:32 +00001335 xmlC14NFreeCtx(ctx);
1336 return (ret);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001337}
1338
1339/**
1340 * xmlC14NDocDumpMemory:
1341 * @doc: the XML document for canonization
1342 * @nodes: the nodes set to be included in the canonized image
1343 * or NULL if all document nodes should be included
1344 * @exclusive: the exclusive flag (0 - non-exclusive canonicalization;
1345 * otherwise - exclusive canonicalization)
Daniel Veillarddb1bdba2002-03-09 14:13:11 +00001346 * @inclusive_ns_prefixes: the list of inclusive namespace prefixes
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001347 * ended with a NULL or NULL if there is no
1348 * inclusive namespaces (only for exclusive
1349 * canonicalization, ignored otherwise)
1350 * @with_comments: include comments in the result (!=0) or not (==0)
1351 * @doc_txt_ptr: the memory pointer for allocated canonical XML text;
1352 * the caller of this functions is responsible for calling
1353 * xmlFree() to free allocated memory
1354 *
1355 * Dumps the canonized image of given XML document into memory.
1356 * For details see "Canonical XML" (http://www.w3.org/TR/xml-c14n) or
1357 * "Exclusive XML Canonicalization" (http://www.w3.org/TR/xml-exc-c14n)
1358 *
1359 * Returns the number of bytes written on success or a negative value on fail
1360 */
1361int
Daniel Veillard9ff88172002-03-11 09:15:32 +00001362xmlC14NDocDumpMemory(xmlDocPtr doc, xmlNodeSetPtr nodes,
1363 int exclusive, xmlChar ** inclusive_ns_prefixes,
1364 int with_comments, xmlChar ** doc_txt_ptr)
1365{
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001366 int ret;
1367 xmlOutputBufferPtr buf;
1368
1369 if (doc_txt_ptr == NULL) {
Daniel Veillard9ff88172002-03-11 09:15:32 +00001370#ifdef DEBUG_C14N
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001371 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001372 "xmlC14NDocDumpMemory: null return buffer pointer\n");
1373#endif
1374 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001375 }
1376
1377 *doc_txt_ptr = NULL;
Daniel Veillard9ff88172002-03-11 09:15:32 +00001378
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001379 /*
1380 * create memory buffer with UTF8 (default) encoding
1381 */
1382 buf = xmlAllocOutputBuffer(NULL);
Daniel Veillard9ff88172002-03-11 09:15:32 +00001383 if (buf == NULL) {
1384#ifdef DEBUG_C14N
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001385 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001386 "xmlC14NDocDumpMemory: failed to allocate output buffer.\n");
1387#endif
1388 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001389 }
1390
1391 /*
1392 * canonize document and write to buffer
1393 */
Daniel Veillard9ff88172002-03-11 09:15:32 +00001394 ret = xmlC14NDocSaveTo(doc, nodes, exclusive, inclusive_ns_prefixes,
1395 with_comments, buf);
1396 if (ret < 0) {
1397#ifdef DEBUG_C14N
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001398 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001399 "xmlC14NDocDumpMemory: xmlC14NDocSaveTo failed.\n");
1400#endif
1401 (void) xmlOutputBufferClose(buf);
1402 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001403 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001404
Daniel Veillard9ff88172002-03-11 09:15:32 +00001405 ret = buf->buffer->use;
1406 if (ret > 0) {
1407 *doc_txt_ptr = xmlStrndup(buf->buffer->content, ret);
1408 }
1409 (void) xmlOutputBufferClose(buf);
1410
1411 if ((*doc_txt_ptr == NULL) && (ret > 0)) {
1412#ifdef DEBUG_C14N
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001413 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001414 "xmlC14NDocDumpMemory: failed to allocate memory for document text representation\n");
1415#endif
1416 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001417 }
Daniel Veillard9ff88172002-03-11 09:15:32 +00001418 return (ret);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001419}
1420
1421/**
1422 * xmlC14NDocSave:
1423 * @doc: the XML document for canonization
1424 * @nodes: the nodes set to be included in the canonized image
1425 * or NULL if all document nodes should be included
1426 * @exclusive: the exclusive flag (0 - non-exclusive canonicalization;
1427 * otherwise - exclusive canonicalization)
Daniel Veillarddb1bdba2002-03-09 14:13:11 +00001428 * @inclusive_ns_prefixes: the list of inclusive namespace prefixes
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001429 * ended with a NULL or NULL if there is no
1430 * inclusive namespaces (only for exclusive
1431 * canonicalization, ignored otherwise)
1432 * @with_comments: include comments in the result (!=0) or not (==0)
1433 * @filename: the filename to store canonical XML image
1434 * @compression: the compression level (zlib requred):
1435 * -1 - libxml default,
1436 * 0 - uncompressed,
1437 * >0 - compression level
1438 *
1439 * Dumps the canonized image of given XML document into the file.
1440 * For details see "Canonical XML" (http://www.w3.org/TR/xml-c14n) or
1441 * "Exclusive XML Canonicalization" (http://www.w3.org/TR/xml-exc-c14n)
1442 *
1443 * Returns the number of bytes written success or a negative value on fail
1444 */
1445int
Daniel Veillard9ff88172002-03-11 09:15:32 +00001446xmlC14NDocSave(xmlDocPtr doc, xmlNodeSetPtr nodes,
1447 int exclusive, xmlChar ** inclusive_ns_prefixes,
1448 int with_comments, const char *filename, int compression)
1449{
1450 xmlOutputBufferPtr buf;
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001451 int ret;
1452
Daniel Veillard9ff88172002-03-11 09:15:32 +00001453 if (filename == NULL) {
1454#ifdef DEBUG_C14N
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001455 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001456 "xmlC14NDocSave: filename is NULL\n");
1457#endif
1458 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001459 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001460#ifdef HAVE_ZLIB_H
Daniel Veillard9ff88172002-03-11 09:15:32 +00001461 if (compression < 0)
1462 compression = xmlGetCompressMode();
1463#endif
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001464
1465 /*
1466 * save the content to a temp buffer, use default UTF8 encoding.
1467 */
1468 buf = xmlOutputBufferCreateFilename(filename, NULL, compression);
1469 if (buf == NULL) {
Daniel Veillard9ff88172002-03-11 09:15:32 +00001470#ifdef DEBUG_C14N
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001471 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001472 "xmlC14NDocSave: unable to create buffer for file=\"%s\" with compressin=%d\n",
1473 filename, compression);
1474#endif
1475 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001476 }
Daniel Veillard9ff88172002-03-11 09:15:32 +00001477
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001478 /*
1479 * canonize document and write to buffer
1480 */
Daniel Veillard9ff88172002-03-11 09:15:32 +00001481 ret = xmlC14NDocSaveTo(doc, nodes, exclusive, inclusive_ns_prefixes,
1482 with_comments, buf);
1483 if (ret < 0) {
1484#ifdef DEBUG_C14N
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001485 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001486 "xmlC14NDocSave: xmlC14NDocSaveTo failed.\n");
1487#endif
1488 (void) xmlOutputBufferClose(buf);
1489 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001490 }
Daniel Veillard9ff88172002-03-11 09:15:32 +00001491
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001492 /*
1493 * get the numbers of bytes written
1494 */
1495 ret = xmlOutputBufferClose(buf);
Daniel Veillard9ff88172002-03-11 09:15:32 +00001496 return (ret);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001497}
1498
1499
1500
1501/*
1502 * Macro used to grow the current buffer.
1503 */
1504#define growBufferReentrant() { \
1505 buffer_size *= 2; \
1506 buffer = (xmlChar *) \
1507 xmlRealloc(buffer, buffer_size * sizeof(xmlChar)); \
1508 if (buffer == NULL) { \
1509 perror("realloc failed"); \
1510 return(NULL); \
1511 } \
1512}
1513
1514/**
1515 * xmlC11NNormalizeString:
1516 * @input: the input string
1517 * @mode: the normalization mode (attribute, comment, PI or text)
1518 *
1519 * Converts a string to a canonical (normalized) format. The code is stolen
1520 * from xmlEncodeEntitiesReentrant(). Added normalization of \x09, \x0a, \x0A
1521 * and the @mode parameter
1522 *
1523 * Returns a normalized string (caller is responsible for calling xmlFree())
1524 * or NULL if an error occurs
1525 */
1526static xmlChar *
Daniel Veillard9ff88172002-03-11 09:15:32 +00001527xmlC11NNormalizeString(const xmlChar * input,
1528 xmlC14NNormalizationMode mode)
1529{
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001530 const xmlChar *cur = input;
1531 xmlChar *buffer = NULL;
1532 xmlChar *out = NULL;
1533 int buffer_size = 0;
1534
Daniel Veillard9ff88172002-03-11 09:15:32 +00001535 if (input == NULL)
1536 return (NULL);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001537
1538 /*
1539 * allocate an translation buffer.
1540 */
1541 buffer_size = 1000;
1542 buffer = (xmlChar *) xmlMalloc(buffer_size * sizeof(xmlChar));
1543 if (buffer == NULL) {
Daniel Veillard9ff88172002-03-11 09:15:32 +00001544 perror("malloc failed");
1545 return (NULL);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001546 }
1547 out = buffer;
1548
1549 while (*cur != '\0') {
Daniel Veillard9ff88172002-03-11 09:15:32 +00001550 if ((out - buffer) > (buffer_size - 10)) {
1551 int indx = out - buffer;
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001552
Daniel Veillard9ff88172002-03-11 09:15:32 +00001553 growBufferReentrant();
1554 out = &buffer[indx];
1555 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001556
Daniel Veillard9ff88172002-03-11 09:15:32 +00001557 if ((*cur == '<') && ((mode == XMLC14N_NORMALIZE_ATTR) ||
1558 (mode == XMLC14N_NORMALIZE_TEXT))) {
1559 *out++ = '&';
1560 *out++ = 'l';
1561 *out++ = 't';
1562 *out++ = ';';
1563 } else if ((*cur == '>') && (mode == XMLC14N_NORMALIZE_TEXT)) {
1564 *out++ = '&';
1565 *out++ = 'g';
1566 *out++ = 't';
1567 *out++ = ';';
1568 } else if ((*cur == '&') && ((mode == XMLC14N_NORMALIZE_ATTR) ||
1569 (mode == XMLC14N_NORMALIZE_TEXT))) {
1570 *out++ = '&';
1571 *out++ = 'a';
1572 *out++ = 'm';
1573 *out++ = 'p';
1574 *out++ = ';';
1575 } else if ((*cur == '"') && (mode == XMLC14N_NORMALIZE_ATTR)) {
1576 *out++ = '&';
1577 *out++ = 'q';
1578 *out++ = 'u';
1579 *out++ = 'o';
1580 *out++ = 't';
1581 *out++ = ';';
1582 } else if ((*cur == '\x09') && (mode == XMLC14N_NORMALIZE_ATTR)) {
1583 *out++ = '&';
1584 *out++ = '#';
1585 *out++ = 'x';
1586 *out++ = '9';
1587 *out++ = ';';
1588 } else if ((*cur == '\x0A') && (mode == XMLC14N_NORMALIZE_ATTR)) {
1589 *out++ = '&';
1590 *out++ = '#';
1591 *out++ = 'x';
1592 *out++ = 'A';
1593 *out++ = ';';
1594 } else if ((*cur == '\x0D') && ((mode == XMLC14N_NORMALIZE_ATTR) ||
1595 (mode == XMLC14N_NORMALIZE_TEXT) ||
1596 (mode == XMLC14N_NORMALIZE_COMMENT) ||
1597 (mode == XMLC14N_NORMALIZE_PI))) {
1598 *out++ = '&';
1599 *out++ = '#';
1600 *out++ = 'x';
1601 *out++ = 'D';
1602 *out++ = ';';
1603 } else {
1604 /*
1605 * Works because on UTF-8, all extended sequences cannot
1606 * result in bytes in the ASCII range.
1607 */
1608 *out++ = *cur;
1609 }
1610 cur++;
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001611 }
1612 *out++ = 0;
Daniel Veillard9ff88172002-03-11 09:15:32 +00001613 return (buffer);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001614}
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001615#endif /* LIBXML_C14N_ENABLED */