blob: 27e25b8217579a28d83584340184b2f754e20d05 [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;
790
Daniel Veillard9ff88172002-03-11 09:15:32 +0000791 if ((ctx == NULL) || (cur == NULL) || (cur->type != XML_ELEMENT_NODE)) {
792#ifdef DEBUG_C14N
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000793 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +0000794 "xmlC14NProcessElementNode: Null context or node pointer or type != XML_ELEMENT_NODE.\n");
795#endif
796 return (-1);
797 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000798
799 /*
800 * Check relative relative namespaces:
801 * implementations of XML canonicalization MUST report an operation
802 * failure on documents containing relative namespace URIs.
803 */
Daniel Veillard9ff88172002-03-11 09:15:32 +0000804 if (xmlC14NCheckForRelativeNamespaces(ctx, cur) < 0) {
805#ifdef DEBUG_C14N
806 xmlGenericError(xmlGenericErrorContext,
807 "xmlC14NProcessElementNode: xmlC14NCheckForRelativeNamespaces failed.\n");
808#endif
809 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000810 }
811
812
813 /*
814 * Save ns_rendered stack position for exclusive
815 * processing
816 */
Daniel Veillard9ff88172002-03-11 09:15:32 +0000817 if ((ctx->exclusive) && (ctx->ns_rendered != NULL)) {
818 ns_rendered_pos = ctx->ns_rendered->nodeNr;
819 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000820
Daniel Veillard9ff88172002-03-11 09:15:32 +0000821 if (visible) {
822 if (ctx->parent_is_doc) {
823 ctx->pos = XMLC14N_INSIDE_DOCUMENT_ELEMENT;
824 }
825 xmlOutputBufferWriteString(ctx->buf, "<");
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000826
Daniel Veillard9ff88172002-03-11 09:15:32 +0000827 if ((cur->ns != NULL) && (xmlStrlen(cur->ns->prefix) > 0)) {
828 xmlOutputBufferWriteString(ctx->buf,
829 (const char *) cur->ns->prefix);
830 xmlOutputBufferWriteString(ctx->buf, ":");
831 }
832 xmlOutputBufferWriteString(ctx->buf, (const char *) cur->name);
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000833
Daniel Veillard9ff88172002-03-11 09:15:32 +0000834 if (ctx->exclusive) {
835 ret = xmlExcC14NProcessNamespacesAxis(ctx, cur);
836 } else {
837 ret = xmlC14NProcessNamespacesAxis(ctx, cur);
838 }
839 if (ret < 0) {
840#ifdef DEBUG_C14N
841 xmlGenericError(xmlGenericErrorContext,
842 "xmlC14NProcessElementNode: xmlC14NProcessNamespacesAxis failed.\n");
843#endif
844 return (-1);
845 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000846
Daniel Veillard9ff88172002-03-11 09:15:32 +0000847 ret = xmlC14NProcessAttrsAxis(ctx, cur);
848 if (ret < 0) {
849#ifdef DEBUG_C14N
850 xmlGenericError(xmlGenericErrorContext,
851 "xmlC14NProcessElementNode: xmlC14NProcessAttrsAxis failed.\n");
852#endif
853 return (-1);
854 }
855
856 xmlOutputBufferWriteString(ctx->buf, ">");
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000857 }
858 if (cur->children != NULL) {
Daniel Veillard9ff88172002-03-11 09:15:32 +0000859 ret = xmlC14NProcessNodeList(ctx, cur->children);
860 if (ret < 0) {
861#ifdef DEBUG_C14N
862 xmlGenericError(xmlGenericErrorContext,
863 "xmlC14NProcessElementNode: xmlC14NProcessNodeList failed.\n");
864#endif
865 return (-1);
866 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000867 }
Daniel Veillard9ff88172002-03-11 09:15:32 +0000868 if (visible) {
869 xmlOutputBufferWriteString(ctx->buf, "</");
870 if ((cur->ns != NULL) && (xmlStrlen(cur->ns->prefix) > 0)) {
871 xmlOutputBufferWriteString(ctx->buf,
872 (const char *) cur->ns->prefix);
873 xmlOutputBufferWriteString(ctx->buf, ":");
874 }
875 xmlOutputBufferWriteString(ctx->buf, (const char *) cur->name);
876 xmlOutputBufferWriteString(ctx->buf, ">");
877 if (ctx->parent_is_doc) {
878 ctx->pos = XMLC14N_AFTER_DOCUMENT_ELEMENT;
879 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000880 }
881
882 /*
883 * Restore ns_rendered stack position for exclusive
884 * processing
885 */
Daniel Veillard9ff88172002-03-11 09:15:32 +0000886 if ((ctx->exclusive) && (ctx->ns_rendered != NULL)) {
887 ctx->ns_rendered->nodeNr = ns_rendered_pos;
888 }
889 return (0);
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000890}
891
892/**
893 * xmlC14NProcessNode:
894 * @ctx: the pointer to C14N context object
895 * @cur: the node to process
896 *
897 * Processes the given node
898 *
899 * Returns non-negative value on success or negative value on fail
900 */
901static int
Daniel Veillard9ff88172002-03-11 09:15:32 +0000902xmlC14NProcessNode(xmlC14NCtxPtr ctx, xmlNodePtr cur)
903{
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000904 int ret = 0;
905 int visible;
Daniel Veillard9ff88172002-03-11 09:15:32 +0000906
907 if ((ctx == NULL) || (cur == NULL)) {
908#ifdef DEBUG_C14N
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000909 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +0000910 "xmlC14NProcessNode: Null context or node pointer.\n");
911#endif
912 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000913 }
914
915 visible = xmlC14NIsVisible(ctx, cur);
Daniel Veillard9ff88172002-03-11 09:15:32 +0000916 switch (cur->type) {
917 case XML_ELEMENT_NODE:
918 ret = xmlC14NProcessElementNode(ctx, cur, visible);
919 break;
920 case XML_CDATA_SECTION_NODE:
921 case XML_TEXT_NODE:
922 /*
923 * Text Nodes
924 * the string value, except all ampersands are replaced
925 * by &amp;, all open angle brackets (<) are replaced by &lt;, all closing
926 * angle brackets (>) are replaced by &gt;, and all #xD characters are
927 * replaced by &#xD;.
928 */
929 /* cdata sections are processed as text nodes */
930 /* todo: verify that cdata sections are included in XPath nodes set */
931 if ((visible) && (cur->content != NULL)) {
932 xmlChar *buffer;
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000933
Daniel Veillard9ff88172002-03-11 09:15:32 +0000934 buffer = xmlC11NNormalizeText(cur->content);
935 if (buffer != NULL) {
936 xmlOutputBufferWriteString(ctx->buf,
937 (const char *) buffer);
938 xmlFree(buffer);
939 } else {
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000940#ifdef DEBUG_C14N
Daniel Veillard9ff88172002-03-11 09:15:32 +0000941 xmlGenericError(xmlGenericErrorContext,
942 "xmlC14NProcessNode: xmlC11NNormalizeText() failed\n");
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000943#endif
Daniel Veillard9ff88172002-03-11 09:15:32 +0000944 return (-1);
945 }
946 }
947 break;
948 case XML_PI_NODE:
949 /*
950 * Processing Instruction (PI) Nodes-
951 * The opening PI symbol (<?), the PI target name of the node,
952 * a leading space and the string value if it is not empty, and
953 * the closing PI symbol (?>). If the string value is empty,
954 * then the leading space is not added. Also, a trailing #xA is
955 * rendered after the closing PI symbol for PI children of the
956 * root node with a lesser document order than the document
957 * element, and a leading #xA is rendered before the opening PI
958 * symbol of PI children of the root node with a greater document
959 * order than the document element.
960 */
961 if (visible) {
962 if (ctx->pos == XMLC14N_AFTER_DOCUMENT_ELEMENT) {
963 xmlOutputBufferWriteString(ctx->buf, "\x0A<?");
964 } else {
965 xmlOutputBufferWriteString(ctx->buf, "<?");
966 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000967
Daniel Veillard9ff88172002-03-11 09:15:32 +0000968 xmlOutputBufferWriteString(ctx->buf,
969 (const char *) cur->name);
970 if ((cur->content != NULL) && (*(cur->content) != '\0')) {
971 xmlChar *buffer;
972
973 xmlOutputBufferWriteString(ctx->buf, " ");
974
975 /* todo: do we need to normalize pi? */
976 buffer = xmlC11NNormalizePI(cur->content);
977 if (buffer != NULL) {
978 xmlOutputBufferWriteString(ctx->buf,
979 (const char *) buffer);
980 xmlFree(buffer);
981 } else {
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000982#ifdef DEBUG_C14N
Daniel Veillard9ff88172002-03-11 09:15:32 +0000983 xmlGenericError(xmlGenericErrorContext,
984 "xmlC14NProcessNode: xmlC11NNormalizePI() failed\n");
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000985#endif
Daniel Veillard9ff88172002-03-11 09:15:32 +0000986 return (-1);
987 }
988 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000989
Daniel Veillard9ff88172002-03-11 09:15:32 +0000990 if (ctx->pos == XMLC14N_BEFORE_DOCUMENT_ELEMENT) {
991 xmlOutputBufferWriteString(ctx->buf, "?>\x0A");
992 } else {
993 xmlOutputBufferWriteString(ctx->buf, "?>");
994 }
995 }
996 break;
997 case XML_COMMENT_NODE:
998 /*
999 * Comment Nodes
1000 * Nothing if generating canonical XML without comments. For
1001 * canonical XML with comments, generate the opening comment
1002 * symbol (<!--), the string value of the node, and the
1003 * closing comment symbol (-->). Also, a trailing #xA is rendered
1004 * after the closing comment symbol for comment children of the
1005 * root node with a lesser document order than the document
1006 * element, and a leading #xA is rendered before the opening
1007 * comment symbol of comment children of the root node with a
1008 * greater document order than the document element. (Comment
1009 * children of the root node represent comments outside of the
1010 * top-level document element and outside of the document type
1011 * declaration).
1012 */
1013 if (visible && ctx->with_comments) {
1014 if (ctx->pos == XMLC14N_AFTER_DOCUMENT_ELEMENT) {
1015 xmlOutputBufferWriteString(ctx->buf, "\x0A<!--");
1016 } else {
1017 xmlOutputBufferWriteString(ctx->buf, "<!--");
1018 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001019
Daniel Veillard9ff88172002-03-11 09:15:32 +00001020 if (cur->content != NULL) {
1021 xmlChar *buffer;
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001022
Daniel Veillard9ff88172002-03-11 09:15:32 +00001023 /* todo: do we need to normalize comment? */
1024 buffer = xmlC11NNormalizeComment(cur->content);
1025 if (buffer != NULL) {
1026 xmlOutputBufferWriteString(ctx->buf,
1027 (const char *) buffer);
1028 xmlFree(buffer);
1029 } else {
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001030#ifdef DEBUG_C14N
Daniel Veillard9ff88172002-03-11 09:15:32 +00001031 xmlGenericError(xmlGenericErrorContext,
1032 "xmlC14NProcessNode: xmlC11NNormalizeComment() failed\n");
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001033#endif
Daniel Veillard9ff88172002-03-11 09:15:32 +00001034 return (-1);
1035 }
1036 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001037
Daniel Veillard9ff88172002-03-11 09:15:32 +00001038 if (ctx->pos == XMLC14N_BEFORE_DOCUMENT_ELEMENT) {
1039 xmlOutputBufferWriteString(ctx->buf, "-->\x0A");
1040 } else {
1041 xmlOutputBufferWriteString(ctx->buf, "-->");
1042 }
1043 }
1044 break;
1045 case XML_DOCUMENT_NODE:
1046 case XML_DOCUMENT_FRAG_NODE: /* should be processed as document? */
1047 case XML_DOCB_DOCUMENT_NODE: /* should be processed as document? */
1048 case XML_HTML_DOCUMENT_NODE: /* should be processed as document? */
1049 if (cur->children != NULL) {
1050 ctx->pos = XMLC14N_BEFORE_DOCUMENT_ELEMENT;
1051 ctx->parent_is_doc = 1;
1052 ret = xmlC14NProcessNodeList(ctx, cur->children);
1053 }
1054 break;
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001055
Daniel Veillard9ff88172002-03-11 09:15:32 +00001056 case XML_ATTRIBUTE_NODE:
1057 xmlGenericError(xmlGenericErrorContext,
1058 "xmlC14NProcessNode: XML_ATTRIBUTE_NODE is illegal here\n");
1059 return (-1);
1060 case XML_NAMESPACE_DECL:
1061 xmlGenericError(xmlGenericErrorContext,
1062 "xmlC14NProcessNode: XML_NAMESPACE_DECL is illegal here\n");
1063 return (-1);
1064 case XML_ENTITY_REF_NODE:
1065 xmlGenericError(xmlGenericErrorContext,
1066 "xmlC14NProcessNode: XML_ENTITY_REF_NODE is illegal here\n");
1067 return (-1);
1068 case XML_ENTITY_NODE:
1069 xmlGenericError(xmlGenericErrorContext,
1070 "xmlC14NProcessNode: XML_ENTITY_NODE is illegal here\n");
1071 return (-1);
1072
1073 case XML_DOCUMENT_TYPE_NODE:
1074 case XML_NOTATION_NODE:
1075 case XML_DTD_NODE:
1076 case XML_ELEMENT_DECL:
1077 case XML_ATTRIBUTE_DECL:
1078 case XML_ENTITY_DECL:
1079 case XML_XINCLUDE_START:
1080 case XML_XINCLUDE_END:
1081 /*
1082 * should be ignored according to "W3C Canonical XML"
1083 */
1084 break;
1085 default:
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001086#ifdef DEBUG_C14N
Daniel Veillard9ff88172002-03-11 09:15:32 +00001087 xmlGenericError(xmlGenericErrorContext,
1088 "xmlC14NProcessNode: unknown node type = %d\n",
1089 cur->type);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001090#endif
Daniel Veillard9ff88172002-03-11 09:15:32 +00001091 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001092 }
1093
Daniel Veillard9ff88172002-03-11 09:15:32 +00001094 return (ret);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001095}
1096
1097/**
1098 * xmlC14NProcessNodeList:
1099 * @ctx: the pointer to C14N context object
1100 * @cur: the node to start from
1101 *
1102 * Processes all nodes in the row starting from cur.
1103 *
1104 * Returns non-negative value on success or negative value on fail
1105 */
1106static int
Daniel Veillard9ff88172002-03-11 09:15:32 +00001107xmlC14NProcessNodeList(xmlC14NCtxPtr ctx, xmlNodePtr cur)
1108{
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001109 int ret;
Daniel Veillard9ff88172002-03-11 09:15:32 +00001110
1111 if (ctx == NULL) {
1112#ifdef DEBUG_C14N
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001113 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001114 "xmlC14NProcessNodeList: Null context pointer.\n");
1115#endif
1116 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001117 }
1118
Daniel Veillard9ff88172002-03-11 09:15:32 +00001119 for (ret = 0; cur != NULL && ret >= 0; cur = cur->next) {
1120 ret = xmlC14NProcessNode(ctx, cur);
1121 }
1122 return (ret);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001123}
1124
1125
1126/**
1127 * xmlC14NFreeCtx:
1128 * @ctx: the pointer to C14N context object
1129 *
1130 * Cleanups the C14N context object.
1131 */
1132
1133static void
Daniel Veillard9ff88172002-03-11 09:15:32 +00001134xmlC14NFreeCtx(xmlC14NCtxPtr ctx)
1135{
1136 if (ctx == NULL) {
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001137#ifdef DEBUG_C14N
1138 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001139 "xmlC14NFreeCtx: ctx == NULL\n");
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001140#endif
Daniel Veillard9ff88172002-03-11 09:15:32 +00001141 return;
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001142 }
Daniel Veillard9ff88172002-03-11 09:15:32 +00001143
1144 if (ctx->ns_rendered != NULL) {
1145 xmlXPathFreeNodeSet(ctx->ns_rendered);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001146 }
1147 xmlFree(ctx);
1148}
1149
1150/**
1151 * xmlC14NNewCtx:
1152 * @doc: the XML document for canonization
1153 * @nodes: the nodes set to be included in the canonized image
1154 * or NULL if all document nodes should be included
1155 * @exclusive: the exclusive flag (0 - non-exclusive canonicalization;
1156 * otherwise - exclusive canonicalization)
1157 * @inclusive_ns_prefixe the list of inclusive namespace prefixes
1158 * ended with a NULL or NULL if there is no
1159 * inclusive namespaces (only for exclusive
1160 * canonicalization)
1161 * @with_comments: include comments in the result (!=0) or not (==0)
1162 * @buf: the output buffer to store canonical XML; this
1163 * buffer MUST have encoder==NULL because C14N requires
1164 * UTF-8 output
1165 *
1166 * Creates new C14N context object to store C14N parameters.
1167 *
1168 * Returns pointer to newly created object (success) or NULL (fail)
1169 */
1170static xmlC14NCtxPtr
Daniel Veillard9ff88172002-03-11 09:15:32 +00001171xmlC14NNewCtx(xmlDocPtr doc, xmlNodeSetPtr nodes,
1172 int exclusive, xmlChar ** inclusive_ns_prefixes,
1173 int with_comments, xmlOutputBufferPtr buf)
1174{
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001175 xmlC14NCtxPtr ctx;
1176
Daniel Veillard9ff88172002-03-11 09:15:32 +00001177 if ((doc == NULL) || (buf == NULL)) {
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001178#ifdef DEBUG_C14N
1179 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001180 "xmlC14NNewCtx: pointer to document or output buffer is NULL\n");
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001181#endif
Daniel Veillard9ff88172002-03-11 09:15:32 +00001182 return (NULL);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001183 }
1184
1185 /*
1186 * Validate the encoding output buffer encoding
1187 */
Daniel Veillard9ff88172002-03-11 09:15:32 +00001188 if (buf->encoder != NULL) {
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001189 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001190 "xmlC14NNewCtx: output buffer encoder != NULL but C14N requires UTF8 output\n");
1191 return (NULL);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001192 }
1193
1194 /*
1195 * Validate the XML document encoding value, if provided.
1196 */
Daniel Veillard9ff88172002-03-11 09:15:32 +00001197 if (doc->charset != XML_CHAR_ENCODING_UTF8) {
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001198 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001199 "xmlC14NNewCtx: source document not in UTF8\n");
1200 return (NULL);
1201 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001202
1203 /*
1204 * Allocate a new xmlC14NCtxPtr and fill the fields.
1205 */
1206 ctx = (xmlC14NCtxPtr) xmlMalloc(sizeof(xmlC14NCtx));
1207 if (ctx == NULL) {
1208 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001209 "xmlC14NNewCtx: malloc failed\n");
1210 return (NULL);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001211 }
1212 memset(ctx, 0, sizeof(xmlC14NCtx));
1213
1214 /*
1215 * initialize C14N context
Daniel Veillard9ff88172002-03-11 09:15:32 +00001216 */
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001217 ctx->doc = doc;
1218 ctx->with_comments = with_comments;
1219 ctx->visible_nodes = nodes;
1220 ctx->buf = buf;
1221 ctx->parent_is_doc = 1;
1222 ctx->pos = XMLC14N_BEFORE_DOCUMENT_ELEMENT;
1223
1224 /*
1225 * Set "exclusive" flag, create a nodes set for namespaces
1226 * stack and remember list of incluseve prefixes
1227 */
Daniel Veillard9ff88172002-03-11 09:15:32 +00001228 if (exclusive) {
1229 ctx->exclusive = 1;
1230 ctx->ns_rendered = xmlXPathNodeSetCreate(NULL);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001231 ctx->inclusive_ns_prefixes = inclusive_ns_prefixes;
1232 }
Daniel Veillard9ff88172002-03-11 09:15:32 +00001233 return (ctx);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001234}
1235
1236/**
1237 * xmlC14NDocSaveTo:
1238 * @doc: the XML document for canonization
1239 * @nodes: the nodes set to be included in the canonized image
1240 * or NULL if all document nodes should be included
1241 * @exclusive: the exclusive flag (0 - non-exclusive canonicalization;
1242 * otherwise - exclusive canonicalization)
Daniel Veillarddb1bdba2002-03-09 14:13:11 +00001243 * @inclusive_ns_prefixes: the list of inclusive namespace prefixes
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001244 * ended with a NULL or NULL if there is no
1245 * inclusive namespaces (only for exclusive
1246 * canonicalization, ignored otherwise)
1247 * @with_comments: include comments in the result (!=0) or not (==0)
1248 * @buf: the output buffer to store canonical XML; this
1249 * buffer MUST have encoder==NULL because C14N requires
1250 * UTF-8 output
1251 *
1252 * Dumps the canonized image of given XML document into the provided buffer.
1253 * For details see "Canonical XML" (http://www.w3.org/TR/xml-c14n) or
1254 * "Exclusive XML Canonicalization" (http://www.w3.org/TR/xml-exc-c14n)
1255 *
1256 * Returns non-negative value on success or a negative value on fail
1257 */
1258int
Daniel Veillard9ff88172002-03-11 09:15:32 +00001259xmlC14NDocSaveTo(xmlDocPtr doc, xmlNodeSetPtr nodes,
1260 int exclusive, xmlChar ** inclusive_ns_prefixes,
1261 int with_comments, xmlOutputBufferPtr buf)
1262{
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001263 xmlC14NCtxPtr ctx;
1264 int ret;
Daniel Veillard9ff88172002-03-11 09:15:32 +00001265
1266 if ((buf == NULL) || (doc == NULL)) {
1267#ifdef DEBUG_C14N
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001268 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001269 "xmlC14NDocSaveTo: null return buffer or doc pointer\n");
1270#endif
1271 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001272 }
1273
1274 /*
1275 * Validate the encoding output buffer encoding
1276 */
Daniel Veillard9ff88172002-03-11 09:15:32 +00001277 if (buf->encoder != NULL) {
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001278 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001279 "xmlC14NDocSaveTo: output buffer encoder != NULL but C14N requires UTF8 output\n");
1280 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001281 }
1282
1283 ctx = xmlC14NNewCtx(doc, nodes, exclusive, inclusive_ns_prefixes,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001284 with_comments, buf);
1285 if (ctx == NULL) {
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001286 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001287 "xmlC14NDocSaveTo: unable to create C14N context\n");
1288 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001289 }
1290
1291
1292
1293 /*
1294 * Root Node
1295 * The root node is the parent of the top-level document element. The
1296 * result of processing each of its child nodes that is in the node-set
1297 * in document order. The root node does not generate a byte order mark,
1298 * XML declaration, nor anything from within the document type
1299 * declaration.
1300 */
Daniel Veillard9ff88172002-03-11 09:15:32 +00001301 if (doc->children != NULL) {
1302 ret = xmlC14NProcessNodeList(ctx, doc->children);
1303 if (ret < 0) {
1304#ifdef DEBUG_C14N
1305 xmlGenericError(xmlGenericErrorContext,
1306 "xmlC14NDocSaveTo: process childrens' list failed.\n");
1307#endif
1308 xmlC14NFreeCtx(ctx);
1309 return (-1);
1310 }
1311 }
1312
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001313 /*
1314 * Flush buffer to get number of bytes written
1315 */
1316 ret = xmlOutputBufferFlush(buf);
Daniel Veillard9ff88172002-03-11 09:15:32 +00001317 if (ret < 0) {
1318#ifdef DEBUG_C14N
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001319 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001320 "xmlC14NDocSaveTo: buffer flush failed.\n");
1321#endif
1322 xmlC14NFreeCtx(ctx);
1323 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001324 }
1325
1326 /*
1327 * Cleanup
1328 */
Daniel Veillard9ff88172002-03-11 09:15:32 +00001329 xmlC14NFreeCtx(ctx);
1330 return (ret);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001331}
1332
1333/**
1334 * xmlC14NDocDumpMemory:
1335 * @doc: the XML document for canonization
1336 * @nodes: the nodes set to be included in the canonized image
1337 * or NULL if all document nodes should be included
1338 * @exclusive: the exclusive flag (0 - non-exclusive canonicalization;
1339 * otherwise - exclusive canonicalization)
Daniel Veillarddb1bdba2002-03-09 14:13:11 +00001340 * @inclusive_ns_prefixes: the list of inclusive namespace prefixes
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001341 * ended with a NULL or NULL if there is no
1342 * inclusive namespaces (only for exclusive
1343 * canonicalization, ignored otherwise)
1344 * @with_comments: include comments in the result (!=0) or not (==0)
1345 * @doc_txt_ptr: the memory pointer for allocated canonical XML text;
1346 * the caller of this functions is responsible for calling
1347 * xmlFree() to free allocated memory
1348 *
1349 * Dumps the canonized image of given XML document into memory.
1350 * For details see "Canonical XML" (http://www.w3.org/TR/xml-c14n) or
1351 * "Exclusive XML Canonicalization" (http://www.w3.org/TR/xml-exc-c14n)
1352 *
1353 * Returns the number of bytes written on success or a negative value on fail
1354 */
1355int
Daniel Veillard9ff88172002-03-11 09:15:32 +00001356xmlC14NDocDumpMemory(xmlDocPtr doc, xmlNodeSetPtr nodes,
1357 int exclusive, xmlChar ** inclusive_ns_prefixes,
1358 int with_comments, xmlChar ** doc_txt_ptr)
1359{
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001360 int ret;
1361 xmlOutputBufferPtr buf;
1362
1363 if (doc_txt_ptr == NULL) {
Daniel Veillard9ff88172002-03-11 09:15:32 +00001364#ifdef DEBUG_C14N
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001365 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001366 "xmlC14NDocDumpMemory: null return buffer pointer\n");
1367#endif
1368 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001369 }
1370
1371 *doc_txt_ptr = NULL;
Daniel Veillard9ff88172002-03-11 09:15:32 +00001372
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001373 /*
1374 * create memory buffer with UTF8 (default) encoding
1375 */
1376 buf = xmlAllocOutputBuffer(NULL);
Daniel Veillard9ff88172002-03-11 09:15:32 +00001377 if (buf == NULL) {
1378#ifdef DEBUG_C14N
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001379 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001380 "xmlC14NDocDumpMemory: failed to allocate output buffer.\n");
1381#endif
1382 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001383 }
1384
1385 /*
1386 * canonize document and write to buffer
1387 */
Daniel Veillard9ff88172002-03-11 09:15:32 +00001388 ret = xmlC14NDocSaveTo(doc, nodes, exclusive, inclusive_ns_prefixes,
1389 with_comments, buf);
1390 if (ret < 0) {
1391#ifdef DEBUG_C14N
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001392 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001393 "xmlC14NDocDumpMemory: xmlC14NDocSaveTo failed.\n");
1394#endif
1395 (void) xmlOutputBufferClose(buf);
1396 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001397 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001398
Daniel Veillard9ff88172002-03-11 09:15:32 +00001399 ret = buf->buffer->use;
1400 if (ret > 0) {
1401 *doc_txt_ptr = xmlStrndup(buf->buffer->content, ret);
1402 }
1403 (void) xmlOutputBufferClose(buf);
1404
1405 if ((*doc_txt_ptr == NULL) && (ret > 0)) {
1406#ifdef DEBUG_C14N
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001407 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001408 "xmlC14NDocDumpMemory: failed to allocate memory for document text representation\n");
1409#endif
1410 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001411 }
Daniel Veillard9ff88172002-03-11 09:15:32 +00001412 return (ret);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001413}
1414
1415/**
1416 * xmlC14NDocSave:
1417 * @doc: the XML document for canonization
1418 * @nodes: the nodes set to be included in the canonized image
1419 * or NULL if all document nodes should be included
1420 * @exclusive: the exclusive flag (0 - non-exclusive canonicalization;
1421 * otherwise - exclusive canonicalization)
Daniel Veillarddb1bdba2002-03-09 14:13:11 +00001422 * @inclusive_ns_prefixes: the list of inclusive namespace prefixes
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001423 * ended with a NULL or NULL if there is no
1424 * inclusive namespaces (only for exclusive
1425 * canonicalization, ignored otherwise)
1426 * @with_comments: include comments in the result (!=0) or not (==0)
1427 * @filename: the filename to store canonical XML image
1428 * @compression: the compression level (zlib requred):
1429 * -1 - libxml default,
1430 * 0 - uncompressed,
1431 * >0 - compression level
1432 *
1433 * Dumps the canonized image of given XML document into the file.
1434 * For details see "Canonical XML" (http://www.w3.org/TR/xml-c14n) or
1435 * "Exclusive XML Canonicalization" (http://www.w3.org/TR/xml-exc-c14n)
1436 *
1437 * Returns the number of bytes written success or a negative value on fail
1438 */
1439int
Daniel Veillard9ff88172002-03-11 09:15:32 +00001440xmlC14NDocSave(xmlDocPtr doc, xmlNodeSetPtr nodes,
1441 int exclusive, xmlChar ** inclusive_ns_prefixes,
1442 int with_comments, const char *filename, int compression)
1443{
1444 xmlOutputBufferPtr buf;
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001445 int ret;
1446
Daniel Veillard9ff88172002-03-11 09:15:32 +00001447 if (filename == NULL) {
1448#ifdef DEBUG_C14N
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001449 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001450 "xmlC14NDocSave: filename is NULL\n");
1451#endif
1452 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001453 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001454#ifdef HAVE_ZLIB_H
Daniel Veillard9ff88172002-03-11 09:15:32 +00001455 if (compression < 0)
1456 compression = xmlGetCompressMode();
1457#endif
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001458
1459 /*
1460 * save the content to a temp buffer, use default UTF8 encoding.
1461 */
1462 buf = xmlOutputBufferCreateFilename(filename, NULL, compression);
1463 if (buf == NULL) {
Daniel Veillard9ff88172002-03-11 09:15:32 +00001464#ifdef DEBUG_C14N
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001465 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001466 "xmlC14NDocSave: unable to create buffer for file=\"%s\" with compressin=%d\n",
1467 filename, compression);
1468#endif
1469 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001470 }
Daniel Veillard9ff88172002-03-11 09:15:32 +00001471
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001472 /*
1473 * canonize document and write to buffer
1474 */
Daniel Veillard9ff88172002-03-11 09:15:32 +00001475 ret = xmlC14NDocSaveTo(doc, nodes, exclusive, inclusive_ns_prefixes,
1476 with_comments, buf);
1477 if (ret < 0) {
1478#ifdef DEBUG_C14N
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001479 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001480 "xmlC14NDocSave: xmlC14NDocSaveTo failed.\n");
1481#endif
1482 (void) xmlOutputBufferClose(buf);
1483 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001484 }
Daniel Veillard9ff88172002-03-11 09:15:32 +00001485
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001486 /*
1487 * get the numbers of bytes written
1488 */
1489 ret = xmlOutputBufferClose(buf);
Daniel Veillard9ff88172002-03-11 09:15:32 +00001490 return (ret);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001491}
1492
1493
1494
1495/*
1496 * Macro used to grow the current buffer.
1497 */
1498#define growBufferReentrant() { \
1499 buffer_size *= 2; \
1500 buffer = (xmlChar *) \
1501 xmlRealloc(buffer, buffer_size * sizeof(xmlChar)); \
1502 if (buffer == NULL) { \
1503 perror("realloc failed"); \
1504 return(NULL); \
1505 } \
1506}
1507
1508/**
1509 * xmlC11NNormalizeString:
1510 * @input: the input string
1511 * @mode: the normalization mode (attribute, comment, PI or text)
1512 *
1513 * Converts a string to a canonical (normalized) format. The code is stolen
1514 * from xmlEncodeEntitiesReentrant(). Added normalization of \x09, \x0a, \x0A
1515 * and the @mode parameter
1516 *
1517 * Returns a normalized string (caller is responsible for calling xmlFree())
1518 * or NULL if an error occurs
1519 */
1520static xmlChar *
Daniel Veillard9ff88172002-03-11 09:15:32 +00001521xmlC11NNormalizeString(const xmlChar * input,
1522 xmlC14NNormalizationMode mode)
1523{
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001524 const xmlChar *cur = input;
1525 xmlChar *buffer = NULL;
1526 xmlChar *out = NULL;
1527 int buffer_size = 0;
1528
Daniel Veillard9ff88172002-03-11 09:15:32 +00001529 if (input == NULL)
1530 return (NULL);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001531
1532 /*
1533 * allocate an translation buffer.
1534 */
1535 buffer_size = 1000;
1536 buffer = (xmlChar *) xmlMalloc(buffer_size * sizeof(xmlChar));
1537 if (buffer == NULL) {
Daniel Veillard9ff88172002-03-11 09:15:32 +00001538 perror("malloc failed");
1539 return (NULL);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001540 }
1541 out = buffer;
1542
1543 while (*cur != '\0') {
Daniel Veillard9ff88172002-03-11 09:15:32 +00001544 if ((out - buffer) > (buffer_size - 10)) {
1545 int indx = out - buffer;
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001546
Daniel Veillard9ff88172002-03-11 09:15:32 +00001547 growBufferReentrant();
1548 out = &buffer[indx];
1549 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001550
Daniel Veillard9ff88172002-03-11 09:15:32 +00001551 if ((*cur == '<') && ((mode == XMLC14N_NORMALIZE_ATTR) ||
1552 (mode == XMLC14N_NORMALIZE_TEXT))) {
1553 *out++ = '&';
1554 *out++ = 'l';
1555 *out++ = 't';
1556 *out++ = ';';
1557 } else if ((*cur == '>') && (mode == XMLC14N_NORMALIZE_TEXT)) {
1558 *out++ = '&';
1559 *out++ = 'g';
1560 *out++ = 't';
1561 *out++ = ';';
1562 } else if ((*cur == '&') && ((mode == XMLC14N_NORMALIZE_ATTR) ||
1563 (mode == XMLC14N_NORMALIZE_TEXT))) {
1564 *out++ = '&';
1565 *out++ = 'a';
1566 *out++ = 'm';
1567 *out++ = 'p';
1568 *out++ = ';';
1569 } else if ((*cur == '"') && (mode == XMLC14N_NORMALIZE_ATTR)) {
1570 *out++ = '&';
1571 *out++ = 'q';
1572 *out++ = 'u';
1573 *out++ = 'o';
1574 *out++ = 't';
1575 *out++ = ';';
1576 } else if ((*cur == '\x09') && (mode == XMLC14N_NORMALIZE_ATTR)) {
1577 *out++ = '&';
1578 *out++ = '#';
1579 *out++ = 'x';
1580 *out++ = '9';
1581 *out++ = ';';
1582 } else if ((*cur == '\x0A') && (mode == XMLC14N_NORMALIZE_ATTR)) {
1583 *out++ = '&';
1584 *out++ = '#';
1585 *out++ = 'x';
1586 *out++ = 'A';
1587 *out++ = ';';
1588 } else if ((*cur == '\x0D') && ((mode == XMLC14N_NORMALIZE_ATTR) ||
1589 (mode == XMLC14N_NORMALIZE_TEXT) ||
1590 (mode == XMLC14N_NORMALIZE_COMMENT) ||
1591 (mode == XMLC14N_NORMALIZE_PI))) {
1592 *out++ = '&';
1593 *out++ = '#';
1594 *out++ = 'x';
1595 *out++ = 'D';
1596 *out++ = ';';
1597 } else {
1598 /*
1599 * Works because on UTF-8, all extended sequences cannot
1600 * result in bytes in the ASCII range.
1601 */
1602 *out++ = *cur;
1603 }
1604 cur++;
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001605 }
1606 *out++ = 0;
Daniel Veillard9ff88172002-03-11 09:15:32 +00001607 return (buffer);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001608}
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001609#endif /* LIBXML_C14N_ENABLED */