blob: 9ff189e1564e975b0d83b8b421abb2dafa9998ad [file] [log] [blame]
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001/*
2 * "Canonical XML" implementation
3 * http://www.w3.org/TR/xml-c14n
4 *
5 * "Exclusive XML Canonicalization" implementation
6 * http://www.w3.org/TR/xml-exc-c14n
7 *
8 * See Copyright for the status of this software.
9 *
10 * Author: Aleksey Sanin <aleksey@aleksey.com>
11 */
Daniel Veillard34ce8be2002-03-18 19:37:11 +000012#define IN_LIBXML
Daniel Veillard044fc6b2002-03-04 17:09:44 +000013#include "libxml.h"
14#ifdef LIBXML_C14N_ENABLED
15
16#ifdef HAVE_STDLIB_H
17#include <stdlib.h>
18#endif
19#include <string.h>
20
21#include <libxml/tree.h>
22#include <libxml/parser.h>
Daniel Veillard9ff88172002-03-11 09:15:32 +000023#include <libxml/uri.h>
Daniel Veillard044fc6b2002-03-04 17:09:44 +000024#include <libxml/xmlerror.h>
25#include <libxml/globals.h>
26#include <libxml/xpathInternals.h>
27#include <libxml/c14n.h>
28
29/************************************************************************
30 * *
31 * Some declaration better left private ATM *
32 * *
33 ************************************************************************/
34
Daniel Veillard9ff88172002-03-11 09:15:32 +000035typedef enum {
36 XMLC14N_BEFORE_DOCUMENT_ELEMENT = 0,
37 XMLC14N_INSIDE_DOCUMENT_ELEMENT = 1,
38 XMLC14N_AFTER_DOCUMENT_ELEMENT = 2
Daniel Veillard044fc6b2002-03-04 17:09:44 +000039} xmlC14NPosition;
40
41typedef struct _xmlC14NCtx {
42 /* input parameters */
Daniel Veillard9ff88172002-03-11 09:15:32 +000043 xmlDocPtr doc;
44 xmlNodeSetPtr visible_nodes;
45 int with_comments;
46 xmlOutputBufferPtr buf;
Daniel Veillard044fc6b2002-03-04 17:09:44 +000047
48 /* position in the XML document */
Daniel Veillard9ff88172002-03-11 09:15:32 +000049 xmlC14NPosition pos;
50 int parent_is_doc;
Daniel Veillard044fc6b2002-03-04 17:09:44 +000051
52 /* exclusive canonicalization */
Daniel Veillard9ff88172002-03-11 09:15:32 +000053 int exclusive;
54 xmlNodeSetPtr ns_rendered;
55 xmlChar **inclusive_ns_prefixes;
Daniel Veillard044fc6b2002-03-04 17:09:44 +000056} xmlC14NCtx, *xmlC14NCtxPtr;
57
58
Daniel Veillard9ff88172002-03-11 09:15:32 +000059static int xmlC14NProcessNode(xmlC14NCtxPtr ctx, xmlNodePtr cur);
60static int xmlC14NProcessNodeList(xmlC14NCtxPtr ctx, xmlNodePtr cur);
Daniel Veillard044fc6b2002-03-04 17:09:44 +000061typedef enum {
Daniel Veillard9ff88172002-03-11 09:15:32 +000062 XMLC14N_NORMALIZE_ATTR = 0,
63 XMLC14N_NORMALIZE_COMMENT = 1,
64 XMLC14N_NORMALIZE_PI = 2,
65 XMLC14N_NORMALIZE_TEXT = 3
66} xmlC14NNormalizationMode;
Daniel Veillard044fc6b2002-03-04 17:09:44 +000067
Daniel Veillard9ff88172002-03-11 09:15:32 +000068static xmlChar *xmlC11NNormalizeString(const xmlChar * input,
69 xmlC14NNormalizationMode mode);
Daniel Veillard044fc6b2002-03-04 17:09:44 +000070
71#define xmlC11NNormalizeAttr( a ) \
Daniel Veillard9ff88172002-03-11 09:15:32 +000072 xmlC11NNormalizeString((a), XMLC14N_NORMALIZE_ATTR)
Daniel Veillard044fc6b2002-03-04 17:09:44 +000073#define xmlC11NNormalizeComment( a ) \
Daniel Veillard9ff88172002-03-11 09:15:32 +000074 xmlC11NNormalizeString((a), XMLC14N_NORMALIZE_COMMENT)
Daniel Veillard044fc6b2002-03-04 17:09:44 +000075#define xmlC11NNormalizePI( a ) \
Daniel Veillard9ff88172002-03-11 09:15:32 +000076 xmlC11NNormalizeString((a), XMLC14N_NORMALIZE_PI)
Daniel Veillard044fc6b2002-03-04 17:09:44 +000077#define xmlC11NNormalizeText( a ) \
Daniel Veillard9ff88172002-03-11 09:15:32 +000078 xmlC11NNormalizeString((a), XMLC14N_NORMALIZE_TEXT)
Daniel Veillard044fc6b2002-03-04 17:09:44 +000079
80/************************************************************************
81 * *
82 * The implementation internals *
83 * *
84 ************************************************************************/
85
86/**
87 * xmlC14NIsVisible:
88 * @ctx: the C14N context
89 * @node: the node to check
90 *
91 * Checks whether the given node is visible. If the XML document normalization
92 * was called for the whole document then it is always "true".
93 *
94 * Returns 1 if the node is visible or 0 otherwise.
95 */
Daniel Veillard9ff88172002-03-11 09:15:32 +000096
Daniel Veillard044fc6b2002-03-04 17:09:44 +000097/* todo: make it a define? */
Daniel Veillard9ff88172002-03-11 09:15:32 +000098static int
99xmlC14NIsVisible(xmlC14NCtxPtr ctx, void *node)
100{
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000101 /*
102 * If the input is an XPath node-set, then the node-set must explicitly
103 * contain every node to be rendered to the canonical form.
104 */
Daniel Veillard9ff88172002-03-11 09:15:32 +0000105 if ((ctx->visible_nodes != NULL) &&
106 (!xmlXPathNodeSetContains(ctx->visible_nodes, (xmlNodePtr) node)))
107 {
108 return (0);
109 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000110
Daniel Veillard9ff88172002-03-11 09:15:32 +0000111 return (1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000112}
113
114/**
115 * xmlC14NIsXmlNs:
116 * @ns: the namespace to check
117 *
118 * Checks whether the given namespace is a default "xml:" namespace
119 * with href="http://www.w3.org/XML/1998/namespace"
120 *
121 * Returns 1 if the node is default or 0 otherwise
122 */
Daniel Veillard9ff88172002-03-11 09:15:32 +0000123
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000124/* todo: make it a define? */
125static int
Daniel Veillard9ff88172002-03-11 09:15:32 +0000126xmlC14NIsXmlNs(xmlNsPtr ns)
127{
128 return ((ns != NULL) &&
129 (xmlStrEqual(ns->prefix, BAD_CAST "xml")) &&
130 (xmlStrEqual(ns->href,
131 BAD_CAST
132 "http://www.w3.org/XML/1998/namespace")));
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000133}
134
135/**
136 * xmlExcC14NIsRendered:
137 * @ctx the C14N context
138 * @ns the namespace to check
139 *
140 * Checks whether the given namespace was already rendered or not
141 *
142 * Returns 1 if we already wrote this namespace or 0 otherwise
143 */
144static int
Daniel Veillard9ff88172002-03-11 09:15:32 +0000145xmlExcC14NIsRendered(xmlC14NCtxPtr ctx, xmlNsPtr ns)
146{
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000147 int i;
Daniel Veillard9ff88172002-03-11 09:15:32 +0000148
149 if ((ctx == NULL) || (ctx->ns_rendered == NULL) || (ns == NULL)) {
150 return (0);
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000151 }
152
Daniel Veillard9ff88172002-03-11 09:15:32 +0000153 if (ctx->ns_rendered->nodeTab != NULL) {
154 for (i = ctx->ns_rendered->nodeNr - 1; i >= 0; --i) {
155 xmlNsPtr ns1 = (xmlNsPtr) ctx->ns_rendered->nodeTab[i];
156
157 if (xmlStrEqual(ns1->prefix, ns->prefix)) {
158 return (xmlStrEqual(ns1->href, ns->href));
159 }
160 }
161 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000162 /*
163 * if the default namespace xmlns="" is not defined yet then
164 * we do not want to print it out
165 */
Daniel Veillard9ff88172002-03-11 09:15:32 +0000166 return ((xmlStrlen(ns->prefix) == 0) && (xmlStrlen(ns->href) == 0));
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000167}
168
169/**
170 * xmlC14NNamespacesCompare:
171 * @ns1: the pointer to first namespace
172 * @ns2: the pointer to second namespace
173 *
174 * Compares the namespaces by names (prefixes).
175 *
176 * Returns -1 if ns1 < ns2, 0 if ns1 == ns2 or 1 if ns1 > ns2.
177 */
Daniel Veillard9ff88172002-03-11 09:15:32 +0000178static int
179xmlC14NNamespacesCompare(xmlNsPtr ns1, xmlNsPtr ns2)
180{
181 if (ns1 == ns2)
182 return (0);
183 if (ns1 == NULL)
184 return (-1);
185 if (ns2 == NULL)
186 return (1);
187
188 return (xmlStrcmp(ns1->prefix, ns2->prefix));
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000189}
190
191
192/**
193 * xmlC14NPrintNamespaces:
194 * @ns: the pointer to namespace
195 * @ctx: the C14N context
196 *
197 * Prints the given namespace to the output buffer from C14N context.
198 *
199 * Returns 1 on success or 0 on fail.
200 */
201static int
Daniel Veillard9ff88172002-03-11 09:15:32 +0000202xmlC14NPrintNamespaces(const xmlNsPtr ns, xmlC14NCtxPtr ctx)
203{
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000204
Daniel Veillard9ff88172002-03-11 09:15:32 +0000205 if ((ns == NULL) || (ctx == NULL)) {
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000206#ifdef DEBUG_C14N
207 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +0000208 "xmlC14NPrintNamespace: namespace or context pointer is null\n");
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000209#endif
Daniel Veillard9ff88172002-03-11 09:15:32 +0000210 return 0;
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000211 }
212
Daniel Veillard9ff88172002-03-11 09:15:32 +0000213 if (ns->prefix != NULL) {
214 xmlOutputBufferWriteString(ctx->buf, " xmlns:");
215 xmlOutputBufferWriteString(ctx->buf, (const char *) ns->prefix);
216 xmlOutputBufferWriteString(ctx->buf, "=\"");
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000217 } else {
Daniel Veillard9ff88172002-03-11 09:15:32 +0000218 xmlOutputBufferWriteString(ctx->buf, " xmlns=\"");
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000219 }
220 xmlOutputBufferWriteString(ctx->buf, (const char *) ns->href);
221 xmlOutputBufferWriteString(ctx->buf, "\"");
Daniel Veillard9ff88172002-03-11 09:15:32 +0000222 return (1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000223}
224
225/**
226 * xmlC14NProcessNamespacesAxis:
227 * @ctx: the C14N context
228 * @node: the current node
229 *
230 * Prints out canonical namespace axis of the current node to the
231 * buffer from C14N context as follows
232 *
233 * Canonical XML v 1.0 (http://www.w3.org/TR/xml-c14n)
234 *
235 * Namespace Axis
236 * Consider a list L containing only namespace nodes in the
237 * axis and in the node-set in lexicographic order (ascending). To begin
238 * processing L, if the first node is not the default namespace node (a node
239 * with no namespace URI and no local name), then generate a space followed
240 * by xmlns="" if and only if the following conditions are met:
241 * - the element E that owns the axis is in the node-set
242 * - The nearest ancestor element of E in the node-set has a default
243 * namespace node in the node-set (default namespace nodes always
244 * have non-empty values in XPath)
245 * The latter condition eliminates unnecessary occurrences of xmlns="" in
246 * the canonical form since an element only receives an xmlns="" if its
247 * default namespace is empty and if it has an immediate parent in the
248 * canonical form that has a non-empty default namespace. To finish
249 * processing L, simply process every namespace node in L, except omit
250 * namespace node with local name xml, which defines the xml prefix,
251 * if its string value is http://www.w3.org/XML/1998/namespace.
252 *
253 * Exclusive XML Canonicalization v 1.0 (http://www.w3.org/TR/xml-exc-c14n)
254 * Canonical XML applied to a document subset requires the search of the
255 * ancestor nodes of each orphan element node for attributes in the xml
256 * namespace, such as xml:lang and xml:space. These are copied into the
257 * element node except if a declaration of the same attribute is already
258 * in the attribute axis of the element (whether or not it is included in
259 * the document subset). This search and copying are omitted from the
260 * Exclusive XML Canonicalization method.
261 *
262 * Returns 0 on success or -1 on fail.
263 */
264static int
Daniel Veillard9ff88172002-03-11 09:15:32 +0000265xmlC14NProcessNamespacesAxis(xmlC14NCtxPtr ctx, xmlNodePtr cur)
266{
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000267 xmlNsPtr ns;
268 xmlListPtr list;
269 xmlNodePtr visible_parent;
Daniel Veillard5c396542002-03-15 07:57:50 +0000270 xmlNodePtr node;
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000271 xmlNsPtr prev;
Daniel Veillard5c396542002-03-15 07:57:50 +0000272
Daniel Veillard9ff88172002-03-11 09:15:32 +0000273 if ((ctx == NULL) || (cur == NULL) || (cur->type != XML_ELEMENT_NODE)) {
274#ifdef DEBUG_C14N
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000275 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +0000276 "xmlC14NProcessNamespacesAxis: Null context or node pointer or type != XML_ELEMENT_NODE.\n");
277#endif
278 return (-1);
279 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000280
281 /*
282 * Create a sorted list to store element namespaces
283 */
Daniel Veillard9ff88172002-03-11 09:15:32 +0000284 list =
285 xmlListCreate(NULL, (xmlListDataCompare) xmlC14NNamespacesCompare);
286 if (list == NULL) {
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000287#ifdef DEBUG_C14N
288 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +0000289 "xmlC14NProcessNamespacesAxis: list creation failed\n");
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000290#endif
Daniel Veillard9ff88172002-03-11 09:15:32 +0000291 return (-1);
292 }
293
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000294 /* find nearest visible parent */
295 visible_parent = cur->parent;
Daniel Veillard9ff88172002-03-11 09:15:32 +0000296 while ((visible_parent != NULL) &&
297 (!xmlC14NIsVisible(ctx, visible_parent))) {
298 visible_parent = visible_parent->parent;
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000299 }
300
301 /*
302 * todo: the libxml XPath implementation does not create
303 * nodes for all namespaces known to the node (i.e. for namespaces
304 * defined in node parents). By this we need to now walk thru
305 * all namespace in current node and all invisible ancesstors
306 */
Daniel Veillard5c396542002-03-15 07:57:50 +0000307 node = cur;
Daniel Veillard9ff88172002-03-11 09:15:32 +0000308 while (cur != visible_parent) {
309 for (ns = cur->nsDef; ns != NULL; ns = ns->next) {
310 /*
311 * first of all ignore default "xml" namespace and
312 * already included namespace
313 */
314 if ((xmlC14NIsXmlNs(ns)) || (xmlListSearch(list, ns) != NULL)) {
315 continue;
316 }
Daniel Veillard5c396542002-03-15 07:57:50 +0000317 prev = xmlSearchNs(ctx->doc, node, ns->prefix);
318 if(prev != ns) {
319 /* we already processed a namespace with this name */
320 continue;
321 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000322
Daniel Veillard9ff88172002-03-11 09:15:32 +0000323 /*
324 * Lookup nearest namespace after visible parent having
325 * the same prefix. Namespace included if and only if one of
326 * the following:
327 * - another namespace having the same prefix but
328 * different value found or
329 * - there is no namespaces having the same prefix and
330 * it is not a default xmlns="" namespace (empty prefix
331 * and empty href)
332 */
333 prev = xmlSearchNs(ctx->doc, visible_parent, ns->prefix);
334 if ((prev == NULL) && ((xmlStrlen(ns->prefix) > 0) ||
335 (xmlStrlen(ns->href) > 0))) {
336 xmlListInsert(list, ns);
337 } else if ((prev != NULL)
338 && (!xmlStrEqual(ns->href, prev->href))) {
339 xmlListInsert(list, ns);
340 }
341 }
342 cur = cur->parent;
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000343 }
344
345 /*
346 * print out all elements from list
347 */
Daniel Veillard9ff88172002-03-11 09:15:32 +0000348 xmlListWalk(list, (xmlListWalker) xmlC14NPrintNamespaces,
349 (const void *) ctx);
350
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000351 /*
352 * Cleanup
353 */
354 xmlListDelete(list);
Daniel Veillard9ff88172002-03-11 09:15:32 +0000355 return (0);
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000356}
357
358/**
359 * xmlExcC14NProcessNamespacesAxis:
360 * @ctx: the C14N context
361 * @node: the current node
362 *
363 * Prints out exclusive canonical namespace axis of the current node to the
364 * buffer from C14N context as follows
365 *
366 * Exclusive XML Canonicalization
367 * http://www.w3.org/TR/xml-exc-c14n
368 *
369 * If the element node is in the XPath subset then output the node in
370 * accordance with Canonical XML except for namespace nodes which are
371 * rendered as follows:
372 *
373 * 1. Render each namespace node iff:
374 * * it is visibly utilized by the immediate parent element or one of
375 * its attributes, or is present in InclusiveNamespaces PrefixList, and
376 * * its prefix and value do not appear in ns_rendered. ns_rendered is
377 * obtained by popping the state stack in order to obtain a list of
378 * prefixes and their values which have already been rendered by
379 * an output ancestor of the namespace node's parent element.
380 * 2. Append the rendered namespace node to the list ns_rendered of namespace
381 * nodes rendered by output ancestors. Push ns_rendered on state stack and
382 * recurse.
383 * 3. After the recursion returns, pop thestate stack.
384 *
385 *
386 * Returns 0 on success or -1 on fail.
387 */
388static int
Daniel Veillard9ff88172002-03-11 09:15:32 +0000389xmlExcC14NProcessNamespacesAxis(xmlC14NCtxPtr ctx, xmlNodePtr cur)
390{
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000391 xmlListPtr list;
392 xmlAttrPtr attr;
393 xmlNsPtr ns;
Daniel Veillard9ff88172002-03-11 09:15:32 +0000394
395 if ((ctx == NULL) || (cur == NULL) || (cur->type != XML_ELEMENT_NODE)) {
396#ifdef DEBUG_C14N
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000397 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +0000398 "xmlExcC14NProcessNamespacesAxis: Null context or node pointer or type != XML_ELEMENT_NODE.\n");
399#endif
400 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000401 }
Daniel Veillard9ff88172002-03-11 09:15:32 +0000402
403 if ((!ctx->exclusive) || (ctx->ns_rendered == NULL)) {
404#ifdef DEBUG_C14N
405 xmlGenericError(xmlGenericErrorContext,
406 "xmlExcC14NProcessNamespacesAxis: called for non-exclusive canonization or rendered stack is NULL.\n");
407#endif
408 return (-1);
409
410 }
411
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000412 /*
413 * Create a sorted list to store element namespaces
414 */
Daniel Veillard9ff88172002-03-11 09:15:32 +0000415 list =
416 xmlListCreate(NULL, (xmlListDataCompare) xmlC14NNamespacesCompare);
417 if (list == NULL) {
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000418#ifdef DEBUG_C14N
419 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +0000420 "xmlExcC14NProcessNamespacesAxis: list creation failed\n");
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000421#endif
Daniel Veillard9ff88172002-03-11 09:15:32 +0000422 return (-1);
423 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000424
425 /*
426 * First of all, add all namespaces required by current node
427 * (i.e. node namespace and all attribute namespaces)
428 * todo: do we need to check for default "xml:" namespace
429 */
430 ns = (cur->ns != NULL) ? cur->ns : xmlSearchNs(ctx->doc, cur, NULL);
Daniel Veillard9ff88172002-03-11 09:15:32 +0000431 if ((ns != NULL) && (!xmlC14NIsXmlNs(ns)) &&
432 (xmlListSearch(list, ns) == NULL)) {
433 if (!xmlExcC14NIsRendered(ctx, ns)) {
434 xmlListInsert(list, ns);
435 xmlXPathNodeSetAdd(ctx->ns_rendered, (xmlNodePtr) ns);
436 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000437 }
438 attr = cur->properties;
Daniel Veillard9ff88172002-03-11 09:15:32 +0000439 while (attr != NULL) {
440 /*
Daniel Veillard2d347fa2002-03-17 10:34:11 +0000441 * we need to check that attribute is visible and has non
442 * default namespace (XML Namespaces: "default namespaces
443 * do not apply directly to attributes")
Daniel Veillard9ff88172002-03-11 09:15:32 +0000444 */
Daniel Veillard2d347fa2002-03-17 10:34:11 +0000445 if ((attr->ns != NULL) && xmlC14NIsVisible(ctx, attr) &&
446 (xmlListSearch(list, attr->ns) == NULL) &&
447 (!xmlExcC14NIsRendered(ctx, attr->ns))) {
448 xmlListInsert(list, attr->ns);
449 xmlXPathNodeSetAdd(ctx->ns_rendered, (xmlNodePtr) attr->ns);
Daniel Veillard9ff88172002-03-11 09:15:32 +0000450 }
451 attr = attr->next;
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000452 }
Daniel Veillard9ff88172002-03-11 09:15:32 +0000453
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000454 /*
455 * Next add all inclusive namespaces if needed.
456 */
Daniel Veillard9ff88172002-03-11 09:15:32 +0000457 if (ctx->inclusive_ns_prefixes != NULL) {
458 int i;
459 xmlChar *prefix;
460
461 for (i = 0; ctx->inclusive_ns_prefixes[i] != NULL; ++i) {
462 prefix = ctx->inclusive_ns_prefixes[i];
463 /*
464 * Special values for namespace with empty prefix
465 */
466 if (xmlStrEqual(prefix, BAD_CAST "#default")
467 || xmlStrEqual(prefix, BAD_CAST "")) {
468 prefix = NULL;
469 }
470 ns = xmlSearchNs(ctx->doc, cur, prefix);
471 if ((ns != NULL) && (!xmlC14NIsXmlNs(ns))) {
472 if (xmlListSearch(list, ns) == NULL &&
473 !xmlExcC14NIsRendered(ctx, ns)) {
474 xmlListInsert(list, ns);
475 xmlXPathNodeSetAdd(ctx->ns_rendered, (xmlNodePtr) ns);
476 }
477 }
478 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000479 }
Daniel Veillard9ff88172002-03-11 09:15:32 +0000480
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000481 /*
482 * print out all elements from list
483 */
Daniel Veillard9ff88172002-03-11 09:15:32 +0000484 xmlListWalk(list, (xmlListWalker) xmlC14NPrintNamespaces,
485 (const void *) ctx);
486
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000487 /*
488 * Cleanup
489 */
490 xmlListDelete(list);
Daniel Veillard9ff88172002-03-11 09:15:32 +0000491 return (0);
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000492}
493
494
495/**
496 * xmlC14NAttrsCompare:
497 * @attr1: the pointer to first attr
498 * @attr2: the pointer to second attr
499 *
500 * Prints the given attribute to the output buffer from C14N context.
501 *
502 * Returns -1 if attr1 < attr2, 0 if attr1 == attr2 or 1 if attr1 > attr2.
503 */
504static int
Daniel Veillard9ff88172002-03-11 09:15:32 +0000505xmlC14NAttrsCompare(xmlAttrPtr attr1, xmlAttrPtr attr2)
506{
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000507 int ret = 0;
508
509 /*
510 * Simple cases
511 */
Daniel Veillard9ff88172002-03-11 09:15:32 +0000512 if (attr1 == attr2)
513 return (0);
514 if (attr1 == NULL)
515 return (-1);
516 if (attr2 == NULL)
517 return (1);
518 if (attr1->ns == attr2->ns) {
519 return (xmlStrcmp(attr1->name, attr2->name));
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000520 }
521
522 /*
523 * Attributes in the default namespace are first
524 * because the default namespace is not applied to
525 * unqualified attributes
526 */
Daniel Veillard9ff88172002-03-11 09:15:32 +0000527 if (attr1->ns == NULL)
528 return (-1);
529 if (attr2->ns == NULL)
530 return (1);
531 if (attr1->ns->prefix == NULL)
532 return (-1);
533 if (attr2->ns->prefix == NULL)
534 return (1);
535
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000536 ret = xmlStrcmp(attr1->ns->href, attr2->ns->href);
Daniel Veillard9ff88172002-03-11 09:15:32 +0000537 if (ret == 0) {
538 ret = xmlStrcmp(attr1->name, attr2->name);
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000539 }
Daniel Veillard9ff88172002-03-11 09:15:32 +0000540 return (ret);
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000541}
542
543
544/**
545 * xmlC14NPrintAttrs:
546 * @attr: the pointer to attr
547 * @ctx: the C14N context
548 *
549 * Prints out canonical attribute urrent node to the
550 * buffer from C14N context as follows
551 *
552 * Canonical XML v 1.0 (http://www.w3.org/TR/xml-c14n)
553 *
554 * Returns 1 on success or 0 on fail.
555 */
556static int
Daniel Veillard9ff88172002-03-11 09:15:32 +0000557xmlC14NPrintAttrs(const xmlAttrPtr attr, xmlC14NCtxPtr ctx)
558{
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000559 xmlChar *value;
560 xmlChar *buffer;
561
Daniel Veillard9ff88172002-03-11 09:15:32 +0000562 if ((attr == NULL) || (ctx == NULL)) {
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000563#ifdef DEBUG_C14N
564 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +0000565 "xmlC14NPrintAttrs: attr == NULL or ctx == NULL\n");
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000566#endif
Daniel Veillard9ff88172002-03-11 09:15:32 +0000567 return (0);
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000568 }
569
570 xmlOutputBufferWriteString(ctx->buf, " ");
Daniel Veillard9ff88172002-03-11 09:15:32 +0000571 if (attr->ns != NULL && xmlStrlen(attr->ns->prefix) > 0) {
572 xmlOutputBufferWriteString(ctx->buf,
573 (const char *) attr->ns->prefix);
574 xmlOutputBufferWriteString(ctx->buf, ":");
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000575 }
576 xmlOutputBufferWriteString(ctx->buf, (const char *) attr->name);
577 xmlOutputBufferWriteString(ctx->buf, "=\"");
578
579 value = xmlNodeListGetString(attr->doc, attr->children, 1);
580 /* todo: should we log an error if value==NULL ? */
Daniel Veillard9ff88172002-03-11 09:15:32 +0000581 if (value != NULL) {
582 buffer = xmlC11NNormalizeAttr(value);
583 xmlFree(value);
584 if (buffer != NULL) {
585 xmlOutputBufferWriteString(ctx->buf, (const char *) buffer);
586 xmlFree(buffer);
587 } else {
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000588#ifdef DEBUG_C14N
Daniel Veillard9ff88172002-03-11 09:15:32 +0000589 xmlGenericError(xmlGenericErrorContext,
590 "xmlC14NPrintAttrs: xmlC11NNormalizeAttr failed\n");
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000591#endif
Daniel Veillard9ff88172002-03-11 09:15:32 +0000592 return (0);
593 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000594 }
595 xmlOutputBufferWriteString(ctx->buf, "\"");
Daniel Veillard9ff88172002-03-11 09:15:32 +0000596 return (1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000597}
598
599/**
600 * xmlC14NProcessAttrsAxis:
601 * @ctx: the C14N context
602 * @cur: the current node
603 *
604 * Prints out canonical attribute axis of the current node to the
605 * buffer from C14N context as follows
606 *
607 * Canonical XML v 1.0 (http://www.w3.org/TR/xml-c14n)
608 *
609 * Attribute Axis
610 * In lexicographic order (ascending), process each node that
611 * is in the element's attribute axis and in the node-set.
612 *
613 * The processing of an element node E MUST be modified slightly
614 * when an XPath node-set is given as input and the element's
615 * parent is omitted from the node-set.
616 *
617 *
618 * Exclusive XML Canonicalization v 1.0 (http://www.w3.org/TR/xml-exc-c14n)
619 *
620 * Canonical XML applied to a document subset requires the search of the
621 * ancestor nodes of each orphan element node for attributes in the xml
622 * namespace, such as xml:lang and xml:space. These are copied into the
623 * element node except if a declaration of the same attribute is already
624 * in the attribute axis of the element (whether or not it is included in
625 * the document subset). This search and copying are omitted from the
626 * Exclusive XML Canonicalization method.
627 *
628 * Returns 0 on success or -1 on fail.
629 */
630static int
Daniel Veillard9ff88172002-03-11 09:15:32 +0000631xmlC14NProcessAttrsAxis(xmlC14NCtxPtr ctx, xmlNodePtr cur)
632{
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000633 xmlAttrPtr attr;
634 xmlListPtr list;
Daniel Veillard9ff88172002-03-11 09:15:32 +0000635
636 if ((ctx == NULL) || (cur == NULL) || (cur->type != XML_ELEMENT_NODE)) {
637#ifdef DEBUG_C14N
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000638 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +0000639 "xmlC14NProcessAttrsAxis: Null context or node pointer or type != XML_ELEMENT_NODE.\n");
640#endif
641 return (-1);
642 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000643
644 /*
645 * Create a sorted list to store element attributes
646 */
Daniel Veillard9ff88172002-03-11 09:15:32 +0000647 list = xmlListCreate(NULL, (xmlListDataCompare) xmlC14NAttrsCompare);
648 if (list == NULL) {
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000649#ifdef DEBUG_C14N
650 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +0000651 "xmlC14NProcessAttrsAxis: list creation failed\n");
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000652#endif
Daniel Veillard9ff88172002-03-11 09:15:32 +0000653 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000654 }
655
656 /*
657 * Add all visible attributes from current node.
658 */
659 attr = cur->properties;
Daniel Veillard9ff88172002-03-11 09:15:32 +0000660 while (attr != NULL) {
661 /* check that attribute is visible */
662 if (xmlC14NIsVisible(ctx, attr)) {
663 xmlListInsert(list, attr);
664 }
665 attr = attr->next;
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000666 }
Daniel Veillard9ff88172002-03-11 09:15:32 +0000667
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000668 /*
669 * include attributes in "xml" namespace defined in ancestors
670 * (only for non-exclusive XML Canonicalization)
671 */
Daniel Veillard9ff88172002-03-11 09:15:32 +0000672 if ((!ctx->exclusive) && (cur->parent != NULL)
673 && (!xmlC14NIsVisible(ctx, cur->parent))) {
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000674 /*
Daniel Veillard9ff88172002-03-11 09:15:32 +0000675 * If XPath node-set is not specified then the parent is always
676 * visible!
677 */
678 cur = cur->parent;
679 while (cur != NULL) {
680 attr = cur->properties;
681 while (attr != NULL) {
682 if ((attr->ns != NULL)
683 && (xmlStrEqual(attr->ns->prefix, BAD_CAST "xml"))) {
684 if (xmlListSearch(list, attr) == NULL) {
685 xmlListInsert(list, attr);
686 }
687 }
688 attr = attr->next;
689 }
690 cur = cur->parent;
691 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000692 }
Daniel Veillard9ff88172002-03-11 09:15:32 +0000693
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000694 /*
695 * print out all elements from list
696 */
Daniel Veillard9ff88172002-03-11 09:15:32 +0000697 xmlListWalk(list, (xmlListWalker) xmlC14NPrintAttrs,
698 (const void *) ctx);
699
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000700 /*
701 * Cleanup
702 */
Daniel Veillard9ff88172002-03-11 09:15:32 +0000703 xmlListDelete(list);
704 return (0);
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000705}
706
707/**
708 * xmlC14NCheckForRelativeNamespaces:
709 * @ctx: the C14N context
710 * @cur: the current element node
711 *
712 * Checks that current element node has no relative namespaces defined
713 *
714 * Returns 0 if the node has no relative namespaces or -1 otherwise.
715 */
716static int
Daniel Veillard9ff88172002-03-11 09:15:32 +0000717xmlC14NCheckForRelativeNamespaces(xmlC14NCtxPtr ctx, xmlNodePtr cur)
718{
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000719 xmlNsPtr ns;
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000720
Daniel Veillard9ff88172002-03-11 09:15:32 +0000721 if ((ctx == NULL) || (cur == NULL) || (cur->type != XML_ELEMENT_NODE)) {
722#ifdef DEBUG_C14N
723 xmlGenericError(xmlGenericErrorContext,
724 "xmlC14NCheckForRelativeNamespaces: Null context or node pointer or type != XML_ELEMENT_NODE.\n");
725#endif
726 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000727 }
Daniel Veillard9ff88172002-03-11 09:15:32 +0000728
729 ns = cur->nsDef;
730 while (ns != NULL) {
731 if (xmlStrlen(ns->href) > 0) {
732 xmlURIPtr uri;
733
734 uri = xmlParseURI((const char *) ns->href);
735 if (uri == NULL) {
736#ifdef DEBUG_C14N
737 xmlGenericError(xmlGenericErrorContext,
738 "xmlC14NCheckForRelativeNamespaces: unable to parse uri=\"%s\".\n",
739 ns->href);
740#endif
741 return (-1);
742 }
743 if (xmlStrlen((const xmlChar *) uri->scheme) == 0) {
744 xmlFreeURI(uri);
745 return (-1);
746 }
747 if ((!xmlStrEqual
748 ((const xmlChar *) uri->scheme, BAD_CAST "urn"))
749 && (xmlStrlen((const xmlChar *) uri->server) == 0)) {
750 xmlFreeURI(uri);
751 return (-1);
752 }
753 xmlFreeURI(uri);
754 }
755 ns = ns->next;
756 }
757 return (0);
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000758}
759
760/**
761 * xmlC14NProcessElementNode:
762 * @ctx: the pointer to C14N context object
763 * @cur: the node to process
764 *
765 * Canonical XML v 1.0 (http://www.w3.org/TR/xml-c14n)
766 *
767 * Element Nodes
768 * If the element is not in the node-set, then the result is obtained
769 * by processing the namespace axis, then the attribute axis, then
770 * processing the child nodes of the element that are in the node-set
771 * (in document order). If the element is in the node-set, then the result
772 * is an open angle bracket (<), the element QName, the result of
773 * processing the namespace axis, the result of processing the attribute
774 * axis, a close angle bracket (>), the result of processing the child
775 * nodes of the element that are in the node-set (in document order), an
776 * open angle bracket, a forward slash (/), the element QName, and a close
777 * angle bracket.
778 *
779 * Returns non-negative value on success or negative value on fail
780 */
781static int
Daniel Veillard9ff88172002-03-11 09:15:32 +0000782xmlC14NProcessElementNode(xmlC14NCtxPtr ctx, xmlNodePtr cur, int visible)
783{
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000784 int ret;
785 int ns_rendered_pos = 0;
Daniel Veillard6f293b12002-03-15 09:42:33 +0000786 int parent_is_doc = 0;
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000787
Daniel Veillard9ff88172002-03-11 09:15:32 +0000788 if ((ctx == NULL) || (cur == NULL) || (cur->type != XML_ELEMENT_NODE)) {
789#ifdef DEBUG_C14N
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000790 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +0000791 "xmlC14NProcessElementNode: Null context or node pointer or type != XML_ELEMENT_NODE.\n");
792#endif
793 return (-1);
794 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000795
796 /*
797 * Check relative relative namespaces:
798 * implementations of XML canonicalization MUST report an operation
799 * failure on documents containing relative namespace URIs.
800 */
Daniel Veillard9ff88172002-03-11 09:15:32 +0000801 if (xmlC14NCheckForRelativeNamespaces(ctx, cur) < 0) {
802#ifdef DEBUG_C14N
803 xmlGenericError(xmlGenericErrorContext,
804 "xmlC14NProcessElementNode: xmlC14NCheckForRelativeNamespaces failed.\n");
805#endif
806 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000807 }
808
809
810 /*
811 * Save ns_rendered stack position for exclusive
812 * processing
813 */
Daniel Veillard9ff88172002-03-11 09:15:32 +0000814 if ((ctx->exclusive) && (ctx->ns_rendered != NULL)) {
815 ns_rendered_pos = ctx->ns_rendered->nodeNr;
816 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000817
Daniel Veillard6f293b12002-03-15 09:42:33 +0000818 if (visible) {
Daniel Veillard9ff88172002-03-11 09:15:32 +0000819 if (ctx->parent_is_doc) {
Daniel Veillard6f293b12002-03-15 09:42:33 +0000820 /* save this flag into the stack */
821 parent_is_doc = ctx->parent_is_doc;
822 ctx->parent_is_doc = 0;
Daniel Veillard9ff88172002-03-11 09:15:32 +0000823 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, ">");
Daniel Veillard6f293b12002-03-15 09:42:33 +0000877 if (parent_is_doc) {
878 /* restore this flag from the stack for next node */
879 ctx->parent_is_doc = parent_is_doc;
880 ctx->pos = XMLC14N_AFTER_DOCUMENT_ELEMENT;
Daniel Veillard9ff88172002-03-11 09:15:32 +0000881 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000882 }
883
884 /*
885 * Restore ns_rendered stack position for exclusive
886 * processing
887 */
Daniel Veillard9ff88172002-03-11 09:15:32 +0000888 if ((ctx->exclusive) && (ctx->ns_rendered != NULL)) {
889 ctx->ns_rendered->nodeNr = ns_rendered_pos;
890 }
891 return (0);
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000892}
893
894/**
895 * xmlC14NProcessNode:
896 * @ctx: the pointer to C14N context object
897 * @cur: the node to process
898 *
899 * Processes the given node
900 *
901 * Returns non-negative value on success or negative value on fail
902 */
903static int
Daniel Veillard9ff88172002-03-11 09:15:32 +0000904xmlC14NProcessNode(xmlC14NCtxPtr ctx, xmlNodePtr cur)
905{
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000906 int ret = 0;
907 int visible;
Daniel Veillard9ff88172002-03-11 09:15:32 +0000908
909 if ((ctx == NULL) || (cur == NULL)) {
910#ifdef DEBUG_C14N
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000911 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +0000912 "xmlC14NProcessNode: Null context or node pointer.\n");
913#endif
914 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000915 }
916
917 visible = xmlC14NIsVisible(ctx, cur);
Daniel Veillard9ff88172002-03-11 09:15:32 +0000918 switch (cur->type) {
919 case XML_ELEMENT_NODE:
920 ret = xmlC14NProcessElementNode(ctx, cur, visible);
921 break;
922 case XML_CDATA_SECTION_NODE:
923 case XML_TEXT_NODE:
924 /*
925 * Text Nodes
926 * the string value, except all ampersands are replaced
927 * by &amp;, all open angle brackets (<) are replaced by &lt;, all closing
928 * angle brackets (>) are replaced by &gt;, and all #xD characters are
929 * replaced by &#xD;.
930 */
931 /* cdata sections are processed as text nodes */
932 /* todo: verify that cdata sections are included in XPath nodes set */
933 if ((visible) && (cur->content != NULL)) {
934 xmlChar *buffer;
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000935
Daniel Veillard9ff88172002-03-11 09:15:32 +0000936 buffer = xmlC11NNormalizeText(cur->content);
937 if (buffer != NULL) {
938 xmlOutputBufferWriteString(ctx->buf,
939 (const char *) buffer);
940 xmlFree(buffer);
941 } else {
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000942#ifdef DEBUG_C14N
Daniel Veillard9ff88172002-03-11 09:15:32 +0000943 xmlGenericError(xmlGenericErrorContext,
944 "xmlC14NProcessNode: xmlC11NNormalizeText() failed\n");
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000945#endif
Daniel Veillard9ff88172002-03-11 09:15:32 +0000946 return (-1);
947 }
948 }
949 break;
950 case XML_PI_NODE:
951 /*
952 * Processing Instruction (PI) Nodes-
953 * The opening PI symbol (<?), the PI target name of the node,
954 * a leading space and the string value if it is not empty, and
955 * the closing PI symbol (?>). If the string value is empty,
956 * then the leading space is not added. Also, a trailing #xA is
957 * rendered after the closing PI symbol for PI children of the
958 * root node with a lesser document order than the document
959 * element, and a leading #xA is rendered before the opening PI
960 * symbol of PI children of the root node with a greater document
961 * order than the document element.
962 */
963 if (visible) {
964 if (ctx->pos == XMLC14N_AFTER_DOCUMENT_ELEMENT) {
965 xmlOutputBufferWriteString(ctx->buf, "\x0A<?");
966 } else {
967 xmlOutputBufferWriteString(ctx->buf, "<?");
968 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000969
Daniel Veillard9ff88172002-03-11 09:15:32 +0000970 xmlOutputBufferWriteString(ctx->buf,
971 (const char *) cur->name);
972 if ((cur->content != NULL) && (*(cur->content) != '\0')) {
973 xmlChar *buffer;
974
975 xmlOutputBufferWriteString(ctx->buf, " ");
976
977 /* todo: do we need to normalize pi? */
978 buffer = xmlC11NNormalizePI(cur->content);
979 if (buffer != NULL) {
980 xmlOutputBufferWriteString(ctx->buf,
981 (const char *) buffer);
982 xmlFree(buffer);
983 } else {
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000984#ifdef DEBUG_C14N
Daniel Veillard9ff88172002-03-11 09:15:32 +0000985 xmlGenericError(xmlGenericErrorContext,
986 "xmlC14NProcessNode: xmlC11NNormalizePI() failed\n");
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000987#endif
Daniel Veillard9ff88172002-03-11 09:15:32 +0000988 return (-1);
989 }
990 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000991
Daniel Veillard9ff88172002-03-11 09:15:32 +0000992 if (ctx->pos == XMLC14N_BEFORE_DOCUMENT_ELEMENT) {
993 xmlOutputBufferWriteString(ctx->buf, "?>\x0A");
994 } else {
995 xmlOutputBufferWriteString(ctx->buf, "?>");
996 }
997 }
998 break;
999 case XML_COMMENT_NODE:
1000 /*
1001 * Comment Nodes
1002 * Nothing if generating canonical XML without comments. For
1003 * canonical XML with comments, generate the opening comment
1004 * symbol (<!--), the string value of the node, and the
1005 * closing comment symbol (-->). Also, a trailing #xA is rendered
1006 * after the closing comment symbol for comment children of the
1007 * root node with a lesser document order than the document
1008 * element, and a leading #xA is rendered before the opening
1009 * comment symbol of comment children of the root node with a
1010 * greater document order than the document element. (Comment
1011 * children of the root node represent comments outside of the
1012 * top-level document element and outside of the document type
1013 * declaration).
1014 */
1015 if (visible && ctx->with_comments) {
1016 if (ctx->pos == XMLC14N_AFTER_DOCUMENT_ELEMENT) {
1017 xmlOutputBufferWriteString(ctx->buf, "\x0A<!--");
1018 } else {
1019 xmlOutputBufferWriteString(ctx->buf, "<!--");
1020 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001021
Daniel Veillard9ff88172002-03-11 09:15:32 +00001022 if (cur->content != NULL) {
1023 xmlChar *buffer;
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001024
Daniel Veillard9ff88172002-03-11 09:15:32 +00001025 /* todo: do we need to normalize comment? */
1026 buffer = xmlC11NNormalizeComment(cur->content);
1027 if (buffer != NULL) {
1028 xmlOutputBufferWriteString(ctx->buf,
1029 (const char *) buffer);
1030 xmlFree(buffer);
1031 } else {
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001032#ifdef DEBUG_C14N
Daniel Veillard9ff88172002-03-11 09:15:32 +00001033 xmlGenericError(xmlGenericErrorContext,
1034 "xmlC14NProcessNode: xmlC11NNormalizeComment() failed\n");
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001035#endif
Daniel Veillard9ff88172002-03-11 09:15:32 +00001036 return (-1);
1037 }
1038 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001039
Daniel Veillard9ff88172002-03-11 09:15:32 +00001040 if (ctx->pos == XMLC14N_BEFORE_DOCUMENT_ELEMENT) {
1041 xmlOutputBufferWriteString(ctx->buf, "-->\x0A");
1042 } else {
1043 xmlOutputBufferWriteString(ctx->buf, "-->");
1044 }
1045 }
1046 break;
1047 case XML_DOCUMENT_NODE:
1048 case XML_DOCUMENT_FRAG_NODE: /* should be processed as document? */
Daniel Veillard1840ef02002-03-21 08:05:23 +00001049#ifdef LIBXML_DOCB_ENABLED
Daniel Veillard9ff88172002-03-11 09:15:32 +00001050 case XML_DOCB_DOCUMENT_NODE: /* should be processed as document? */
Daniel Veillard1840ef02002-03-21 08:05:23 +00001051#endif
1052#ifdef LIBXML_HTML_ENABLED
Daniel Veillard9ff88172002-03-11 09:15:32 +00001053 case XML_HTML_DOCUMENT_NODE: /* should be processed as document? */
Daniel Veillard1840ef02002-03-21 08:05:23 +00001054#endif
Daniel Veillard9ff88172002-03-11 09:15:32 +00001055 if (cur->children != NULL) {
1056 ctx->pos = XMLC14N_BEFORE_DOCUMENT_ELEMENT;
1057 ctx->parent_is_doc = 1;
1058 ret = xmlC14NProcessNodeList(ctx, cur->children);
1059 }
1060 break;
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001061
Daniel Veillard9ff88172002-03-11 09:15:32 +00001062 case XML_ATTRIBUTE_NODE:
1063 xmlGenericError(xmlGenericErrorContext,
1064 "xmlC14NProcessNode: XML_ATTRIBUTE_NODE is illegal here\n");
1065 return (-1);
1066 case XML_NAMESPACE_DECL:
1067 xmlGenericError(xmlGenericErrorContext,
1068 "xmlC14NProcessNode: XML_NAMESPACE_DECL is illegal here\n");
1069 return (-1);
1070 case XML_ENTITY_REF_NODE:
1071 xmlGenericError(xmlGenericErrorContext,
1072 "xmlC14NProcessNode: XML_ENTITY_REF_NODE is illegal here\n");
1073 return (-1);
1074 case XML_ENTITY_NODE:
1075 xmlGenericError(xmlGenericErrorContext,
1076 "xmlC14NProcessNode: XML_ENTITY_NODE is illegal here\n");
1077 return (-1);
1078
1079 case XML_DOCUMENT_TYPE_NODE:
1080 case XML_NOTATION_NODE:
1081 case XML_DTD_NODE:
1082 case XML_ELEMENT_DECL:
1083 case XML_ATTRIBUTE_DECL:
1084 case XML_ENTITY_DECL:
Daniel Veillard1840ef02002-03-21 08:05:23 +00001085#ifdef LIBXML_XINCLUDE_ENABLED
Daniel Veillard9ff88172002-03-11 09:15:32 +00001086 case XML_XINCLUDE_START:
1087 case XML_XINCLUDE_END:
Daniel Veillard1840ef02002-03-21 08:05:23 +00001088#endif
Daniel Veillard9ff88172002-03-11 09:15:32 +00001089 /*
1090 * should be ignored according to "W3C Canonical XML"
1091 */
1092 break;
1093 default:
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001094#ifdef DEBUG_C14N
Daniel Veillard9ff88172002-03-11 09:15:32 +00001095 xmlGenericError(xmlGenericErrorContext,
1096 "xmlC14NProcessNode: unknown node type = %d\n",
1097 cur->type);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001098#endif
Daniel Veillard9ff88172002-03-11 09:15:32 +00001099 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001100 }
1101
Daniel Veillard9ff88172002-03-11 09:15:32 +00001102 return (ret);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001103}
1104
1105/**
1106 * xmlC14NProcessNodeList:
1107 * @ctx: the pointer to C14N context object
1108 * @cur: the node to start from
1109 *
1110 * Processes all nodes in the row starting from cur.
1111 *
1112 * Returns non-negative value on success or negative value on fail
1113 */
1114static int
Daniel Veillard9ff88172002-03-11 09:15:32 +00001115xmlC14NProcessNodeList(xmlC14NCtxPtr ctx, xmlNodePtr cur)
1116{
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001117 int ret;
Daniel Veillard9ff88172002-03-11 09:15:32 +00001118
1119 if (ctx == NULL) {
1120#ifdef DEBUG_C14N
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001121 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001122 "xmlC14NProcessNodeList: Null context pointer.\n");
1123#endif
1124 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001125 }
1126
Daniel Veillard9ff88172002-03-11 09:15:32 +00001127 for (ret = 0; cur != NULL && ret >= 0; cur = cur->next) {
1128 ret = xmlC14NProcessNode(ctx, cur);
1129 }
1130 return (ret);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001131}
1132
1133
1134/**
1135 * xmlC14NFreeCtx:
1136 * @ctx: the pointer to C14N context object
1137 *
1138 * Cleanups the C14N context object.
1139 */
1140
1141static void
Daniel Veillard9ff88172002-03-11 09:15:32 +00001142xmlC14NFreeCtx(xmlC14NCtxPtr ctx)
1143{
1144 if (ctx == NULL) {
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001145#ifdef DEBUG_C14N
1146 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001147 "xmlC14NFreeCtx: ctx == NULL\n");
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001148#endif
Daniel Veillard9ff88172002-03-11 09:15:32 +00001149 return;
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001150 }
Daniel Veillard9ff88172002-03-11 09:15:32 +00001151
1152 if (ctx->ns_rendered != NULL) {
1153 xmlXPathFreeNodeSet(ctx->ns_rendered);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001154 }
1155 xmlFree(ctx);
1156}
1157
1158/**
1159 * xmlC14NNewCtx:
1160 * @doc: the XML document for canonization
1161 * @nodes: the nodes set to be included in the canonized image
1162 * or NULL if all document nodes should be included
1163 * @exclusive: the exclusive flag (0 - non-exclusive canonicalization;
1164 * otherwise - exclusive canonicalization)
1165 * @inclusive_ns_prefixe the list of inclusive namespace prefixes
1166 * ended with a NULL or NULL if there is no
1167 * inclusive namespaces (only for exclusive
1168 * canonicalization)
1169 * @with_comments: include comments in the result (!=0) or not (==0)
1170 * @buf: the output buffer to store canonical XML; this
1171 * buffer MUST have encoder==NULL because C14N requires
1172 * UTF-8 output
1173 *
1174 * Creates new C14N context object to store C14N parameters.
1175 *
1176 * Returns pointer to newly created object (success) or NULL (fail)
1177 */
1178static xmlC14NCtxPtr
Daniel Veillard9ff88172002-03-11 09:15:32 +00001179xmlC14NNewCtx(xmlDocPtr doc, xmlNodeSetPtr nodes,
1180 int exclusive, xmlChar ** inclusive_ns_prefixes,
1181 int with_comments, xmlOutputBufferPtr buf)
1182{
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001183 xmlC14NCtxPtr ctx;
1184
Daniel Veillard9ff88172002-03-11 09:15:32 +00001185 if ((doc == NULL) || (buf == NULL)) {
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001186#ifdef DEBUG_C14N
1187 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001188 "xmlC14NNewCtx: pointer to document or output buffer is NULL\n");
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001189#endif
Daniel Veillard9ff88172002-03-11 09:15:32 +00001190 return (NULL);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001191 }
1192
1193 /*
1194 * Validate the encoding output buffer encoding
1195 */
Daniel Veillard9ff88172002-03-11 09:15:32 +00001196 if (buf->encoder != NULL) {
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001197 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001198 "xmlC14NNewCtx: output buffer encoder != NULL but C14N requires UTF8 output\n");
1199 return (NULL);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001200 }
1201
1202 /*
1203 * Validate the XML document encoding value, if provided.
1204 */
Daniel Veillard9ff88172002-03-11 09:15:32 +00001205 if (doc->charset != XML_CHAR_ENCODING_UTF8) {
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001206 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001207 "xmlC14NNewCtx: source document not in UTF8\n");
1208 return (NULL);
1209 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001210
1211 /*
1212 * Allocate a new xmlC14NCtxPtr and fill the fields.
1213 */
1214 ctx = (xmlC14NCtxPtr) xmlMalloc(sizeof(xmlC14NCtx));
1215 if (ctx == NULL) {
1216 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001217 "xmlC14NNewCtx: malloc failed\n");
1218 return (NULL);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001219 }
1220 memset(ctx, 0, sizeof(xmlC14NCtx));
1221
1222 /*
1223 * initialize C14N context
Daniel Veillard9ff88172002-03-11 09:15:32 +00001224 */
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001225 ctx->doc = doc;
1226 ctx->with_comments = with_comments;
1227 ctx->visible_nodes = nodes;
1228 ctx->buf = buf;
1229 ctx->parent_is_doc = 1;
1230 ctx->pos = XMLC14N_BEFORE_DOCUMENT_ELEMENT;
1231
1232 /*
1233 * Set "exclusive" flag, create a nodes set for namespaces
1234 * stack and remember list of incluseve prefixes
1235 */
Daniel Veillard9ff88172002-03-11 09:15:32 +00001236 if (exclusive) {
1237 ctx->exclusive = 1;
1238 ctx->ns_rendered = xmlXPathNodeSetCreate(NULL);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001239 ctx->inclusive_ns_prefixes = inclusive_ns_prefixes;
1240 }
Daniel Veillard9ff88172002-03-11 09:15:32 +00001241 return (ctx);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001242}
1243
1244/**
1245 * xmlC14NDocSaveTo:
1246 * @doc: the XML document for canonization
1247 * @nodes: the nodes set to be included in the canonized image
1248 * or NULL if all document nodes should be included
1249 * @exclusive: the exclusive flag (0 - non-exclusive canonicalization;
1250 * otherwise - exclusive canonicalization)
Daniel Veillarddb1bdba2002-03-09 14:13:11 +00001251 * @inclusive_ns_prefixes: the list of inclusive namespace prefixes
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001252 * ended with a NULL or NULL if there is no
1253 * inclusive namespaces (only for exclusive
1254 * canonicalization, ignored otherwise)
1255 * @with_comments: include comments in the result (!=0) or not (==0)
1256 * @buf: the output buffer to store canonical XML; this
1257 * buffer MUST have encoder==NULL because C14N requires
1258 * UTF-8 output
1259 *
1260 * Dumps the canonized image of given XML document into the provided buffer.
1261 * For details see "Canonical XML" (http://www.w3.org/TR/xml-c14n) or
1262 * "Exclusive XML Canonicalization" (http://www.w3.org/TR/xml-exc-c14n)
1263 *
1264 * Returns non-negative value on success or a negative value on fail
1265 */
1266int
Daniel Veillard9ff88172002-03-11 09:15:32 +00001267xmlC14NDocSaveTo(xmlDocPtr doc, xmlNodeSetPtr nodes,
1268 int exclusive, xmlChar ** inclusive_ns_prefixes,
1269 int with_comments, xmlOutputBufferPtr buf)
1270{
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001271 xmlC14NCtxPtr ctx;
1272 int ret;
Daniel Veillard9ff88172002-03-11 09:15:32 +00001273
1274 if ((buf == NULL) || (doc == NULL)) {
1275#ifdef DEBUG_C14N
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001276 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001277 "xmlC14NDocSaveTo: null return buffer or doc pointer\n");
1278#endif
1279 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001280 }
1281
1282 /*
1283 * Validate the encoding output buffer encoding
1284 */
Daniel Veillard9ff88172002-03-11 09:15:32 +00001285 if (buf->encoder != NULL) {
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001286 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001287 "xmlC14NDocSaveTo: output buffer encoder != NULL but C14N requires UTF8 output\n");
1288 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001289 }
1290
1291 ctx = xmlC14NNewCtx(doc, nodes, exclusive, inclusive_ns_prefixes,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001292 with_comments, buf);
1293 if (ctx == NULL) {
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001294 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001295 "xmlC14NDocSaveTo: unable to create C14N context\n");
1296 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001297 }
1298
1299
1300
1301 /*
1302 * Root Node
1303 * The root node is the parent of the top-level document element. The
1304 * result of processing each of its child nodes that is in the node-set
1305 * in document order. The root node does not generate a byte order mark,
1306 * XML declaration, nor anything from within the document type
1307 * declaration.
1308 */
Daniel Veillard9ff88172002-03-11 09:15:32 +00001309 if (doc->children != NULL) {
1310 ret = xmlC14NProcessNodeList(ctx, doc->children);
1311 if (ret < 0) {
1312#ifdef DEBUG_C14N
1313 xmlGenericError(xmlGenericErrorContext,
1314 "xmlC14NDocSaveTo: process childrens' list failed.\n");
1315#endif
1316 xmlC14NFreeCtx(ctx);
1317 return (-1);
1318 }
1319 }
1320
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001321 /*
1322 * Flush buffer to get number of bytes written
1323 */
1324 ret = xmlOutputBufferFlush(buf);
Daniel Veillard9ff88172002-03-11 09:15:32 +00001325 if (ret < 0) {
1326#ifdef DEBUG_C14N
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001327 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001328 "xmlC14NDocSaveTo: buffer flush failed.\n");
1329#endif
1330 xmlC14NFreeCtx(ctx);
1331 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001332 }
1333
1334 /*
1335 * Cleanup
1336 */
Daniel Veillard9ff88172002-03-11 09:15:32 +00001337 xmlC14NFreeCtx(ctx);
1338 return (ret);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001339}
1340
1341/**
1342 * xmlC14NDocDumpMemory:
1343 * @doc: the XML document for canonization
1344 * @nodes: the nodes set to be included in the canonized image
1345 * or NULL if all document nodes should be included
1346 * @exclusive: the exclusive flag (0 - non-exclusive canonicalization;
1347 * otherwise - exclusive canonicalization)
Daniel Veillarddb1bdba2002-03-09 14:13:11 +00001348 * @inclusive_ns_prefixes: the list of inclusive namespace prefixes
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001349 * ended with a NULL or NULL if there is no
1350 * inclusive namespaces (only for exclusive
1351 * canonicalization, ignored otherwise)
1352 * @with_comments: include comments in the result (!=0) or not (==0)
1353 * @doc_txt_ptr: the memory pointer for allocated canonical XML text;
1354 * the caller of this functions is responsible for calling
1355 * xmlFree() to free allocated memory
1356 *
1357 * Dumps the canonized image of given XML document into memory.
1358 * For details see "Canonical XML" (http://www.w3.org/TR/xml-c14n) or
1359 * "Exclusive XML Canonicalization" (http://www.w3.org/TR/xml-exc-c14n)
1360 *
1361 * Returns the number of bytes written on success or a negative value on fail
1362 */
1363int
Daniel Veillard9ff88172002-03-11 09:15:32 +00001364xmlC14NDocDumpMemory(xmlDocPtr doc, xmlNodeSetPtr nodes,
1365 int exclusive, xmlChar ** inclusive_ns_prefixes,
1366 int with_comments, xmlChar ** doc_txt_ptr)
1367{
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001368 int ret;
1369 xmlOutputBufferPtr buf;
1370
1371 if (doc_txt_ptr == NULL) {
Daniel Veillard9ff88172002-03-11 09:15:32 +00001372#ifdef DEBUG_C14N
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001373 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001374 "xmlC14NDocDumpMemory: null return buffer pointer\n");
1375#endif
1376 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001377 }
1378
1379 *doc_txt_ptr = NULL;
Daniel Veillard9ff88172002-03-11 09:15:32 +00001380
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001381 /*
1382 * create memory buffer with UTF8 (default) encoding
1383 */
1384 buf = xmlAllocOutputBuffer(NULL);
Daniel Veillard9ff88172002-03-11 09:15:32 +00001385 if (buf == NULL) {
1386#ifdef DEBUG_C14N
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001387 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001388 "xmlC14NDocDumpMemory: failed to allocate output buffer.\n");
1389#endif
1390 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001391 }
1392
1393 /*
1394 * canonize document and write to buffer
1395 */
Daniel Veillard9ff88172002-03-11 09:15:32 +00001396 ret = xmlC14NDocSaveTo(doc, nodes, exclusive, inclusive_ns_prefixes,
1397 with_comments, buf);
1398 if (ret < 0) {
1399#ifdef DEBUG_C14N
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001400 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001401 "xmlC14NDocDumpMemory: xmlC14NDocSaveTo failed.\n");
1402#endif
1403 (void) xmlOutputBufferClose(buf);
1404 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001405 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001406
Daniel Veillard9ff88172002-03-11 09:15:32 +00001407 ret = buf->buffer->use;
1408 if (ret > 0) {
1409 *doc_txt_ptr = xmlStrndup(buf->buffer->content, ret);
1410 }
1411 (void) xmlOutputBufferClose(buf);
1412
1413 if ((*doc_txt_ptr == NULL) && (ret > 0)) {
1414#ifdef DEBUG_C14N
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001415 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001416 "xmlC14NDocDumpMemory: failed to allocate memory for document text representation\n");
1417#endif
1418 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001419 }
Daniel Veillard9ff88172002-03-11 09:15:32 +00001420 return (ret);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001421}
1422
1423/**
1424 * xmlC14NDocSave:
1425 * @doc: the XML document for canonization
1426 * @nodes: the nodes set to be included in the canonized image
1427 * or NULL if all document nodes should be included
1428 * @exclusive: the exclusive flag (0 - non-exclusive canonicalization;
1429 * otherwise - exclusive canonicalization)
Daniel Veillarddb1bdba2002-03-09 14:13:11 +00001430 * @inclusive_ns_prefixes: the list of inclusive namespace prefixes
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001431 * ended with a NULL or NULL if there is no
1432 * inclusive namespaces (only for exclusive
1433 * canonicalization, ignored otherwise)
1434 * @with_comments: include comments in the result (!=0) or not (==0)
1435 * @filename: the filename to store canonical XML image
1436 * @compression: the compression level (zlib requred):
1437 * -1 - libxml default,
1438 * 0 - uncompressed,
1439 * >0 - compression level
1440 *
1441 * Dumps the canonized image of given XML document into the file.
1442 * For details see "Canonical XML" (http://www.w3.org/TR/xml-c14n) or
1443 * "Exclusive XML Canonicalization" (http://www.w3.org/TR/xml-exc-c14n)
1444 *
1445 * Returns the number of bytes written success or a negative value on fail
1446 */
1447int
Daniel Veillard9ff88172002-03-11 09:15:32 +00001448xmlC14NDocSave(xmlDocPtr doc, xmlNodeSetPtr nodes,
1449 int exclusive, xmlChar ** inclusive_ns_prefixes,
1450 int with_comments, const char *filename, int compression)
1451{
1452 xmlOutputBufferPtr buf;
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001453 int ret;
1454
Daniel Veillard9ff88172002-03-11 09:15:32 +00001455 if (filename == NULL) {
1456#ifdef DEBUG_C14N
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001457 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001458 "xmlC14NDocSave: filename is NULL\n");
1459#endif
1460 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001461 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001462#ifdef HAVE_ZLIB_H
Daniel Veillard9ff88172002-03-11 09:15:32 +00001463 if (compression < 0)
1464 compression = xmlGetCompressMode();
1465#endif
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001466
1467 /*
1468 * save the content to a temp buffer, use default UTF8 encoding.
1469 */
1470 buf = xmlOutputBufferCreateFilename(filename, NULL, compression);
1471 if (buf == NULL) {
Daniel Veillard9ff88172002-03-11 09:15:32 +00001472#ifdef DEBUG_C14N
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001473 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001474 "xmlC14NDocSave: unable to create buffer for file=\"%s\" with compressin=%d\n",
1475 filename, compression);
1476#endif
1477 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001478 }
Daniel Veillard9ff88172002-03-11 09:15:32 +00001479
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001480 /*
1481 * canonize document and write to buffer
1482 */
Daniel Veillard9ff88172002-03-11 09:15:32 +00001483 ret = xmlC14NDocSaveTo(doc, nodes, exclusive, inclusive_ns_prefixes,
1484 with_comments, buf);
1485 if (ret < 0) {
1486#ifdef DEBUG_C14N
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001487 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001488 "xmlC14NDocSave: xmlC14NDocSaveTo failed.\n");
1489#endif
1490 (void) xmlOutputBufferClose(buf);
1491 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001492 }
Daniel Veillard9ff88172002-03-11 09:15:32 +00001493
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001494 /*
1495 * get the numbers of bytes written
1496 */
1497 ret = xmlOutputBufferClose(buf);
Daniel Veillard9ff88172002-03-11 09:15:32 +00001498 return (ret);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001499}
1500
1501
1502
1503/*
1504 * Macro used to grow the current buffer.
1505 */
1506#define growBufferReentrant() { \
1507 buffer_size *= 2; \
1508 buffer = (xmlChar *) \
1509 xmlRealloc(buffer, buffer_size * sizeof(xmlChar)); \
1510 if (buffer == NULL) { \
1511 perror("realloc failed"); \
1512 return(NULL); \
1513 } \
1514}
1515
1516/**
1517 * xmlC11NNormalizeString:
1518 * @input: the input string
1519 * @mode: the normalization mode (attribute, comment, PI or text)
1520 *
1521 * Converts a string to a canonical (normalized) format. The code is stolen
1522 * from xmlEncodeEntitiesReentrant(). Added normalization of \x09, \x0a, \x0A
1523 * and the @mode parameter
1524 *
1525 * Returns a normalized string (caller is responsible for calling xmlFree())
1526 * or NULL if an error occurs
1527 */
1528static xmlChar *
Daniel Veillard9ff88172002-03-11 09:15:32 +00001529xmlC11NNormalizeString(const xmlChar * input,
1530 xmlC14NNormalizationMode mode)
1531{
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001532 const xmlChar *cur = input;
1533 xmlChar *buffer = NULL;
1534 xmlChar *out = NULL;
1535 int buffer_size = 0;
1536
Daniel Veillard9ff88172002-03-11 09:15:32 +00001537 if (input == NULL)
1538 return (NULL);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001539
1540 /*
1541 * allocate an translation buffer.
1542 */
1543 buffer_size = 1000;
1544 buffer = (xmlChar *) xmlMalloc(buffer_size * sizeof(xmlChar));
1545 if (buffer == NULL) {
Daniel Veillard9ff88172002-03-11 09:15:32 +00001546 perror("malloc failed");
1547 return (NULL);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001548 }
1549 out = buffer;
1550
1551 while (*cur != '\0') {
Daniel Veillard9ff88172002-03-11 09:15:32 +00001552 if ((out - buffer) > (buffer_size - 10)) {
1553 int indx = out - buffer;
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001554
Daniel Veillard9ff88172002-03-11 09:15:32 +00001555 growBufferReentrant();
1556 out = &buffer[indx];
1557 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001558
Daniel Veillard9ff88172002-03-11 09:15:32 +00001559 if ((*cur == '<') && ((mode == XMLC14N_NORMALIZE_ATTR) ||
1560 (mode == XMLC14N_NORMALIZE_TEXT))) {
1561 *out++ = '&';
1562 *out++ = 'l';
1563 *out++ = 't';
1564 *out++ = ';';
1565 } else if ((*cur == '>') && (mode == XMLC14N_NORMALIZE_TEXT)) {
1566 *out++ = '&';
1567 *out++ = 'g';
1568 *out++ = 't';
1569 *out++ = ';';
1570 } else if ((*cur == '&') && ((mode == XMLC14N_NORMALIZE_ATTR) ||
1571 (mode == XMLC14N_NORMALIZE_TEXT))) {
1572 *out++ = '&';
1573 *out++ = 'a';
1574 *out++ = 'm';
1575 *out++ = 'p';
1576 *out++ = ';';
1577 } else if ((*cur == '"') && (mode == XMLC14N_NORMALIZE_ATTR)) {
1578 *out++ = '&';
1579 *out++ = 'q';
1580 *out++ = 'u';
1581 *out++ = 'o';
1582 *out++ = 't';
1583 *out++ = ';';
1584 } else if ((*cur == '\x09') && (mode == XMLC14N_NORMALIZE_ATTR)) {
1585 *out++ = '&';
1586 *out++ = '#';
1587 *out++ = 'x';
1588 *out++ = '9';
1589 *out++ = ';';
1590 } else if ((*cur == '\x0A') && (mode == XMLC14N_NORMALIZE_ATTR)) {
1591 *out++ = '&';
1592 *out++ = '#';
1593 *out++ = 'x';
1594 *out++ = 'A';
1595 *out++ = ';';
1596 } else if ((*cur == '\x0D') && ((mode == XMLC14N_NORMALIZE_ATTR) ||
1597 (mode == XMLC14N_NORMALIZE_TEXT) ||
1598 (mode == XMLC14N_NORMALIZE_COMMENT) ||
1599 (mode == XMLC14N_NORMALIZE_PI))) {
1600 *out++ = '&';
1601 *out++ = '#';
1602 *out++ = 'x';
1603 *out++ = 'D';
1604 *out++ = ';';
1605 } else {
1606 /*
1607 * Works because on UTF-8, all extended sequences cannot
1608 * result in bytes in the ASCII range.
1609 */
1610 *out++ = *cur;
1611 }
1612 cur++;
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001613 }
1614 *out++ = 0;
Daniel Veillard9ff88172002-03-11 09:15:32 +00001615 return (buffer);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001616}
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001617#endif /* LIBXML_C14N_ENABLED */