blob: 89589ca64d3538fa086d64d17f43e50d49637866 [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
Aleksey Saninf8cb6dd2002-06-04 04:27:06 +000041typedef struct _xmlC14NVisibleNsStack {
Aleksey Sanin2c135a12002-08-01 06:31:50 +000042 int nsCurEnd; /* number of nodes in the set */
43 int nsPrevStart; /* the begginning of the stack for previous visible node */
44 int nsPrevEnd; /* the end of the stack for previous visible node */
45 int nsMax; /* size of the array as allocated */
46 xmlNsPtr *nsTab; /* array of ns in no particular order */
47 xmlNodePtr *nodeTab;/* array of nodes in no particular order */
Aleksey Saninf8cb6dd2002-06-04 04:27:06 +000048} xmlC14NVisibleNsStack, *xmlC14NVisibleNsStackPtr;
Aleksey Sanindffd5c82002-05-31 04:24:13 +000049
Daniel Veillard044fc6b2002-03-04 17:09:44 +000050typedef struct _xmlC14NCtx {
51 /* input parameters */
Daniel Veillard9ff88172002-03-11 09:15:32 +000052 xmlDocPtr doc;
Aleksey Sanin2c135a12002-08-01 06:31:50 +000053 xmlC14NIsVisibleCallback is_visible_callback;
54 void* user_data;
Daniel Veillard9ff88172002-03-11 09:15:32 +000055 int with_comments;
56 xmlOutputBufferPtr buf;
Daniel Veillard044fc6b2002-03-04 17:09:44 +000057
58 /* position in the XML document */
Daniel Veillard9ff88172002-03-11 09:15:32 +000059 xmlC14NPosition pos;
60 int parent_is_doc;
Aleksey Saninf8cb6dd2002-06-04 04:27:06 +000061 xmlC14NVisibleNsStackPtr ns_rendered;
Daniel Veillard044fc6b2002-03-04 17:09:44 +000062
63 /* exclusive canonicalization */
Daniel Veillard9ff88172002-03-11 09:15:32 +000064 int exclusive;
Daniel Veillard9ff88172002-03-11 09:15:32 +000065 xmlChar **inclusive_ns_prefixes;
Daniel Veillard044fc6b2002-03-04 17:09:44 +000066} xmlC14NCtx, *xmlC14NCtxPtr;
67
Aleksey Sanin2c135a12002-08-01 06:31:50 +000068static xmlC14NVisibleNsStackPtr xmlC14NVisibleNsStackCreate (void);
69static void xmlC14NVisibleNsStackDestroy (xmlC14NVisibleNsStackPtr cur);
70static void xmlC14NVisibleNsStackAdd (xmlC14NVisibleNsStackPtr cur,
71 xmlNsPtr ns,
72 xmlNodePtr node);
73static void xmlC14NVisibleNsStackSave (xmlC14NVisibleNsStackPtr cur,
74 xmlC14NVisibleNsStackPtr state);
75static void xmlC14NVisibleNsStackRestore (xmlC14NVisibleNsStackPtr cur,
76 xmlC14NVisibleNsStackPtr state);
77static void xmlC14NVisibleNsStackShift (xmlC14NVisibleNsStackPtr cur);
78static int xmlC14NVisibleNsStackFind (xmlC14NVisibleNsStackPtr cur,
79 xmlNsPtr ns);
80static int xmlExcC14NVisibleNsStackFind (xmlC14NVisibleNsStackPtr cur,
81 xmlNsPtr ns,
82 xmlC14NCtxPtr ctx);
83
84static int xmlC14NIsNodeInNodeset (xmlNodeSetPtr nodes,
85 xmlNodePtr node,
86 xmlNodePtr parent);
87
88
Daniel Veillard044fc6b2002-03-04 17:09:44 +000089
Daniel Veillard9ff88172002-03-11 09:15:32 +000090static int xmlC14NProcessNode(xmlC14NCtxPtr ctx, xmlNodePtr cur);
91static int xmlC14NProcessNodeList(xmlC14NCtxPtr ctx, xmlNodePtr cur);
Daniel Veillard044fc6b2002-03-04 17:09:44 +000092typedef enum {
Daniel Veillard9ff88172002-03-11 09:15:32 +000093 XMLC14N_NORMALIZE_ATTR = 0,
94 XMLC14N_NORMALIZE_COMMENT = 1,
95 XMLC14N_NORMALIZE_PI = 2,
96 XMLC14N_NORMALIZE_TEXT = 3
97} xmlC14NNormalizationMode;
Daniel Veillard044fc6b2002-03-04 17:09:44 +000098
Daniel Veillard9ff88172002-03-11 09:15:32 +000099static xmlChar *xmlC11NNormalizeString(const xmlChar * input,
100 xmlC14NNormalizationMode mode);
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000101
102#define xmlC11NNormalizeAttr( a ) \
Daniel Veillard9ff88172002-03-11 09:15:32 +0000103 xmlC11NNormalizeString((a), XMLC14N_NORMALIZE_ATTR)
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000104#define xmlC11NNormalizeComment( a ) \
Daniel Veillard9ff88172002-03-11 09:15:32 +0000105 xmlC11NNormalizeString((a), XMLC14N_NORMALIZE_COMMENT)
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000106#define xmlC11NNormalizePI( a ) \
Daniel Veillard9ff88172002-03-11 09:15:32 +0000107 xmlC11NNormalizeString((a), XMLC14N_NORMALIZE_PI)
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000108#define xmlC11NNormalizeText( a ) \
Daniel Veillard9ff88172002-03-11 09:15:32 +0000109 xmlC11NNormalizeString((a), XMLC14N_NORMALIZE_TEXT)
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000110
Aleksey Sanin2c135a12002-08-01 06:31:50 +0000111#define xmlC14NIsVisible( ctx, node, parent ) \
112 (((ctx)->is_visible_callback != NULL) ? \
113 (ctx)->is_visible_callback((ctx)->user_data, \
114 (xmlNodePtr)(node), (xmlNodePtr)(parent)) : 1)
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000115/************************************************************************
116 * *
117 * The implementation internals *
118 * *
119 ************************************************************************/
Aleksey Sanindffd5c82002-05-31 04:24:13 +0000120#define XML_NAMESPACES_DEFAULT 16
121
Aleksey Sanin2c135a12002-08-01 06:31:50 +0000122static int
123xmlC14NIsNodeInNodeset(xmlNodeSetPtr nodes, xmlNodePtr node, xmlNodePtr parent) {
124 if((nodes != NULL) && (node != NULL)) {
125 if(node->type != XML_NAMESPACE_DECL) {
126 return(xmlXPathNodeSetContains(nodes, node));
127 } else {
128 xmlNs ns;
129
130 memcpy(&ns, node, sizeof(ns));
131 ns.next = (xmlNsPtr)parent; /* this is a libxml hack! check xpath.c for details */
132
133 /*
134 * If the input is an XPath node-set, then the node-set must explicitly
135 * contain every node to be rendered to the canonical form.
136 */
137 return(xmlXPathNodeSetContains(nodes, (xmlNodePtr)&ns));
138 }
139 }
140 return(1);
141}
142
Aleksey Saninf8cb6dd2002-06-04 04:27:06 +0000143static xmlC14NVisibleNsStackPtr
144xmlC14NVisibleNsStackCreate(void) {
145 xmlC14NVisibleNsStackPtr ret;
Aleksey Sanindffd5c82002-05-31 04:24:13 +0000146
Aleksey Saninf8cb6dd2002-06-04 04:27:06 +0000147 ret = (xmlC14NVisibleNsStackPtr) xmlMalloc(sizeof(xmlC14NVisibleNsStack));
Aleksey Sanindffd5c82002-05-31 04:24:13 +0000148 if (ret == NULL) {
149 xmlGenericError(xmlGenericErrorContext,
Aleksey Saninf8cb6dd2002-06-04 04:27:06 +0000150 "xmlC14NVisibleNsStackCreate: out of memory\n");
Aleksey Sanindffd5c82002-05-31 04:24:13 +0000151 return(NULL);
152 }
Aleksey Saninf8cb6dd2002-06-04 04:27:06 +0000153 memset(ret, 0 , (size_t) sizeof(xmlC14NVisibleNsStack));
Aleksey Sanindffd5c82002-05-31 04:24:13 +0000154 return(ret);
155}
156
157static void
Aleksey Saninf8cb6dd2002-06-04 04:27:06 +0000158xmlC14NVisibleNsStackDestroy(xmlC14NVisibleNsStackPtr cur) {
Aleksey Sanindffd5c82002-05-31 04:24:13 +0000159 if(cur == NULL) {
Aleksey Saninf8cb6dd2002-06-04 04:27:06 +0000160#ifdef DEBUG_C14N
161 xmlGenericError(xmlGenericErrorContext,
Aleksey Sanin2c135a12002-08-01 06:31:50 +0000162 "xmlC14NVisibleNsStackDestroy: cur is null.\n");
Aleksey Saninf8cb6dd2002-06-04 04:27:06 +0000163#endif
Aleksey Sanindffd5c82002-05-31 04:24:13 +0000164 return;
165 }
166 if(cur->nsTab != NULL) {
167 memset(cur->nsTab, 0, cur->nsMax * sizeof(xmlNsPtr));
168 xmlFree(cur->nsTab);
169 }
Aleksey Saninea4272a2002-08-02 23:50:03 +0000170 if(cur->nodeTab != NULL) {
171 memset(cur->nodeTab, 0, cur->nsMax * sizeof(xmlNodePtr));
172 xmlFree(cur->nodeTab);
173 }
Aleksey Saninf8cb6dd2002-06-04 04:27:06 +0000174 memset(cur, 0, sizeof(xmlC14NVisibleNsStack));
Aleksey Sanindffd5c82002-05-31 04:24:13 +0000175 xmlFree(cur);
176
177}
178
Aleksey Saninf8cb6dd2002-06-04 04:27:06 +0000179static void
Aleksey Sanin2c135a12002-08-01 06:31:50 +0000180xmlC14NVisibleNsStackAdd(xmlC14NVisibleNsStackPtr cur, xmlNsPtr ns, xmlNodePtr node) {
181 if((cur == NULL) ||
182 ((cur->nsTab == NULL) && (cur->nodeTab != NULL)) ||
183 ((cur->nsTab != NULL) && (cur->nodeTab == NULL))) {
Aleksey Saninf8cb6dd2002-06-04 04:27:06 +0000184#ifdef DEBUG_C14N
185 xmlGenericError(xmlGenericErrorContext,
186 "xmlC14NVisibleNsStackAdd: cur is null.\n");
187#endif
Aleksey Sanindffd5c82002-05-31 04:24:13 +0000188 return;
189 }
190
Aleksey Sanin2c135a12002-08-01 06:31:50 +0000191 if ((cur->nsTab == NULL) && (cur->nodeTab == NULL)) {
Aleksey Sanindffd5c82002-05-31 04:24:13 +0000192 cur->nsTab = (xmlNsPtr*) xmlMalloc(XML_NAMESPACES_DEFAULT * sizeof(xmlNsPtr));
Aleksey Sanin2c135a12002-08-01 06:31:50 +0000193 cur->nodeTab = (xmlNodePtr*) xmlMalloc(XML_NAMESPACES_DEFAULT * sizeof(xmlNodePtr));
194 if ((cur->nsTab == NULL) || (cur->nodeTab == NULL)) {
Aleksey Sanindffd5c82002-05-31 04:24:13 +0000195 xmlGenericError(xmlGenericErrorContext,
Aleksey Saninf8cb6dd2002-06-04 04:27:06 +0000196 "xmlC14NVisibleNsStackAdd: out of memory\n");
Aleksey Sanindffd5c82002-05-31 04:24:13 +0000197 return;
198 }
199 memset(cur->nsTab, 0 , XML_NAMESPACES_DEFAULT * sizeof(xmlNsPtr));
Aleksey Sanin2c135a12002-08-01 06:31:50 +0000200 memset(cur->nodeTab, 0 , XML_NAMESPACES_DEFAULT * sizeof(xmlNodePtr));
Aleksey Sanindffd5c82002-05-31 04:24:13 +0000201 cur->nsMax = XML_NAMESPACES_DEFAULT;
Aleksey Saninf8cb6dd2002-06-04 04:27:06 +0000202 } else if(cur->nsMax == cur->nsCurEnd) {
Aleksey Sanin2c135a12002-08-01 06:31:50 +0000203 void *tmp;
Aleksey Sanindffd5c82002-05-31 04:24:13 +0000204 int tmpSize;
205
206 tmpSize = 2 * cur->nsMax;
Aleksey Sanin2c135a12002-08-01 06:31:50 +0000207 tmp = xmlRealloc(cur->nsTab, tmpSize * sizeof(xmlNsPtr));
Aleksey Sanindffd5c82002-05-31 04:24:13 +0000208 if (tmp == NULL) {
209 xmlGenericError(xmlGenericErrorContext,
Aleksey Saninf8cb6dd2002-06-04 04:27:06 +0000210 "xmlC14NVisibleNsStackAdd: out of memory\n");
Aleksey Sanindffd5c82002-05-31 04:24:13 +0000211 return;
212 }
Aleksey Sanin2c135a12002-08-01 06:31:50 +0000213 cur->nsTab = (xmlNsPtr*)tmp;
214
215 tmp = xmlRealloc(cur->nodeTab, tmpSize * sizeof(xmlNodePtr));
216 if (tmp == NULL) {
217 xmlGenericError(xmlGenericErrorContext,
218 "xmlC14NVisibleNsStackAdd: out of memory\n");
219 return;
220 }
221 cur->nodeTab = (xmlNodePtr*)tmp;
222
Aleksey Sanindffd5c82002-05-31 04:24:13 +0000223 cur->nsMax = tmpSize;
224 }
Aleksey Sanin2c135a12002-08-01 06:31:50 +0000225 cur->nsTab[cur->nsCurEnd] = ns;
226 cur->nodeTab[cur->nsCurEnd] = node;
227
228 ++cur->nsCurEnd;
Aleksey Saninf8cb6dd2002-06-04 04:27:06 +0000229}
230
231static void
232xmlC14NVisibleNsStackSave(xmlC14NVisibleNsStackPtr cur, xmlC14NVisibleNsStackPtr state) {
233 if((cur == NULL) || (state == NULL)) {
234#ifdef DEBUG_C14N
235 xmlGenericError(xmlGenericErrorContext,
236 "xmlC14NVisibleNsStackSave: cur or state is null.\n");
237#endif
238 return;
239 }
240
241 state->nsCurEnd = cur->nsCurEnd;
242 state->nsPrevStart = cur->nsPrevStart;
243 state->nsPrevEnd = cur->nsPrevEnd;
244}
245
246static void
247xmlC14NVisibleNsStackRestore(xmlC14NVisibleNsStackPtr cur, xmlC14NVisibleNsStackPtr state) {
248 if((cur == NULL) || (state == NULL)) {
249#ifdef DEBUG_C14N
250 xmlGenericError(xmlGenericErrorContext,
251 "xmlC14NVisibleNsStackRestore: cur or state is null.\n");
252#endif
253 return;
254 }
255 cur->nsCurEnd = state->nsCurEnd;
256 cur->nsPrevStart = state->nsPrevStart;
257 cur->nsPrevEnd = state->nsPrevEnd;
258}
259
260static void
261xmlC14NVisibleNsStackShift(xmlC14NVisibleNsStackPtr cur) {
262 if(cur == NULL) {
263#ifdef DEBUG_C14N
264 xmlGenericError(xmlGenericErrorContext,
265 "xmlC14NVisibleNsStackRestore: cur is null.\n");
266#endif
267 return;
268 }
269 cur->nsPrevStart = cur->nsPrevEnd;
270 cur->nsPrevEnd = cur->nsCurEnd;
271}
272
273static int
274xmlC14NStrEqual(const xmlChar *str1, const xmlChar *str2) {
275 if (str1 == str2) return(1);
276 if (str1 == NULL) return((*str2) == '\0');
277 if (str2 == NULL) return((*str1) == '\0');
278 do {
279 if (*str1++ != *str2) return(0);
280 } while (*str2++);
281 return(1);
282}
283
284/**
285 * xmlC14NVisibleNsStackFind:
Daniel Veillard01c13b52002-12-10 15:19:08 +0000286 * @ctx: the C14N context
287 * @ns: the namespace to check
Aleksey Saninf8cb6dd2002-06-04 04:27:06 +0000288 *
289 * Checks whether the given namespace was already rendered or not
290 *
291 * Returns 1 if we already wrote this namespace or 0 otherwise
292 */
293static int
Aleksey Sanin2c135a12002-08-01 06:31:50 +0000294xmlC14NVisibleNsStackFind(xmlC14NVisibleNsStackPtr cur, xmlNsPtr ns)
Aleksey Saninf8cb6dd2002-06-04 04:27:06 +0000295{
296 int i;
297 const xmlChar *prefix;
298 const xmlChar *href;
Aleksey Sanin2c135a12002-08-01 06:31:50 +0000299 int has_empty_ns;
Aleksey Saninf8cb6dd2002-06-04 04:27:06 +0000300
301 if(cur == NULL) {
302#ifdef DEBUG_C14N
303 xmlGenericError(xmlGenericErrorContext,
304 "xmlC14NVisibleNsStackFind: cur is null.\n");
305#endif
306 return (0);
307 }
308
309 /*
310 * if the default namespace xmlns="" is not defined yet then
311 * we do not want to print it out
312 */
313 prefix = ((ns == NULL) || (ns->prefix == NULL)) ? BAD_CAST "" : ns->prefix;
314 href = ((ns == NULL) || (ns->href == NULL)) ? BAD_CAST "" : ns->href;
Aleksey Sanin2c135a12002-08-01 06:31:50 +0000315 has_empty_ns = (xmlC14NStrEqual(prefix, NULL) && xmlC14NStrEqual(href, NULL));
316
Aleksey Saninf8cb6dd2002-06-04 04:27:06 +0000317 if (cur->nsTab != NULL) {
Aleksey Sanin2c135a12002-08-01 06:31:50 +0000318 int start = (has_empty_ns) ? 0 : cur->nsPrevStart;
Aleksey Saninf8cb6dd2002-06-04 04:27:06 +0000319 for (i = cur->nsCurEnd - 1; i >= start; --i) {
320 xmlNsPtr ns1 = cur->nsTab[i];
321
322 if(xmlC14NStrEqual(prefix, (ns1 != NULL) ? ns1->prefix : NULL)) {
323 return(xmlC14NStrEqual(href, (ns1 != NULL) ? ns1->href : NULL));
324 }
325 }
326 }
Aleksey Sanin2c135a12002-08-01 06:31:50 +0000327 return(has_empty_ns);
Aleksey Sanindffd5c82002-05-31 04:24:13 +0000328}
329
Aleksey Sanin2c135a12002-08-01 06:31:50 +0000330static int
331xmlExcC14NVisibleNsStackFind(xmlC14NVisibleNsStackPtr cur, xmlNsPtr ns, xmlC14NCtxPtr ctx) {
332 int i;
333 const xmlChar *prefix;
334 const xmlChar *href;
335 int has_empty_ns;
336
337 if(cur == NULL) {
338#ifdef DEBUG_C14N
339 xmlGenericError(xmlGenericErrorContext,
340 "xmlExcC14NVisibleNsStackFind: cur is null.\n");
341#endif
Daniel Veillard9ff88172002-03-11 09:15:32 +0000342 return (0);
343 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000344
Aleksey Sanin2c135a12002-08-01 06:31:50 +0000345 /*
346 * if the default namespace xmlns="" is not defined yet then
347 * we do not want to print it out
Aleksey Saninf8cb6dd2002-06-04 04:27:06 +0000348 */
Aleksey Sanin2c135a12002-08-01 06:31:50 +0000349 prefix = ((ns == NULL) || (ns->prefix == NULL)) ? BAD_CAST "" : ns->prefix;
350 href = ((ns == NULL) || (ns->href == NULL)) ? BAD_CAST "" : ns->href;
351 has_empty_ns = (xmlC14NStrEqual(prefix, NULL) && xmlC14NStrEqual(href, NULL));
Aleksey Saninf8cb6dd2002-06-04 04:27:06 +0000352
Aleksey Sanin2c135a12002-08-01 06:31:50 +0000353 if (cur->nsTab != NULL) {
354 int start = 0;
355 for (i = cur->nsCurEnd - 1; i >= start; --i) {
356 xmlNsPtr ns1 = cur->nsTab[i];
357
358 if(xmlC14NStrEqual(prefix, (ns1 != NULL) ? ns1->prefix : NULL)) {
359 if(xmlC14NStrEqual(href, (ns1 != NULL) ? ns1->href : NULL)) {
360 return(xmlC14NIsVisible(ctx, ns1, cur->nodeTab[i]));
361 } else {
362 return(0);
363 }
364 }
365 }
366 }
367 return(has_empty_ns);
Aleksey Saninf8cb6dd2002-06-04 04:27:06 +0000368}
369
Aleksey Sanin2c135a12002-08-01 06:31:50 +0000370
371
372
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000373/**
374 * xmlC14NIsXmlNs:
375 * @ns: the namespace to check
376 *
377 * Checks whether the given namespace is a default "xml:" namespace
378 * with href="http://www.w3.org/XML/1998/namespace"
379 *
380 * Returns 1 if the node is default or 0 otherwise
381 */
Daniel Veillard9ff88172002-03-11 09:15:32 +0000382
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000383/* todo: make it a define? */
384static int
Daniel Veillard9ff88172002-03-11 09:15:32 +0000385xmlC14NIsXmlNs(xmlNsPtr ns)
386{
387 return ((ns != NULL) &&
388 (xmlStrEqual(ns->prefix, BAD_CAST "xml")) &&
389 (xmlStrEqual(ns->href,
390 BAD_CAST
391 "http://www.w3.org/XML/1998/namespace")));
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000392}
393
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000394
395/**
Aleksey Saninf8cb6dd2002-06-04 04:27:06 +0000396 * xmlC14NNsCompare:
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000397 * @ns1: the pointer to first namespace
398 * @ns2: the pointer to second namespace
399 *
400 * Compares the namespaces by names (prefixes).
401 *
402 * Returns -1 if ns1 < ns2, 0 if ns1 == ns2 or 1 if ns1 > ns2.
403 */
Daniel Veillard9ff88172002-03-11 09:15:32 +0000404static int
Aleksey Saninf8cb6dd2002-06-04 04:27:06 +0000405xmlC14NNsCompare(xmlNsPtr ns1, xmlNsPtr ns2)
Daniel Veillard9ff88172002-03-11 09:15:32 +0000406{
407 if (ns1 == ns2)
408 return (0);
409 if (ns1 == NULL)
410 return (-1);
411 if (ns2 == NULL)
412 return (1);
413
414 return (xmlStrcmp(ns1->prefix, ns2->prefix));
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000415}
416
417
418/**
419 * xmlC14NPrintNamespaces:
420 * @ns: the pointer to namespace
421 * @ctx: the C14N context
422 *
423 * Prints the given namespace to the output buffer from C14N context.
424 *
425 * Returns 1 on success or 0 on fail.
426 */
427static int
Daniel Veillard9ff88172002-03-11 09:15:32 +0000428xmlC14NPrintNamespaces(const xmlNsPtr ns, xmlC14NCtxPtr ctx)
429{
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000430
Daniel Veillard9ff88172002-03-11 09:15:32 +0000431 if ((ns == NULL) || (ctx == NULL)) {
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000432#ifdef DEBUG_C14N
433 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +0000434 "xmlC14NPrintNamespace: namespace or context pointer is null\n");
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000435#endif
Daniel Veillard9ff88172002-03-11 09:15:32 +0000436 return 0;
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000437 }
438
Daniel Veillard9ff88172002-03-11 09:15:32 +0000439 if (ns->prefix != NULL) {
440 xmlOutputBufferWriteString(ctx->buf, " xmlns:");
441 xmlOutputBufferWriteString(ctx->buf, (const char *) ns->prefix);
442 xmlOutputBufferWriteString(ctx->buf, "=\"");
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000443 } else {
Daniel Veillard9ff88172002-03-11 09:15:32 +0000444 xmlOutputBufferWriteString(ctx->buf, " xmlns=\"");
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000445 }
Aleksey Saninf8cb6dd2002-06-04 04:27:06 +0000446 if(ns->href != NULL) {
447 xmlOutputBufferWriteString(ctx->buf, (const char *) ns->href);
448 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000449 xmlOutputBufferWriteString(ctx->buf, "\"");
Daniel Veillard9ff88172002-03-11 09:15:32 +0000450 return (1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000451}
452
453/**
454 * xmlC14NProcessNamespacesAxis:
455 * @ctx: the C14N context
456 * @node: the current node
457 *
458 * Prints out canonical namespace axis of the current node to the
459 * buffer from C14N context as follows
460 *
461 * Canonical XML v 1.0 (http://www.w3.org/TR/xml-c14n)
462 *
463 * Namespace Axis
464 * Consider a list L containing only namespace nodes in the
465 * axis and in the node-set in lexicographic order (ascending). To begin
466 * processing L, if the first node is not the default namespace node (a node
467 * with no namespace URI and no local name), then generate a space followed
468 * by xmlns="" if and only if the following conditions are met:
469 * - the element E that owns the axis is in the node-set
470 * - The nearest ancestor element of E in the node-set has a default
471 * namespace node in the node-set (default namespace nodes always
472 * have non-empty values in XPath)
473 * The latter condition eliminates unnecessary occurrences of xmlns="" in
474 * the canonical form since an element only receives an xmlns="" if its
475 * default namespace is empty and if it has an immediate parent in the
476 * canonical form that has a non-empty default namespace. To finish
477 * processing L, simply process every namespace node in L, except omit
478 * namespace node with local name xml, which defines the xml prefix,
479 * if its string value is http://www.w3.org/XML/1998/namespace.
480 *
481 * Exclusive XML Canonicalization v 1.0 (http://www.w3.org/TR/xml-exc-c14n)
482 * Canonical XML applied to a document subset requires the search of the
483 * ancestor nodes of each orphan element node for attributes in the xml
484 * namespace, such as xml:lang and xml:space. These are copied into the
485 * element node except if a declaration of the same attribute is already
486 * in the attribute axis of the element (whether or not it is included in
487 * the document subset). This search and copying are omitted from the
488 * Exclusive XML Canonicalization method.
489 *
490 * Returns 0 on success or -1 on fail.
491 */
492static int
Aleksey Saninf8cb6dd2002-06-04 04:27:06 +0000493xmlC14NProcessNamespacesAxis(xmlC14NCtxPtr ctx, xmlNodePtr cur, int visible)
Daniel Veillard9ff88172002-03-11 09:15:32 +0000494{
Aleksey Sanin2c135a12002-08-01 06:31:50 +0000495 xmlNodePtr n;
496 xmlNsPtr ns, tmp;
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000497 xmlListPtr list;
Aleksey Sanin2c135a12002-08-01 06:31:50 +0000498 int already_rendered;
499 int has_empty_ns = 0;
Daniel Veillard5c396542002-03-15 07:57:50 +0000500
Daniel Veillard9ff88172002-03-11 09:15:32 +0000501 if ((ctx == NULL) || (cur == NULL) || (cur->type != XML_ELEMENT_NODE)) {
502#ifdef DEBUG_C14N
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000503 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +0000504 "xmlC14NProcessNamespacesAxis: Null context or node pointer or type != XML_ELEMENT_NODE.\n");
505#endif
506 return (-1);
507 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000508
509 /*
510 * Create a sorted list to store element namespaces
511 */
Aleksey Sanin2c135a12002-08-01 06:31:50 +0000512 list = xmlListCreate(NULL, (xmlListDataCompare) xmlC14NNsCompare);
Daniel Veillard9ff88172002-03-11 09:15:32 +0000513 if (list == NULL) {
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000514#ifdef DEBUG_C14N
515 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +0000516 "xmlC14NProcessNamespacesAxis: list creation failed\n");
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000517#endif
Daniel Veillard9ff88172002-03-11 09:15:32 +0000518 return (-1);
519 }
520
Aleksey Sanin2c135a12002-08-01 06:31:50 +0000521 /* check all namespaces */
522 for(n = cur; n != NULL; n = n->parent) {
523 for(ns = n->nsDef; ns != NULL; ns = ns->next) {
524 tmp = xmlSearchNs(cur->doc, cur, ns->prefix);
525
526 if((tmp == ns) && !xmlC14NIsXmlNs(ns) && xmlC14NIsVisible(ctx, ns, cur)) {
527 already_rendered = xmlC14NVisibleNsStackFind(ctx->ns_rendered, ns);
528 if(visible) {
529 xmlC14NVisibleNsStackAdd(ctx->ns_rendered, ns, cur);
530 }
531 if(!already_rendered) {
Aleksey Saninc57f9c12002-05-31 19:14:57 +0000532 xmlListInsert(list, ns);
533 }
Aleksey Sanin2c135a12002-08-01 06:31:50 +0000534 if(xmlStrlen(ns->prefix) == 0) {
535 has_empty_ns = 1;
536 }
Aleksey Saninc57f9c12002-05-31 19:14:57 +0000537 }
Aleksey Sanindffd5c82002-05-31 04:24:13 +0000538 }
Aleksey Sanin2c135a12002-08-01 06:31:50 +0000539 }
Aleksey Sanindffd5c82002-05-31 04:24:13 +0000540
Aleksey Sanin2c135a12002-08-01 06:31:50 +0000541 /**
542 * if the first node is not the default namespace node (a node with no
543 * namespace URI and no local name), then generate a space followed by
544 * xmlns="" if and only if the following conditions are met:
545 * - the element E that owns the axis is in the node-set
546 * - the nearest ancestor element of E in the node-set has a default
547 * namespace node in the node-set (default namespace nodes always
548 * have non-empty values in XPath)
549 */
550 if(visible && !has_empty_ns) {
551 static xmlNs ns_default;
552
553 memset(&ns_default, 0, sizeof(ns_default));
554 if(!xmlC14NVisibleNsStackFind(ctx->ns_rendered, &ns_default)) {
555 xmlC14NPrintNamespaces(&ns_default, ctx);
Aleksey Saninf8cb6dd2002-06-04 04:27:06 +0000556 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000557 }
Aleksey Saninf8cb6dd2002-06-04 04:27:06 +0000558
559
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000560 /*
561 * print out all elements from list
562 */
Aleksey Saninf8cb6dd2002-06-04 04:27:06 +0000563 xmlListWalk(list, (xmlListWalker) xmlC14NPrintNamespaces, (const void *) ctx);
Daniel Veillard9ff88172002-03-11 09:15:32 +0000564
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000565 /*
566 * Cleanup
567 */
568 xmlListDelete(list);
Daniel Veillard9ff88172002-03-11 09:15:32 +0000569 return (0);
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000570}
571
Aleksey Sanindffd5c82002-05-31 04:24:13 +0000572
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000573/**
574 * xmlExcC14NProcessNamespacesAxis:
575 * @ctx: the C14N context
576 * @node: the current node
577 *
578 * Prints out exclusive canonical namespace axis of the current node to the
579 * buffer from C14N context as follows
580 *
581 * Exclusive XML Canonicalization
582 * http://www.w3.org/TR/xml-exc-c14n
583 *
584 * If the element node is in the XPath subset then output the node in
585 * accordance with Canonical XML except for namespace nodes which are
586 * rendered as follows:
587 *
588 * 1. Render each namespace node iff:
589 * * it is visibly utilized by the immediate parent element or one of
590 * its attributes, or is present in InclusiveNamespaces PrefixList, and
591 * * its prefix and value do not appear in ns_rendered. ns_rendered is
592 * obtained by popping the state stack in order to obtain a list of
593 * prefixes and their values which have already been rendered by
594 * an output ancestor of the namespace node's parent element.
595 * 2. Append the rendered namespace node to the list ns_rendered of namespace
596 * nodes rendered by output ancestors. Push ns_rendered on state stack and
597 * recurse.
598 * 3. After the recursion returns, pop thestate stack.
599 *
600 *
601 * Returns 0 on success or -1 on fail.
602 */
603static int
Aleksey Saninf8cb6dd2002-06-04 04:27:06 +0000604xmlExcC14NProcessNamespacesAxis(xmlC14NCtxPtr ctx, xmlNodePtr cur, int visible)
Daniel Veillard9ff88172002-03-11 09:15:32 +0000605{
Aleksey Sanin2c135a12002-08-01 06:31:50 +0000606 xmlNsPtr ns;
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000607 xmlListPtr list;
608 xmlAttrPtr attr;
Aleksey Sanin2c135a12002-08-01 06:31:50 +0000609 int already_rendered;
610 int has_empty_ns = 0;
611 int has_visibly_utilized_empty_ns = 0;
612 int has_empty_ns_in_inclusive_list = 0;
613
Daniel Veillard9ff88172002-03-11 09:15:32 +0000614 if ((ctx == NULL) || (cur == NULL) || (cur->type != XML_ELEMENT_NODE)) {
615#ifdef DEBUG_C14N
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000616 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +0000617 "xmlExcC14NProcessNamespacesAxis: Null context or node pointer or type != XML_ELEMENT_NODE.\n");
618#endif
619 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000620 }
Daniel Veillard9ff88172002-03-11 09:15:32 +0000621
Aleksey Saninf8cb6dd2002-06-04 04:27:06 +0000622 if(!ctx->exclusive) {
Daniel Veillard9ff88172002-03-11 09:15:32 +0000623#ifdef DEBUG_C14N
624 xmlGenericError(xmlGenericErrorContext,
625 "xmlExcC14NProcessNamespacesAxis: called for non-exclusive canonization or rendered stack is NULL.\n");
626#endif
627 return (-1);
628
629 }
630
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000631 /*
632 * Create a sorted list to store element namespaces
633 */
Aleksey Saninf8cb6dd2002-06-04 04:27:06 +0000634 list = xmlListCreate(NULL, (xmlListDataCompare) xmlC14NNsCompare);
Daniel Veillard9ff88172002-03-11 09:15:32 +0000635 if (list == NULL) {
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000636#ifdef DEBUG_C14N
637 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +0000638 "xmlExcC14NProcessNamespacesAxis: list creation failed\n");
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000639#endif
Daniel Veillard9ff88172002-03-11 09:15:32 +0000640 return (-1);
641 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000642
Aleksey Sanin2c135a12002-08-01 06:31:50 +0000643 /*
644 * process inclusive namespaces:
645 * All namespace nodes appearing on inclusive ns list are
646 * handled as provided in Canonical XML
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000647 */
Aleksey Sanin2c135a12002-08-01 06:31:50 +0000648 if(ctx->inclusive_ns_prefixes != NULL) {
649 xmlChar *prefix;
650 int i;
651
652 for (i = 0; ctx->inclusive_ns_prefixes[i] != NULL; ++i) {
653 prefix = ctx->inclusive_ns_prefixes[i];
654 /*
655 * Special values for namespace with empty prefix
656 */
657 if (xmlStrEqual(prefix, BAD_CAST "#default")
658 || xmlStrEqual(prefix, BAD_CAST "")) {
659 prefix = NULL;
660 has_empty_ns_in_inclusive_list = 1;
661 }
662
663 ns = xmlSearchNs(cur->doc, cur, prefix);
664 if((ns != NULL) && !xmlC14NIsXmlNs(ns) && xmlC14NIsVisible(ctx, ns, cur)) {
665 already_rendered = xmlC14NVisibleNsStackFind(ctx->ns_rendered, ns);
666 if(visible) {
667 xmlC14NVisibleNsStackAdd(ctx->ns_rendered, ns, cur);
668 }
669 if(!already_rendered) {
670 xmlListInsert(list, ns);
671 }
672 if(xmlStrlen(ns->prefix) == 0) {
673 has_empty_ns = 1;
674 }
Aleksey Saninf8cb6dd2002-06-04 04:27:06 +0000675 }
Aleksey Saninc57f9c12002-05-31 19:14:57 +0000676 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000677 }
Aleksey Saninf8cb6dd2002-06-04 04:27:06 +0000678
Aleksey Sanin2c135a12002-08-01 06:31:50 +0000679 /* add node namespace */
680 if(cur->ns != NULL) {
681 ns = cur->ns;
682 } else {
683 ns = xmlSearchNs(cur->doc, cur, NULL);
684 has_visibly_utilized_empty_ns = 1;
685 }
686 if((ns != NULL) && !xmlC14NIsXmlNs(ns)) {
687 if(visible && xmlC14NIsVisible(ctx, ns, cur)) {
688 if(!xmlExcC14NVisibleNsStackFind(ctx->ns_rendered, ns, ctx)) {
689 xmlListInsert(list, ns);
690 }
691 }
692 if(visible) {
693 xmlC14NVisibleNsStackAdd(ctx->ns_rendered, ns, cur);
694 }
695 if(xmlStrlen(ns->prefix) == 0) {
696 has_empty_ns = 1;
697 }
698 }
699
700
701 /* add attributes */
702 for(attr = cur->properties; attr != NULL; attr = attr->next) {
Daniel Veillard9ff88172002-03-11 09:15:32 +0000703 /*
Daniel Veillard2d347fa2002-03-17 10:34:11 +0000704 * we need to check that attribute is visible and has non
705 * default namespace (XML Namespaces: "default namespaces
Aleksey Sanin2c135a12002-08-01 06:31:50 +0000706 * do not apply directly to attributes")
Daniel Veillard9ff88172002-03-11 09:15:32 +0000707 */
Aleksey Sanin2c135a12002-08-01 06:31:50 +0000708 if((attr->ns != NULL) && xmlC14NIsVisible(ctx, attr, cur)) {
709 already_rendered = xmlExcC14NVisibleNsStackFind(ctx->ns_rendered, attr->ns, ctx);
710 xmlC14NVisibleNsStackAdd(ctx->ns_rendered, attr->ns, (xmlNodePtr)attr);
711 if(!already_rendered && visible) {
712 xmlListInsert(list, attr->ns);
713 }
714 if(xmlStrlen(attr->ns->prefix) == 0) {
715 has_empty_ns = 1;
716 }
717 } else if(attr->ns == NULL) {
718 has_visibly_utilized_empty_ns = 1;
719 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000720 }
Daniel Veillard9ff88172002-03-11 09:15:32 +0000721
Aleksey Sanin2c135a12002-08-01 06:31:50 +0000722 /*
723 * Process xmlns=""
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000724 */
Aleksey Sanin2c135a12002-08-01 06:31:50 +0000725 if(visible && has_visibly_utilized_empty_ns &&
726 !has_empty_ns && !has_empty_ns_in_inclusive_list) {
727 static xmlNs ns_default;
Daniel Veillard9ff88172002-03-11 09:15:32 +0000728
Aleksey Sanin2c135a12002-08-01 06:31:50 +0000729 memset(&ns_default, 0, sizeof(ns_default));
730
731 already_rendered = xmlExcC14NVisibleNsStackFind(ctx->ns_rendered, &ns_default, ctx);
732 if(!already_rendered) {
733 xmlC14NPrintNamespaces(&ns_default, ctx);
734 }
735 } else if(visible && !has_empty_ns && has_empty_ns_in_inclusive_list) {
736 static xmlNs ns_default;
737
738 memset(&ns_default, 0, sizeof(ns_default));
739 if(!xmlC14NVisibleNsStackFind(ctx->ns_rendered, &ns_default)) {
740 xmlC14NPrintNamespaces(&ns_default, ctx);
741 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000742 }
Daniel Veillard9ff88172002-03-11 09:15:32 +0000743
Aleksey Sanin2c135a12002-08-01 06:31:50 +0000744
745
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000746 /*
747 * print out all elements from list
748 */
Aleksey Saninf8cb6dd2002-06-04 04:27:06 +0000749 xmlListWalk(list, (xmlListWalker) xmlC14NPrintNamespaces, (const void *) ctx);
Daniel Veillard9ff88172002-03-11 09:15:32 +0000750
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000751 /*
752 * Cleanup
753 */
754 xmlListDelete(list);
Daniel Veillard9ff88172002-03-11 09:15:32 +0000755 return (0);
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000756}
757
758
759/**
760 * xmlC14NAttrsCompare:
Aleksey Saninf8cb6dd2002-06-04 04:27:06 +0000761 * @attr1: the pointer tls o first attr
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000762 * @attr2: the pointer to second attr
763 *
764 * Prints the given attribute to the output buffer from C14N context.
765 *
766 * Returns -1 if attr1 < attr2, 0 if attr1 == attr2 or 1 if attr1 > attr2.
767 */
768static int
Daniel Veillard9ff88172002-03-11 09:15:32 +0000769xmlC14NAttrsCompare(xmlAttrPtr attr1, xmlAttrPtr attr2)
770{
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000771 int ret = 0;
772
773 /*
774 * Simple cases
775 */
Daniel Veillard9ff88172002-03-11 09:15:32 +0000776 if (attr1 == attr2)
777 return (0);
778 if (attr1 == NULL)
779 return (-1);
780 if (attr2 == NULL)
781 return (1);
782 if (attr1->ns == attr2->ns) {
783 return (xmlStrcmp(attr1->name, attr2->name));
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000784 }
785
786 /*
787 * Attributes in the default namespace are first
788 * because the default namespace is not applied to
789 * unqualified attributes
790 */
Daniel Veillard9ff88172002-03-11 09:15:32 +0000791 if (attr1->ns == NULL)
792 return (-1);
793 if (attr2->ns == NULL)
794 return (1);
795 if (attr1->ns->prefix == NULL)
796 return (-1);
797 if (attr2->ns->prefix == NULL)
798 return (1);
799
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000800 ret = xmlStrcmp(attr1->ns->href, attr2->ns->href);
Daniel Veillard9ff88172002-03-11 09:15:32 +0000801 if (ret == 0) {
802 ret = xmlStrcmp(attr1->name, attr2->name);
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000803 }
Daniel Veillard9ff88172002-03-11 09:15:32 +0000804 return (ret);
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000805}
806
807
808/**
809 * xmlC14NPrintAttrs:
810 * @attr: the pointer to attr
811 * @ctx: the C14N context
812 *
813 * Prints out canonical attribute urrent node to the
814 * buffer from C14N context as follows
815 *
816 * Canonical XML v 1.0 (http://www.w3.org/TR/xml-c14n)
817 *
818 * Returns 1 on success or 0 on fail.
819 */
820static int
Daniel Veillard9ff88172002-03-11 09:15:32 +0000821xmlC14NPrintAttrs(const xmlAttrPtr attr, xmlC14NCtxPtr ctx)
822{
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000823 xmlChar *value;
824 xmlChar *buffer;
825
Daniel Veillard9ff88172002-03-11 09:15:32 +0000826 if ((attr == NULL) || (ctx == NULL)) {
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000827#ifdef DEBUG_C14N
828 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +0000829 "xmlC14NPrintAttrs: attr == NULL or ctx == NULL\n");
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000830#endif
Daniel Veillard9ff88172002-03-11 09:15:32 +0000831 return (0);
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000832 }
833
834 xmlOutputBufferWriteString(ctx->buf, " ");
Daniel Veillard9ff88172002-03-11 09:15:32 +0000835 if (attr->ns != NULL && xmlStrlen(attr->ns->prefix) > 0) {
836 xmlOutputBufferWriteString(ctx->buf,
837 (const char *) attr->ns->prefix);
838 xmlOutputBufferWriteString(ctx->buf, ":");
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000839 }
840 xmlOutputBufferWriteString(ctx->buf, (const char *) attr->name);
841 xmlOutputBufferWriteString(ctx->buf, "=\"");
842
843 value = xmlNodeListGetString(attr->doc, attr->children, 1);
844 /* todo: should we log an error if value==NULL ? */
Daniel Veillard9ff88172002-03-11 09:15:32 +0000845 if (value != NULL) {
846 buffer = xmlC11NNormalizeAttr(value);
847 xmlFree(value);
848 if (buffer != NULL) {
849 xmlOutputBufferWriteString(ctx->buf, (const char *) buffer);
850 xmlFree(buffer);
851 } else {
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000852#ifdef DEBUG_C14N
Daniel Veillard9ff88172002-03-11 09:15:32 +0000853 xmlGenericError(xmlGenericErrorContext,
854 "xmlC14NPrintAttrs: xmlC11NNormalizeAttr failed\n");
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000855#endif
Daniel Veillard9ff88172002-03-11 09:15:32 +0000856 return (0);
857 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000858 }
859 xmlOutputBufferWriteString(ctx->buf, "\"");
Daniel Veillard9ff88172002-03-11 09:15:32 +0000860 return (1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000861}
862
863/**
864 * xmlC14NProcessAttrsAxis:
865 * @ctx: the C14N context
866 * @cur: the current node
867 *
868 * Prints out canonical attribute axis of the current node to the
869 * buffer from C14N context as follows
870 *
871 * Canonical XML v 1.0 (http://www.w3.org/TR/xml-c14n)
872 *
873 * Attribute Axis
874 * In lexicographic order (ascending), process each node that
875 * is in the element's attribute axis and in the node-set.
876 *
877 * The processing of an element node E MUST be modified slightly
878 * when an XPath node-set is given as input and the element's
879 * parent is omitted from the node-set.
880 *
881 *
882 * Exclusive XML Canonicalization v 1.0 (http://www.w3.org/TR/xml-exc-c14n)
883 *
884 * Canonical XML applied to a document subset requires the search of the
885 * ancestor nodes of each orphan element node for attributes in the xml
886 * namespace, such as xml:lang and xml:space. These are copied into the
887 * element node except if a declaration of the same attribute is already
888 * in the attribute axis of the element (whether or not it is included in
889 * the document subset). This search and copying are omitted from the
890 * Exclusive XML Canonicalization method.
891 *
892 * Returns 0 on success or -1 on fail.
893 */
894static int
Daniel Veillard9ff88172002-03-11 09:15:32 +0000895xmlC14NProcessAttrsAxis(xmlC14NCtxPtr ctx, xmlNodePtr cur)
896{
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000897 xmlAttrPtr attr;
898 xmlListPtr list;
Daniel Veillard9ff88172002-03-11 09:15:32 +0000899
900 if ((ctx == NULL) || (cur == NULL) || (cur->type != XML_ELEMENT_NODE)) {
901#ifdef DEBUG_C14N
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000902 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +0000903 "xmlC14NProcessAttrsAxis: Null context or node pointer or type != XML_ELEMENT_NODE.\n");
904#endif
905 return (-1);
906 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000907
908 /*
909 * Create a sorted list to store element attributes
910 */
Daniel Veillard9ff88172002-03-11 09:15:32 +0000911 list = xmlListCreate(NULL, (xmlListDataCompare) xmlC14NAttrsCompare);
912 if (list == NULL) {
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000913#ifdef DEBUG_C14N
914 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +0000915 "xmlC14NProcessAttrsAxis: list creation failed\n");
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000916#endif
Daniel Veillard9ff88172002-03-11 09:15:32 +0000917 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000918 }
919
920 /*
921 * Add all visible attributes from current node.
922 */
923 attr = cur->properties;
Daniel Veillard9ff88172002-03-11 09:15:32 +0000924 while (attr != NULL) {
925 /* check that attribute is visible */
Aleksey Sanin2c135a12002-08-01 06:31:50 +0000926 if (xmlC14NIsVisible(ctx, attr, cur)) {
Daniel Veillard9ff88172002-03-11 09:15:32 +0000927 xmlListInsert(list, attr);
928 }
929 attr = attr->next;
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000930 }
Daniel Veillard9ff88172002-03-11 09:15:32 +0000931
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000932 /*
933 * include attributes in "xml" namespace defined in ancestors
934 * (only for non-exclusive XML Canonicalization)
935 */
Daniel Veillard9ff88172002-03-11 09:15:32 +0000936 if ((!ctx->exclusive) && (cur->parent != NULL)
Aleksey Sanin2c135a12002-08-01 06:31:50 +0000937 && (!xmlC14NIsVisible(ctx, cur->parent, cur->parent->parent))) {
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000938 /*
Daniel Veillard9ff88172002-03-11 09:15:32 +0000939 * If XPath node-set is not specified then the parent is always
940 * visible!
941 */
942 cur = cur->parent;
943 while (cur != NULL) {
944 attr = cur->properties;
945 while (attr != NULL) {
946 if ((attr->ns != NULL)
947 && (xmlStrEqual(attr->ns->prefix, BAD_CAST "xml"))) {
948 if (xmlListSearch(list, attr) == NULL) {
949 xmlListInsert(list, attr);
950 }
951 }
952 attr = attr->next;
953 }
954 cur = cur->parent;
955 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000956 }
Daniel Veillard9ff88172002-03-11 09:15:32 +0000957
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000958 /*
959 * print out all elements from list
960 */
Aleksey Saninf8cb6dd2002-06-04 04:27:06 +0000961 xmlListWalk(list, (xmlListWalker) xmlC14NPrintAttrs, (const void *) ctx);
Daniel Veillard9ff88172002-03-11 09:15:32 +0000962
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000963 /*
964 * Cleanup
965 */
Daniel Veillard9ff88172002-03-11 09:15:32 +0000966 xmlListDelete(list);
967 return (0);
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000968}
969
970/**
971 * xmlC14NCheckForRelativeNamespaces:
972 * @ctx: the C14N context
973 * @cur: the current element node
974 *
975 * Checks that current element node has no relative namespaces defined
976 *
977 * Returns 0 if the node has no relative namespaces or -1 otherwise.
978 */
979static int
Daniel Veillard9ff88172002-03-11 09:15:32 +0000980xmlC14NCheckForRelativeNamespaces(xmlC14NCtxPtr ctx, xmlNodePtr cur)
981{
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000982 xmlNsPtr ns;
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000983
Daniel Veillard9ff88172002-03-11 09:15:32 +0000984 if ((ctx == NULL) || (cur == NULL) || (cur->type != XML_ELEMENT_NODE)) {
985#ifdef DEBUG_C14N
986 xmlGenericError(xmlGenericErrorContext,
987 "xmlC14NCheckForRelativeNamespaces: Null context or node pointer or type != XML_ELEMENT_NODE.\n");
988#endif
989 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000990 }
Daniel Veillard9ff88172002-03-11 09:15:32 +0000991
992 ns = cur->nsDef;
993 while (ns != NULL) {
994 if (xmlStrlen(ns->href) > 0) {
995 xmlURIPtr uri;
996
997 uri = xmlParseURI((const char *) ns->href);
998 if (uri == NULL) {
999#ifdef DEBUG_C14N
1000 xmlGenericError(xmlGenericErrorContext,
1001 "xmlC14NCheckForRelativeNamespaces: unable to parse uri=\"%s\".\n",
1002 ns->href);
1003#endif
1004 return (-1);
1005 }
1006 if (xmlStrlen((const xmlChar *) uri->scheme) == 0) {
1007 xmlFreeURI(uri);
1008 return (-1);
1009 }
1010 if ((!xmlStrEqual
1011 ((const xmlChar *) uri->scheme, BAD_CAST "urn"))
1012 && (xmlStrlen((const xmlChar *) uri->server) == 0)) {
1013 xmlFreeURI(uri);
1014 return (-1);
1015 }
1016 xmlFreeURI(uri);
1017 }
1018 ns = ns->next;
1019 }
1020 return (0);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001021}
1022
1023/**
1024 * xmlC14NProcessElementNode:
1025 * @ctx: the pointer to C14N context object
1026 * @cur: the node to process
1027 *
1028 * Canonical XML v 1.0 (http://www.w3.org/TR/xml-c14n)
1029 *
1030 * Element Nodes
1031 * If the element is not in the node-set, then the result is obtained
1032 * by processing the namespace axis, then the attribute axis, then
1033 * processing the child nodes of the element that are in the node-set
1034 * (in document order). If the element is in the node-set, then the result
1035 * is an open angle bracket (<), the element QName, the result of
1036 * processing the namespace axis, the result of processing the attribute
1037 * axis, a close angle bracket (>), the result of processing the child
1038 * nodes of the element that are in the node-set (in document order), an
1039 * open angle bracket, a forward slash (/), the element QName, and a close
1040 * angle bracket.
1041 *
1042 * Returns non-negative value on success or negative value on fail
1043 */
1044static int
Daniel Veillard9ff88172002-03-11 09:15:32 +00001045xmlC14NProcessElementNode(xmlC14NCtxPtr ctx, xmlNodePtr cur, int visible)
1046{
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001047 int ret;
Aleksey Saninf8cb6dd2002-06-04 04:27:06 +00001048 xmlC14NVisibleNsStack state;
Daniel Veillard6f293b12002-03-15 09:42:33 +00001049 int parent_is_doc = 0;
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001050
Daniel Veillard9ff88172002-03-11 09:15:32 +00001051 if ((ctx == NULL) || (cur == NULL) || (cur->type != XML_ELEMENT_NODE)) {
1052#ifdef DEBUG_C14N
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001053 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001054 "xmlC14NProcessElementNode: Null context or node pointer or type != XML_ELEMENT_NODE.\n");
1055#endif
1056 return (-1);
1057 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001058
1059 /*
1060 * Check relative relative namespaces:
1061 * implementations of XML canonicalization MUST report an operation
1062 * failure on documents containing relative namespace URIs.
1063 */
Daniel Veillard9ff88172002-03-11 09:15:32 +00001064 if (xmlC14NCheckForRelativeNamespaces(ctx, cur) < 0) {
1065#ifdef DEBUG_C14N
1066 xmlGenericError(xmlGenericErrorContext,
1067 "xmlC14NProcessElementNode: xmlC14NCheckForRelativeNamespaces failed.\n");
1068#endif
1069 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001070 }
1071
1072
1073 /*
Aleksey Saninf8cb6dd2002-06-04 04:27:06 +00001074 * Save ns_rendered stack position
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001075 */
Aleksey Saninf8cb6dd2002-06-04 04:27:06 +00001076 xmlC14NVisibleNsStackSave(ctx->ns_rendered, &state);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001077
Daniel Veillard6f293b12002-03-15 09:42:33 +00001078 if (visible) {
Daniel Veillard9ff88172002-03-11 09:15:32 +00001079 if (ctx->parent_is_doc) {
Daniel Veillard6f293b12002-03-15 09:42:33 +00001080 /* save this flag into the stack */
1081 parent_is_doc = ctx->parent_is_doc;
1082 ctx->parent_is_doc = 0;
Daniel Veillard9ff88172002-03-11 09:15:32 +00001083 ctx->pos = XMLC14N_INSIDE_DOCUMENT_ELEMENT;
1084 }
1085 xmlOutputBufferWriteString(ctx->buf, "<");
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001086
Daniel Veillard9ff88172002-03-11 09:15:32 +00001087 if ((cur->ns != NULL) && (xmlStrlen(cur->ns->prefix) > 0)) {
1088 xmlOutputBufferWriteString(ctx->buf,
1089 (const char *) cur->ns->prefix);
1090 xmlOutputBufferWriteString(ctx->buf, ":");
1091 }
1092 xmlOutputBufferWriteString(ctx->buf, (const char *) cur->name);
Aleksey Saninf8cb6dd2002-06-04 04:27:06 +00001093 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001094
Aleksey Saninf8cb6dd2002-06-04 04:27:06 +00001095 if (!ctx->exclusive) {
1096 ret = xmlC14NProcessNamespacesAxis(ctx, cur, visible);
1097 } else {
1098 ret = xmlExcC14NProcessNamespacesAxis(ctx, cur, visible);
1099 }
1100 if (ret < 0) {
Daniel Veillard9ff88172002-03-11 09:15:32 +00001101#ifdef DEBUG_C14N
Aleksey Saninf8cb6dd2002-06-04 04:27:06 +00001102 xmlGenericError(xmlGenericErrorContext,
1103 "xmlC14NProcessElementNode: xmlC14NProcessNamespacesAxis failed.\n");
Daniel Veillard9ff88172002-03-11 09:15:32 +00001104#endif
Aleksey Saninf8cb6dd2002-06-04 04:27:06 +00001105 return (-1);
1106 }
1107 /* todo: shouldn't this go to "visible only"? */
1108 if(visible) {
1109 xmlC14NVisibleNsStackShift(ctx->ns_rendered);
1110 }
1111
1112 if(visible) {
1113 ret = xmlC14NProcessAttrsAxis(ctx, cur);
1114 if (ret < 0) {
1115#ifdef DEBUG_C14N
1116 xmlGenericError(xmlGenericErrorContext,
1117 "xmlC14NProcessElementNode: xmlC14NProcessAttrsAxis failed.\n");
1118#endif
1119 return (-1);
Aleksey Saninc57f9c12002-05-31 19:14:57 +00001120 }
Aleksey Saninf8cb6dd2002-06-04 04:27:06 +00001121 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001122
Aleksey Saninf8cb6dd2002-06-04 04:27:06 +00001123 if (visible) {
Daniel Veillard9ff88172002-03-11 09:15:32 +00001124 xmlOutputBufferWriteString(ctx->buf, ">");
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001125 }
1126 if (cur->children != NULL) {
Daniel Veillard9ff88172002-03-11 09:15:32 +00001127 ret = xmlC14NProcessNodeList(ctx, cur->children);
1128 if (ret < 0) {
1129#ifdef DEBUG_C14N
1130 xmlGenericError(xmlGenericErrorContext,
1131 "xmlC14NProcessElementNode: xmlC14NProcessNodeList failed.\n");
1132#endif
1133 return (-1);
1134 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001135 }
Daniel Veillard9ff88172002-03-11 09:15:32 +00001136 if (visible) {
1137 xmlOutputBufferWriteString(ctx->buf, "</");
1138 if ((cur->ns != NULL) && (xmlStrlen(cur->ns->prefix) > 0)) {
1139 xmlOutputBufferWriteString(ctx->buf,
1140 (const char *) cur->ns->prefix);
1141 xmlOutputBufferWriteString(ctx->buf, ":");
1142 }
1143 xmlOutputBufferWriteString(ctx->buf, (const char *) cur->name);
1144 xmlOutputBufferWriteString(ctx->buf, ">");
Daniel Veillard6f293b12002-03-15 09:42:33 +00001145 if (parent_is_doc) {
1146 /* restore this flag from the stack for next node */
1147 ctx->parent_is_doc = parent_is_doc;
1148 ctx->pos = XMLC14N_AFTER_DOCUMENT_ELEMENT;
Daniel Veillard9ff88172002-03-11 09:15:32 +00001149 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001150 }
1151
1152 /*
Aleksey Saninf8cb6dd2002-06-04 04:27:06 +00001153 * Restore ns_rendered stack position
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001154 */
Aleksey Saninf8cb6dd2002-06-04 04:27:06 +00001155 xmlC14NVisibleNsStackRestore(ctx->ns_rendered, &state);
Daniel Veillard9ff88172002-03-11 09:15:32 +00001156 return (0);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001157}
1158
1159/**
1160 * xmlC14NProcessNode:
1161 * @ctx: the pointer to C14N context object
1162 * @cur: the node to process
1163 *
1164 * Processes the given node
1165 *
1166 * Returns non-negative value on success or negative value on fail
1167 */
1168static int
Daniel Veillard9ff88172002-03-11 09:15:32 +00001169xmlC14NProcessNode(xmlC14NCtxPtr ctx, xmlNodePtr cur)
1170{
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001171 int ret = 0;
1172 int visible;
Daniel Veillard9ff88172002-03-11 09:15:32 +00001173
1174 if ((ctx == NULL) || (cur == NULL)) {
1175#ifdef DEBUG_C14N
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001176 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001177 "xmlC14NProcessNode: Null context or node pointer.\n");
1178#endif
1179 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001180 }
1181
Aleksey Sanin2c135a12002-08-01 06:31:50 +00001182 visible = xmlC14NIsVisible(ctx, cur, cur->parent);
Daniel Veillard9ff88172002-03-11 09:15:32 +00001183 switch (cur->type) {
1184 case XML_ELEMENT_NODE:
1185 ret = xmlC14NProcessElementNode(ctx, cur, visible);
1186 break;
1187 case XML_CDATA_SECTION_NODE:
1188 case XML_TEXT_NODE:
1189 /*
1190 * Text Nodes
1191 * the string value, except all ampersands are replaced
1192 * by &amp;, all open angle brackets (<) are replaced by &lt;, all closing
1193 * angle brackets (>) are replaced by &gt;, and all #xD characters are
1194 * replaced by &#xD;.
1195 */
1196 /* cdata sections are processed as text nodes */
1197 /* todo: verify that cdata sections are included in XPath nodes set */
1198 if ((visible) && (cur->content != NULL)) {
1199 xmlChar *buffer;
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001200
Daniel Veillard9ff88172002-03-11 09:15:32 +00001201 buffer = xmlC11NNormalizeText(cur->content);
1202 if (buffer != NULL) {
1203 xmlOutputBufferWriteString(ctx->buf,
1204 (const char *) buffer);
1205 xmlFree(buffer);
1206 } else {
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001207#ifdef DEBUG_C14N
Daniel Veillard9ff88172002-03-11 09:15:32 +00001208 xmlGenericError(xmlGenericErrorContext,
1209 "xmlC14NProcessNode: xmlC11NNormalizeText() failed\n");
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001210#endif
Daniel Veillard9ff88172002-03-11 09:15:32 +00001211 return (-1);
1212 }
1213 }
1214 break;
1215 case XML_PI_NODE:
1216 /*
1217 * Processing Instruction (PI) Nodes-
1218 * The opening PI symbol (<?), the PI target name of the node,
1219 * a leading space and the string value if it is not empty, and
1220 * the closing PI symbol (?>). If the string value is empty,
1221 * then the leading space is not added. Also, a trailing #xA is
1222 * rendered after the closing PI symbol for PI children of the
1223 * root node with a lesser document order than the document
1224 * element, and a leading #xA is rendered before the opening PI
1225 * symbol of PI children of the root node with a greater document
1226 * order than the document element.
1227 */
1228 if (visible) {
1229 if (ctx->pos == XMLC14N_AFTER_DOCUMENT_ELEMENT) {
1230 xmlOutputBufferWriteString(ctx->buf, "\x0A<?");
1231 } else {
1232 xmlOutputBufferWriteString(ctx->buf, "<?");
1233 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001234
Daniel Veillard9ff88172002-03-11 09:15:32 +00001235 xmlOutputBufferWriteString(ctx->buf,
1236 (const char *) cur->name);
1237 if ((cur->content != NULL) && (*(cur->content) != '\0')) {
1238 xmlChar *buffer;
1239
1240 xmlOutputBufferWriteString(ctx->buf, " ");
1241
1242 /* todo: do we need to normalize pi? */
1243 buffer = xmlC11NNormalizePI(cur->content);
1244 if (buffer != NULL) {
1245 xmlOutputBufferWriteString(ctx->buf,
1246 (const char *) buffer);
1247 xmlFree(buffer);
1248 } else {
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001249#ifdef DEBUG_C14N
Daniel Veillard9ff88172002-03-11 09:15:32 +00001250 xmlGenericError(xmlGenericErrorContext,
1251 "xmlC14NProcessNode: xmlC11NNormalizePI() failed\n");
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001252#endif
Daniel Veillard9ff88172002-03-11 09:15:32 +00001253 return (-1);
1254 }
1255 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001256
Daniel Veillard9ff88172002-03-11 09:15:32 +00001257 if (ctx->pos == XMLC14N_BEFORE_DOCUMENT_ELEMENT) {
1258 xmlOutputBufferWriteString(ctx->buf, "?>\x0A");
1259 } else {
1260 xmlOutputBufferWriteString(ctx->buf, "?>");
1261 }
1262 }
1263 break;
1264 case XML_COMMENT_NODE:
1265 /*
1266 * Comment Nodes
1267 * Nothing if generating canonical XML without comments. For
1268 * canonical XML with comments, generate the opening comment
1269 * symbol (<!--), the string value of the node, and the
1270 * closing comment symbol (-->). Also, a trailing #xA is rendered
1271 * after the closing comment symbol for comment children of the
1272 * root node with a lesser document order than the document
1273 * element, and a leading #xA is rendered before the opening
1274 * comment symbol of comment children of the root node with a
1275 * greater document order than the document element. (Comment
1276 * children of the root node represent comments outside of the
1277 * top-level document element and outside of the document type
1278 * declaration).
1279 */
1280 if (visible && ctx->with_comments) {
1281 if (ctx->pos == XMLC14N_AFTER_DOCUMENT_ELEMENT) {
1282 xmlOutputBufferWriteString(ctx->buf, "\x0A<!--");
1283 } else {
1284 xmlOutputBufferWriteString(ctx->buf, "<!--");
1285 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001286
Daniel Veillard9ff88172002-03-11 09:15:32 +00001287 if (cur->content != NULL) {
1288 xmlChar *buffer;
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001289
Daniel Veillard9ff88172002-03-11 09:15:32 +00001290 /* todo: do we need to normalize comment? */
1291 buffer = xmlC11NNormalizeComment(cur->content);
1292 if (buffer != NULL) {
1293 xmlOutputBufferWriteString(ctx->buf,
1294 (const char *) buffer);
1295 xmlFree(buffer);
1296 } else {
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001297#ifdef DEBUG_C14N
Daniel Veillard9ff88172002-03-11 09:15:32 +00001298 xmlGenericError(xmlGenericErrorContext,
1299 "xmlC14NProcessNode: xmlC11NNormalizeComment() failed\n");
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001300#endif
Daniel Veillard9ff88172002-03-11 09:15:32 +00001301 return (-1);
1302 }
1303 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001304
Daniel Veillard9ff88172002-03-11 09:15:32 +00001305 if (ctx->pos == XMLC14N_BEFORE_DOCUMENT_ELEMENT) {
1306 xmlOutputBufferWriteString(ctx->buf, "-->\x0A");
1307 } else {
1308 xmlOutputBufferWriteString(ctx->buf, "-->");
1309 }
1310 }
1311 break;
1312 case XML_DOCUMENT_NODE:
1313 case XML_DOCUMENT_FRAG_NODE: /* should be processed as document? */
Daniel Veillard1840ef02002-03-21 08:05:23 +00001314#ifdef LIBXML_DOCB_ENABLED
Daniel Veillard9ff88172002-03-11 09:15:32 +00001315 case XML_DOCB_DOCUMENT_NODE: /* should be processed as document? */
Daniel Veillard1840ef02002-03-21 08:05:23 +00001316#endif
1317#ifdef LIBXML_HTML_ENABLED
Daniel Veillard9ff88172002-03-11 09:15:32 +00001318 case XML_HTML_DOCUMENT_NODE: /* should be processed as document? */
Daniel Veillard1840ef02002-03-21 08:05:23 +00001319#endif
Daniel Veillard9ff88172002-03-11 09:15:32 +00001320 if (cur->children != NULL) {
1321 ctx->pos = XMLC14N_BEFORE_DOCUMENT_ELEMENT;
1322 ctx->parent_is_doc = 1;
1323 ret = xmlC14NProcessNodeList(ctx, cur->children);
1324 }
1325 break;
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001326
Daniel Veillard9ff88172002-03-11 09:15:32 +00001327 case XML_ATTRIBUTE_NODE:
1328 xmlGenericError(xmlGenericErrorContext,
1329 "xmlC14NProcessNode: XML_ATTRIBUTE_NODE is illegal here\n");
1330 return (-1);
1331 case XML_NAMESPACE_DECL:
1332 xmlGenericError(xmlGenericErrorContext,
1333 "xmlC14NProcessNode: XML_NAMESPACE_DECL is illegal here\n");
1334 return (-1);
1335 case XML_ENTITY_REF_NODE:
1336 xmlGenericError(xmlGenericErrorContext,
1337 "xmlC14NProcessNode: XML_ENTITY_REF_NODE is illegal here\n");
1338 return (-1);
1339 case XML_ENTITY_NODE:
1340 xmlGenericError(xmlGenericErrorContext,
1341 "xmlC14NProcessNode: XML_ENTITY_NODE is illegal here\n");
1342 return (-1);
1343
1344 case XML_DOCUMENT_TYPE_NODE:
1345 case XML_NOTATION_NODE:
1346 case XML_DTD_NODE:
1347 case XML_ELEMENT_DECL:
1348 case XML_ATTRIBUTE_DECL:
1349 case XML_ENTITY_DECL:
Daniel Veillard1840ef02002-03-21 08:05:23 +00001350#ifdef LIBXML_XINCLUDE_ENABLED
Daniel Veillard9ff88172002-03-11 09:15:32 +00001351 case XML_XINCLUDE_START:
1352 case XML_XINCLUDE_END:
Daniel Veillard1840ef02002-03-21 08:05:23 +00001353#endif
Daniel Veillard9ff88172002-03-11 09:15:32 +00001354 /*
1355 * should be ignored according to "W3C Canonical XML"
1356 */
1357 break;
1358 default:
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001359#ifdef DEBUG_C14N
Daniel Veillard9ff88172002-03-11 09:15:32 +00001360 xmlGenericError(xmlGenericErrorContext,
1361 "xmlC14NProcessNode: unknown node type = %d\n",
1362 cur->type);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001363#endif
Daniel Veillard9ff88172002-03-11 09:15:32 +00001364 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001365 }
1366
Daniel Veillard9ff88172002-03-11 09:15:32 +00001367 return (ret);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001368}
1369
1370/**
1371 * xmlC14NProcessNodeList:
1372 * @ctx: the pointer to C14N context object
1373 * @cur: the node to start from
1374 *
1375 * Processes all nodes in the row starting from cur.
1376 *
1377 * Returns non-negative value on success or negative value on fail
1378 */
1379static int
Daniel Veillard9ff88172002-03-11 09:15:32 +00001380xmlC14NProcessNodeList(xmlC14NCtxPtr ctx, xmlNodePtr cur)
1381{
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001382 int ret;
Daniel Veillard9ff88172002-03-11 09:15:32 +00001383
1384 if (ctx == NULL) {
1385#ifdef DEBUG_C14N
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001386 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001387 "xmlC14NProcessNodeList: Null context pointer.\n");
1388#endif
1389 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001390 }
1391
Daniel Veillard9ff88172002-03-11 09:15:32 +00001392 for (ret = 0; cur != NULL && ret >= 0; cur = cur->next) {
1393 ret = xmlC14NProcessNode(ctx, cur);
1394 }
1395 return (ret);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001396}
1397
1398
1399/**
1400 * xmlC14NFreeCtx:
1401 * @ctx: the pointer to C14N context object
1402 *
1403 * Cleanups the C14N context object.
1404 */
1405
1406static void
Daniel Veillard9ff88172002-03-11 09:15:32 +00001407xmlC14NFreeCtx(xmlC14NCtxPtr ctx)
1408{
1409 if (ctx == NULL) {
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001410#ifdef DEBUG_C14N
1411 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001412 "xmlC14NFreeCtx: ctx == NULL\n");
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001413#endif
Daniel Veillard9ff88172002-03-11 09:15:32 +00001414 return;
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001415 }
Daniel Veillard9ff88172002-03-11 09:15:32 +00001416
1417 if (ctx->ns_rendered != NULL) {
Aleksey Saninf8cb6dd2002-06-04 04:27:06 +00001418 xmlC14NVisibleNsStackDestroy(ctx->ns_rendered);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001419 }
1420 xmlFree(ctx);
1421}
1422
1423/**
1424 * xmlC14NNewCtx:
1425 * @doc: the XML document for canonization
Aleksey Sanin2c135a12002-08-01 06:31:50 +00001426 * @is_visible_callback:the function to use to determine is node visible
1427 * or not
1428 * @user_data: the first parameter for @is_visible_callback function
1429 * (in most cases, it is nodes set)
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001430 * @inclusive_ns_prefixe the list of inclusive namespace prefixes
1431 * ended with a NULL or NULL if there is no
1432 * inclusive namespaces (only for exclusive
1433 * canonicalization)
1434 * @with_comments: include comments in the result (!=0) or not (==0)
1435 * @buf: the output buffer to store canonical XML; this
1436 * buffer MUST have encoder==NULL because C14N requires
1437 * UTF-8 output
1438 *
1439 * Creates new C14N context object to store C14N parameters.
1440 *
1441 * Returns pointer to newly created object (success) or NULL (fail)
1442 */
1443static xmlC14NCtxPtr
Aleksey Sanin2c135a12002-08-01 06:31:50 +00001444xmlC14NNewCtx(xmlDocPtr doc,
1445 xmlC14NIsVisibleCallback is_visible_callback, void* user_data,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001446 int exclusive, xmlChar ** inclusive_ns_prefixes,
1447 int with_comments, xmlOutputBufferPtr buf)
1448{
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001449 xmlC14NCtxPtr ctx;
1450
Daniel Veillard9ff88172002-03-11 09:15:32 +00001451 if ((doc == NULL) || (buf == NULL)) {
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001452#ifdef DEBUG_C14N
1453 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001454 "xmlC14NNewCtx: pointer to document or output buffer is NULL\n");
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001455#endif
Daniel Veillard9ff88172002-03-11 09:15:32 +00001456 return (NULL);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001457 }
1458
1459 /*
1460 * Validate the encoding output buffer encoding
1461 */
Daniel Veillard9ff88172002-03-11 09:15:32 +00001462 if (buf->encoder != NULL) {
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001463 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001464 "xmlC14NNewCtx: output buffer encoder != NULL but C14N requires UTF8 output\n");
1465 return (NULL);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001466 }
1467
1468 /*
1469 * Validate the XML document encoding value, if provided.
1470 */
Daniel Veillard9ff88172002-03-11 09:15:32 +00001471 if (doc->charset != XML_CHAR_ENCODING_UTF8) {
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001472 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001473 "xmlC14NNewCtx: source document not in UTF8\n");
1474 return (NULL);
1475 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001476
1477 /*
1478 * Allocate a new xmlC14NCtxPtr and fill the fields.
1479 */
1480 ctx = (xmlC14NCtxPtr) xmlMalloc(sizeof(xmlC14NCtx));
1481 if (ctx == NULL) {
1482 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001483 "xmlC14NNewCtx: malloc failed\n");
1484 return (NULL);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001485 }
1486 memset(ctx, 0, sizeof(xmlC14NCtx));
1487
1488 /*
1489 * initialize C14N context
Daniel Veillard9ff88172002-03-11 09:15:32 +00001490 */
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001491 ctx->doc = doc;
1492 ctx->with_comments = with_comments;
Aleksey Sanin2c135a12002-08-01 06:31:50 +00001493 ctx->is_visible_callback = is_visible_callback;
1494 ctx->user_data = user_data;
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001495 ctx->buf = buf;
1496 ctx->parent_is_doc = 1;
1497 ctx->pos = XMLC14N_BEFORE_DOCUMENT_ELEMENT;
Aleksey Saninf8cb6dd2002-06-04 04:27:06 +00001498 ctx->ns_rendered = xmlC14NVisibleNsStackCreate();
1499
1500 if(ctx->ns_rendered == NULL) {
1501 xmlGenericError(xmlGenericErrorContext,
1502 "xmlC14NNewCtx: xmlC14NVisibleNsStackCreate failed\n");
1503 xmlC14NFreeCtx(ctx);
1504 return (NULL);
1505 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001506
1507 /*
1508 * Set "exclusive" flag, create a nodes set for namespaces
1509 * stack and remember list of incluseve prefixes
1510 */
Daniel Veillard9ff88172002-03-11 09:15:32 +00001511 if (exclusive) {
1512 ctx->exclusive = 1;
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001513 ctx->inclusive_ns_prefixes = inclusive_ns_prefixes;
1514 }
Daniel Veillard9ff88172002-03-11 09:15:32 +00001515 return (ctx);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001516}
1517
1518/**
Aleksey Sanin2c135a12002-08-01 06:31:50 +00001519 * xmlC14NExecute:
1520 * @doc: the XML document for canonization
1521 * @is_visible_callback:the function to use to determine is node visible
1522 * or not
1523 * @user_data: the first parameter for @is_visible_callback function
1524 * (in most cases, it is nodes set)
1525 * @exclusive: the exclusive flag (0 - non-exclusive canonicalization;
1526 * otherwise - exclusive canonicalization)
1527 * @inclusive_ns_prefixes: the list of inclusive namespace prefixes
1528 * ended with a NULL or NULL if there is no
1529 * inclusive namespaces (only for exclusive
1530 * canonicalization, ignored otherwise)
1531 * @with_comments: include comments in the result (!=0) or not (==0)
1532 * @buf: the output buffer to store canonical XML; this
1533 * buffer MUST have encoder==NULL because C14N requires
1534 * UTF-8 output
1535 *
1536 * Dumps the canonized image of given XML document into the provided buffer.
1537 * For details see "Canonical XML" (http://www.w3.org/TR/xml-c14n) or
1538 * "Exclusive XML Canonicalization" (http://www.w3.org/TR/xml-exc-c14n)
1539 *
1540 * Returns non-negative value on success or a negative value on fail
1541 */
1542int
1543xmlC14NExecute(xmlDocPtr doc, xmlC14NIsVisibleCallback is_visible_callback,
1544 void* user_data, int exclusive, xmlChar **inclusive_ns_prefixes,
1545 int with_comments, xmlOutputBufferPtr buf) {
1546
1547 xmlC14NCtxPtr ctx;
1548 int ret;
1549
1550 if ((buf == NULL) || (doc == NULL)) {
1551#ifdef DEBUG_C14N
1552 xmlGenericError(xmlGenericErrorContext,
1553 "xmlC14NExecute: null return buffer or doc pointer\n");
1554#endif
1555 return (-1);
1556 }
1557
1558 /*
1559 * Validate the encoding output buffer encoding
1560 */
1561 if (buf->encoder != NULL) {
1562 xmlGenericError(xmlGenericErrorContext,
1563 "xmlC14NExecute: output buffer encoder != NULL but C14N requires UTF8 output\n");
1564 return (-1);
1565 }
1566
1567 ctx = xmlC14NNewCtx(doc, is_visible_callback, user_data,
1568 exclusive, inclusive_ns_prefixes,
1569 with_comments, buf);
1570 if (ctx == NULL) {
1571 xmlGenericError(xmlGenericErrorContext,
1572 "xmlC14NExecute: unable to create C14N context\n");
1573 return (-1);
1574 }
1575
1576
1577
1578 /*
1579 * Root Node
1580 * The root node is the parent of the top-level document element. The
1581 * result of processing each of its child nodes that is in the node-set
1582 * in document order. The root node does not generate a byte order mark,
1583 * XML declaration, nor anything from within the document type
1584 * declaration.
1585 */
1586 if (doc->children != NULL) {
1587 ret = xmlC14NProcessNodeList(ctx, doc->children);
1588 if (ret < 0) {
1589#ifdef DEBUG_C14N
1590 xmlGenericError(xmlGenericErrorContext,
1591 "xmlC14NExecute: process childrens' list failed.\n");
1592#endif
1593 xmlC14NFreeCtx(ctx);
1594 return (-1);
1595 }
1596 }
1597
1598 /*
1599 * Flush buffer to get number of bytes written
1600 */
1601 ret = xmlOutputBufferFlush(buf);
1602 if (ret < 0) {
1603#ifdef DEBUG_C14N
1604 xmlGenericError(xmlGenericErrorContext,
1605 "xmlC14NExecute: buffer flush failed.\n");
1606#endif
1607 xmlC14NFreeCtx(ctx);
1608 return (-1);
1609 }
1610
1611 /*
1612 * Cleanup
1613 */
1614 xmlC14NFreeCtx(ctx);
1615 return (ret);
1616}
1617
1618/**
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001619 * xmlC14NDocSaveTo:
1620 * @doc: the XML document for canonization
1621 * @nodes: the nodes set to be included in the canonized image
1622 * or NULL if all document nodes should be included
1623 * @exclusive: the exclusive flag (0 - non-exclusive canonicalization;
1624 * otherwise - exclusive canonicalization)
Daniel Veillarddb1bdba2002-03-09 14:13:11 +00001625 * @inclusive_ns_prefixes: the list of inclusive namespace prefixes
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001626 * ended with a NULL or NULL if there is no
1627 * inclusive namespaces (only for exclusive
1628 * canonicalization, ignored otherwise)
1629 * @with_comments: include comments in the result (!=0) or not (==0)
1630 * @buf: the output buffer to store canonical XML; this
1631 * buffer MUST have encoder==NULL because C14N requires
1632 * UTF-8 output
1633 *
1634 * Dumps the canonized image of given XML document into the provided buffer.
1635 * For details see "Canonical XML" (http://www.w3.org/TR/xml-c14n) or
1636 * "Exclusive XML Canonicalization" (http://www.w3.org/TR/xml-exc-c14n)
1637 *
1638 * Returns non-negative value on success or a negative value on fail
1639 */
1640int
Daniel Veillard9ff88172002-03-11 09:15:32 +00001641xmlC14NDocSaveTo(xmlDocPtr doc, xmlNodeSetPtr nodes,
1642 int exclusive, xmlChar ** inclusive_ns_prefixes,
Aleksey Sanin2c135a12002-08-01 06:31:50 +00001643 int with_comments, xmlOutputBufferPtr buf) {
1644 return(xmlC14NExecute(doc,
1645 (xmlC14NIsVisibleCallback)xmlC14NIsNodeInNodeset,
1646 nodes,
1647 exclusive,
1648 inclusive_ns_prefixes,
1649 with_comments,
1650 buf));
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001651}
1652
Aleksey Sanin2c135a12002-08-01 06:31:50 +00001653
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001654/**
1655 * xmlC14NDocDumpMemory:
1656 * @doc: the XML document for canonization
1657 * @nodes: the nodes set to be included in the canonized image
1658 * or NULL if all document nodes should be included
1659 * @exclusive: the exclusive flag (0 - non-exclusive canonicalization;
1660 * otherwise - exclusive canonicalization)
Daniel Veillarddb1bdba2002-03-09 14:13:11 +00001661 * @inclusive_ns_prefixes: the list of inclusive namespace prefixes
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001662 * ended with a NULL or NULL if there is no
1663 * inclusive namespaces (only for exclusive
1664 * canonicalization, ignored otherwise)
1665 * @with_comments: include comments in the result (!=0) or not (==0)
1666 * @doc_txt_ptr: the memory pointer for allocated canonical XML text;
1667 * the caller of this functions is responsible for calling
1668 * xmlFree() to free allocated memory
1669 *
1670 * Dumps the canonized image of given XML document into memory.
1671 * For details see "Canonical XML" (http://www.w3.org/TR/xml-c14n) or
1672 * "Exclusive XML Canonicalization" (http://www.w3.org/TR/xml-exc-c14n)
1673 *
1674 * Returns the number of bytes written on success or a negative value on fail
1675 */
1676int
Daniel Veillard9ff88172002-03-11 09:15:32 +00001677xmlC14NDocDumpMemory(xmlDocPtr doc, xmlNodeSetPtr nodes,
1678 int exclusive, xmlChar ** inclusive_ns_prefixes,
1679 int with_comments, xmlChar ** doc_txt_ptr)
1680{
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001681 int ret;
1682 xmlOutputBufferPtr buf;
1683
1684 if (doc_txt_ptr == NULL) {
Daniel Veillard9ff88172002-03-11 09:15:32 +00001685#ifdef DEBUG_C14N
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001686 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001687 "xmlC14NDocDumpMemory: null return buffer pointer\n");
1688#endif
1689 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001690 }
1691
1692 *doc_txt_ptr = NULL;
Daniel Veillard9ff88172002-03-11 09:15:32 +00001693
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001694 /*
1695 * create memory buffer with UTF8 (default) encoding
1696 */
1697 buf = xmlAllocOutputBuffer(NULL);
Daniel Veillard9ff88172002-03-11 09:15:32 +00001698 if (buf == NULL) {
1699#ifdef DEBUG_C14N
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001700 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001701 "xmlC14NDocDumpMemory: failed to allocate output buffer.\n");
1702#endif
1703 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001704 }
1705
1706 /*
1707 * canonize document and write to buffer
1708 */
Daniel Veillard9ff88172002-03-11 09:15:32 +00001709 ret = xmlC14NDocSaveTo(doc, nodes, exclusive, inclusive_ns_prefixes,
1710 with_comments, buf);
1711 if (ret < 0) {
1712#ifdef DEBUG_C14N
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001713 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001714 "xmlC14NDocDumpMemory: xmlC14NDocSaveTo failed.\n");
1715#endif
1716 (void) xmlOutputBufferClose(buf);
1717 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001718 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001719
Daniel Veillard9ff88172002-03-11 09:15:32 +00001720 ret = buf->buffer->use;
1721 if (ret > 0) {
1722 *doc_txt_ptr = xmlStrndup(buf->buffer->content, ret);
1723 }
1724 (void) xmlOutputBufferClose(buf);
1725
1726 if ((*doc_txt_ptr == NULL) && (ret > 0)) {
1727#ifdef DEBUG_C14N
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001728 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001729 "xmlC14NDocDumpMemory: failed to allocate memory for document text representation\n");
1730#endif
1731 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001732 }
Daniel Veillard9ff88172002-03-11 09:15:32 +00001733 return (ret);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001734}
1735
1736/**
1737 * xmlC14NDocSave:
1738 * @doc: the XML document for canonization
1739 * @nodes: the nodes set to be included in the canonized image
1740 * or NULL if all document nodes should be included
1741 * @exclusive: the exclusive flag (0 - non-exclusive canonicalization;
1742 * otherwise - exclusive canonicalization)
Daniel Veillarddb1bdba2002-03-09 14:13:11 +00001743 * @inclusive_ns_prefixes: the list of inclusive namespace prefixes
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001744 * ended with a NULL or NULL if there is no
1745 * inclusive namespaces (only for exclusive
1746 * canonicalization, ignored otherwise)
1747 * @with_comments: include comments in the result (!=0) or not (==0)
1748 * @filename: the filename to store canonical XML image
1749 * @compression: the compression level (zlib requred):
1750 * -1 - libxml default,
1751 * 0 - uncompressed,
1752 * >0 - compression level
1753 *
1754 * Dumps the canonized image of given XML document into the file.
1755 * For details see "Canonical XML" (http://www.w3.org/TR/xml-c14n) or
1756 * "Exclusive XML Canonicalization" (http://www.w3.org/TR/xml-exc-c14n)
1757 *
1758 * Returns the number of bytes written success or a negative value on fail
1759 */
1760int
Daniel Veillard9ff88172002-03-11 09:15:32 +00001761xmlC14NDocSave(xmlDocPtr doc, xmlNodeSetPtr nodes,
1762 int exclusive, xmlChar ** inclusive_ns_prefixes,
1763 int with_comments, const char *filename, int compression)
1764{
1765 xmlOutputBufferPtr buf;
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001766 int ret;
1767
Daniel Veillard9ff88172002-03-11 09:15:32 +00001768 if (filename == NULL) {
1769#ifdef DEBUG_C14N
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001770 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001771 "xmlC14NDocSave: filename is NULL\n");
1772#endif
1773 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001774 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001775#ifdef HAVE_ZLIB_H
Daniel Veillard9ff88172002-03-11 09:15:32 +00001776 if (compression < 0)
1777 compression = xmlGetCompressMode();
1778#endif
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001779
1780 /*
1781 * save the content to a temp buffer, use default UTF8 encoding.
1782 */
1783 buf = xmlOutputBufferCreateFilename(filename, NULL, compression);
1784 if (buf == NULL) {
Daniel Veillard9ff88172002-03-11 09:15:32 +00001785#ifdef DEBUG_C14N
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001786 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001787 "xmlC14NDocSave: unable to create buffer for file=\"%s\" with compressin=%d\n",
1788 filename, compression);
1789#endif
1790 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001791 }
Daniel Veillard9ff88172002-03-11 09:15:32 +00001792
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001793 /*
1794 * canonize document and write to buffer
1795 */
Daniel Veillard9ff88172002-03-11 09:15:32 +00001796 ret = xmlC14NDocSaveTo(doc, nodes, exclusive, inclusive_ns_prefixes,
1797 with_comments, buf);
1798 if (ret < 0) {
1799#ifdef DEBUG_C14N
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001800 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001801 "xmlC14NDocSave: xmlC14NDocSaveTo failed.\n");
1802#endif
1803 (void) xmlOutputBufferClose(buf);
1804 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001805 }
Daniel Veillard9ff88172002-03-11 09:15:32 +00001806
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001807 /*
1808 * get the numbers of bytes written
1809 */
1810 ret = xmlOutputBufferClose(buf);
Daniel Veillard9ff88172002-03-11 09:15:32 +00001811 return (ret);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001812}
1813
1814
1815
1816/*
1817 * Macro used to grow the current buffer.
1818 */
1819#define growBufferReentrant() { \
1820 buffer_size *= 2; \
1821 buffer = (xmlChar *) \
1822 xmlRealloc(buffer, buffer_size * sizeof(xmlChar)); \
1823 if (buffer == NULL) { \
Daniel Veillard3487c8d2002-09-05 11:33:25 +00001824 xmlGenericError(xmlGenericErrorContext, "realloc failed"); \
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001825 return(NULL); \
1826 } \
1827}
1828
1829/**
1830 * xmlC11NNormalizeString:
1831 * @input: the input string
1832 * @mode: the normalization mode (attribute, comment, PI or text)
1833 *
1834 * Converts a string to a canonical (normalized) format. The code is stolen
1835 * from xmlEncodeEntitiesReentrant(). Added normalization of \x09, \x0a, \x0A
1836 * and the @mode parameter
1837 *
1838 * Returns a normalized string (caller is responsible for calling xmlFree())
1839 * or NULL if an error occurs
1840 */
1841static xmlChar *
Daniel Veillard9ff88172002-03-11 09:15:32 +00001842xmlC11NNormalizeString(const xmlChar * input,
1843 xmlC14NNormalizationMode mode)
1844{
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001845 const xmlChar *cur = input;
1846 xmlChar *buffer = NULL;
1847 xmlChar *out = NULL;
1848 int buffer_size = 0;
1849
Daniel Veillard9ff88172002-03-11 09:15:32 +00001850 if (input == NULL)
1851 return (NULL);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001852
1853 /*
1854 * allocate an translation buffer.
1855 */
1856 buffer_size = 1000;
Daniel Veillard3c908dc2003-04-19 00:07:51 +00001857 buffer = (xmlChar *) xmlMallocAtomic(buffer_size * sizeof(xmlChar));
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001858 if (buffer == NULL) {
Daniel Veillard3487c8d2002-09-05 11:33:25 +00001859 xmlGenericError(xmlGenericErrorContext, "malloc failed");
Daniel Veillard9ff88172002-03-11 09:15:32 +00001860 return (NULL);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001861 }
1862 out = buffer;
1863
1864 while (*cur != '\0') {
Daniel Veillard9ff88172002-03-11 09:15:32 +00001865 if ((out - buffer) > (buffer_size - 10)) {
1866 int indx = out - buffer;
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001867
Daniel Veillard9ff88172002-03-11 09:15:32 +00001868 growBufferReentrant();
1869 out = &buffer[indx];
1870 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001871
Daniel Veillard9ff88172002-03-11 09:15:32 +00001872 if ((*cur == '<') && ((mode == XMLC14N_NORMALIZE_ATTR) ||
1873 (mode == XMLC14N_NORMALIZE_TEXT))) {
1874 *out++ = '&';
1875 *out++ = 'l';
1876 *out++ = 't';
1877 *out++ = ';';
1878 } else if ((*cur == '>') && (mode == XMLC14N_NORMALIZE_TEXT)) {
1879 *out++ = '&';
1880 *out++ = 'g';
1881 *out++ = 't';
1882 *out++ = ';';
1883 } else if ((*cur == '&') && ((mode == XMLC14N_NORMALIZE_ATTR) ||
1884 (mode == XMLC14N_NORMALIZE_TEXT))) {
1885 *out++ = '&';
1886 *out++ = 'a';
1887 *out++ = 'm';
1888 *out++ = 'p';
1889 *out++ = ';';
1890 } else if ((*cur == '"') && (mode == XMLC14N_NORMALIZE_ATTR)) {
1891 *out++ = '&';
1892 *out++ = 'q';
1893 *out++ = 'u';
1894 *out++ = 'o';
1895 *out++ = 't';
1896 *out++ = ';';
1897 } else if ((*cur == '\x09') && (mode == XMLC14N_NORMALIZE_ATTR)) {
1898 *out++ = '&';
1899 *out++ = '#';
1900 *out++ = 'x';
1901 *out++ = '9';
1902 *out++ = ';';
1903 } else if ((*cur == '\x0A') && (mode == XMLC14N_NORMALIZE_ATTR)) {
1904 *out++ = '&';
1905 *out++ = '#';
1906 *out++ = 'x';
1907 *out++ = 'A';
1908 *out++ = ';';
1909 } else if ((*cur == '\x0D') && ((mode == XMLC14N_NORMALIZE_ATTR) ||
1910 (mode == XMLC14N_NORMALIZE_TEXT) ||
1911 (mode == XMLC14N_NORMALIZE_COMMENT) ||
1912 (mode == XMLC14N_NORMALIZE_PI))) {
1913 *out++ = '&';
1914 *out++ = '#';
1915 *out++ = 'x';
1916 *out++ = 'D';
1917 *out++ = ';';
1918 } else {
1919 /*
1920 * Works because on UTF-8, all extended sequences cannot
1921 * result in bytes in the ASCII range.
1922 */
1923 *out++ = *cur;
1924 }
1925 cur++;
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001926 }
1927 *out++ = 0;
Daniel Veillard9ff88172002-03-11 09:15:32 +00001928 return (buffer);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001929}
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001930#endif /* LIBXML_C14N_ENABLED */