blob: 8be82bcbb5895e0f8275da6059ab1faaf59d2537 [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)
Daniel Veillard54761132002-04-18 21:00:44 +0000428 * we also need to check for default "xml:" namespace
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000429 */
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)) &&
Daniel Veillard54761132002-04-18 21:00:44 +0000432 (xmlListSearch(list, ns) == NULL) && !xmlExcC14NIsRendered(ctx, ns)) {
Daniel Veillard9ff88172002-03-11 09:15:32 +0000433 xmlListInsert(list, ns);
434 xmlXPathNodeSetAdd(ctx->ns_rendered, (xmlNodePtr) ns);
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000435 }
436 attr = cur->properties;
Daniel Veillard9ff88172002-03-11 09:15:32 +0000437 while (attr != NULL) {
438 /*
Daniel Veillard2d347fa2002-03-17 10:34:11 +0000439 * we need to check that attribute is visible and has non
440 * default namespace (XML Namespaces: "default namespaces
441 * do not apply directly to attributes")
Daniel Veillard9ff88172002-03-11 09:15:32 +0000442 */
Daniel Veillard2d347fa2002-03-17 10:34:11 +0000443 if ((attr->ns != NULL) && xmlC14NIsVisible(ctx, attr) &&
Daniel Veillard54761132002-04-18 21:00:44 +0000444 (!xmlC14NIsXmlNs(attr->ns)) &&
445 (xmlListSearch(list, attr->ns) == NULL) && (!xmlExcC14NIsRendered(ctx, attr->ns))) {
446 xmlListInsert(list, attr->ns);
447 xmlXPathNodeSetAdd(ctx->ns_rendered, (xmlNodePtr) attr->ns);
Daniel Veillard9ff88172002-03-11 09:15:32 +0000448 }
449 attr = attr->next;
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000450 }
Daniel Veillard9ff88172002-03-11 09:15:32 +0000451
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000452 /*
453 * Next add all inclusive namespaces if needed.
454 */
Daniel Veillard9ff88172002-03-11 09:15:32 +0000455 if (ctx->inclusive_ns_prefixes != NULL) {
456 int i;
457 xmlChar *prefix;
458
459 for (i = 0; ctx->inclusive_ns_prefixes[i] != NULL; ++i) {
460 prefix = ctx->inclusive_ns_prefixes[i];
461 /*
462 * Special values for namespace with empty prefix
463 */
464 if (xmlStrEqual(prefix, BAD_CAST "#default")
465 || xmlStrEqual(prefix, BAD_CAST "")) {
466 prefix = NULL;
467 }
468 ns = xmlSearchNs(ctx->doc, cur, prefix);
469 if ((ns != NULL) && (!xmlC14NIsXmlNs(ns))) {
470 if (xmlListSearch(list, ns) == NULL &&
471 !xmlExcC14NIsRendered(ctx, ns)) {
472 xmlListInsert(list, ns);
473 xmlXPathNodeSetAdd(ctx->ns_rendered, (xmlNodePtr) ns);
474 }
475 }
476 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000477 }
Daniel Veillard9ff88172002-03-11 09:15:32 +0000478
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000479 /*
480 * print out all elements from list
481 */
Daniel Veillard9ff88172002-03-11 09:15:32 +0000482 xmlListWalk(list, (xmlListWalker) xmlC14NPrintNamespaces,
483 (const void *) ctx);
484
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000485 /*
486 * Cleanup
487 */
488 xmlListDelete(list);
Daniel Veillard9ff88172002-03-11 09:15:32 +0000489 return (0);
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000490}
491
492
493/**
494 * xmlC14NAttrsCompare:
495 * @attr1: the pointer to first attr
496 * @attr2: the pointer to second attr
497 *
498 * Prints the given attribute to the output buffer from C14N context.
499 *
500 * Returns -1 if attr1 < attr2, 0 if attr1 == attr2 or 1 if attr1 > attr2.
501 */
502static int
Daniel Veillard9ff88172002-03-11 09:15:32 +0000503xmlC14NAttrsCompare(xmlAttrPtr attr1, xmlAttrPtr attr2)
504{
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000505 int ret = 0;
506
507 /*
508 * Simple cases
509 */
Daniel Veillard9ff88172002-03-11 09:15:32 +0000510 if (attr1 == attr2)
511 return (0);
512 if (attr1 == NULL)
513 return (-1);
514 if (attr2 == NULL)
515 return (1);
516 if (attr1->ns == attr2->ns) {
517 return (xmlStrcmp(attr1->name, attr2->name));
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000518 }
519
520 /*
521 * Attributes in the default namespace are first
522 * because the default namespace is not applied to
523 * unqualified attributes
524 */
Daniel Veillard9ff88172002-03-11 09:15:32 +0000525 if (attr1->ns == NULL)
526 return (-1);
527 if (attr2->ns == NULL)
528 return (1);
529 if (attr1->ns->prefix == NULL)
530 return (-1);
531 if (attr2->ns->prefix == NULL)
532 return (1);
533
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000534 ret = xmlStrcmp(attr1->ns->href, attr2->ns->href);
Daniel Veillard9ff88172002-03-11 09:15:32 +0000535 if (ret == 0) {
536 ret = xmlStrcmp(attr1->name, attr2->name);
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000537 }
Daniel Veillard9ff88172002-03-11 09:15:32 +0000538 return (ret);
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000539}
540
541
542/**
543 * xmlC14NPrintAttrs:
544 * @attr: the pointer to attr
545 * @ctx: the C14N context
546 *
547 * Prints out canonical attribute urrent node to the
548 * buffer from C14N context as follows
549 *
550 * Canonical XML v 1.0 (http://www.w3.org/TR/xml-c14n)
551 *
552 * Returns 1 on success or 0 on fail.
553 */
554static int
Daniel Veillard9ff88172002-03-11 09:15:32 +0000555xmlC14NPrintAttrs(const xmlAttrPtr attr, xmlC14NCtxPtr ctx)
556{
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000557 xmlChar *value;
558 xmlChar *buffer;
559
Daniel Veillard9ff88172002-03-11 09:15:32 +0000560 if ((attr == NULL) || (ctx == NULL)) {
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000561#ifdef DEBUG_C14N
562 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +0000563 "xmlC14NPrintAttrs: attr == NULL or ctx == NULL\n");
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000564#endif
Daniel Veillard9ff88172002-03-11 09:15:32 +0000565 return (0);
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000566 }
567
568 xmlOutputBufferWriteString(ctx->buf, " ");
Daniel Veillard9ff88172002-03-11 09:15:32 +0000569 if (attr->ns != NULL && xmlStrlen(attr->ns->prefix) > 0) {
570 xmlOutputBufferWriteString(ctx->buf,
571 (const char *) attr->ns->prefix);
572 xmlOutputBufferWriteString(ctx->buf, ":");
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000573 }
574 xmlOutputBufferWriteString(ctx->buf, (const char *) attr->name);
575 xmlOutputBufferWriteString(ctx->buf, "=\"");
576
577 value = xmlNodeListGetString(attr->doc, attr->children, 1);
578 /* todo: should we log an error if value==NULL ? */
Daniel Veillard9ff88172002-03-11 09:15:32 +0000579 if (value != NULL) {
580 buffer = xmlC11NNormalizeAttr(value);
581 xmlFree(value);
582 if (buffer != NULL) {
583 xmlOutputBufferWriteString(ctx->buf, (const char *) buffer);
584 xmlFree(buffer);
585 } else {
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000586#ifdef DEBUG_C14N
Daniel Veillard9ff88172002-03-11 09:15:32 +0000587 xmlGenericError(xmlGenericErrorContext,
588 "xmlC14NPrintAttrs: xmlC11NNormalizeAttr failed\n");
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000589#endif
Daniel Veillard9ff88172002-03-11 09:15:32 +0000590 return (0);
591 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000592 }
593 xmlOutputBufferWriteString(ctx->buf, "\"");
Daniel Veillard9ff88172002-03-11 09:15:32 +0000594 return (1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000595}
596
597/**
598 * xmlC14NProcessAttrsAxis:
599 * @ctx: the C14N context
600 * @cur: the current node
601 *
602 * Prints out canonical attribute axis of the current node to the
603 * buffer from C14N context as follows
604 *
605 * Canonical XML v 1.0 (http://www.w3.org/TR/xml-c14n)
606 *
607 * Attribute Axis
608 * In lexicographic order (ascending), process each node that
609 * is in the element's attribute axis and in the node-set.
610 *
611 * The processing of an element node E MUST be modified slightly
612 * when an XPath node-set is given as input and the element's
613 * parent is omitted from the node-set.
614 *
615 *
616 * Exclusive XML Canonicalization v 1.0 (http://www.w3.org/TR/xml-exc-c14n)
617 *
618 * Canonical XML applied to a document subset requires the search of the
619 * ancestor nodes of each orphan element node for attributes in the xml
620 * namespace, such as xml:lang and xml:space. These are copied into the
621 * element node except if a declaration of the same attribute is already
622 * in the attribute axis of the element (whether or not it is included in
623 * the document subset). This search and copying are omitted from the
624 * Exclusive XML Canonicalization method.
625 *
626 * Returns 0 on success or -1 on fail.
627 */
628static int
Daniel Veillard9ff88172002-03-11 09:15:32 +0000629xmlC14NProcessAttrsAxis(xmlC14NCtxPtr ctx, xmlNodePtr cur)
630{
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000631 xmlAttrPtr attr;
632 xmlListPtr list;
Daniel Veillard9ff88172002-03-11 09:15:32 +0000633
634 if ((ctx == NULL) || (cur == NULL) || (cur->type != XML_ELEMENT_NODE)) {
635#ifdef DEBUG_C14N
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000636 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +0000637 "xmlC14NProcessAttrsAxis: Null context or node pointer or type != XML_ELEMENT_NODE.\n");
638#endif
639 return (-1);
640 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000641
642 /*
643 * Create a sorted list to store element attributes
644 */
Daniel Veillard9ff88172002-03-11 09:15:32 +0000645 list = xmlListCreate(NULL, (xmlListDataCompare) xmlC14NAttrsCompare);
646 if (list == NULL) {
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000647#ifdef DEBUG_C14N
648 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +0000649 "xmlC14NProcessAttrsAxis: list creation failed\n");
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000650#endif
Daniel Veillard9ff88172002-03-11 09:15:32 +0000651 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000652 }
653
654 /*
655 * Add all visible attributes from current node.
656 */
657 attr = cur->properties;
Daniel Veillard9ff88172002-03-11 09:15:32 +0000658 while (attr != NULL) {
659 /* check that attribute is visible */
660 if (xmlC14NIsVisible(ctx, attr)) {
661 xmlListInsert(list, attr);
662 }
663 attr = attr->next;
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000664 }
Daniel Veillard9ff88172002-03-11 09:15:32 +0000665
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000666 /*
667 * include attributes in "xml" namespace defined in ancestors
668 * (only for non-exclusive XML Canonicalization)
669 */
Daniel Veillard9ff88172002-03-11 09:15:32 +0000670 if ((!ctx->exclusive) && (cur->parent != NULL)
671 && (!xmlC14NIsVisible(ctx, cur->parent))) {
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000672 /*
Daniel Veillard9ff88172002-03-11 09:15:32 +0000673 * If XPath node-set is not specified then the parent is always
674 * visible!
675 */
676 cur = cur->parent;
677 while (cur != NULL) {
678 attr = cur->properties;
679 while (attr != NULL) {
680 if ((attr->ns != NULL)
681 && (xmlStrEqual(attr->ns->prefix, BAD_CAST "xml"))) {
682 if (xmlListSearch(list, attr) == NULL) {
683 xmlListInsert(list, attr);
684 }
685 }
686 attr = attr->next;
687 }
688 cur = cur->parent;
689 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000690 }
Daniel Veillard9ff88172002-03-11 09:15:32 +0000691
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000692 /*
693 * print out all elements from list
694 */
Daniel Veillard9ff88172002-03-11 09:15:32 +0000695 xmlListWalk(list, (xmlListWalker) xmlC14NPrintAttrs,
696 (const void *) ctx);
697
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000698 /*
699 * Cleanup
700 */
Daniel Veillard9ff88172002-03-11 09:15:32 +0000701 xmlListDelete(list);
702 return (0);
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000703}
704
705/**
706 * xmlC14NCheckForRelativeNamespaces:
707 * @ctx: the C14N context
708 * @cur: the current element node
709 *
710 * Checks that current element node has no relative namespaces defined
711 *
712 * Returns 0 if the node has no relative namespaces or -1 otherwise.
713 */
714static int
Daniel Veillard9ff88172002-03-11 09:15:32 +0000715xmlC14NCheckForRelativeNamespaces(xmlC14NCtxPtr ctx, xmlNodePtr cur)
716{
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000717 xmlNsPtr ns;
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000718
Daniel Veillard9ff88172002-03-11 09:15:32 +0000719 if ((ctx == NULL) || (cur == NULL) || (cur->type != XML_ELEMENT_NODE)) {
720#ifdef DEBUG_C14N
721 xmlGenericError(xmlGenericErrorContext,
722 "xmlC14NCheckForRelativeNamespaces: Null context or node pointer or type != XML_ELEMENT_NODE.\n");
723#endif
724 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000725 }
Daniel Veillard9ff88172002-03-11 09:15:32 +0000726
727 ns = cur->nsDef;
728 while (ns != NULL) {
729 if (xmlStrlen(ns->href) > 0) {
730 xmlURIPtr uri;
731
732 uri = xmlParseURI((const char *) ns->href);
733 if (uri == NULL) {
734#ifdef DEBUG_C14N
735 xmlGenericError(xmlGenericErrorContext,
736 "xmlC14NCheckForRelativeNamespaces: unable to parse uri=\"%s\".\n",
737 ns->href);
738#endif
739 return (-1);
740 }
741 if (xmlStrlen((const xmlChar *) uri->scheme) == 0) {
742 xmlFreeURI(uri);
743 return (-1);
744 }
745 if ((!xmlStrEqual
746 ((const xmlChar *) uri->scheme, BAD_CAST "urn"))
747 && (xmlStrlen((const xmlChar *) uri->server) == 0)) {
748 xmlFreeURI(uri);
749 return (-1);
750 }
751 xmlFreeURI(uri);
752 }
753 ns = ns->next;
754 }
755 return (0);
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000756}
757
758/**
759 * xmlC14NProcessElementNode:
760 * @ctx: the pointer to C14N context object
761 * @cur: the node to process
762 *
763 * Canonical XML v 1.0 (http://www.w3.org/TR/xml-c14n)
764 *
765 * Element Nodes
766 * If the element is not in the node-set, then the result is obtained
767 * by processing the namespace axis, then the attribute axis, then
768 * processing the child nodes of the element that are in the node-set
769 * (in document order). If the element is in the node-set, then the result
770 * is an open angle bracket (<), the element QName, the result of
771 * processing the namespace axis, the result of processing the attribute
772 * axis, a close angle bracket (>), the result of processing the child
773 * nodes of the element that are in the node-set (in document order), an
774 * open angle bracket, a forward slash (/), the element QName, and a close
775 * angle bracket.
776 *
777 * Returns non-negative value on success or negative value on fail
778 */
779static int
Daniel Veillard9ff88172002-03-11 09:15:32 +0000780xmlC14NProcessElementNode(xmlC14NCtxPtr ctx, xmlNodePtr cur, int visible)
781{
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000782 int ret;
783 int ns_rendered_pos = 0;
Daniel Veillard6f293b12002-03-15 09:42:33 +0000784 int parent_is_doc = 0;
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000785
Daniel Veillard9ff88172002-03-11 09:15:32 +0000786 if ((ctx == NULL) || (cur == NULL) || (cur->type != XML_ELEMENT_NODE)) {
787#ifdef DEBUG_C14N
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000788 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +0000789 "xmlC14NProcessElementNode: Null context or node pointer or type != XML_ELEMENT_NODE.\n");
790#endif
791 return (-1);
792 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000793
794 /*
795 * Check relative relative namespaces:
796 * implementations of XML canonicalization MUST report an operation
797 * failure on documents containing relative namespace URIs.
798 */
Daniel Veillard9ff88172002-03-11 09:15:32 +0000799 if (xmlC14NCheckForRelativeNamespaces(ctx, cur) < 0) {
800#ifdef DEBUG_C14N
801 xmlGenericError(xmlGenericErrorContext,
802 "xmlC14NProcessElementNode: xmlC14NCheckForRelativeNamespaces failed.\n");
803#endif
804 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000805 }
806
807
808 /*
809 * Save ns_rendered stack position for exclusive
810 * processing
811 */
Daniel Veillard9ff88172002-03-11 09:15:32 +0000812 if ((ctx->exclusive) && (ctx->ns_rendered != NULL)) {
813 ns_rendered_pos = ctx->ns_rendered->nodeNr;
814 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000815
Daniel Veillard6f293b12002-03-15 09:42:33 +0000816 if (visible) {
Daniel Veillard9ff88172002-03-11 09:15:32 +0000817 if (ctx->parent_is_doc) {
Daniel Veillard6f293b12002-03-15 09:42:33 +0000818 /* save this flag into the stack */
819 parent_is_doc = ctx->parent_is_doc;
820 ctx->parent_is_doc = 0;
Daniel Veillard9ff88172002-03-11 09:15:32 +0000821 ctx->pos = XMLC14N_INSIDE_DOCUMENT_ELEMENT;
822 }
823 xmlOutputBufferWriteString(ctx->buf, "<");
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000824
Daniel Veillard9ff88172002-03-11 09:15:32 +0000825 if ((cur->ns != NULL) && (xmlStrlen(cur->ns->prefix) > 0)) {
826 xmlOutputBufferWriteString(ctx->buf,
827 (const char *) cur->ns->prefix);
828 xmlOutputBufferWriteString(ctx->buf, ":");
829 }
830 xmlOutputBufferWriteString(ctx->buf, (const char *) cur->name);
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000831
Daniel Veillard9ff88172002-03-11 09:15:32 +0000832 if (ctx->exclusive) {
833 ret = xmlExcC14NProcessNamespacesAxis(ctx, cur);
834 } else {
835 ret = xmlC14NProcessNamespacesAxis(ctx, cur);
836 }
837 if (ret < 0) {
838#ifdef DEBUG_C14N
839 xmlGenericError(xmlGenericErrorContext,
840 "xmlC14NProcessElementNode: xmlC14NProcessNamespacesAxis failed.\n");
841#endif
842 return (-1);
843 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000844
Daniel Veillard9ff88172002-03-11 09:15:32 +0000845 ret = xmlC14NProcessAttrsAxis(ctx, cur);
846 if (ret < 0) {
847#ifdef DEBUG_C14N
848 xmlGenericError(xmlGenericErrorContext,
849 "xmlC14NProcessElementNode: xmlC14NProcessAttrsAxis failed.\n");
850#endif
851 return (-1);
852 }
853
854 xmlOutputBufferWriteString(ctx->buf, ">");
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000855 }
856 if (cur->children != NULL) {
Daniel Veillard9ff88172002-03-11 09:15:32 +0000857 ret = xmlC14NProcessNodeList(ctx, cur->children);
858 if (ret < 0) {
859#ifdef DEBUG_C14N
860 xmlGenericError(xmlGenericErrorContext,
861 "xmlC14NProcessElementNode: xmlC14NProcessNodeList failed.\n");
862#endif
863 return (-1);
864 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000865 }
Daniel Veillard9ff88172002-03-11 09:15:32 +0000866 if (visible) {
867 xmlOutputBufferWriteString(ctx->buf, "</");
868 if ((cur->ns != NULL) && (xmlStrlen(cur->ns->prefix) > 0)) {
869 xmlOutputBufferWriteString(ctx->buf,
870 (const char *) cur->ns->prefix);
871 xmlOutputBufferWriteString(ctx->buf, ":");
872 }
873 xmlOutputBufferWriteString(ctx->buf, (const char *) cur->name);
874 xmlOutputBufferWriteString(ctx->buf, ">");
Daniel Veillard6f293b12002-03-15 09:42:33 +0000875 if (parent_is_doc) {
876 /* restore this flag from the stack for next node */
877 ctx->parent_is_doc = parent_is_doc;
878 ctx->pos = XMLC14N_AFTER_DOCUMENT_ELEMENT;
Daniel Veillard9ff88172002-03-11 09:15:32 +0000879 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000880 }
881
882 /*
883 * Restore ns_rendered stack position for exclusive
884 * processing
885 */
Daniel Veillard9ff88172002-03-11 09:15:32 +0000886 if ((ctx->exclusive) && (ctx->ns_rendered != NULL)) {
887 ctx->ns_rendered->nodeNr = ns_rendered_pos;
888 }
889 return (0);
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000890}
891
892/**
893 * xmlC14NProcessNode:
894 * @ctx: the pointer to C14N context object
895 * @cur: the node to process
896 *
897 * Processes the given node
898 *
899 * Returns non-negative value on success or negative value on fail
900 */
901static int
Daniel Veillard9ff88172002-03-11 09:15:32 +0000902xmlC14NProcessNode(xmlC14NCtxPtr ctx, xmlNodePtr cur)
903{
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000904 int ret = 0;
905 int visible;
Daniel Veillard9ff88172002-03-11 09:15:32 +0000906
907 if ((ctx == NULL) || (cur == NULL)) {
908#ifdef DEBUG_C14N
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000909 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +0000910 "xmlC14NProcessNode: Null context or node pointer.\n");
911#endif
912 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000913 }
914
915 visible = xmlC14NIsVisible(ctx, cur);
Daniel Veillard9ff88172002-03-11 09:15:32 +0000916 switch (cur->type) {
917 case XML_ELEMENT_NODE:
918 ret = xmlC14NProcessElementNode(ctx, cur, visible);
919 break;
920 case XML_CDATA_SECTION_NODE:
921 case XML_TEXT_NODE:
922 /*
923 * Text Nodes
924 * the string value, except all ampersands are replaced
925 * by &amp;, all open angle brackets (<) are replaced by &lt;, all closing
926 * angle brackets (>) are replaced by &gt;, and all #xD characters are
927 * replaced by &#xD;.
928 */
929 /* cdata sections are processed as text nodes */
930 /* todo: verify that cdata sections are included in XPath nodes set */
931 if ((visible) && (cur->content != NULL)) {
932 xmlChar *buffer;
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000933
Daniel Veillard9ff88172002-03-11 09:15:32 +0000934 buffer = xmlC11NNormalizeText(cur->content);
935 if (buffer != NULL) {
936 xmlOutputBufferWriteString(ctx->buf,
937 (const char *) buffer);
938 xmlFree(buffer);
939 } else {
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000940#ifdef DEBUG_C14N
Daniel Veillard9ff88172002-03-11 09:15:32 +0000941 xmlGenericError(xmlGenericErrorContext,
942 "xmlC14NProcessNode: xmlC11NNormalizeText() failed\n");
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000943#endif
Daniel Veillard9ff88172002-03-11 09:15:32 +0000944 return (-1);
945 }
946 }
947 break;
948 case XML_PI_NODE:
949 /*
950 * Processing Instruction (PI) Nodes-
951 * The opening PI symbol (<?), the PI target name of the node,
952 * a leading space and the string value if it is not empty, and
953 * the closing PI symbol (?>). If the string value is empty,
954 * then the leading space is not added. Also, a trailing #xA is
955 * rendered after the closing PI symbol for PI children of the
956 * root node with a lesser document order than the document
957 * element, and a leading #xA is rendered before the opening PI
958 * symbol of PI children of the root node with a greater document
959 * order than the document element.
960 */
961 if (visible) {
962 if (ctx->pos == XMLC14N_AFTER_DOCUMENT_ELEMENT) {
963 xmlOutputBufferWriteString(ctx->buf, "\x0A<?");
964 } else {
965 xmlOutputBufferWriteString(ctx->buf, "<?");
966 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000967
Daniel Veillard9ff88172002-03-11 09:15:32 +0000968 xmlOutputBufferWriteString(ctx->buf,
969 (const char *) cur->name);
970 if ((cur->content != NULL) && (*(cur->content) != '\0')) {
971 xmlChar *buffer;
972
973 xmlOutputBufferWriteString(ctx->buf, " ");
974
975 /* todo: do we need to normalize pi? */
976 buffer = xmlC11NNormalizePI(cur->content);
977 if (buffer != NULL) {
978 xmlOutputBufferWriteString(ctx->buf,
979 (const char *) buffer);
980 xmlFree(buffer);
981 } else {
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000982#ifdef DEBUG_C14N
Daniel Veillard9ff88172002-03-11 09:15:32 +0000983 xmlGenericError(xmlGenericErrorContext,
984 "xmlC14NProcessNode: xmlC11NNormalizePI() failed\n");
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000985#endif
Daniel Veillard9ff88172002-03-11 09:15:32 +0000986 return (-1);
987 }
988 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000989
Daniel Veillard9ff88172002-03-11 09:15:32 +0000990 if (ctx->pos == XMLC14N_BEFORE_DOCUMENT_ELEMENT) {
991 xmlOutputBufferWriteString(ctx->buf, "?>\x0A");
992 } else {
993 xmlOutputBufferWriteString(ctx->buf, "?>");
994 }
995 }
996 break;
997 case XML_COMMENT_NODE:
998 /*
999 * Comment Nodes
1000 * Nothing if generating canonical XML without comments. For
1001 * canonical XML with comments, generate the opening comment
1002 * symbol (<!--), the string value of the node, and the
1003 * closing comment symbol (-->). Also, a trailing #xA is rendered
1004 * after the closing comment symbol for comment children of the
1005 * root node with a lesser document order than the document
1006 * element, and a leading #xA is rendered before the opening
1007 * comment symbol of comment children of the root node with a
1008 * greater document order than the document element. (Comment
1009 * children of the root node represent comments outside of the
1010 * top-level document element and outside of the document type
1011 * declaration).
1012 */
1013 if (visible && ctx->with_comments) {
1014 if (ctx->pos == XMLC14N_AFTER_DOCUMENT_ELEMENT) {
1015 xmlOutputBufferWriteString(ctx->buf, "\x0A<!--");
1016 } else {
1017 xmlOutputBufferWriteString(ctx->buf, "<!--");
1018 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001019
Daniel Veillard9ff88172002-03-11 09:15:32 +00001020 if (cur->content != NULL) {
1021 xmlChar *buffer;
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001022
Daniel Veillard9ff88172002-03-11 09:15:32 +00001023 /* todo: do we need to normalize comment? */
1024 buffer = xmlC11NNormalizeComment(cur->content);
1025 if (buffer != NULL) {
1026 xmlOutputBufferWriteString(ctx->buf,
1027 (const char *) buffer);
1028 xmlFree(buffer);
1029 } else {
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001030#ifdef DEBUG_C14N
Daniel Veillard9ff88172002-03-11 09:15:32 +00001031 xmlGenericError(xmlGenericErrorContext,
1032 "xmlC14NProcessNode: xmlC11NNormalizeComment() failed\n");
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001033#endif
Daniel Veillard9ff88172002-03-11 09:15:32 +00001034 return (-1);
1035 }
1036 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001037
Daniel Veillard9ff88172002-03-11 09:15:32 +00001038 if (ctx->pos == XMLC14N_BEFORE_DOCUMENT_ELEMENT) {
1039 xmlOutputBufferWriteString(ctx->buf, "-->\x0A");
1040 } else {
1041 xmlOutputBufferWriteString(ctx->buf, "-->");
1042 }
1043 }
1044 break;
1045 case XML_DOCUMENT_NODE:
1046 case XML_DOCUMENT_FRAG_NODE: /* should be processed as document? */
Daniel Veillard1840ef02002-03-21 08:05:23 +00001047#ifdef LIBXML_DOCB_ENABLED
Daniel Veillard9ff88172002-03-11 09:15:32 +00001048 case XML_DOCB_DOCUMENT_NODE: /* should be processed as document? */
Daniel Veillard1840ef02002-03-21 08:05:23 +00001049#endif
1050#ifdef LIBXML_HTML_ENABLED
Daniel Veillard9ff88172002-03-11 09:15:32 +00001051 case XML_HTML_DOCUMENT_NODE: /* should be processed as document? */
Daniel Veillard1840ef02002-03-21 08:05:23 +00001052#endif
Daniel Veillard9ff88172002-03-11 09:15:32 +00001053 if (cur->children != NULL) {
1054 ctx->pos = XMLC14N_BEFORE_DOCUMENT_ELEMENT;
1055 ctx->parent_is_doc = 1;
1056 ret = xmlC14NProcessNodeList(ctx, cur->children);
1057 }
1058 break;
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001059
Daniel Veillard9ff88172002-03-11 09:15:32 +00001060 case XML_ATTRIBUTE_NODE:
1061 xmlGenericError(xmlGenericErrorContext,
1062 "xmlC14NProcessNode: XML_ATTRIBUTE_NODE is illegal here\n");
1063 return (-1);
1064 case XML_NAMESPACE_DECL:
1065 xmlGenericError(xmlGenericErrorContext,
1066 "xmlC14NProcessNode: XML_NAMESPACE_DECL is illegal here\n");
1067 return (-1);
1068 case XML_ENTITY_REF_NODE:
1069 xmlGenericError(xmlGenericErrorContext,
1070 "xmlC14NProcessNode: XML_ENTITY_REF_NODE is illegal here\n");
1071 return (-1);
1072 case XML_ENTITY_NODE:
1073 xmlGenericError(xmlGenericErrorContext,
1074 "xmlC14NProcessNode: XML_ENTITY_NODE is illegal here\n");
1075 return (-1);
1076
1077 case XML_DOCUMENT_TYPE_NODE:
1078 case XML_NOTATION_NODE:
1079 case XML_DTD_NODE:
1080 case XML_ELEMENT_DECL:
1081 case XML_ATTRIBUTE_DECL:
1082 case XML_ENTITY_DECL:
Daniel Veillard1840ef02002-03-21 08:05:23 +00001083#ifdef LIBXML_XINCLUDE_ENABLED
Daniel Veillard9ff88172002-03-11 09:15:32 +00001084 case XML_XINCLUDE_START:
1085 case XML_XINCLUDE_END:
Daniel Veillard1840ef02002-03-21 08:05:23 +00001086#endif
Daniel Veillard9ff88172002-03-11 09:15:32 +00001087 /*
1088 * should be ignored according to "W3C Canonical XML"
1089 */
1090 break;
1091 default:
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001092#ifdef DEBUG_C14N
Daniel Veillard9ff88172002-03-11 09:15:32 +00001093 xmlGenericError(xmlGenericErrorContext,
1094 "xmlC14NProcessNode: unknown node type = %d\n",
1095 cur->type);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001096#endif
Daniel Veillard9ff88172002-03-11 09:15:32 +00001097 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001098 }
1099
Daniel Veillard9ff88172002-03-11 09:15:32 +00001100 return (ret);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001101}
1102
1103/**
1104 * xmlC14NProcessNodeList:
1105 * @ctx: the pointer to C14N context object
1106 * @cur: the node to start from
1107 *
1108 * Processes all nodes in the row starting from cur.
1109 *
1110 * Returns non-negative value on success or negative value on fail
1111 */
1112static int
Daniel Veillard9ff88172002-03-11 09:15:32 +00001113xmlC14NProcessNodeList(xmlC14NCtxPtr ctx, xmlNodePtr cur)
1114{
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001115 int ret;
Daniel Veillard9ff88172002-03-11 09:15:32 +00001116
1117 if (ctx == NULL) {
1118#ifdef DEBUG_C14N
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001119 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001120 "xmlC14NProcessNodeList: Null context pointer.\n");
1121#endif
1122 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001123 }
1124
Daniel Veillard9ff88172002-03-11 09:15:32 +00001125 for (ret = 0; cur != NULL && ret >= 0; cur = cur->next) {
1126 ret = xmlC14NProcessNode(ctx, cur);
1127 }
1128 return (ret);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001129}
1130
1131
1132/**
1133 * xmlC14NFreeCtx:
1134 * @ctx: the pointer to C14N context object
1135 *
1136 * Cleanups the C14N context object.
1137 */
1138
1139static void
Daniel Veillard9ff88172002-03-11 09:15:32 +00001140xmlC14NFreeCtx(xmlC14NCtxPtr ctx)
1141{
1142 if (ctx == NULL) {
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001143#ifdef DEBUG_C14N
1144 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001145 "xmlC14NFreeCtx: ctx == NULL\n");
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001146#endif
Daniel Veillard9ff88172002-03-11 09:15:32 +00001147 return;
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001148 }
Daniel Veillard9ff88172002-03-11 09:15:32 +00001149
1150 if (ctx->ns_rendered != NULL) {
1151 xmlXPathFreeNodeSet(ctx->ns_rendered);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001152 }
1153 xmlFree(ctx);
1154}
1155
1156/**
1157 * xmlC14NNewCtx:
1158 * @doc: the XML document for canonization
1159 * @nodes: the nodes set to be included in the canonized image
1160 * or NULL if all document nodes should be included
1161 * @exclusive: the exclusive flag (0 - non-exclusive canonicalization;
1162 * otherwise - exclusive canonicalization)
1163 * @inclusive_ns_prefixe the list of inclusive namespace prefixes
1164 * ended with a NULL or NULL if there is no
1165 * inclusive namespaces (only for exclusive
1166 * canonicalization)
1167 * @with_comments: include comments in the result (!=0) or not (==0)
1168 * @buf: the output buffer to store canonical XML; this
1169 * buffer MUST have encoder==NULL because C14N requires
1170 * UTF-8 output
1171 *
1172 * Creates new C14N context object to store C14N parameters.
1173 *
1174 * Returns pointer to newly created object (success) or NULL (fail)
1175 */
1176static xmlC14NCtxPtr
Daniel Veillard9ff88172002-03-11 09:15:32 +00001177xmlC14NNewCtx(xmlDocPtr doc, xmlNodeSetPtr nodes,
1178 int exclusive, xmlChar ** inclusive_ns_prefixes,
1179 int with_comments, xmlOutputBufferPtr buf)
1180{
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001181 xmlC14NCtxPtr ctx;
1182
Daniel Veillard9ff88172002-03-11 09:15:32 +00001183 if ((doc == NULL) || (buf == NULL)) {
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001184#ifdef DEBUG_C14N
1185 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001186 "xmlC14NNewCtx: pointer to document or output buffer is NULL\n");
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001187#endif
Daniel Veillard9ff88172002-03-11 09:15:32 +00001188 return (NULL);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001189 }
1190
1191 /*
1192 * Validate the encoding output buffer encoding
1193 */
Daniel Veillard9ff88172002-03-11 09:15:32 +00001194 if (buf->encoder != NULL) {
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001195 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001196 "xmlC14NNewCtx: output buffer encoder != NULL but C14N requires UTF8 output\n");
1197 return (NULL);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001198 }
1199
1200 /*
1201 * Validate the XML document encoding value, if provided.
1202 */
Daniel Veillard9ff88172002-03-11 09:15:32 +00001203 if (doc->charset != XML_CHAR_ENCODING_UTF8) {
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001204 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001205 "xmlC14NNewCtx: source document not in UTF8\n");
1206 return (NULL);
1207 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001208
1209 /*
1210 * Allocate a new xmlC14NCtxPtr and fill the fields.
1211 */
1212 ctx = (xmlC14NCtxPtr) xmlMalloc(sizeof(xmlC14NCtx));
1213 if (ctx == NULL) {
1214 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001215 "xmlC14NNewCtx: malloc failed\n");
1216 return (NULL);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001217 }
1218 memset(ctx, 0, sizeof(xmlC14NCtx));
1219
1220 /*
1221 * initialize C14N context
Daniel Veillard9ff88172002-03-11 09:15:32 +00001222 */
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001223 ctx->doc = doc;
1224 ctx->with_comments = with_comments;
1225 ctx->visible_nodes = nodes;
1226 ctx->buf = buf;
1227 ctx->parent_is_doc = 1;
1228 ctx->pos = XMLC14N_BEFORE_DOCUMENT_ELEMENT;
1229
1230 /*
1231 * Set "exclusive" flag, create a nodes set for namespaces
1232 * stack and remember list of incluseve prefixes
1233 */
Daniel Veillard9ff88172002-03-11 09:15:32 +00001234 if (exclusive) {
1235 ctx->exclusive = 1;
1236 ctx->ns_rendered = xmlXPathNodeSetCreate(NULL);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001237 ctx->inclusive_ns_prefixes = inclusive_ns_prefixes;
1238 }
Daniel Veillard9ff88172002-03-11 09:15:32 +00001239 return (ctx);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001240}
1241
1242/**
1243 * xmlC14NDocSaveTo:
1244 * @doc: the XML document for canonization
1245 * @nodes: the nodes set to be included in the canonized image
1246 * or NULL if all document nodes should be included
1247 * @exclusive: the exclusive flag (0 - non-exclusive canonicalization;
1248 * otherwise - exclusive canonicalization)
Daniel Veillarddb1bdba2002-03-09 14:13:11 +00001249 * @inclusive_ns_prefixes: the list of inclusive namespace prefixes
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001250 * ended with a NULL or NULL if there is no
1251 * inclusive namespaces (only for exclusive
1252 * canonicalization, ignored otherwise)
1253 * @with_comments: include comments in the result (!=0) or not (==0)
1254 * @buf: the output buffer to store canonical XML; this
1255 * buffer MUST have encoder==NULL because C14N requires
1256 * UTF-8 output
1257 *
1258 * Dumps the canonized image of given XML document into the provided buffer.
1259 * For details see "Canonical XML" (http://www.w3.org/TR/xml-c14n) or
1260 * "Exclusive XML Canonicalization" (http://www.w3.org/TR/xml-exc-c14n)
1261 *
1262 * Returns non-negative value on success or a negative value on fail
1263 */
1264int
Daniel Veillard9ff88172002-03-11 09:15:32 +00001265xmlC14NDocSaveTo(xmlDocPtr doc, xmlNodeSetPtr nodes,
1266 int exclusive, xmlChar ** inclusive_ns_prefixes,
1267 int with_comments, xmlOutputBufferPtr buf)
1268{
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001269 xmlC14NCtxPtr ctx;
1270 int ret;
Daniel Veillard9ff88172002-03-11 09:15:32 +00001271
1272 if ((buf == NULL) || (doc == NULL)) {
1273#ifdef DEBUG_C14N
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001274 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001275 "xmlC14NDocSaveTo: null return buffer or doc pointer\n");
1276#endif
1277 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001278 }
1279
1280 /*
1281 * Validate the encoding output buffer encoding
1282 */
Daniel Veillard9ff88172002-03-11 09:15:32 +00001283 if (buf->encoder != NULL) {
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001284 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001285 "xmlC14NDocSaveTo: output buffer encoder != NULL but C14N requires UTF8 output\n");
1286 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001287 }
1288
1289 ctx = xmlC14NNewCtx(doc, nodes, exclusive, inclusive_ns_prefixes,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001290 with_comments, buf);
1291 if (ctx == NULL) {
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001292 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001293 "xmlC14NDocSaveTo: unable to create C14N context\n");
1294 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001295 }
1296
1297
1298
1299 /*
1300 * Root Node
1301 * The root node is the parent of the top-level document element. The
1302 * result of processing each of its child nodes that is in the node-set
1303 * in document order. The root node does not generate a byte order mark,
1304 * XML declaration, nor anything from within the document type
1305 * declaration.
1306 */
Daniel Veillard9ff88172002-03-11 09:15:32 +00001307 if (doc->children != NULL) {
1308 ret = xmlC14NProcessNodeList(ctx, doc->children);
1309 if (ret < 0) {
1310#ifdef DEBUG_C14N
1311 xmlGenericError(xmlGenericErrorContext,
1312 "xmlC14NDocSaveTo: process childrens' list failed.\n");
1313#endif
1314 xmlC14NFreeCtx(ctx);
1315 return (-1);
1316 }
1317 }
1318
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001319 /*
1320 * Flush buffer to get number of bytes written
1321 */
1322 ret = xmlOutputBufferFlush(buf);
Daniel Veillard9ff88172002-03-11 09:15:32 +00001323 if (ret < 0) {
1324#ifdef DEBUG_C14N
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001325 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001326 "xmlC14NDocSaveTo: buffer flush failed.\n");
1327#endif
1328 xmlC14NFreeCtx(ctx);
1329 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001330 }
1331
1332 /*
1333 * Cleanup
1334 */
Daniel Veillard9ff88172002-03-11 09:15:32 +00001335 xmlC14NFreeCtx(ctx);
1336 return (ret);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001337}
1338
1339/**
1340 * xmlC14NDocDumpMemory:
1341 * @doc: the XML document for canonization
1342 * @nodes: the nodes set to be included in the canonized image
1343 * or NULL if all document nodes should be included
1344 * @exclusive: the exclusive flag (0 - non-exclusive canonicalization;
1345 * otherwise - exclusive canonicalization)
Daniel Veillarddb1bdba2002-03-09 14:13:11 +00001346 * @inclusive_ns_prefixes: the list of inclusive namespace prefixes
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001347 * ended with a NULL or NULL if there is no
1348 * inclusive namespaces (only for exclusive
1349 * canonicalization, ignored otherwise)
1350 * @with_comments: include comments in the result (!=0) or not (==0)
1351 * @doc_txt_ptr: the memory pointer for allocated canonical XML text;
1352 * the caller of this functions is responsible for calling
1353 * xmlFree() to free allocated memory
1354 *
1355 * Dumps the canonized image of given XML document into memory.
1356 * For details see "Canonical XML" (http://www.w3.org/TR/xml-c14n) or
1357 * "Exclusive XML Canonicalization" (http://www.w3.org/TR/xml-exc-c14n)
1358 *
1359 * Returns the number of bytes written on success or a negative value on fail
1360 */
1361int
Daniel Veillard9ff88172002-03-11 09:15:32 +00001362xmlC14NDocDumpMemory(xmlDocPtr doc, xmlNodeSetPtr nodes,
1363 int exclusive, xmlChar ** inclusive_ns_prefixes,
1364 int with_comments, xmlChar ** doc_txt_ptr)
1365{
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001366 int ret;
1367 xmlOutputBufferPtr buf;
1368
1369 if (doc_txt_ptr == NULL) {
Daniel Veillard9ff88172002-03-11 09:15:32 +00001370#ifdef DEBUG_C14N
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001371 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001372 "xmlC14NDocDumpMemory: null return buffer pointer\n");
1373#endif
1374 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001375 }
1376
1377 *doc_txt_ptr = NULL;
Daniel Veillard9ff88172002-03-11 09:15:32 +00001378
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001379 /*
1380 * create memory buffer with UTF8 (default) encoding
1381 */
1382 buf = xmlAllocOutputBuffer(NULL);
Daniel Veillard9ff88172002-03-11 09:15:32 +00001383 if (buf == NULL) {
1384#ifdef DEBUG_C14N
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001385 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001386 "xmlC14NDocDumpMemory: failed to allocate output buffer.\n");
1387#endif
1388 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001389 }
1390
1391 /*
1392 * canonize document and write to buffer
1393 */
Daniel Veillard9ff88172002-03-11 09:15:32 +00001394 ret = xmlC14NDocSaveTo(doc, nodes, exclusive, inclusive_ns_prefixes,
1395 with_comments, buf);
1396 if (ret < 0) {
1397#ifdef DEBUG_C14N
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001398 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001399 "xmlC14NDocDumpMemory: xmlC14NDocSaveTo failed.\n");
1400#endif
1401 (void) xmlOutputBufferClose(buf);
1402 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001403 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001404
Daniel Veillard9ff88172002-03-11 09:15:32 +00001405 ret = buf->buffer->use;
1406 if (ret > 0) {
1407 *doc_txt_ptr = xmlStrndup(buf->buffer->content, ret);
1408 }
1409 (void) xmlOutputBufferClose(buf);
1410
1411 if ((*doc_txt_ptr == NULL) && (ret > 0)) {
1412#ifdef DEBUG_C14N
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001413 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001414 "xmlC14NDocDumpMemory: failed to allocate memory for document text representation\n");
1415#endif
1416 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001417 }
Daniel Veillard9ff88172002-03-11 09:15:32 +00001418 return (ret);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001419}
1420
1421/**
1422 * xmlC14NDocSave:
1423 * @doc: the XML document for canonization
1424 * @nodes: the nodes set to be included in the canonized image
1425 * or NULL if all document nodes should be included
1426 * @exclusive: the exclusive flag (0 - non-exclusive canonicalization;
1427 * otherwise - exclusive canonicalization)
Daniel Veillarddb1bdba2002-03-09 14:13:11 +00001428 * @inclusive_ns_prefixes: the list of inclusive namespace prefixes
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001429 * ended with a NULL or NULL if there is no
1430 * inclusive namespaces (only for exclusive
1431 * canonicalization, ignored otherwise)
1432 * @with_comments: include comments in the result (!=0) or not (==0)
1433 * @filename: the filename to store canonical XML image
1434 * @compression: the compression level (zlib requred):
1435 * -1 - libxml default,
1436 * 0 - uncompressed,
1437 * >0 - compression level
1438 *
1439 * Dumps the canonized image of given XML document into the file.
1440 * For details see "Canonical XML" (http://www.w3.org/TR/xml-c14n) or
1441 * "Exclusive XML Canonicalization" (http://www.w3.org/TR/xml-exc-c14n)
1442 *
1443 * Returns the number of bytes written success or a negative value on fail
1444 */
1445int
Daniel Veillard9ff88172002-03-11 09:15:32 +00001446xmlC14NDocSave(xmlDocPtr doc, xmlNodeSetPtr nodes,
1447 int exclusive, xmlChar ** inclusive_ns_prefixes,
1448 int with_comments, const char *filename, int compression)
1449{
1450 xmlOutputBufferPtr buf;
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001451 int ret;
1452
Daniel Veillard9ff88172002-03-11 09:15:32 +00001453 if (filename == NULL) {
1454#ifdef DEBUG_C14N
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001455 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001456 "xmlC14NDocSave: filename is NULL\n");
1457#endif
1458 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001459 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001460#ifdef HAVE_ZLIB_H
Daniel Veillard9ff88172002-03-11 09:15:32 +00001461 if (compression < 0)
1462 compression = xmlGetCompressMode();
1463#endif
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001464
1465 /*
1466 * save the content to a temp buffer, use default UTF8 encoding.
1467 */
1468 buf = xmlOutputBufferCreateFilename(filename, NULL, compression);
1469 if (buf == NULL) {
Daniel Veillard9ff88172002-03-11 09:15:32 +00001470#ifdef DEBUG_C14N
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001471 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001472 "xmlC14NDocSave: unable to create buffer for file=\"%s\" with compressin=%d\n",
1473 filename, compression);
1474#endif
1475 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001476 }
Daniel Veillard9ff88172002-03-11 09:15:32 +00001477
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001478 /*
1479 * canonize document and write to buffer
1480 */
Daniel Veillard9ff88172002-03-11 09:15:32 +00001481 ret = xmlC14NDocSaveTo(doc, nodes, exclusive, inclusive_ns_prefixes,
1482 with_comments, buf);
1483 if (ret < 0) {
1484#ifdef DEBUG_C14N
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001485 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001486 "xmlC14NDocSave: xmlC14NDocSaveTo failed.\n");
1487#endif
1488 (void) xmlOutputBufferClose(buf);
1489 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001490 }
Daniel Veillard9ff88172002-03-11 09:15:32 +00001491
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001492 /*
1493 * get the numbers of bytes written
1494 */
1495 ret = xmlOutputBufferClose(buf);
Daniel Veillard9ff88172002-03-11 09:15:32 +00001496 return (ret);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001497}
1498
1499
1500
1501/*
1502 * Macro used to grow the current buffer.
1503 */
1504#define growBufferReentrant() { \
1505 buffer_size *= 2; \
1506 buffer = (xmlChar *) \
1507 xmlRealloc(buffer, buffer_size * sizeof(xmlChar)); \
1508 if (buffer == NULL) { \
1509 perror("realloc failed"); \
1510 return(NULL); \
1511 } \
1512}
1513
1514/**
1515 * xmlC11NNormalizeString:
1516 * @input: the input string
1517 * @mode: the normalization mode (attribute, comment, PI or text)
1518 *
1519 * Converts a string to a canonical (normalized) format. The code is stolen
1520 * from xmlEncodeEntitiesReentrant(). Added normalization of \x09, \x0a, \x0A
1521 * and the @mode parameter
1522 *
1523 * Returns a normalized string (caller is responsible for calling xmlFree())
1524 * or NULL if an error occurs
1525 */
1526static xmlChar *
Daniel Veillard9ff88172002-03-11 09:15:32 +00001527xmlC11NNormalizeString(const xmlChar * input,
1528 xmlC14NNormalizationMode mode)
1529{
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001530 const xmlChar *cur = input;
1531 xmlChar *buffer = NULL;
1532 xmlChar *out = NULL;
1533 int buffer_size = 0;
1534
Daniel Veillard9ff88172002-03-11 09:15:32 +00001535 if (input == NULL)
1536 return (NULL);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001537
1538 /*
1539 * allocate an translation buffer.
1540 */
1541 buffer_size = 1000;
1542 buffer = (xmlChar *) xmlMalloc(buffer_size * sizeof(xmlChar));
1543 if (buffer == NULL) {
Daniel Veillard9ff88172002-03-11 09:15:32 +00001544 perror("malloc failed");
1545 return (NULL);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001546 }
1547 out = buffer;
1548
1549 while (*cur != '\0') {
Daniel Veillard9ff88172002-03-11 09:15:32 +00001550 if ((out - buffer) > (buffer_size - 10)) {
1551 int indx = out - buffer;
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001552
Daniel Veillard9ff88172002-03-11 09:15:32 +00001553 growBufferReentrant();
1554 out = &buffer[indx];
1555 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001556
Daniel Veillard9ff88172002-03-11 09:15:32 +00001557 if ((*cur == '<') && ((mode == XMLC14N_NORMALIZE_ATTR) ||
1558 (mode == XMLC14N_NORMALIZE_TEXT))) {
1559 *out++ = '&';
1560 *out++ = 'l';
1561 *out++ = 't';
1562 *out++ = ';';
1563 } else if ((*cur == '>') && (mode == XMLC14N_NORMALIZE_TEXT)) {
1564 *out++ = '&';
1565 *out++ = 'g';
1566 *out++ = 't';
1567 *out++ = ';';
1568 } else if ((*cur == '&') && ((mode == XMLC14N_NORMALIZE_ATTR) ||
1569 (mode == XMLC14N_NORMALIZE_TEXT))) {
1570 *out++ = '&';
1571 *out++ = 'a';
1572 *out++ = 'm';
1573 *out++ = 'p';
1574 *out++ = ';';
1575 } else if ((*cur == '"') && (mode == XMLC14N_NORMALIZE_ATTR)) {
1576 *out++ = '&';
1577 *out++ = 'q';
1578 *out++ = 'u';
1579 *out++ = 'o';
1580 *out++ = 't';
1581 *out++ = ';';
1582 } else if ((*cur == '\x09') && (mode == XMLC14N_NORMALIZE_ATTR)) {
1583 *out++ = '&';
1584 *out++ = '#';
1585 *out++ = 'x';
1586 *out++ = '9';
1587 *out++ = ';';
1588 } else if ((*cur == '\x0A') && (mode == XMLC14N_NORMALIZE_ATTR)) {
1589 *out++ = '&';
1590 *out++ = '#';
1591 *out++ = 'x';
1592 *out++ = 'A';
1593 *out++ = ';';
1594 } else if ((*cur == '\x0D') && ((mode == XMLC14N_NORMALIZE_ATTR) ||
1595 (mode == XMLC14N_NORMALIZE_TEXT) ||
1596 (mode == XMLC14N_NORMALIZE_COMMENT) ||
1597 (mode == XMLC14N_NORMALIZE_PI))) {
1598 *out++ = '&';
1599 *out++ = '#';
1600 *out++ = 'x';
1601 *out++ = 'D';
1602 *out++ = ';';
1603 } else {
1604 /*
1605 * Works because on UTF-8, all extended sequences cannot
1606 * result in bytes in the ASCII range.
1607 */
1608 *out++ = *cur;
1609 }
1610 cur++;
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001611 }
1612 *out++ = 0;
Daniel Veillard9ff88172002-03-11 09:15:32 +00001613 return (buffer);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001614}
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001615#endif /* LIBXML_C14N_ENABLED */