blob: 25d9b9cfa161ee87dcf19790ca94b558d6ca27d8 [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 /*
Daniel Veillard2d347fa2002-03-17 10:34:11 +0000440 * we need to check that attribute is visible and has non
441 * default namespace (XML Namespaces: "default namespaces
442 * do not apply directly to attributes")
Daniel Veillard9ff88172002-03-11 09:15:32 +0000443 */
Daniel Veillard2d347fa2002-03-17 10:34:11 +0000444 if ((attr->ns != NULL) && xmlC14NIsVisible(ctx, attr) &&
445 (xmlListSearch(list, attr->ns) == NULL) &&
446 (!xmlExcC14NIsRendered(ctx, attr->ns))) {
447 xmlListInsert(list, attr->ns);
448 xmlXPathNodeSetAdd(ctx->ns_rendered, (xmlNodePtr) attr->ns);
Daniel Veillard9ff88172002-03-11 09:15:32 +0000449 }
450 attr = attr->next;
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000451 }
Daniel Veillard9ff88172002-03-11 09:15:32 +0000452
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000453 /*
454 * Next add all inclusive namespaces if needed.
455 */
Daniel Veillard9ff88172002-03-11 09:15:32 +0000456 if (ctx->inclusive_ns_prefixes != NULL) {
457 int i;
458 xmlChar *prefix;
459
460 for (i = 0; ctx->inclusive_ns_prefixes[i] != NULL; ++i) {
461 prefix = ctx->inclusive_ns_prefixes[i];
462 /*
463 * Special values for namespace with empty prefix
464 */
465 if (xmlStrEqual(prefix, BAD_CAST "#default")
466 || xmlStrEqual(prefix, BAD_CAST "")) {
467 prefix = NULL;
468 }
469 ns = xmlSearchNs(ctx->doc, cur, prefix);
470 if ((ns != NULL) && (!xmlC14NIsXmlNs(ns))) {
471 if (xmlListSearch(list, ns) == NULL &&
472 !xmlExcC14NIsRendered(ctx, ns)) {
473 xmlListInsert(list, ns);
474 xmlXPathNodeSetAdd(ctx->ns_rendered, (xmlNodePtr) ns);
475 }
476 }
477 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000478 }
Daniel Veillard9ff88172002-03-11 09:15:32 +0000479
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000480 /*
481 * print out all elements from list
482 */
Daniel Veillard9ff88172002-03-11 09:15:32 +0000483 xmlListWalk(list, (xmlListWalker) xmlC14NPrintNamespaces,
484 (const void *) ctx);
485
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000486 /*
487 * Cleanup
488 */
489 xmlListDelete(list);
Daniel Veillard9ff88172002-03-11 09:15:32 +0000490 return (0);
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000491}
492
493
494/**
495 * xmlC14NAttrsCompare:
496 * @attr1: the pointer to first attr
497 * @attr2: the pointer to second attr
498 *
499 * Prints the given attribute to the output buffer from C14N context.
500 *
501 * Returns -1 if attr1 < attr2, 0 if attr1 == attr2 or 1 if attr1 > attr2.
502 */
503static int
Daniel Veillard9ff88172002-03-11 09:15:32 +0000504xmlC14NAttrsCompare(xmlAttrPtr attr1, xmlAttrPtr attr2)
505{
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000506 int ret = 0;
507
508 /*
509 * Simple cases
510 */
Daniel Veillard9ff88172002-03-11 09:15:32 +0000511 if (attr1 == attr2)
512 return (0);
513 if (attr1 == NULL)
514 return (-1);
515 if (attr2 == NULL)
516 return (1);
517 if (attr1->ns == attr2->ns) {
518 return (xmlStrcmp(attr1->name, attr2->name));
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000519 }
520
521 /*
522 * Attributes in the default namespace are first
523 * because the default namespace is not applied to
524 * unqualified attributes
525 */
Daniel Veillard9ff88172002-03-11 09:15:32 +0000526 if (attr1->ns == NULL)
527 return (-1);
528 if (attr2->ns == NULL)
529 return (1);
530 if (attr1->ns->prefix == NULL)
531 return (-1);
532 if (attr2->ns->prefix == NULL)
533 return (1);
534
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000535 ret = xmlStrcmp(attr1->ns->href, attr2->ns->href);
Daniel Veillard9ff88172002-03-11 09:15:32 +0000536 if (ret == 0) {
537 ret = xmlStrcmp(attr1->name, attr2->name);
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000538 }
Daniel Veillard9ff88172002-03-11 09:15:32 +0000539 return (ret);
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000540}
541
542
543/**
544 * xmlC14NPrintAttrs:
545 * @attr: the pointer to attr
546 * @ctx: the C14N context
547 *
548 * Prints out canonical attribute urrent node to the
549 * buffer from C14N context as follows
550 *
551 * Canonical XML v 1.0 (http://www.w3.org/TR/xml-c14n)
552 *
553 * Returns 1 on success or 0 on fail.
554 */
555static int
Daniel Veillard9ff88172002-03-11 09:15:32 +0000556xmlC14NPrintAttrs(const xmlAttrPtr attr, xmlC14NCtxPtr ctx)
557{
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000558 xmlChar *value;
559 xmlChar *buffer;
560
Daniel Veillard9ff88172002-03-11 09:15:32 +0000561 if ((attr == NULL) || (ctx == NULL)) {
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000562#ifdef DEBUG_C14N
563 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +0000564 "xmlC14NPrintAttrs: attr == NULL or ctx == NULL\n");
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000565#endif
Daniel Veillard9ff88172002-03-11 09:15:32 +0000566 return (0);
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000567 }
568
569 xmlOutputBufferWriteString(ctx->buf, " ");
Daniel Veillard9ff88172002-03-11 09:15:32 +0000570 if (attr->ns != NULL && xmlStrlen(attr->ns->prefix) > 0) {
571 xmlOutputBufferWriteString(ctx->buf,
572 (const char *) attr->ns->prefix);
573 xmlOutputBufferWriteString(ctx->buf, ":");
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000574 }
575 xmlOutputBufferWriteString(ctx->buf, (const char *) attr->name);
576 xmlOutputBufferWriteString(ctx->buf, "=\"");
577
578 value = xmlNodeListGetString(attr->doc, attr->children, 1);
579 /* todo: should we log an error if value==NULL ? */
Daniel Veillard9ff88172002-03-11 09:15:32 +0000580 if (value != NULL) {
581 buffer = xmlC11NNormalizeAttr(value);
582 xmlFree(value);
583 if (buffer != NULL) {
584 xmlOutputBufferWriteString(ctx->buf, (const char *) buffer);
585 xmlFree(buffer);
586 } else {
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000587#ifdef DEBUG_C14N
Daniel Veillard9ff88172002-03-11 09:15:32 +0000588 xmlGenericError(xmlGenericErrorContext,
589 "xmlC14NPrintAttrs: xmlC11NNormalizeAttr failed\n");
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000590#endif
Daniel Veillard9ff88172002-03-11 09:15:32 +0000591 return (0);
592 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000593 }
594 xmlOutputBufferWriteString(ctx->buf, "\"");
Daniel Veillard9ff88172002-03-11 09:15:32 +0000595 return (1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000596}
597
598/**
599 * xmlC14NProcessAttrsAxis:
600 * @ctx: the C14N context
601 * @cur: the current node
602 *
603 * Prints out canonical attribute axis of the current node to the
604 * buffer from C14N context as follows
605 *
606 * Canonical XML v 1.0 (http://www.w3.org/TR/xml-c14n)
607 *
608 * Attribute Axis
609 * In lexicographic order (ascending), process each node that
610 * is in the element's attribute axis and in the node-set.
611 *
612 * The processing of an element node E MUST be modified slightly
613 * when an XPath node-set is given as input and the element's
614 * parent is omitted from the node-set.
615 *
616 *
617 * Exclusive XML Canonicalization v 1.0 (http://www.w3.org/TR/xml-exc-c14n)
618 *
619 * Canonical XML applied to a document subset requires the search of the
620 * ancestor nodes of each orphan element node for attributes in the xml
621 * namespace, such as xml:lang and xml:space. These are copied into the
622 * element node except if a declaration of the same attribute is already
623 * in the attribute axis of the element (whether or not it is included in
624 * the document subset). This search and copying are omitted from the
625 * Exclusive XML Canonicalization method.
626 *
627 * Returns 0 on success or -1 on fail.
628 */
629static int
Daniel Veillard9ff88172002-03-11 09:15:32 +0000630xmlC14NProcessAttrsAxis(xmlC14NCtxPtr ctx, xmlNodePtr cur)
631{
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000632 xmlAttrPtr attr;
633 xmlListPtr list;
Daniel Veillard9ff88172002-03-11 09:15:32 +0000634
635 if ((ctx == NULL) || (cur == NULL) || (cur->type != XML_ELEMENT_NODE)) {
636#ifdef DEBUG_C14N
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000637 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +0000638 "xmlC14NProcessAttrsAxis: Null context or node pointer or type != XML_ELEMENT_NODE.\n");
639#endif
640 return (-1);
641 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000642
643 /*
644 * Create a sorted list to store element attributes
645 */
Daniel Veillard9ff88172002-03-11 09:15:32 +0000646 list = xmlListCreate(NULL, (xmlListDataCompare) xmlC14NAttrsCompare);
647 if (list == NULL) {
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000648#ifdef DEBUG_C14N
649 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +0000650 "xmlC14NProcessAttrsAxis: list creation failed\n");
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000651#endif
Daniel Veillard9ff88172002-03-11 09:15:32 +0000652 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000653 }
654
655 /*
656 * Add all visible attributes from current node.
657 */
658 attr = cur->properties;
Daniel Veillard9ff88172002-03-11 09:15:32 +0000659 while (attr != NULL) {
660 /* check that attribute is visible */
661 if (xmlC14NIsVisible(ctx, attr)) {
662 xmlListInsert(list, attr);
663 }
664 attr = attr->next;
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000665 }
Daniel Veillard9ff88172002-03-11 09:15:32 +0000666
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000667 /*
668 * include attributes in "xml" namespace defined in ancestors
669 * (only for non-exclusive XML Canonicalization)
670 */
Daniel Veillard9ff88172002-03-11 09:15:32 +0000671 if ((!ctx->exclusive) && (cur->parent != NULL)
672 && (!xmlC14NIsVisible(ctx, cur->parent))) {
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000673 /*
Daniel Veillard9ff88172002-03-11 09:15:32 +0000674 * If XPath node-set is not specified then the parent is always
675 * visible!
676 */
677 cur = cur->parent;
678 while (cur != NULL) {
679 attr = cur->properties;
680 while (attr != NULL) {
681 if ((attr->ns != NULL)
682 && (xmlStrEqual(attr->ns->prefix, BAD_CAST "xml"))) {
683 if (xmlListSearch(list, attr) == NULL) {
684 xmlListInsert(list, attr);
685 }
686 }
687 attr = attr->next;
688 }
689 cur = cur->parent;
690 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000691 }
Daniel Veillard9ff88172002-03-11 09:15:32 +0000692
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000693 /*
694 * print out all elements from list
695 */
Daniel Veillard9ff88172002-03-11 09:15:32 +0000696 xmlListWalk(list, (xmlListWalker) xmlC14NPrintAttrs,
697 (const void *) ctx);
698
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000699 /*
700 * Cleanup
701 */
Daniel Veillard9ff88172002-03-11 09:15:32 +0000702 xmlListDelete(list);
703 return (0);
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000704}
705
706/**
707 * xmlC14NCheckForRelativeNamespaces:
708 * @ctx: the C14N context
709 * @cur: the current element node
710 *
711 * Checks that current element node has no relative namespaces defined
712 *
713 * Returns 0 if the node has no relative namespaces or -1 otherwise.
714 */
715static int
Daniel Veillard9ff88172002-03-11 09:15:32 +0000716xmlC14NCheckForRelativeNamespaces(xmlC14NCtxPtr ctx, xmlNodePtr cur)
717{
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000718 xmlNsPtr ns;
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000719
Daniel Veillard9ff88172002-03-11 09:15:32 +0000720 if ((ctx == NULL) || (cur == NULL) || (cur->type != XML_ELEMENT_NODE)) {
721#ifdef DEBUG_C14N
722 xmlGenericError(xmlGenericErrorContext,
723 "xmlC14NCheckForRelativeNamespaces: Null context or node pointer or type != XML_ELEMENT_NODE.\n");
724#endif
725 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000726 }
Daniel Veillard9ff88172002-03-11 09:15:32 +0000727
728 ns = cur->nsDef;
729 while (ns != NULL) {
730 if (xmlStrlen(ns->href) > 0) {
731 xmlURIPtr uri;
732
733 uri = xmlParseURI((const char *) ns->href);
734 if (uri == NULL) {
735#ifdef DEBUG_C14N
736 xmlGenericError(xmlGenericErrorContext,
737 "xmlC14NCheckForRelativeNamespaces: unable to parse uri=\"%s\".\n",
738 ns->href);
739#endif
740 return (-1);
741 }
742 if (xmlStrlen((const xmlChar *) uri->scheme) == 0) {
743 xmlFreeURI(uri);
744 return (-1);
745 }
746 if ((!xmlStrEqual
747 ((const xmlChar *) uri->scheme, BAD_CAST "urn"))
748 && (xmlStrlen((const xmlChar *) uri->server) == 0)) {
749 xmlFreeURI(uri);
750 return (-1);
751 }
752 xmlFreeURI(uri);
753 }
754 ns = ns->next;
755 }
756 return (0);
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000757}
758
759/**
760 * xmlC14NProcessElementNode:
761 * @ctx: the pointer to C14N context object
762 * @cur: the node to process
763 *
764 * Canonical XML v 1.0 (http://www.w3.org/TR/xml-c14n)
765 *
766 * Element Nodes
767 * If the element is not in the node-set, then the result is obtained
768 * by processing the namespace axis, then the attribute axis, then
769 * processing the child nodes of the element that are in the node-set
770 * (in document order). If the element is in the node-set, then the result
771 * is an open angle bracket (<), the element QName, the result of
772 * processing the namespace axis, the result of processing the attribute
773 * axis, a close angle bracket (>), the result of processing the child
774 * nodes of the element that are in the node-set (in document order), an
775 * open angle bracket, a forward slash (/), the element QName, and a close
776 * angle bracket.
777 *
778 * Returns non-negative value on success or negative value on fail
779 */
780static int
Daniel Veillard9ff88172002-03-11 09:15:32 +0000781xmlC14NProcessElementNode(xmlC14NCtxPtr ctx, xmlNodePtr cur, int visible)
782{
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000783 int ret;
784 int ns_rendered_pos = 0;
Daniel Veillard6f293b12002-03-15 09:42:33 +0000785 int parent_is_doc = 0;
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000786
Daniel Veillard9ff88172002-03-11 09:15:32 +0000787 if ((ctx == NULL) || (cur == NULL) || (cur->type != XML_ELEMENT_NODE)) {
788#ifdef DEBUG_C14N
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000789 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +0000790 "xmlC14NProcessElementNode: Null context or node pointer or type != XML_ELEMENT_NODE.\n");
791#endif
792 return (-1);
793 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000794
795 /*
796 * Check relative relative namespaces:
797 * implementations of XML canonicalization MUST report an operation
798 * failure on documents containing relative namespace URIs.
799 */
Daniel Veillard9ff88172002-03-11 09:15:32 +0000800 if (xmlC14NCheckForRelativeNamespaces(ctx, cur) < 0) {
801#ifdef DEBUG_C14N
802 xmlGenericError(xmlGenericErrorContext,
803 "xmlC14NProcessElementNode: xmlC14NCheckForRelativeNamespaces failed.\n");
804#endif
805 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000806 }
807
808
809 /*
810 * Save ns_rendered stack position for exclusive
811 * processing
812 */
Daniel Veillard9ff88172002-03-11 09:15:32 +0000813 if ((ctx->exclusive) && (ctx->ns_rendered != NULL)) {
814 ns_rendered_pos = ctx->ns_rendered->nodeNr;
815 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000816
Daniel Veillard6f293b12002-03-15 09:42:33 +0000817 if (visible) {
Daniel Veillard9ff88172002-03-11 09:15:32 +0000818 if (ctx->parent_is_doc) {
Daniel Veillard6f293b12002-03-15 09:42:33 +0000819 /* save this flag into the stack */
820 parent_is_doc = ctx->parent_is_doc;
821 ctx->parent_is_doc = 0;
Daniel Veillard9ff88172002-03-11 09:15:32 +0000822 ctx->pos = XMLC14N_INSIDE_DOCUMENT_ELEMENT;
823 }
824 xmlOutputBufferWriteString(ctx->buf, "<");
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000825
Daniel Veillard9ff88172002-03-11 09:15:32 +0000826 if ((cur->ns != NULL) && (xmlStrlen(cur->ns->prefix) > 0)) {
827 xmlOutputBufferWriteString(ctx->buf,
828 (const char *) cur->ns->prefix);
829 xmlOutputBufferWriteString(ctx->buf, ":");
830 }
831 xmlOutputBufferWriteString(ctx->buf, (const char *) cur->name);
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000832
Daniel Veillard9ff88172002-03-11 09:15:32 +0000833 if (ctx->exclusive) {
834 ret = xmlExcC14NProcessNamespacesAxis(ctx, cur);
835 } else {
836 ret = xmlC14NProcessNamespacesAxis(ctx, cur);
837 }
838 if (ret < 0) {
839#ifdef DEBUG_C14N
840 xmlGenericError(xmlGenericErrorContext,
841 "xmlC14NProcessElementNode: xmlC14NProcessNamespacesAxis failed.\n");
842#endif
843 return (-1);
844 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000845
Daniel Veillard9ff88172002-03-11 09:15:32 +0000846 ret = xmlC14NProcessAttrsAxis(ctx, cur);
847 if (ret < 0) {
848#ifdef DEBUG_C14N
849 xmlGenericError(xmlGenericErrorContext,
850 "xmlC14NProcessElementNode: xmlC14NProcessAttrsAxis failed.\n");
851#endif
852 return (-1);
853 }
854
855 xmlOutputBufferWriteString(ctx->buf, ">");
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000856 }
857 if (cur->children != NULL) {
Daniel Veillard9ff88172002-03-11 09:15:32 +0000858 ret = xmlC14NProcessNodeList(ctx, cur->children);
859 if (ret < 0) {
860#ifdef DEBUG_C14N
861 xmlGenericError(xmlGenericErrorContext,
862 "xmlC14NProcessElementNode: xmlC14NProcessNodeList failed.\n");
863#endif
864 return (-1);
865 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000866 }
Daniel Veillard9ff88172002-03-11 09:15:32 +0000867 if (visible) {
868 xmlOutputBufferWriteString(ctx->buf, "</");
869 if ((cur->ns != NULL) && (xmlStrlen(cur->ns->prefix) > 0)) {
870 xmlOutputBufferWriteString(ctx->buf,
871 (const char *) cur->ns->prefix);
872 xmlOutputBufferWriteString(ctx->buf, ":");
873 }
874 xmlOutputBufferWriteString(ctx->buf, (const char *) cur->name);
875 xmlOutputBufferWriteString(ctx->buf, ">");
Daniel Veillard6f293b12002-03-15 09:42:33 +0000876 if (parent_is_doc) {
877 /* restore this flag from the stack for next node */
878 ctx->parent_is_doc = parent_is_doc;
879 ctx->pos = XMLC14N_AFTER_DOCUMENT_ELEMENT;
Daniel Veillard9ff88172002-03-11 09:15:32 +0000880 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000881 }
882
883 /*
884 * Restore ns_rendered stack position for exclusive
885 * processing
886 */
Daniel Veillard9ff88172002-03-11 09:15:32 +0000887 if ((ctx->exclusive) && (ctx->ns_rendered != NULL)) {
888 ctx->ns_rendered->nodeNr = ns_rendered_pos;
889 }
890 return (0);
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000891}
892
893/**
894 * xmlC14NProcessNode:
895 * @ctx: the pointer to C14N context object
896 * @cur: the node to process
897 *
898 * Processes the given node
899 *
900 * Returns non-negative value on success or negative value on fail
901 */
902static int
Daniel Veillard9ff88172002-03-11 09:15:32 +0000903xmlC14NProcessNode(xmlC14NCtxPtr ctx, xmlNodePtr cur)
904{
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000905 int ret = 0;
906 int visible;
Daniel Veillard9ff88172002-03-11 09:15:32 +0000907
908 if ((ctx == NULL) || (cur == NULL)) {
909#ifdef DEBUG_C14N
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000910 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +0000911 "xmlC14NProcessNode: Null context or node pointer.\n");
912#endif
913 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000914 }
915
916 visible = xmlC14NIsVisible(ctx, cur);
Daniel Veillard9ff88172002-03-11 09:15:32 +0000917 switch (cur->type) {
918 case XML_ELEMENT_NODE:
919 ret = xmlC14NProcessElementNode(ctx, cur, visible);
920 break;
921 case XML_CDATA_SECTION_NODE:
922 case XML_TEXT_NODE:
923 /*
924 * Text Nodes
925 * the string value, except all ampersands are replaced
926 * by &amp;, all open angle brackets (<) are replaced by &lt;, all closing
927 * angle brackets (>) are replaced by &gt;, and all #xD characters are
928 * replaced by &#xD;.
929 */
930 /* cdata sections are processed as text nodes */
931 /* todo: verify that cdata sections are included in XPath nodes set */
932 if ((visible) && (cur->content != NULL)) {
933 xmlChar *buffer;
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000934
Daniel Veillard9ff88172002-03-11 09:15:32 +0000935 buffer = xmlC11NNormalizeText(cur->content);
936 if (buffer != NULL) {
937 xmlOutputBufferWriteString(ctx->buf,
938 (const char *) buffer);
939 xmlFree(buffer);
940 } else {
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000941#ifdef DEBUG_C14N
Daniel Veillard9ff88172002-03-11 09:15:32 +0000942 xmlGenericError(xmlGenericErrorContext,
943 "xmlC14NProcessNode: xmlC11NNormalizeText() failed\n");
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000944#endif
Daniel Veillard9ff88172002-03-11 09:15:32 +0000945 return (-1);
946 }
947 }
948 break;
949 case XML_PI_NODE:
950 /*
951 * Processing Instruction (PI) Nodes-
952 * The opening PI symbol (<?), the PI target name of the node,
953 * a leading space and the string value if it is not empty, and
954 * the closing PI symbol (?>). If the string value is empty,
955 * then the leading space is not added. Also, a trailing #xA is
956 * rendered after the closing PI symbol for PI children of the
957 * root node with a lesser document order than the document
958 * element, and a leading #xA is rendered before the opening PI
959 * symbol of PI children of the root node with a greater document
960 * order than the document element.
961 */
962 if (visible) {
963 if (ctx->pos == XMLC14N_AFTER_DOCUMENT_ELEMENT) {
964 xmlOutputBufferWriteString(ctx->buf, "\x0A<?");
965 } else {
966 xmlOutputBufferWriteString(ctx->buf, "<?");
967 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000968
Daniel Veillard9ff88172002-03-11 09:15:32 +0000969 xmlOutputBufferWriteString(ctx->buf,
970 (const char *) cur->name);
971 if ((cur->content != NULL) && (*(cur->content) != '\0')) {
972 xmlChar *buffer;
973
974 xmlOutputBufferWriteString(ctx->buf, " ");
975
976 /* todo: do we need to normalize pi? */
977 buffer = xmlC11NNormalizePI(cur->content);
978 if (buffer != NULL) {
979 xmlOutputBufferWriteString(ctx->buf,
980 (const char *) buffer);
981 xmlFree(buffer);
982 } else {
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000983#ifdef DEBUG_C14N
Daniel Veillard9ff88172002-03-11 09:15:32 +0000984 xmlGenericError(xmlGenericErrorContext,
985 "xmlC14NProcessNode: xmlC11NNormalizePI() failed\n");
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000986#endif
Daniel Veillard9ff88172002-03-11 09:15:32 +0000987 return (-1);
988 }
989 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000990
Daniel Veillard9ff88172002-03-11 09:15:32 +0000991 if (ctx->pos == XMLC14N_BEFORE_DOCUMENT_ELEMENT) {
992 xmlOutputBufferWriteString(ctx->buf, "?>\x0A");
993 } else {
994 xmlOutputBufferWriteString(ctx->buf, "?>");
995 }
996 }
997 break;
998 case XML_COMMENT_NODE:
999 /*
1000 * Comment Nodes
1001 * Nothing if generating canonical XML without comments. For
1002 * canonical XML with comments, generate the opening comment
1003 * symbol (<!--), the string value of the node, and the
1004 * closing comment symbol (-->). Also, a trailing #xA is rendered
1005 * after the closing comment symbol for comment children of the
1006 * root node with a lesser document order than the document
1007 * element, and a leading #xA is rendered before the opening
1008 * comment symbol of comment children of the root node with a
1009 * greater document order than the document element. (Comment
1010 * children of the root node represent comments outside of the
1011 * top-level document element and outside of the document type
1012 * declaration).
1013 */
1014 if (visible && ctx->with_comments) {
1015 if (ctx->pos == XMLC14N_AFTER_DOCUMENT_ELEMENT) {
1016 xmlOutputBufferWriteString(ctx->buf, "\x0A<!--");
1017 } else {
1018 xmlOutputBufferWriteString(ctx->buf, "<!--");
1019 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001020
Daniel Veillard9ff88172002-03-11 09:15:32 +00001021 if (cur->content != NULL) {
1022 xmlChar *buffer;
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001023
Daniel Veillard9ff88172002-03-11 09:15:32 +00001024 /* todo: do we need to normalize comment? */
1025 buffer = xmlC11NNormalizeComment(cur->content);
1026 if (buffer != NULL) {
1027 xmlOutputBufferWriteString(ctx->buf,
1028 (const char *) buffer);
1029 xmlFree(buffer);
1030 } else {
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001031#ifdef DEBUG_C14N
Daniel Veillard9ff88172002-03-11 09:15:32 +00001032 xmlGenericError(xmlGenericErrorContext,
1033 "xmlC14NProcessNode: xmlC11NNormalizeComment() failed\n");
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001034#endif
Daniel Veillard9ff88172002-03-11 09:15:32 +00001035 return (-1);
1036 }
1037 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001038
Daniel Veillard9ff88172002-03-11 09:15:32 +00001039 if (ctx->pos == XMLC14N_BEFORE_DOCUMENT_ELEMENT) {
1040 xmlOutputBufferWriteString(ctx->buf, "-->\x0A");
1041 } else {
1042 xmlOutputBufferWriteString(ctx->buf, "-->");
1043 }
1044 }
1045 break;
1046 case XML_DOCUMENT_NODE:
1047 case XML_DOCUMENT_FRAG_NODE: /* should be processed as document? */
1048 case XML_DOCB_DOCUMENT_NODE: /* should be processed as document? */
1049 case XML_HTML_DOCUMENT_NODE: /* should be processed as document? */
1050 if (cur->children != NULL) {
1051 ctx->pos = XMLC14N_BEFORE_DOCUMENT_ELEMENT;
1052 ctx->parent_is_doc = 1;
1053 ret = xmlC14NProcessNodeList(ctx, cur->children);
1054 }
1055 break;
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001056
Daniel Veillard9ff88172002-03-11 09:15:32 +00001057 case XML_ATTRIBUTE_NODE:
1058 xmlGenericError(xmlGenericErrorContext,
1059 "xmlC14NProcessNode: XML_ATTRIBUTE_NODE is illegal here\n");
1060 return (-1);
1061 case XML_NAMESPACE_DECL:
1062 xmlGenericError(xmlGenericErrorContext,
1063 "xmlC14NProcessNode: XML_NAMESPACE_DECL is illegal here\n");
1064 return (-1);
1065 case XML_ENTITY_REF_NODE:
1066 xmlGenericError(xmlGenericErrorContext,
1067 "xmlC14NProcessNode: XML_ENTITY_REF_NODE is illegal here\n");
1068 return (-1);
1069 case XML_ENTITY_NODE:
1070 xmlGenericError(xmlGenericErrorContext,
1071 "xmlC14NProcessNode: XML_ENTITY_NODE is illegal here\n");
1072 return (-1);
1073
1074 case XML_DOCUMENT_TYPE_NODE:
1075 case XML_NOTATION_NODE:
1076 case XML_DTD_NODE:
1077 case XML_ELEMENT_DECL:
1078 case XML_ATTRIBUTE_DECL:
1079 case XML_ENTITY_DECL:
1080 case XML_XINCLUDE_START:
1081 case XML_XINCLUDE_END:
1082 /*
1083 * should be ignored according to "W3C Canonical XML"
1084 */
1085 break;
1086 default:
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001087#ifdef DEBUG_C14N
Daniel Veillard9ff88172002-03-11 09:15:32 +00001088 xmlGenericError(xmlGenericErrorContext,
1089 "xmlC14NProcessNode: unknown node type = %d\n",
1090 cur->type);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001091#endif
Daniel Veillard9ff88172002-03-11 09:15:32 +00001092 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001093 }
1094
Daniel Veillard9ff88172002-03-11 09:15:32 +00001095 return (ret);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001096}
1097
1098/**
1099 * xmlC14NProcessNodeList:
1100 * @ctx: the pointer to C14N context object
1101 * @cur: the node to start from
1102 *
1103 * Processes all nodes in the row starting from cur.
1104 *
1105 * Returns non-negative value on success or negative value on fail
1106 */
1107static int
Daniel Veillard9ff88172002-03-11 09:15:32 +00001108xmlC14NProcessNodeList(xmlC14NCtxPtr ctx, xmlNodePtr cur)
1109{
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001110 int ret;
Daniel Veillard9ff88172002-03-11 09:15:32 +00001111
1112 if (ctx == NULL) {
1113#ifdef DEBUG_C14N
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001114 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001115 "xmlC14NProcessNodeList: Null context pointer.\n");
1116#endif
1117 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001118 }
1119
Daniel Veillard9ff88172002-03-11 09:15:32 +00001120 for (ret = 0; cur != NULL && ret >= 0; cur = cur->next) {
1121 ret = xmlC14NProcessNode(ctx, cur);
1122 }
1123 return (ret);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001124}
1125
1126
1127/**
1128 * xmlC14NFreeCtx:
1129 * @ctx: the pointer to C14N context object
1130 *
1131 * Cleanups the C14N context object.
1132 */
1133
1134static void
Daniel Veillard9ff88172002-03-11 09:15:32 +00001135xmlC14NFreeCtx(xmlC14NCtxPtr ctx)
1136{
1137 if (ctx == NULL) {
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001138#ifdef DEBUG_C14N
1139 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001140 "xmlC14NFreeCtx: ctx == NULL\n");
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001141#endif
Daniel Veillard9ff88172002-03-11 09:15:32 +00001142 return;
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001143 }
Daniel Veillard9ff88172002-03-11 09:15:32 +00001144
1145 if (ctx->ns_rendered != NULL) {
1146 xmlXPathFreeNodeSet(ctx->ns_rendered);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001147 }
1148 xmlFree(ctx);
1149}
1150
1151/**
1152 * xmlC14NNewCtx:
1153 * @doc: the XML document for canonization
1154 * @nodes: the nodes set to be included in the canonized image
1155 * or NULL if all document nodes should be included
1156 * @exclusive: the exclusive flag (0 - non-exclusive canonicalization;
1157 * otherwise - exclusive canonicalization)
1158 * @inclusive_ns_prefixe the list of inclusive namespace prefixes
1159 * ended with a NULL or NULL if there is no
1160 * inclusive namespaces (only for exclusive
1161 * canonicalization)
1162 * @with_comments: include comments in the result (!=0) or not (==0)
1163 * @buf: the output buffer to store canonical XML; this
1164 * buffer MUST have encoder==NULL because C14N requires
1165 * UTF-8 output
1166 *
1167 * Creates new C14N context object to store C14N parameters.
1168 *
1169 * Returns pointer to newly created object (success) or NULL (fail)
1170 */
1171static xmlC14NCtxPtr
Daniel Veillard9ff88172002-03-11 09:15:32 +00001172xmlC14NNewCtx(xmlDocPtr doc, xmlNodeSetPtr nodes,
1173 int exclusive, xmlChar ** inclusive_ns_prefixes,
1174 int with_comments, xmlOutputBufferPtr buf)
1175{
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001176 xmlC14NCtxPtr ctx;
1177
Daniel Veillard9ff88172002-03-11 09:15:32 +00001178 if ((doc == NULL) || (buf == NULL)) {
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001179#ifdef DEBUG_C14N
1180 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001181 "xmlC14NNewCtx: pointer to document or output buffer is NULL\n");
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001182#endif
Daniel Veillard9ff88172002-03-11 09:15:32 +00001183 return (NULL);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001184 }
1185
1186 /*
1187 * Validate the encoding output buffer encoding
1188 */
Daniel Veillard9ff88172002-03-11 09:15:32 +00001189 if (buf->encoder != NULL) {
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001190 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001191 "xmlC14NNewCtx: output buffer encoder != NULL but C14N requires UTF8 output\n");
1192 return (NULL);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001193 }
1194
1195 /*
1196 * Validate the XML document encoding value, if provided.
1197 */
Daniel Veillard9ff88172002-03-11 09:15:32 +00001198 if (doc->charset != XML_CHAR_ENCODING_UTF8) {
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001199 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001200 "xmlC14NNewCtx: source document not in UTF8\n");
1201 return (NULL);
1202 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001203
1204 /*
1205 * Allocate a new xmlC14NCtxPtr and fill the fields.
1206 */
1207 ctx = (xmlC14NCtxPtr) xmlMalloc(sizeof(xmlC14NCtx));
1208 if (ctx == NULL) {
1209 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001210 "xmlC14NNewCtx: malloc failed\n");
1211 return (NULL);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001212 }
1213 memset(ctx, 0, sizeof(xmlC14NCtx));
1214
1215 /*
1216 * initialize C14N context
Daniel Veillard9ff88172002-03-11 09:15:32 +00001217 */
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001218 ctx->doc = doc;
1219 ctx->with_comments = with_comments;
1220 ctx->visible_nodes = nodes;
1221 ctx->buf = buf;
1222 ctx->parent_is_doc = 1;
1223 ctx->pos = XMLC14N_BEFORE_DOCUMENT_ELEMENT;
1224
1225 /*
1226 * Set "exclusive" flag, create a nodes set for namespaces
1227 * stack and remember list of incluseve prefixes
1228 */
Daniel Veillard9ff88172002-03-11 09:15:32 +00001229 if (exclusive) {
1230 ctx->exclusive = 1;
1231 ctx->ns_rendered = xmlXPathNodeSetCreate(NULL);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001232 ctx->inclusive_ns_prefixes = inclusive_ns_prefixes;
1233 }
Daniel Veillard9ff88172002-03-11 09:15:32 +00001234 return (ctx);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001235}
1236
1237/**
1238 * xmlC14NDocSaveTo:
1239 * @doc: the XML document for canonization
1240 * @nodes: the nodes set to be included in the canonized image
1241 * or NULL if all document nodes should be included
1242 * @exclusive: the exclusive flag (0 - non-exclusive canonicalization;
1243 * otherwise - exclusive canonicalization)
Daniel Veillarddb1bdba2002-03-09 14:13:11 +00001244 * @inclusive_ns_prefixes: the list of inclusive namespace prefixes
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001245 * ended with a NULL or NULL if there is no
1246 * inclusive namespaces (only for exclusive
1247 * canonicalization, ignored otherwise)
1248 * @with_comments: include comments in the result (!=0) or not (==0)
1249 * @buf: the output buffer to store canonical XML; this
1250 * buffer MUST have encoder==NULL because C14N requires
1251 * UTF-8 output
1252 *
1253 * Dumps the canonized image of given XML document into the provided buffer.
1254 * For details see "Canonical XML" (http://www.w3.org/TR/xml-c14n) or
1255 * "Exclusive XML Canonicalization" (http://www.w3.org/TR/xml-exc-c14n)
1256 *
1257 * Returns non-negative value on success or a negative value on fail
1258 */
1259int
Daniel Veillard9ff88172002-03-11 09:15:32 +00001260xmlC14NDocSaveTo(xmlDocPtr doc, xmlNodeSetPtr nodes,
1261 int exclusive, xmlChar ** inclusive_ns_prefixes,
1262 int with_comments, xmlOutputBufferPtr buf)
1263{
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001264 xmlC14NCtxPtr ctx;
1265 int ret;
Daniel Veillard9ff88172002-03-11 09:15:32 +00001266
1267 if ((buf == NULL) || (doc == NULL)) {
1268#ifdef DEBUG_C14N
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001269 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001270 "xmlC14NDocSaveTo: null return buffer or doc pointer\n");
1271#endif
1272 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001273 }
1274
1275 /*
1276 * Validate the encoding output buffer encoding
1277 */
Daniel Veillard9ff88172002-03-11 09:15:32 +00001278 if (buf->encoder != NULL) {
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001279 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001280 "xmlC14NDocSaveTo: output buffer encoder != NULL but C14N requires UTF8 output\n");
1281 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001282 }
1283
1284 ctx = xmlC14NNewCtx(doc, nodes, exclusive, inclusive_ns_prefixes,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001285 with_comments, buf);
1286 if (ctx == NULL) {
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001287 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001288 "xmlC14NDocSaveTo: unable to create C14N context\n");
1289 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001290 }
1291
1292
1293
1294 /*
1295 * Root Node
1296 * The root node is the parent of the top-level document element. The
1297 * result of processing each of its child nodes that is in the node-set
1298 * in document order. The root node does not generate a byte order mark,
1299 * XML declaration, nor anything from within the document type
1300 * declaration.
1301 */
Daniel Veillard9ff88172002-03-11 09:15:32 +00001302 if (doc->children != NULL) {
1303 ret = xmlC14NProcessNodeList(ctx, doc->children);
1304 if (ret < 0) {
1305#ifdef DEBUG_C14N
1306 xmlGenericError(xmlGenericErrorContext,
1307 "xmlC14NDocSaveTo: process childrens' list failed.\n");
1308#endif
1309 xmlC14NFreeCtx(ctx);
1310 return (-1);
1311 }
1312 }
1313
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001314 /*
1315 * Flush buffer to get number of bytes written
1316 */
1317 ret = xmlOutputBufferFlush(buf);
Daniel Veillard9ff88172002-03-11 09:15:32 +00001318 if (ret < 0) {
1319#ifdef DEBUG_C14N
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001320 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001321 "xmlC14NDocSaveTo: buffer flush failed.\n");
1322#endif
1323 xmlC14NFreeCtx(ctx);
1324 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001325 }
1326
1327 /*
1328 * Cleanup
1329 */
Daniel Veillard9ff88172002-03-11 09:15:32 +00001330 xmlC14NFreeCtx(ctx);
1331 return (ret);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001332}
1333
1334/**
1335 * xmlC14NDocDumpMemory:
1336 * @doc: the XML document for canonization
1337 * @nodes: the nodes set to be included in the canonized image
1338 * or NULL if all document nodes should be included
1339 * @exclusive: the exclusive flag (0 - non-exclusive canonicalization;
1340 * otherwise - exclusive canonicalization)
Daniel Veillarddb1bdba2002-03-09 14:13:11 +00001341 * @inclusive_ns_prefixes: the list of inclusive namespace prefixes
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001342 * ended with a NULL or NULL if there is no
1343 * inclusive namespaces (only for exclusive
1344 * canonicalization, ignored otherwise)
1345 * @with_comments: include comments in the result (!=0) or not (==0)
1346 * @doc_txt_ptr: the memory pointer for allocated canonical XML text;
1347 * the caller of this functions is responsible for calling
1348 * xmlFree() to free allocated memory
1349 *
1350 * Dumps the canonized image of given XML document into memory.
1351 * For details see "Canonical XML" (http://www.w3.org/TR/xml-c14n) or
1352 * "Exclusive XML Canonicalization" (http://www.w3.org/TR/xml-exc-c14n)
1353 *
1354 * Returns the number of bytes written on success or a negative value on fail
1355 */
1356int
Daniel Veillard9ff88172002-03-11 09:15:32 +00001357xmlC14NDocDumpMemory(xmlDocPtr doc, xmlNodeSetPtr nodes,
1358 int exclusive, xmlChar ** inclusive_ns_prefixes,
1359 int with_comments, xmlChar ** doc_txt_ptr)
1360{
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001361 int ret;
1362 xmlOutputBufferPtr buf;
1363
1364 if (doc_txt_ptr == NULL) {
Daniel Veillard9ff88172002-03-11 09:15:32 +00001365#ifdef DEBUG_C14N
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001366 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001367 "xmlC14NDocDumpMemory: null return buffer pointer\n");
1368#endif
1369 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001370 }
1371
1372 *doc_txt_ptr = NULL;
Daniel Veillard9ff88172002-03-11 09:15:32 +00001373
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001374 /*
1375 * create memory buffer with UTF8 (default) encoding
1376 */
1377 buf = xmlAllocOutputBuffer(NULL);
Daniel Veillard9ff88172002-03-11 09:15:32 +00001378 if (buf == NULL) {
1379#ifdef DEBUG_C14N
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001380 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001381 "xmlC14NDocDumpMemory: failed to allocate output buffer.\n");
1382#endif
1383 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001384 }
1385
1386 /*
1387 * canonize document and write to buffer
1388 */
Daniel Veillard9ff88172002-03-11 09:15:32 +00001389 ret = xmlC14NDocSaveTo(doc, nodes, exclusive, inclusive_ns_prefixes,
1390 with_comments, buf);
1391 if (ret < 0) {
1392#ifdef DEBUG_C14N
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001393 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001394 "xmlC14NDocDumpMemory: xmlC14NDocSaveTo failed.\n");
1395#endif
1396 (void) xmlOutputBufferClose(buf);
1397 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001398 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001399
Daniel Veillard9ff88172002-03-11 09:15:32 +00001400 ret = buf->buffer->use;
1401 if (ret > 0) {
1402 *doc_txt_ptr = xmlStrndup(buf->buffer->content, ret);
1403 }
1404 (void) xmlOutputBufferClose(buf);
1405
1406 if ((*doc_txt_ptr == NULL) && (ret > 0)) {
1407#ifdef DEBUG_C14N
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001408 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001409 "xmlC14NDocDumpMemory: failed to allocate memory for document text representation\n");
1410#endif
1411 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001412 }
Daniel Veillard9ff88172002-03-11 09:15:32 +00001413 return (ret);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001414}
1415
1416/**
1417 * xmlC14NDocSave:
1418 * @doc: the XML document for canonization
1419 * @nodes: the nodes set to be included in the canonized image
1420 * or NULL if all document nodes should be included
1421 * @exclusive: the exclusive flag (0 - non-exclusive canonicalization;
1422 * otherwise - exclusive canonicalization)
Daniel Veillarddb1bdba2002-03-09 14:13:11 +00001423 * @inclusive_ns_prefixes: the list of inclusive namespace prefixes
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001424 * ended with a NULL or NULL if there is no
1425 * inclusive namespaces (only for exclusive
1426 * canonicalization, ignored otherwise)
1427 * @with_comments: include comments in the result (!=0) or not (==0)
1428 * @filename: the filename to store canonical XML image
1429 * @compression: the compression level (zlib requred):
1430 * -1 - libxml default,
1431 * 0 - uncompressed,
1432 * >0 - compression level
1433 *
1434 * Dumps the canonized image of given XML document into the file.
1435 * For details see "Canonical XML" (http://www.w3.org/TR/xml-c14n) or
1436 * "Exclusive XML Canonicalization" (http://www.w3.org/TR/xml-exc-c14n)
1437 *
1438 * Returns the number of bytes written success or a negative value on fail
1439 */
1440int
Daniel Veillard9ff88172002-03-11 09:15:32 +00001441xmlC14NDocSave(xmlDocPtr doc, xmlNodeSetPtr nodes,
1442 int exclusive, xmlChar ** inclusive_ns_prefixes,
1443 int with_comments, const char *filename, int compression)
1444{
1445 xmlOutputBufferPtr buf;
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001446 int ret;
1447
Daniel Veillard9ff88172002-03-11 09:15:32 +00001448 if (filename == NULL) {
1449#ifdef DEBUG_C14N
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001450 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001451 "xmlC14NDocSave: filename is NULL\n");
1452#endif
1453 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001454 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001455#ifdef HAVE_ZLIB_H
Daniel Veillard9ff88172002-03-11 09:15:32 +00001456 if (compression < 0)
1457 compression = xmlGetCompressMode();
1458#endif
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001459
1460 /*
1461 * save the content to a temp buffer, use default UTF8 encoding.
1462 */
1463 buf = xmlOutputBufferCreateFilename(filename, NULL, compression);
1464 if (buf == NULL) {
Daniel Veillard9ff88172002-03-11 09:15:32 +00001465#ifdef DEBUG_C14N
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001466 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001467 "xmlC14NDocSave: unable to create buffer for file=\"%s\" with compressin=%d\n",
1468 filename, compression);
1469#endif
1470 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001471 }
Daniel Veillard9ff88172002-03-11 09:15:32 +00001472
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001473 /*
1474 * canonize document and write to buffer
1475 */
Daniel Veillard9ff88172002-03-11 09:15:32 +00001476 ret = xmlC14NDocSaveTo(doc, nodes, exclusive, inclusive_ns_prefixes,
1477 with_comments, buf);
1478 if (ret < 0) {
1479#ifdef DEBUG_C14N
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001480 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001481 "xmlC14NDocSave: xmlC14NDocSaveTo failed.\n");
1482#endif
1483 (void) xmlOutputBufferClose(buf);
1484 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001485 }
Daniel Veillard9ff88172002-03-11 09:15:32 +00001486
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001487 /*
1488 * get the numbers of bytes written
1489 */
1490 ret = xmlOutputBufferClose(buf);
Daniel Veillard9ff88172002-03-11 09:15:32 +00001491 return (ret);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001492}
1493
1494
1495
1496/*
1497 * Macro used to grow the current buffer.
1498 */
1499#define growBufferReentrant() { \
1500 buffer_size *= 2; \
1501 buffer = (xmlChar *) \
1502 xmlRealloc(buffer, buffer_size * sizeof(xmlChar)); \
1503 if (buffer == NULL) { \
1504 perror("realloc failed"); \
1505 return(NULL); \
1506 } \
1507}
1508
1509/**
1510 * xmlC11NNormalizeString:
1511 * @input: the input string
1512 * @mode: the normalization mode (attribute, comment, PI or text)
1513 *
1514 * Converts a string to a canonical (normalized) format. The code is stolen
1515 * from xmlEncodeEntitiesReentrant(). Added normalization of \x09, \x0a, \x0A
1516 * and the @mode parameter
1517 *
1518 * Returns a normalized string (caller is responsible for calling xmlFree())
1519 * or NULL if an error occurs
1520 */
1521static xmlChar *
Daniel Veillard9ff88172002-03-11 09:15:32 +00001522xmlC11NNormalizeString(const xmlChar * input,
1523 xmlC14NNormalizationMode mode)
1524{
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001525 const xmlChar *cur = input;
1526 xmlChar *buffer = NULL;
1527 xmlChar *out = NULL;
1528 int buffer_size = 0;
1529
Daniel Veillard9ff88172002-03-11 09:15:32 +00001530 if (input == NULL)
1531 return (NULL);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001532
1533 /*
1534 * allocate an translation buffer.
1535 */
1536 buffer_size = 1000;
1537 buffer = (xmlChar *) xmlMalloc(buffer_size * sizeof(xmlChar));
1538 if (buffer == NULL) {
Daniel Veillard9ff88172002-03-11 09:15:32 +00001539 perror("malloc failed");
1540 return (NULL);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001541 }
1542 out = buffer;
1543
1544 while (*cur != '\0') {
Daniel Veillard9ff88172002-03-11 09:15:32 +00001545 if ((out - buffer) > (buffer_size - 10)) {
1546 int indx = out - buffer;
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001547
Daniel Veillard9ff88172002-03-11 09:15:32 +00001548 growBufferReentrant();
1549 out = &buffer[indx];
1550 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001551
Daniel Veillard9ff88172002-03-11 09:15:32 +00001552 if ((*cur == '<') && ((mode == XMLC14N_NORMALIZE_ATTR) ||
1553 (mode == XMLC14N_NORMALIZE_TEXT))) {
1554 *out++ = '&';
1555 *out++ = 'l';
1556 *out++ = 't';
1557 *out++ = ';';
1558 } else if ((*cur == '>') && (mode == XMLC14N_NORMALIZE_TEXT)) {
1559 *out++ = '&';
1560 *out++ = 'g';
1561 *out++ = 't';
1562 *out++ = ';';
1563 } else if ((*cur == '&') && ((mode == XMLC14N_NORMALIZE_ATTR) ||
1564 (mode == XMLC14N_NORMALIZE_TEXT))) {
1565 *out++ = '&';
1566 *out++ = 'a';
1567 *out++ = 'm';
1568 *out++ = 'p';
1569 *out++ = ';';
1570 } else if ((*cur == '"') && (mode == XMLC14N_NORMALIZE_ATTR)) {
1571 *out++ = '&';
1572 *out++ = 'q';
1573 *out++ = 'u';
1574 *out++ = 'o';
1575 *out++ = 't';
1576 *out++ = ';';
1577 } else if ((*cur == '\x09') && (mode == XMLC14N_NORMALIZE_ATTR)) {
1578 *out++ = '&';
1579 *out++ = '#';
1580 *out++ = 'x';
1581 *out++ = '9';
1582 *out++ = ';';
1583 } else if ((*cur == '\x0A') && (mode == XMLC14N_NORMALIZE_ATTR)) {
1584 *out++ = '&';
1585 *out++ = '#';
1586 *out++ = 'x';
1587 *out++ = 'A';
1588 *out++ = ';';
1589 } else if ((*cur == '\x0D') && ((mode == XMLC14N_NORMALIZE_ATTR) ||
1590 (mode == XMLC14N_NORMALIZE_TEXT) ||
1591 (mode == XMLC14N_NORMALIZE_COMMENT) ||
1592 (mode == XMLC14N_NORMALIZE_PI))) {
1593 *out++ = '&';
1594 *out++ = '#';
1595 *out++ = 'x';
1596 *out++ = 'D';
1597 *out++ = ';';
1598 } else {
1599 /*
1600 * Works because on UTF-8, all extended sequences cannot
1601 * result in bytes in the ASCII range.
1602 */
1603 *out++ = *cur;
1604 }
1605 cur++;
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001606 }
1607 *out++ = 0;
Daniel Veillard9ff88172002-03-11 09:15:32 +00001608 return (buffer);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001609}
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001610#endif /* LIBXML_C14N_ENABLED */