blob: 687246ef56b77b1af830d3da5407a477004931f6 [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
Daniel Veillarda9cce9c2003-09-29 13:20:24 +000015#ifdef LIBXML_OUTPUT_ENABLED
Daniel Veillard044fc6b2002-03-04 17:09:44 +000016
17#ifdef HAVE_STDLIB_H
18#include <stdlib.h>
19#endif
20#include <string.h>
21
22#include <libxml/tree.h>
23#include <libxml/parser.h>
Daniel Veillard9ff88172002-03-11 09:15:32 +000024#include <libxml/uri.h>
Daniel Veillard044fc6b2002-03-04 17:09:44 +000025#include <libxml/xmlerror.h>
26#include <libxml/globals.h>
27#include <libxml/xpathInternals.h>
28#include <libxml/c14n.h>
29
30/************************************************************************
31 * *
32 * Some declaration better left private ATM *
33 * *
34 ************************************************************************/
35
Daniel Veillard9ff88172002-03-11 09:15:32 +000036typedef enum {
37 XMLC14N_BEFORE_DOCUMENT_ELEMENT = 0,
38 XMLC14N_INSIDE_DOCUMENT_ELEMENT = 1,
39 XMLC14N_AFTER_DOCUMENT_ELEMENT = 2
Daniel Veillard044fc6b2002-03-04 17:09:44 +000040} xmlC14NPosition;
41
Aleksey Saninf8cb6dd2002-06-04 04:27:06 +000042typedef struct _xmlC14NVisibleNsStack {
Aleksey Sanin2c135a12002-08-01 06:31:50 +000043 int nsCurEnd; /* number of nodes in the set */
44 int nsPrevStart; /* the begginning of the stack for previous visible node */
45 int nsPrevEnd; /* the end of the stack for previous visible node */
46 int nsMax; /* size of the array as allocated */
47 xmlNsPtr *nsTab; /* array of ns in no particular order */
48 xmlNodePtr *nodeTab;/* array of nodes in no particular order */
Aleksey Saninf8cb6dd2002-06-04 04:27:06 +000049} xmlC14NVisibleNsStack, *xmlC14NVisibleNsStackPtr;
Aleksey Sanindffd5c82002-05-31 04:24:13 +000050
Daniel Veillard044fc6b2002-03-04 17:09:44 +000051typedef struct _xmlC14NCtx {
52 /* input parameters */
Daniel Veillard9ff88172002-03-11 09:15:32 +000053 xmlDocPtr doc;
Aleksey Sanin2c135a12002-08-01 06:31:50 +000054 xmlC14NIsVisibleCallback is_visible_callback;
55 void* user_data;
Daniel Veillard9ff88172002-03-11 09:15:32 +000056 int with_comments;
57 xmlOutputBufferPtr buf;
Daniel Veillard044fc6b2002-03-04 17:09:44 +000058
59 /* position in the XML document */
Daniel Veillard9ff88172002-03-11 09:15:32 +000060 xmlC14NPosition pos;
61 int parent_is_doc;
Aleksey Saninf8cb6dd2002-06-04 04:27:06 +000062 xmlC14NVisibleNsStackPtr ns_rendered;
Daniel Veillard044fc6b2002-03-04 17:09:44 +000063
64 /* exclusive canonicalization */
Daniel Veillard9ff88172002-03-11 09:15:32 +000065 int exclusive;
Daniel Veillard9ff88172002-03-11 09:15:32 +000066 xmlChar **inclusive_ns_prefixes;
Daniel Veillardd96cce12003-10-10 12:30:37 +000067
68 /* error number */
69 int error;
Daniel Veillard044fc6b2002-03-04 17:09:44 +000070} xmlC14NCtx, *xmlC14NCtxPtr;
71
Aleksey Sanin2c135a12002-08-01 06:31:50 +000072static xmlC14NVisibleNsStackPtr xmlC14NVisibleNsStackCreate (void);
73static void xmlC14NVisibleNsStackDestroy (xmlC14NVisibleNsStackPtr cur);
74static void xmlC14NVisibleNsStackAdd (xmlC14NVisibleNsStackPtr cur,
75 xmlNsPtr ns,
76 xmlNodePtr node);
77static void xmlC14NVisibleNsStackSave (xmlC14NVisibleNsStackPtr cur,
78 xmlC14NVisibleNsStackPtr state);
79static void xmlC14NVisibleNsStackRestore (xmlC14NVisibleNsStackPtr cur,
80 xmlC14NVisibleNsStackPtr state);
81static void xmlC14NVisibleNsStackShift (xmlC14NVisibleNsStackPtr cur);
82static int xmlC14NVisibleNsStackFind (xmlC14NVisibleNsStackPtr cur,
83 xmlNsPtr ns);
84static int xmlExcC14NVisibleNsStackFind (xmlC14NVisibleNsStackPtr cur,
85 xmlNsPtr ns,
86 xmlC14NCtxPtr ctx);
87
88static int xmlC14NIsNodeInNodeset (xmlNodeSetPtr nodes,
89 xmlNodePtr node,
90 xmlNodePtr parent);
91
92
Daniel Veillard044fc6b2002-03-04 17:09:44 +000093
Daniel Veillard9ff88172002-03-11 09:15:32 +000094static int xmlC14NProcessNode(xmlC14NCtxPtr ctx, xmlNodePtr cur);
95static int xmlC14NProcessNodeList(xmlC14NCtxPtr ctx, xmlNodePtr cur);
Daniel Veillard044fc6b2002-03-04 17:09:44 +000096typedef enum {
Daniel Veillard9ff88172002-03-11 09:15:32 +000097 XMLC14N_NORMALIZE_ATTR = 0,
98 XMLC14N_NORMALIZE_COMMENT = 1,
99 XMLC14N_NORMALIZE_PI = 2,
100 XMLC14N_NORMALIZE_TEXT = 3
101} xmlC14NNormalizationMode;
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000102
Daniel Veillard9ff88172002-03-11 09:15:32 +0000103static xmlChar *xmlC11NNormalizeString(const xmlChar * input,
104 xmlC14NNormalizationMode mode);
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000105
106#define xmlC11NNormalizeAttr( a ) \
Daniel Veillard9ff88172002-03-11 09:15:32 +0000107 xmlC11NNormalizeString((a), XMLC14N_NORMALIZE_ATTR)
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000108#define xmlC11NNormalizeComment( a ) \
Daniel Veillard9ff88172002-03-11 09:15:32 +0000109 xmlC11NNormalizeString((a), XMLC14N_NORMALIZE_COMMENT)
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000110#define xmlC11NNormalizePI( a ) \
Daniel Veillard9ff88172002-03-11 09:15:32 +0000111 xmlC11NNormalizeString((a), XMLC14N_NORMALIZE_PI)
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000112#define xmlC11NNormalizeText( a ) \
Daniel Veillard9ff88172002-03-11 09:15:32 +0000113 xmlC11NNormalizeString((a), XMLC14N_NORMALIZE_TEXT)
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000114
Aleksey Sanin2c135a12002-08-01 06:31:50 +0000115#define xmlC14NIsVisible( ctx, node, parent ) \
116 (((ctx)->is_visible_callback != NULL) ? \
117 (ctx)->is_visible_callback((ctx)->user_data, \
118 (xmlNodePtr)(node), (xmlNodePtr)(parent)) : 1)
Daniel Veillardd96cce12003-10-10 12:30:37 +0000119
120/************************************************************************
121 * *
122 * Some factorized error routines *
123 * *
124 ************************************************************************/
125
126/**
127 * xmlC14NErrMemory:
128 * @extra: extra informations
129 *
130 * Handle a redefinition of attribute error
131 */
132static void
133xmlC14NErrMemory(const char *extra)
134{
Daniel Veillard659e71e2003-10-10 14:10:40 +0000135 __xmlRaiseError(NULL, NULL, NULL, NULL, NULL, XML_FROM_C14N,
Daniel Veillardd96cce12003-10-10 12:30:37 +0000136 XML_ERR_NO_MEMORY, XML_ERR_ERROR, NULL, 0, extra,
137 NULL, NULL, 0, 0,
138 "Memory allocation failed : %s\n", extra);
139}
140
141/**
142 * xmlC14NErr:
143 * @ctxt: a C14N evaluation context
144 * @node: the context node
145 * @error: the erorr code
146 * @msg: the message
147 * @extra: extra informations
148 *
149 * Handle a redefinition of attribute error
150 */
151static void
152xmlC14NErr(xmlC14NCtxPtr ctxt, xmlNodePtr node, int error,
153 const char * msg)
154{
155 if (ctxt != NULL)
156 ctxt->error = error;
Daniel Veillard659e71e2003-10-10 14:10:40 +0000157 __xmlRaiseError(NULL, NULL, NULL,
Daniel Veillardd96cce12003-10-10 12:30:37 +0000158 ctxt, node, XML_FROM_C14N, error,
159 XML_ERR_ERROR, NULL, 0,
160 NULL, NULL, NULL, 0, 0, msg);
161}
162
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000163/************************************************************************
164 * *
165 * The implementation internals *
166 * *
167 ************************************************************************/
Aleksey Sanindffd5c82002-05-31 04:24:13 +0000168#define XML_NAMESPACES_DEFAULT 16
169
Aleksey Sanin2c135a12002-08-01 06:31:50 +0000170static int
171xmlC14NIsNodeInNodeset(xmlNodeSetPtr nodes, xmlNodePtr node, xmlNodePtr parent) {
172 if((nodes != NULL) && (node != NULL)) {
173 if(node->type != XML_NAMESPACE_DECL) {
174 return(xmlXPathNodeSetContains(nodes, node));
175 } else {
176 xmlNs ns;
177
178 memcpy(&ns, node, sizeof(ns));
Aleksey Sanin6de6f972004-04-20 02:05:30 +0000179
180 /* this is a libxml hack! check xpath.c for details */
181 if((parent != NULL) && (parent->type == XML_ATTRIBUTE_NODE)) {
182 ns.next = (xmlNsPtr)parent->parent;
183 } else {
184 ns.next = (xmlNsPtr)parent;
185 }
Aleksey Sanin2c135a12002-08-01 06:31:50 +0000186
187 /*
188 * If the input is an XPath node-set, then the node-set must explicitly
189 * contain every node to be rendered to the canonical form.
190 */
191 return(xmlXPathNodeSetContains(nodes, (xmlNodePtr)&ns));
192 }
193 }
194 return(1);
195}
196
Aleksey Saninf8cb6dd2002-06-04 04:27:06 +0000197static xmlC14NVisibleNsStackPtr
198xmlC14NVisibleNsStackCreate(void) {
199 xmlC14NVisibleNsStackPtr ret;
Aleksey Sanindffd5c82002-05-31 04:24:13 +0000200
Aleksey Saninf8cb6dd2002-06-04 04:27:06 +0000201 ret = (xmlC14NVisibleNsStackPtr) xmlMalloc(sizeof(xmlC14NVisibleNsStack));
Aleksey Sanindffd5c82002-05-31 04:24:13 +0000202 if (ret == NULL) {
Daniel Veillardd96cce12003-10-10 12:30:37 +0000203 xmlC14NErrMemory("creating stack");
Aleksey Sanindffd5c82002-05-31 04:24:13 +0000204 return(NULL);
205 }
Aleksey Saninf8cb6dd2002-06-04 04:27:06 +0000206 memset(ret, 0 , (size_t) sizeof(xmlC14NVisibleNsStack));
Aleksey Sanindffd5c82002-05-31 04:24:13 +0000207 return(ret);
208}
209
210static void
Aleksey Saninf8cb6dd2002-06-04 04:27:06 +0000211xmlC14NVisibleNsStackDestroy(xmlC14NVisibleNsStackPtr cur) {
Aleksey Sanindffd5c82002-05-31 04:24:13 +0000212 if(cur == NULL) {
Aleksey Saninf8cb6dd2002-06-04 04:27:06 +0000213#ifdef DEBUG_C14N
214 xmlGenericError(xmlGenericErrorContext,
Aleksey Sanin2c135a12002-08-01 06:31:50 +0000215 "xmlC14NVisibleNsStackDestroy: cur is null.\n");
Aleksey Saninf8cb6dd2002-06-04 04:27:06 +0000216#endif
Aleksey Sanindffd5c82002-05-31 04:24:13 +0000217 return;
218 }
219 if(cur->nsTab != NULL) {
220 memset(cur->nsTab, 0, cur->nsMax * sizeof(xmlNsPtr));
221 xmlFree(cur->nsTab);
222 }
Aleksey Saninea4272a2002-08-02 23:50:03 +0000223 if(cur->nodeTab != NULL) {
224 memset(cur->nodeTab, 0, cur->nsMax * sizeof(xmlNodePtr));
225 xmlFree(cur->nodeTab);
226 }
Aleksey Saninf8cb6dd2002-06-04 04:27:06 +0000227 memset(cur, 0, sizeof(xmlC14NVisibleNsStack));
Aleksey Sanindffd5c82002-05-31 04:24:13 +0000228 xmlFree(cur);
229
230}
231
Aleksey Saninf8cb6dd2002-06-04 04:27:06 +0000232static void
Aleksey Sanin2c135a12002-08-01 06:31:50 +0000233xmlC14NVisibleNsStackAdd(xmlC14NVisibleNsStackPtr cur, xmlNsPtr ns, xmlNodePtr node) {
234 if((cur == NULL) ||
235 ((cur->nsTab == NULL) && (cur->nodeTab != NULL)) ||
236 ((cur->nsTab != NULL) && (cur->nodeTab == NULL))) {
Aleksey Saninf8cb6dd2002-06-04 04:27:06 +0000237#ifdef DEBUG_C14N
238 xmlGenericError(xmlGenericErrorContext,
239 "xmlC14NVisibleNsStackAdd: cur is null.\n");
240#endif
Aleksey Sanindffd5c82002-05-31 04:24:13 +0000241 return;
242 }
243
Aleksey Sanin2c135a12002-08-01 06:31:50 +0000244 if ((cur->nsTab == NULL) && (cur->nodeTab == NULL)) {
Aleksey Sanindffd5c82002-05-31 04:24:13 +0000245 cur->nsTab = (xmlNsPtr*) xmlMalloc(XML_NAMESPACES_DEFAULT * sizeof(xmlNsPtr));
Aleksey Sanin2c135a12002-08-01 06:31:50 +0000246 cur->nodeTab = (xmlNodePtr*) xmlMalloc(XML_NAMESPACES_DEFAULT * sizeof(xmlNodePtr));
247 if ((cur->nsTab == NULL) || (cur->nodeTab == NULL)) {
Daniel Veillardd96cce12003-10-10 12:30:37 +0000248 xmlC14NErrMemory("adding node to stack");
Aleksey Sanindffd5c82002-05-31 04:24:13 +0000249 return;
250 }
251 memset(cur->nsTab, 0 , XML_NAMESPACES_DEFAULT * sizeof(xmlNsPtr));
Aleksey Sanin2c135a12002-08-01 06:31:50 +0000252 memset(cur->nodeTab, 0 , XML_NAMESPACES_DEFAULT * sizeof(xmlNodePtr));
Aleksey Sanindffd5c82002-05-31 04:24:13 +0000253 cur->nsMax = XML_NAMESPACES_DEFAULT;
Aleksey Saninf8cb6dd2002-06-04 04:27:06 +0000254 } else if(cur->nsMax == cur->nsCurEnd) {
Aleksey Sanin2c135a12002-08-01 06:31:50 +0000255 void *tmp;
Aleksey Sanindffd5c82002-05-31 04:24:13 +0000256 int tmpSize;
257
258 tmpSize = 2 * cur->nsMax;
Aleksey Sanin2c135a12002-08-01 06:31:50 +0000259 tmp = xmlRealloc(cur->nsTab, tmpSize * sizeof(xmlNsPtr));
Aleksey Sanindffd5c82002-05-31 04:24:13 +0000260 if (tmp == NULL) {
Daniel Veillardd96cce12003-10-10 12:30:37 +0000261 xmlC14NErrMemory("adding node to stack");
Aleksey Sanindffd5c82002-05-31 04:24:13 +0000262 return;
263 }
Aleksey Sanin2c135a12002-08-01 06:31:50 +0000264 cur->nsTab = (xmlNsPtr*)tmp;
265
266 tmp = xmlRealloc(cur->nodeTab, tmpSize * sizeof(xmlNodePtr));
267 if (tmp == NULL) {
Daniel Veillardd96cce12003-10-10 12:30:37 +0000268 xmlC14NErrMemory("adding node to stack");
Aleksey Sanin2c135a12002-08-01 06:31:50 +0000269 return;
270 }
271 cur->nodeTab = (xmlNodePtr*)tmp;
272
Aleksey Sanindffd5c82002-05-31 04:24:13 +0000273 cur->nsMax = tmpSize;
274 }
Aleksey Sanin2c135a12002-08-01 06:31:50 +0000275 cur->nsTab[cur->nsCurEnd] = ns;
276 cur->nodeTab[cur->nsCurEnd] = node;
277
278 ++cur->nsCurEnd;
Aleksey Saninf8cb6dd2002-06-04 04:27:06 +0000279}
280
281static void
282xmlC14NVisibleNsStackSave(xmlC14NVisibleNsStackPtr cur, xmlC14NVisibleNsStackPtr state) {
283 if((cur == NULL) || (state == NULL)) {
284#ifdef DEBUG_C14N
285 xmlGenericError(xmlGenericErrorContext,
286 "xmlC14NVisibleNsStackSave: cur or state is null.\n");
287#endif
288 return;
289 }
290
291 state->nsCurEnd = cur->nsCurEnd;
292 state->nsPrevStart = cur->nsPrevStart;
293 state->nsPrevEnd = cur->nsPrevEnd;
294}
295
296static void
297xmlC14NVisibleNsStackRestore(xmlC14NVisibleNsStackPtr cur, xmlC14NVisibleNsStackPtr state) {
298 if((cur == NULL) || (state == NULL)) {
299#ifdef DEBUG_C14N
300 xmlGenericError(xmlGenericErrorContext,
301 "xmlC14NVisibleNsStackRestore: cur or state is null.\n");
302#endif
303 return;
304 }
305 cur->nsCurEnd = state->nsCurEnd;
306 cur->nsPrevStart = state->nsPrevStart;
307 cur->nsPrevEnd = state->nsPrevEnd;
308}
309
310static void
311xmlC14NVisibleNsStackShift(xmlC14NVisibleNsStackPtr cur) {
312 if(cur == NULL) {
313#ifdef DEBUG_C14N
314 xmlGenericError(xmlGenericErrorContext,
315 "xmlC14NVisibleNsStackRestore: cur is null.\n");
316#endif
317 return;
318 }
319 cur->nsPrevStart = cur->nsPrevEnd;
320 cur->nsPrevEnd = cur->nsCurEnd;
321}
322
323static int
324xmlC14NStrEqual(const xmlChar *str1, const xmlChar *str2) {
325 if (str1 == str2) return(1);
326 if (str1 == NULL) return((*str2) == '\0');
327 if (str2 == NULL) return((*str1) == '\0');
328 do {
329 if (*str1++ != *str2) return(0);
330 } while (*str2++);
331 return(1);
332}
333
334/**
335 * xmlC14NVisibleNsStackFind:
Daniel Veillard01c13b52002-12-10 15:19:08 +0000336 * @ctx: the C14N context
337 * @ns: the namespace to check
Aleksey Saninf8cb6dd2002-06-04 04:27:06 +0000338 *
339 * Checks whether the given namespace was already rendered or not
340 *
341 * Returns 1 if we already wrote this namespace or 0 otherwise
342 */
343static int
Aleksey Sanin2c135a12002-08-01 06:31:50 +0000344xmlC14NVisibleNsStackFind(xmlC14NVisibleNsStackPtr cur, xmlNsPtr ns)
Aleksey Saninf8cb6dd2002-06-04 04:27:06 +0000345{
346 int i;
347 const xmlChar *prefix;
348 const xmlChar *href;
Aleksey Sanin2c135a12002-08-01 06:31:50 +0000349 int has_empty_ns;
Aleksey Saninf8cb6dd2002-06-04 04:27:06 +0000350
351 if(cur == NULL) {
352#ifdef DEBUG_C14N
353 xmlGenericError(xmlGenericErrorContext,
354 "xmlC14NVisibleNsStackFind: cur is null.\n");
355#endif
356 return (0);
357 }
358
359 /*
360 * if the default namespace xmlns="" is not defined yet then
361 * we do not want to print it out
362 */
363 prefix = ((ns == NULL) || (ns->prefix == NULL)) ? BAD_CAST "" : ns->prefix;
364 href = ((ns == NULL) || (ns->href == NULL)) ? BAD_CAST "" : ns->href;
Aleksey Sanin2c135a12002-08-01 06:31:50 +0000365 has_empty_ns = (xmlC14NStrEqual(prefix, NULL) && xmlC14NStrEqual(href, NULL));
366
Aleksey Saninf8cb6dd2002-06-04 04:27:06 +0000367 if (cur->nsTab != NULL) {
Aleksey Sanin2c135a12002-08-01 06:31:50 +0000368 int start = (has_empty_ns) ? 0 : cur->nsPrevStart;
Aleksey Saninf8cb6dd2002-06-04 04:27:06 +0000369 for (i = cur->nsCurEnd - 1; i >= start; --i) {
370 xmlNsPtr ns1 = cur->nsTab[i];
371
372 if(xmlC14NStrEqual(prefix, (ns1 != NULL) ? ns1->prefix : NULL)) {
373 return(xmlC14NStrEqual(href, (ns1 != NULL) ? ns1->href : NULL));
374 }
375 }
376 }
Aleksey Sanin2c135a12002-08-01 06:31:50 +0000377 return(has_empty_ns);
Aleksey Sanindffd5c82002-05-31 04:24:13 +0000378}
379
Aleksey Sanin2c135a12002-08-01 06:31:50 +0000380static int
381xmlExcC14NVisibleNsStackFind(xmlC14NVisibleNsStackPtr cur, xmlNsPtr ns, xmlC14NCtxPtr ctx) {
382 int i;
383 const xmlChar *prefix;
384 const xmlChar *href;
385 int has_empty_ns;
386
387 if(cur == NULL) {
388#ifdef DEBUG_C14N
389 xmlGenericError(xmlGenericErrorContext,
390 "xmlExcC14NVisibleNsStackFind: cur is null.\n");
391#endif
Daniel Veillard9ff88172002-03-11 09:15:32 +0000392 return (0);
393 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000394
Aleksey Sanin2c135a12002-08-01 06:31:50 +0000395 /*
396 * if the default namespace xmlns="" is not defined yet then
397 * we do not want to print it out
Aleksey Saninf8cb6dd2002-06-04 04:27:06 +0000398 */
Aleksey Sanin2c135a12002-08-01 06:31:50 +0000399 prefix = ((ns == NULL) || (ns->prefix == NULL)) ? BAD_CAST "" : ns->prefix;
400 href = ((ns == NULL) || (ns->href == NULL)) ? BAD_CAST "" : ns->href;
401 has_empty_ns = (xmlC14NStrEqual(prefix, NULL) && xmlC14NStrEqual(href, NULL));
Aleksey Saninf8cb6dd2002-06-04 04:27:06 +0000402
Aleksey Sanin2c135a12002-08-01 06:31:50 +0000403 if (cur->nsTab != NULL) {
404 int start = 0;
405 for (i = cur->nsCurEnd - 1; i >= start; --i) {
406 xmlNsPtr ns1 = cur->nsTab[i];
407
408 if(xmlC14NStrEqual(prefix, (ns1 != NULL) ? ns1->prefix : NULL)) {
409 if(xmlC14NStrEqual(href, (ns1 != NULL) ? ns1->href : NULL)) {
410 return(xmlC14NIsVisible(ctx, ns1, cur->nodeTab[i]));
411 } else {
412 return(0);
413 }
414 }
415 }
416 }
417 return(has_empty_ns);
Aleksey Saninf8cb6dd2002-06-04 04:27:06 +0000418}
419
Aleksey Sanin2c135a12002-08-01 06:31:50 +0000420
421
422
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000423/**
424 * xmlC14NIsXmlNs:
425 * @ns: the namespace to check
426 *
427 * Checks whether the given namespace is a default "xml:" namespace
428 * with href="http://www.w3.org/XML/1998/namespace"
429 *
430 * Returns 1 if the node is default or 0 otherwise
431 */
Daniel Veillard9ff88172002-03-11 09:15:32 +0000432
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000433/* todo: make it a define? */
434static int
Daniel Veillard9ff88172002-03-11 09:15:32 +0000435xmlC14NIsXmlNs(xmlNsPtr ns)
436{
437 return ((ns != NULL) &&
438 (xmlStrEqual(ns->prefix, BAD_CAST "xml")) &&
439 (xmlStrEqual(ns->href,
440 BAD_CAST
441 "http://www.w3.org/XML/1998/namespace")));
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000442}
443
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000444
445/**
Aleksey Saninf8cb6dd2002-06-04 04:27:06 +0000446 * xmlC14NNsCompare:
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000447 * @ns1: the pointer to first namespace
448 * @ns2: the pointer to second namespace
449 *
450 * Compares the namespaces by names (prefixes).
451 *
452 * Returns -1 if ns1 < ns2, 0 if ns1 == ns2 or 1 if ns1 > ns2.
453 */
Daniel Veillard9ff88172002-03-11 09:15:32 +0000454static int
Aleksey Saninf8cb6dd2002-06-04 04:27:06 +0000455xmlC14NNsCompare(xmlNsPtr ns1, xmlNsPtr ns2)
Daniel Veillard9ff88172002-03-11 09:15:32 +0000456{
457 if (ns1 == ns2)
458 return (0);
459 if (ns1 == NULL)
460 return (-1);
461 if (ns2 == NULL)
462 return (1);
463
464 return (xmlStrcmp(ns1->prefix, ns2->prefix));
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000465}
466
467
468/**
469 * xmlC14NPrintNamespaces:
470 * @ns: the pointer to namespace
471 * @ctx: the C14N context
472 *
473 * Prints the given namespace to the output buffer from C14N context.
474 *
475 * Returns 1 on success or 0 on fail.
476 */
477static int
Daniel Veillard9ff88172002-03-11 09:15:32 +0000478xmlC14NPrintNamespaces(const xmlNsPtr ns, xmlC14NCtxPtr ctx)
479{
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000480
Daniel Veillard9ff88172002-03-11 09:15:32 +0000481 if ((ns == NULL) || (ctx == NULL)) {
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000482#ifdef DEBUG_C14N
483 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +0000484 "xmlC14NPrintNamespace: namespace or context pointer is null\n");
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000485#endif
Daniel Veillard9ff88172002-03-11 09:15:32 +0000486 return 0;
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000487 }
488
Daniel Veillard9ff88172002-03-11 09:15:32 +0000489 if (ns->prefix != NULL) {
490 xmlOutputBufferWriteString(ctx->buf, " xmlns:");
491 xmlOutputBufferWriteString(ctx->buf, (const char *) ns->prefix);
492 xmlOutputBufferWriteString(ctx->buf, "=\"");
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000493 } else {
Daniel Veillard9ff88172002-03-11 09:15:32 +0000494 xmlOutputBufferWriteString(ctx->buf, " xmlns=\"");
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000495 }
Aleksey Saninf8cb6dd2002-06-04 04:27:06 +0000496 if(ns->href != NULL) {
497 xmlOutputBufferWriteString(ctx->buf, (const char *) ns->href);
498 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000499 xmlOutputBufferWriteString(ctx->buf, "\"");
Daniel Veillard9ff88172002-03-11 09:15:32 +0000500 return (1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000501}
502
503/**
504 * xmlC14NProcessNamespacesAxis:
505 * @ctx: the C14N context
506 * @node: the current node
507 *
508 * Prints out canonical namespace axis of the current node to the
509 * buffer from C14N context as follows
510 *
511 * Canonical XML v 1.0 (http://www.w3.org/TR/xml-c14n)
512 *
513 * Namespace Axis
514 * Consider a list L containing only namespace nodes in the
515 * axis and in the node-set in lexicographic order (ascending). To begin
516 * processing L, if the first node is not the default namespace node (a node
517 * with no namespace URI and no local name), then generate a space followed
518 * by xmlns="" if and only if the following conditions are met:
519 * - the element E that owns the axis is in the node-set
520 * - The nearest ancestor element of E in the node-set has a default
521 * namespace node in the node-set (default namespace nodes always
522 * have non-empty values in XPath)
523 * The latter condition eliminates unnecessary occurrences of xmlns="" in
524 * the canonical form since an element only receives an xmlns="" if its
525 * default namespace is empty and if it has an immediate parent in the
526 * canonical form that has a non-empty default namespace. To finish
527 * processing L, simply process every namespace node in L, except omit
528 * namespace node with local name xml, which defines the xml prefix,
529 * if its string value is http://www.w3.org/XML/1998/namespace.
530 *
531 * Exclusive XML Canonicalization v 1.0 (http://www.w3.org/TR/xml-exc-c14n)
532 * Canonical XML applied to a document subset requires the search of the
533 * ancestor nodes of each orphan element node for attributes in the xml
534 * namespace, such as xml:lang and xml:space. These are copied into the
535 * element node except if a declaration of the same attribute is already
536 * in the attribute axis of the element (whether or not it is included in
537 * the document subset). This search and copying are omitted from the
538 * Exclusive XML Canonicalization method.
539 *
540 * Returns 0 on success or -1 on fail.
541 */
542static int
Aleksey Saninf8cb6dd2002-06-04 04:27:06 +0000543xmlC14NProcessNamespacesAxis(xmlC14NCtxPtr ctx, xmlNodePtr cur, int visible)
Daniel Veillard9ff88172002-03-11 09:15:32 +0000544{
Aleksey Sanin2c135a12002-08-01 06:31:50 +0000545 xmlNodePtr n;
546 xmlNsPtr ns, tmp;
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000547 xmlListPtr list;
Aleksey Sanin2c135a12002-08-01 06:31:50 +0000548 int already_rendered;
549 int has_empty_ns = 0;
Daniel Veillard5c396542002-03-15 07:57:50 +0000550
Daniel Veillard9ff88172002-03-11 09:15:32 +0000551 if ((ctx == NULL) || (cur == NULL) || (cur->type != XML_ELEMENT_NODE)) {
552#ifdef DEBUG_C14N
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000553 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +0000554 "xmlC14NProcessNamespacesAxis: Null context or node pointer or type != XML_ELEMENT_NODE.\n");
555#endif
556 return (-1);
557 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000558
559 /*
560 * Create a sorted list to store element namespaces
561 */
Aleksey Sanin2c135a12002-08-01 06:31:50 +0000562 list = xmlListCreate(NULL, (xmlListDataCompare) xmlC14NNsCompare);
Daniel Veillard9ff88172002-03-11 09:15:32 +0000563 if (list == NULL) {
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000564#ifdef DEBUG_C14N
565 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +0000566 "xmlC14NProcessNamespacesAxis: list creation failed\n");
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000567#endif
Daniel Veillard9ff88172002-03-11 09:15:32 +0000568 return (-1);
569 }
570
Aleksey Sanin2c135a12002-08-01 06:31:50 +0000571 /* check all namespaces */
572 for(n = cur; n != NULL; n = n->parent) {
573 for(ns = n->nsDef; ns != NULL; ns = ns->next) {
574 tmp = xmlSearchNs(cur->doc, cur, ns->prefix);
575
576 if((tmp == ns) && !xmlC14NIsXmlNs(ns) && xmlC14NIsVisible(ctx, ns, cur)) {
577 already_rendered = xmlC14NVisibleNsStackFind(ctx->ns_rendered, ns);
578 if(visible) {
579 xmlC14NVisibleNsStackAdd(ctx->ns_rendered, ns, cur);
580 }
581 if(!already_rendered) {
Aleksey Saninc57f9c12002-05-31 19:14:57 +0000582 xmlListInsert(list, ns);
583 }
Aleksey Sanin2c135a12002-08-01 06:31:50 +0000584 if(xmlStrlen(ns->prefix) == 0) {
585 has_empty_ns = 1;
586 }
Aleksey Saninc57f9c12002-05-31 19:14:57 +0000587 }
Aleksey Sanindffd5c82002-05-31 04:24:13 +0000588 }
Aleksey Sanin2c135a12002-08-01 06:31:50 +0000589 }
Aleksey Sanindffd5c82002-05-31 04:24:13 +0000590
Aleksey Sanin2c135a12002-08-01 06:31:50 +0000591 /**
592 * if the first node is not the default namespace node (a node with no
593 * namespace URI and no local name), then generate a space followed by
594 * xmlns="" if and only if the following conditions are met:
595 * - the element E that owns the axis is in the node-set
596 * - the nearest ancestor element of E in the node-set has a default
597 * namespace node in the node-set (default namespace nodes always
598 * have non-empty values in XPath)
599 */
600 if(visible && !has_empty_ns) {
601 static xmlNs ns_default;
602
603 memset(&ns_default, 0, sizeof(ns_default));
604 if(!xmlC14NVisibleNsStackFind(ctx->ns_rendered, &ns_default)) {
605 xmlC14NPrintNamespaces(&ns_default, ctx);
Aleksey Saninf8cb6dd2002-06-04 04:27:06 +0000606 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000607 }
Aleksey Saninf8cb6dd2002-06-04 04:27:06 +0000608
609
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000610 /*
611 * print out all elements from list
612 */
Aleksey Saninf8cb6dd2002-06-04 04:27:06 +0000613 xmlListWalk(list, (xmlListWalker) xmlC14NPrintNamespaces, (const void *) ctx);
Daniel Veillard9ff88172002-03-11 09:15:32 +0000614
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000615 /*
616 * Cleanup
617 */
618 xmlListDelete(list);
Daniel Veillard9ff88172002-03-11 09:15:32 +0000619 return (0);
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000620}
621
Aleksey Sanindffd5c82002-05-31 04:24:13 +0000622
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000623/**
624 * xmlExcC14NProcessNamespacesAxis:
625 * @ctx: the C14N context
626 * @node: the current node
627 *
628 * Prints out exclusive canonical namespace axis of the current node to the
629 * buffer from C14N context as follows
630 *
631 * Exclusive XML Canonicalization
632 * http://www.w3.org/TR/xml-exc-c14n
633 *
634 * If the element node is in the XPath subset then output the node in
635 * accordance with Canonical XML except for namespace nodes which are
636 * rendered as follows:
637 *
638 * 1. Render each namespace node iff:
639 * * it is visibly utilized by the immediate parent element or one of
640 * its attributes, or is present in InclusiveNamespaces PrefixList, and
641 * * its prefix and value do not appear in ns_rendered. ns_rendered is
642 * obtained by popping the state stack in order to obtain a list of
643 * prefixes and their values which have already been rendered by
644 * an output ancestor of the namespace node's parent element.
645 * 2. Append the rendered namespace node to the list ns_rendered of namespace
646 * nodes rendered by output ancestors. Push ns_rendered on state stack and
647 * recurse.
648 * 3. After the recursion returns, pop thestate stack.
649 *
650 *
651 * Returns 0 on success or -1 on fail.
652 */
653static int
Aleksey Saninf8cb6dd2002-06-04 04:27:06 +0000654xmlExcC14NProcessNamespacesAxis(xmlC14NCtxPtr ctx, xmlNodePtr cur, int visible)
Daniel Veillard9ff88172002-03-11 09:15:32 +0000655{
Aleksey Sanin2c135a12002-08-01 06:31:50 +0000656 xmlNsPtr ns;
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000657 xmlListPtr list;
658 xmlAttrPtr attr;
Aleksey Sanin2c135a12002-08-01 06:31:50 +0000659 int already_rendered;
660 int has_empty_ns = 0;
661 int has_visibly_utilized_empty_ns = 0;
662 int has_empty_ns_in_inclusive_list = 0;
663
Daniel Veillard9ff88172002-03-11 09:15:32 +0000664 if ((ctx == NULL) || (cur == NULL) || (cur->type != XML_ELEMENT_NODE)) {
665#ifdef DEBUG_C14N
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000666 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +0000667 "xmlExcC14NProcessNamespacesAxis: Null context or node pointer or type != XML_ELEMENT_NODE.\n");
668#endif
669 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000670 }
Daniel Veillard9ff88172002-03-11 09:15:32 +0000671
Aleksey Saninf8cb6dd2002-06-04 04:27:06 +0000672 if(!ctx->exclusive) {
Daniel Veillard9ff88172002-03-11 09:15:32 +0000673#ifdef DEBUG_C14N
674 xmlGenericError(xmlGenericErrorContext,
675 "xmlExcC14NProcessNamespacesAxis: called for non-exclusive canonization or rendered stack is NULL.\n");
676#endif
677 return (-1);
678
679 }
680
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000681 /*
682 * Create a sorted list to store element namespaces
683 */
Aleksey Saninf8cb6dd2002-06-04 04:27:06 +0000684 list = xmlListCreate(NULL, (xmlListDataCompare) xmlC14NNsCompare);
Daniel Veillard9ff88172002-03-11 09:15:32 +0000685 if (list == NULL) {
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000686#ifdef DEBUG_C14N
687 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +0000688 "xmlExcC14NProcessNamespacesAxis: list creation failed\n");
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000689#endif
Daniel Veillard9ff88172002-03-11 09:15:32 +0000690 return (-1);
691 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000692
Aleksey Sanin2c135a12002-08-01 06:31:50 +0000693 /*
694 * process inclusive namespaces:
695 * All namespace nodes appearing on inclusive ns list are
696 * handled as provided in Canonical XML
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000697 */
Aleksey Sanin2c135a12002-08-01 06:31:50 +0000698 if(ctx->inclusive_ns_prefixes != NULL) {
699 xmlChar *prefix;
700 int i;
701
702 for (i = 0; ctx->inclusive_ns_prefixes[i] != NULL; ++i) {
703 prefix = ctx->inclusive_ns_prefixes[i];
704 /*
705 * Special values for namespace with empty prefix
706 */
707 if (xmlStrEqual(prefix, BAD_CAST "#default")
708 || xmlStrEqual(prefix, BAD_CAST "")) {
709 prefix = NULL;
710 has_empty_ns_in_inclusive_list = 1;
711 }
712
713 ns = xmlSearchNs(cur->doc, cur, prefix);
714 if((ns != NULL) && !xmlC14NIsXmlNs(ns) && xmlC14NIsVisible(ctx, ns, cur)) {
715 already_rendered = xmlC14NVisibleNsStackFind(ctx->ns_rendered, ns);
716 if(visible) {
717 xmlC14NVisibleNsStackAdd(ctx->ns_rendered, ns, cur);
718 }
719 if(!already_rendered) {
720 xmlListInsert(list, ns);
721 }
722 if(xmlStrlen(ns->prefix) == 0) {
723 has_empty_ns = 1;
724 }
Aleksey Saninf8cb6dd2002-06-04 04:27:06 +0000725 }
Aleksey Saninc57f9c12002-05-31 19:14:57 +0000726 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000727 }
Aleksey Saninf8cb6dd2002-06-04 04:27:06 +0000728
Aleksey Sanin2c135a12002-08-01 06:31:50 +0000729 /* add node namespace */
730 if(cur->ns != NULL) {
731 ns = cur->ns;
732 } else {
733 ns = xmlSearchNs(cur->doc, cur, NULL);
734 has_visibly_utilized_empty_ns = 1;
735 }
736 if((ns != NULL) && !xmlC14NIsXmlNs(ns)) {
737 if(visible && xmlC14NIsVisible(ctx, ns, cur)) {
738 if(!xmlExcC14NVisibleNsStackFind(ctx->ns_rendered, ns, ctx)) {
739 xmlListInsert(list, ns);
740 }
741 }
742 if(visible) {
743 xmlC14NVisibleNsStackAdd(ctx->ns_rendered, ns, cur);
744 }
745 if(xmlStrlen(ns->prefix) == 0) {
746 has_empty_ns = 1;
747 }
748 }
749
750
751 /* add attributes */
752 for(attr = cur->properties; attr != NULL; attr = attr->next) {
Daniel Veillard9ff88172002-03-11 09:15:32 +0000753 /*
Daniel Veillard2d347fa2002-03-17 10:34:11 +0000754 * we need to check that attribute is visible and has non
755 * default namespace (XML Namespaces: "default namespaces
Aleksey Sanin2c135a12002-08-01 06:31:50 +0000756 * do not apply directly to attributes")
Daniel Veillard9ff88172002-03-11 09:15:32 +0000757 */
Aleksey Sanin2c135a12002-08-01 06:31:50 +0000758 if((attr->ns != NULL) && xmlC14NIsVisible(ctx, attr, cur)) {
759 already_rendered = xmlExcC14NVisibleNsStackFind(ctx->ns_rendered, attr->ns, ctx);
Aleksey Sanin64453bc2004-05-25 17:39:48 +0000760 xmlC14NVisibleNsStackAdd(ctx->ns_rendered, attr->ns, cur);
Aleksey Sanin2c135a12002-08-01 06:31:50 +0000761 if(!already_rendered && visible) {
762 xmlListInsert(list, attr->ns);
763 }
764 if(xmlStrlen(attr->ns->prefix) == 0) {
765 has_empty_ns = 1;
766 }
767 } else if(attr->ns == NULL) {
768 has_visibly_utilized_empty_ns = 1;
769 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000770 }
Daniel Veillard9ff88172002-03-11 09:15:32 +0000771
Aleksey Sanin2c135a12002-08-01 06:31:50 +0000772 /*
773 * Process xmlns=""
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000774 */
Aleksey Sanin2c135a12002-08-01 06:31:50 +0000775 if(visible && has_visibly_utilized_empty_ns &&
776 !has_empty_ns && !has_empty_ns_in_inclusive_list) {
777 static xmlNs ns_default;
Daniel Veillard9ff88172002-03-11 09:15:32 +0000778
Aleksey Sanin2c135a12002-08-01 06:31:50 +0000779 memset(&ns_default, 0, sizeof(ns_default));
780
781 already_rendered = xmlExcC14NVisibleNsStackFind(ctx->ns_rendered, &ns_default, ctx);
782 if(!already_rendered) {
783 xmlC14NPrintNamespaces(&ns_default, ctx);
784 }
785 } else if(visible && !has_empty_ns && has_empty_ns_in_inclusive_list) {
786 static xmlNs ns_default;
787
788 memset(&ns_default, 0, sizeof(ns_default));
789 if(!xmlC14NVisibleNsStackFind(ctx->ns_rendered, &ns_default)) {
790 xmlC14NPrintNamespaces(&ns_default, ctx);
791 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000792 }
Daniel Veillard9ff88172002-03-11 09:15:32 +0000793
Aleksey Sanin2c135a12002-08-01 06:31:50 +0000794
795
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000796 /*
797 * print out all elements from list
798 */
Aleksey Saninf8cb6dd2002-06-04 04:27:06 +0000799 xmlListWalk(list, (xmlListWalker) xmlC14NPrintNamespaces, (const void *) ctx);
Daniel Veillard9ff88172002-03-11 09:15:32 +0000800
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000801 /*
802 * Cleanup
803 */
804 xmlListDelete(list);
Daniel Veillard9ff88172002-03-11 09:15:32 +0000805 return (0);
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000806}
807
808
809/**
810 * xmlC14NAttrsCompare:
Aleksey Saninf8cb6dd2002-06-04 04:27:06 +0000811 * @attr1: the pointer tls o first attr
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000812 * @attr2: the pointer to second attr
813 *
814 * Prints the given attribute to the output buffer from C14N context.
815 *
816 * Returns -1 if attr1 < attr2, 0 if attr1 == attr2 or 1 if attr1 > attr2.
817 */
818static int
Daniel Veillard9ff88172002-03-11 09:15:32 +0000819xmlC14NAttrsCompare(xmlAttrPtr attr1, xmlAttrPtr attr2)
820{
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000821 int ret = 0;
822
823 /*
824 * Simple cases
825 */
Daniel Veillard9ff88172002-03-11 09:15:32 +0000826 if (attr1 == attr2)
827 return (0);
828 if (attr1 == NULL)
829 return (-1);
830 if (attr2 == NULL)
831 return (1);
832 if (attr1->ns == attr2->ns) {
833 return (xmlStrcmp(attr1->name, attr2->name));
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000834 }
835
836 /*
837 * Attributes in the default namespace are first
838 * because the default namespace is not applied to
839 * unqualified attributes
840 */
Daniel Veillard9ff88172002-03-11 09:15:32 +0000841 if (attr1->ns == NULL)
842 return (-1);
843 if (attr2->ns == NULL)
844 return (1);
845 if (attr1->ns->prefix == NULL)
846 return (-1);
847 if (attr2->ns->prefix == NULL)
848 return (1);
849
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000850 ret = xmlStrcmp(attr1->ns->href, attr2->ns->href);
Daniel Veillard9ff88172002-03-11 09:15:32 +0000851 if (ret == 0) {
852 ret = xmlStrcmp(attr1->name, attr2->name);
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000853 }
Daniel Veillard9ff88172002-03-11 09:15:32 +0000854 return (ret);
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000855}
856
857
858/**
859 * xmlC14NPrintAttrs:
860 * @attr: the pointer to attr
861 * @ctx: the C14N context
862 *
863 * Prints out canonical attribute urrent node to the
864 * buffer from C14N context as follows
865 *
866 * Canonical XML v 1.0 (http://www.w3.org/TR/xml-c14n)
867 *
868 * Returns 1 on success or 0 on fail.
869 */
870static int
Daniel Veillard9ff88172002-03-11 09:15:32 +0000871xmlC14NPrintAttrs(const xmlAttrPtr attr, xmlC14NCtxPtr ctx)
872{
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000873 xmlChar *value;
874 xmlChar *buffer;
875
Daniel Veillard9ff88172002-03-11 09:15:32 +0000876 if ((attr == NULL) || (ctx == NULL)) {
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000877#ifdef DEBUG_C14N
878 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +0000879 "xmlC14NPrintAttrs: attr == NULL or ctx == NULL\n");
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000880#endif
Daniel Veillard9ff88172002-03-11 09:15:32 +0000881 return (0);
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000882 }
883
884 xmlOutputBufferWriteString(ctx->buf, " ");
Daniel Veillard9ff88172002-03-11 09:15:32 +0000885 if (attr->ns != NULL && xmlStrlen(attr->ns->prefix) > 0) {
886 xmlOutputBufferWriteString(ctx->buf,
887 (const char *) attr->ns->prefix);
888 xmlOutputBufferWriteString(ctx->buf, ":");
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000889 }
890 xmlOutputBufferWriteString(ctx->buf, (const char *) attr->name);
891 xmlOutputBufferWriteString(ctx->buf, "=\"");
892
893 value = xmlNodeListGetString(attr->doc, attr->children, 1);
894 /* todo: should we log an error if value==NULL ? */
Daniel Veillard9ff88172002-03-11 09:15:32 +0000895 if (value != NULL) {
896 buffer = xmlC11NNormalizeAttr(value);
897 xmlFree(value);
898 if (buffer != NULL) {
899 xmlOutputBufferWriteString(ctx->buf, (const char *) buffer);
900 xmlFree(buffer);
901 } else {
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000902#ifdef DEBUG_C14N
Daniel Veillard9ff88172002-03-11 09:15:32 +0000903 xmlGenericError(xmlGenericErrorContext,
904 "xmlC14NPrintAttrs: xmlC11NNormalizeAttr failed\n");
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000905#endif
Daniel Veillard9ff88172002-03-11 09:15:32 +0000906 return (0);
907 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000908 }
909 xmlOutputBufferWriteString(ctx->buf, "\"");
Daniel Veillard9ff88172002-03-11 09:15:32 +0000910 return (1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000911}
912
913/**
914 * xmlC14NProcessAttrsAxis:
915 * @ctx: the C14N context
916 * @cur: the current node
917 *
918 * Prints out canonical attribute axis of the current node to the
919 * buffer from C14N context as follows
920 *
921 * Canonical XML v 1.0 (http://www.w3.org/TR/xml-c14n)
922 *
923 * Attribute Axis
924 * In lexicographic order (ascending), process each node that
925 * is in the element's attribute axis and in the node-set.
926 *
927 * The processing of an element node E MUST be modified slightly
928 * when an XPath node-set is given as input and the element's
929 * parent is omitted from the node-set.
930 *
931 *
932 * Exclusive XML Canonicalization v 1.0 (http://www.w3.org/TR/xml-exc-c14n)
933 *
934 * Canonical XML applied to a document subset requires the search of the
935 * ancestor nodes of each orphan element node for attributes in the xml
936 * namespace, such as xml:lang and xml:space. These are copied into the
937 * element node except if a declaration of the same attribute is already
938 * in the attribute axis of the element (whether or not it is included in
939 * the document subset). This search and copying are omitted from the
940 * Exclusive XML Canonicalization method.
941 *
942 * Returns 0 on success or -1 on fail.
943 */
944static int
Daniel Veillard9ff88172002-03-11 09:15:32 +0000945xmlC14NProcessAttrsAxis(xmlC14NCtxPtr ctx, xmlNodePtr cur)
946{
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000947 xmlAttrPtr attr;
948 xmlListPtr list;
Daniel Veillard9ff88172002-03-11 09:15:32 +0000949
950 if ((ctx == NULL) || (cur == NULL) || (cur->type != XML_ELEMENT_NODE)) {
951#ifdef DEBUG_C14N
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000952 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +0000953 "xmlC14NProcessAttrsAxis: Null context or node pointer or type != XML_ELEMENT_NODE.\n");
954#endif
955 return (-1);
956 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000957
958 /*
959 * Create a sorted list to store element attributes
960 */
Daniel Veillard9ff88172002-03-11 09:15:32 +0000961 list = xmlListCreate(NULL, (xmlListDataCompare) xmlC14NAttrsCompare);
962 if (list == NULL) {
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000963#ifdef DEBUG_C14N
964 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +0000965 "xmlC14NProcessAttrsAxis: list creation failed\n");
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000966#endif
Daniel Veillard9ff88172002-03-11 09:15:32 +0000967 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000968 }
969
970 /*
971 * Add all visible attributes from current node.
972 */
973 attr = cur->properties;
Daniel Veillard9ff88172002-03-11 09:15:32 +0000974 while (attr != NULL) {
975 /* check that attribute is visible */
Aleksey Sanin2c135a12002-08-01 06:31:50 +0000976 if (xmlC14NIsVisible(ctx, attr, cur)) {
Daniel Veillard9ff88172002-03-11 09:15:32 +0000977 xmlListInsert(list, attr);
978 }
979 attr = attr->next;
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000980 }
Daniel Veillard9ff88172002-03-11 09:15:32 +0000981
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000982 /*
983 * include attributes in "xml" namespace defined in ancestors
984 * (only for non-exclusive XML Canonicalization)
985 */
Daniel Veillard9ff88172002-03-11 09:15:32 +0000986 if ((!ctx->exclusive) && (cur->parent != NULL)
Aleksey Sanin2c135a12002-08-01 06:31:50 +0000987 && (!xmlC14NIsVisible(ctx, cur->parent, cur->parent->parent))) {
Daniel Veillard044fc6b2002-03-04 17:09:44 +0000988 /*
Daniel Veillard9ff88172002-03-11 09:15:32 +0000989 * If XPath node-set is not specified then the parent is always
990 * visible!
991 */
992 cur = cur->parent;
993 while (cur != NULL) {
994 attr = cur->properties;
995 while (attr != NULL) {
996 if ((attr->ns != NULL)
997 && (xmlStrEqual(attr->ns->prefix, BAD_CAST "xml"))) {
998 if (xmlListSearch(list, attr) == NULL) {
999 xmlListInsert(list, attr);
1000 }
1001 }
1002 attr = attr->next;
1003 }
1004 cur = cur->parent;
1005 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001006 }
Daniel Veillard9ff88172002-03-11 09:15:32 +00001007
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001008 /*
1009 * print out all elements from list
1010 */
Aleksey Saninf8cb6dd2002-06-04 04:27:06 +00001011 xmlListWalk(list, (xmlListWalker) xmlC14NPrintAttrs, (const void *) ctx);
Daniel Veillard9ff88172002-03-11 09:15:32 +00001012
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001013 /*
1014 * Cleanup
1015 */
Daniel Veillard9ff88172002-03-11 09:15:32 +00001016 xmlListDelete(list);
1017 return (0);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001018}
1019
1020/**
1021 * xmlC14NCheckForRelativeNamespaces:
1022 * @ctx: the C14N context
1023 * @cur: the current element node
1024 *
1025 * Checks that current element node has no relative namespaces defined
1026 *
1027 * Returns 0 if the node has no relative namespaces or -1 otherwise.
1028 */
1029static int
Daniel Veillard9ff88172002-03-11 09:15:32 +00001030xmlC14NCheckForRelativeNamespaces(xmlC14NCtxPtr ctx, xmlNodePtr cur)
1031{
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001032 xmlNsPtr ns;
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001033
Daniel Veillard9ff88172002-03-11 09:15:32 +00001034 if ((ctx == NULL) || (cur == NULL) || (cur->type != XML_ELEMENT_NODE)) {
1035#ifdef DEBUG_C14N
1036 xmlGenericError(xmlGenericErrorContext,
1037 "xmlC14NCheckForRelativeNamespaces: Null context or node pointer or type != XML_ELEMENT_NODE.\n");
1038#endif
1039 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001040 }
Daniel Veillard9ff88172002-03-11 09:15:32 +00001041
1042 ns = cur->nsDef;
1043 while (ns != NULL) {
1044 if (xmlStrlen(ns->href) > 0) {
1045 xmlURIPtr uri;
1046
1047 uri = xmlParseURI((const char *) ns->href);
1048 if (uri == NULL) {
1049#ifdef DEBUG_C14N
1050 xmlGenericError(xmlGenericErrorContext,
1051 "xmlC14NCheckForRelativeNamespaces: unable to parse uri=\"%s\".\n",
1052 ns->href);
1053#endif
1054 return (-1);
1055 }
1056 if (xmlStrlen((const xmlChar *) uri->scheme) == 0) {
1057 xmlFreeURI(uri);
1058 return (-1);
1059 }
1060 if ((!xmlStrEqual
1061 ((const xmlChar *) uri->scheme, BAD_CAST "urn"))
1062 && (xmlStrlen((const xmlChar *) uri->server) == 0)) {
1063 xmlFreeURI(uri);
1064 return (-1);
1065 }
1066 xmlFreeURI(uri);
1067 }
1068 ns = ns->next;
1069 }
1070 return (0);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001071}
1072
1073/**
1074 * xmlC14NProcessElementNode:
1075 * @ctx: the pointer to C14N context object
1076 * @cur: the node to process
1077 *
1078 * Canonical XML v 1.0 (http://www.w3.org/TR/xml-c14n)
1079 *
1080 * Element Nodes
1081 * If the element is not in the node-set, then the result is obtained
1082 * by processing the namespace axis, then the attribute axis, then
1083 * processing the child nodes of the element that are in the node-set
1084 * (in document order). If the element is in the node-set, then the result
1085 * is an open angle bracket (<), the element QName, the result of
1086 * processing the namespace axis, the result of processing the attribute
1087 * axis, a close angle bracket (>), the result of processing the child
1088 * nodes of the element that are in the node-set (in document order), an
1089 * open angle bracket, a forward slash (/), the element QName, and a close
1090 * angle bracket.
1091 *
1092 * Returns non-negative value on success or negative value on fail
1093 */
1094static int
Daniel Veillard9ff88172002-03-11 09:15:32 +00001095xmlC14NProcessElementNode(xmlC14NCtxPtr ctx, xmlNodePtr cur, int visible)
1096{
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001097 int ret;
Aleksey Saninf8cb6dd2002-06-04 04:27:06 +00001098 xmlC14NVisibleNsStack state;
Daniel Veillard6f293b12002-03-15 09:42:33 +00001099 int parent_is_doc = 0;
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001100
Daniel Veillard9ff88172002-03-11 09:15:32 +00001101 if ((ctx == NULL) || (cur == NULL) || (cur->type != XML_ELEMENT_NODE)) {
1102#ifdef DEBUG_C14N
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001103 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001104 "xmlC14NProcessElementNode: Null context or node pointer or type != XML_ELEMENT_NODE.\n");
1105#endif
1106 return (-1);
1107 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001108
1109 /*
1110 * Check relative relative namespaces:
1111 * implementations of XML canonicalization MUST report an operation
1112 * failure on documents containing relative namespace URIs.
1113 */
Daniel Veillard9ff88172002-03-11 09:15:32 +00001114 if (xmlC14NCheckForRelativeNamespaces(ctx, cur) < 0) {
1115#ifdef DEBUG_C14N
1116 xmlGenericError(xmlGenericErrorContext,
1117 "xmlC14NProcessElementNode: xmlC14NCheckForRelativeNamespaces failed.\n");
1118#endif
1119 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001120 }
1121
1122
1123 /*
Aleksey Saninf8cb6dd2002-06-04 04:27:06 +00001124 * Save ns_rendered stack position
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001125 */
Aleksey Saninf8cb6dd2002-06-04 04:27:06 +00001126 xmlC14NVisibleNsStackSave(ctx->ns_rendered, &state);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001127
Daniel Veillard6f293b12002-03-15 09:42:33 +00001128 if (visible) {
Daniel Veillard9ff88172002-03-11 09:15:32 +00001129 if (ctx->parent_is_doc) {
Daniel Veillard6f293b12002-03-15 09:42:33 +00001130 /* save this flag into the stack */
1131 parent_is_doc = ctx->parent_is_doc;
1132 ctx->parent_is_doc = 0;
Daniel Veillard9ff88172002-03-11 09:15:32 +00001133 ctx->pos = XMLC14N_INSIDE_DOCUMENT_ELEMENT;
1134 }
1135 xmlOutputBufferWriteString(ctx->buf, "<");
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001136
Daniel Veillard9ff88172002-03-11 09:15:32 +00001137 if ((cur->ns != NULL) && (xmlStrlen(cur->ns->prefix) > 0)) {
1138 xmlOutputBufferWriteString(ctx->buf,
1139 (const char *) cur->ns->prefix);
1140 xmlOutputBufferWriteString(ctx->buf, ":");
1141 }
1142 xmlOutputBufferWriteString(ctx->buf, (const char *) cur->name);
Aleksey Saninf8cb6dd2002-06-04 04:27:06 +00001143 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001144
Aleksey Saninf8cb6dd2002-06-04 04:27:06 +00001145 if (!ctx->exclusive) {
1146 ret = xmlC14NProcessNamespacesAxis(ctx, cur, visible);
1147 } else {
1148 ret = xmlExcC14NProcessNamespacesAxis(ctx, cur, visible);
1149 }
1150 if (ret < 0) {
Daniel Veillard9ff88172002-03-11 09:15:32 +00001151#ifdef DEBUG_C14N
Aleksey Saninf8cb6dd2002-06-04 04:27:06 +00001152 xmlGenericError(xmlGenericErrorContext,
1153 "xmlC14NProcessElementNode: xmlC14NProcessNamespacesAxis failed.\n");
Daniel Veillard9ff88172002-03-11 09:15:32 +00001154#endif
Aleksey Saninf8cb6dd2002-06-04 04:27:06 +00001155 return (-1);
1156 }
1157 /* todo: shouldn't this go to "visible only"? */
1158 if(visible) {
1159 xmlC14NVisibleNsStackShift(ctx->ns_rendered);
1160 }
1161
1162 if(visible) {
1163 ret = xmlC14NProcessAttrsAxis(ctx, cur);
1164 if (ret < 0) {
1165#ifdef DEBUG_C14N
1166 xmlGenericError(xmlGenericErrorContext,
1167 "xmlC14NProcessElementNode: xmlC14NProcessAttrsAxis failed.\n");
1168#endif
1169 return (-1);
Aleksey Saninc57f9c12002-05-31 19:14:57 +00001170 }
Aleksey Saninf8cb6dd2002-06-04 04:27:06 +00001171 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001172
Aleksey Saninf8cb6dd2002-06-04 04:27:06 +00001173 if (visible) {
Daniel Veillard9ff88172002-03-11 09:15:32 +00001174 xmlOutputBufferWriteString(ctx->buf, ">");
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001175 }
1176 if (cur->children != NULL) {
Daniel Veillard9ff88172002-03-11 09:15:32 +00001177 ret = xmlC14NProcessNodeList(ctx, cur->children);
1178 if (ret < 0) {
1179#ifdef DEBUG_C14N
1180 xmlGenericError(xmlGenericErrorContext,
1181 "xmlC14NProcessElementNode: xmlC14NProcessNodeList failed.\n");
1182#endif
1183 return (-1);
1184 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001185 }
Daniel Veillard9ff88172002-03-11 09:15:32 +00001186 if (visible) {
1187 xmlOutputBufferWriteString(ctx->buf, "</");
1188 if ((cur->ns != NULL) && (xmlStrlen(cur->ns->prefix) > 0)) {
1189 xmlOutputBufferWriteString(ctx->buf,
1190 (const char *) cur->ns->prefix);
1191 xmlOutputBufferWriteString(ctx->buf, ":");
1192 }
1193 xmlOutputBufferWriteString(ctx->buf, (const char *) cur->name);
1194 xmlOutputBufferWriteString(ctx->buf, ">");
Daniel Veillard6f293b12002-03-15 09:42:33 +00001195 if (parent_is_doc) {
1196 /* restore this flag from the stack for next node */
1197 ctx->parent_is_doc = parent_is_doc;
1198 ctx->pos = XMLC14N_AFTER_DOCUMENT_ELEMENT;
Daniel Veillard9ff88172002-03-11 09:15:32 +00001199 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001200 }
1201
1202 /*
Aleksey Saninf8cb6dd2002-06-04 04:27:06 +00001203 * Restore ns_rendered stack position
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001204 */
Aleksey Saninf8cb6dd2002-06-04 04:27:06 +00001205 xmlC14NVisibleNsStackRestore(ctx->ns_rendered, &state);
Daniel Veillard9ff88172002-03-11 09:15:32 +00001206 return (0);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001207}
1208
1209/**
1210 * xmlC14NProcessNode:
1211 * @ctx: the pointer to C14N context object
1212 * @cur: the node to process
1213 *
1214 * Processes the given node
1215 *
1216 * Returns non-negative value on success or negative value on fail
1217 */
1218static int
Daniel Veillard9ff88172002-03-11 09:15:32 +00001219xmlC14NProcessNode(xmlC14NCtxPtr ctx, xmlNodePtr cur)
1220{
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001221 int ret = 0;
1222 int visible;
Daniel Veillard9ff88172002-03-11 09:15:32 +00001223
1224 if ((ctx == NULL) || (cur == NULL)) {
1225#ifdef DEBUG_C14N
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001226 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001227 "xmlC14NProcessNode: Null context or node pointer.\n");
1228#endif
1229 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001230 }
1231
Aleksey Sanin2c135a12002-08-01 06:31:50 +00001232 visible = xmlC14NIsVisible(ctx, cur, cur->parent);
Daniel Veillard9ff88172002-03-11 09:15:32 +00001233 switch (cur->type) {
1234 case XML_ELEMENT_NODE:
1235 ret = xmlC14NProcessElementNode(ctx, cur, visible);
1236 break;
1237 case XML_CDATA_SECTION_NODE:
1238 case XML_TEXT_NODE:
1239 /*
1240 * Text Nodes
1241 * the string value, except all ampersands are replaced
1242 * by &amp;, all open angle brackets (<) are replaced by &lt;, all closing
1243 * angle brackets (>) are replaced by &gt;, and all #xD characters are
1244 * replaced by &#xD;.
1245 */
1246 /* cdata sections are processed as text nodes */
1247 /* todo: verify that cdata sections are included in XPath nodes set */
1248 if ((visible) && (cur->content != NULL)) {
1249 xmlChar *buffer;
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001250
Daniel Veillard9ff88172002-03-11 09:15:32 +00001251 buffer = xmlC11NNormalizeText(cur->content);
1252 if (buffer != NULL) {
1253 xmlOutputBufferWriteString(ctx->buf,
1254 (const char *) buffer);
1255 xmlFree(buffer);
1256 } else {
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001257#ifdef DEBUG_C14N
Daniel Veillard9ff88172002-03-11 09:15:32 +00001258 xmlGenericError(xmlGenericErrorContext,
1259 "xmlC14NProcessNode: xmlC11NNormalizeText() failed\n");
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001260#endif
Daniel Veillard9ff88172002-03-11 09:15:32 +00001261 return (-1);
1262 }
1263 }
1264 break;
1265 case XML_PI_NODE:
1266 /*
1267 * Processing Instruction (PI) Nodes-
1268 * The opening PI symbol (<?), the PI target name of the node,
1269 * a leading space and the string value if it is not empty, and
1270 * the closing PI symbol (?>). If the string value is empty,
1271 * then the leading space is not added. Also, a trailing #xA is
1272 * rendered after the closing PI symbol for PI children of the
1273 * root node with a lesser document order than the document
1274 * element, and a leading #xA is rendered before the opening PI
1275 * symbol of PI children of the root node with a greater document
1276 * order than the document element.
1277 */
1278 if (visible) {
1279 if (ctx->pos == XMLC14N_AFTER_DOCUMENT_ELEMENT) {
1280 xmlOutputBufferWriteString(ctx->buf, "\x0A<?");
1281 } else {
1282 xmlOutputBufferWriteString(ctx->buf, "<?");
1283 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001284
Daniel Veillard9ff88172002-03-11 09:15:32 +00001285 xmlOutputBufferWriteString(ctx->buf,
1286 (const char *) cur->name);
1287 if ((cur->content != NULL) && (*(cur->content) != '\0')) {
1288 xmlChar *buffer;
1289
1290 xmlOutputBufferWriteString(ctx->buf, " ");
1291
1292 /* todo: do we need to normalize pi? */
1293 buffer = xmlC11NNormalizePI(cur->content);
1294 if (buffer != NULL) {
1295 xmlOutputBufferWriteString(ctx->buf,
1296 (const char *) buffer);
1297 xmlFree(buffer);
1298 } else {
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001299#ifdef DEBUG_C14N
Daniel Veillard9ff88172002-03-11 09:15:32 +00001300 xmlGenericError(xmlGenericErrorContext,
1301 "xmlC14NProcessNode: xmlC11NNormalizePI() failed\n");
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001302#endif
Daniel Veillard9ff88172002-03-11 09:15:32 +00001303 return (-1);
1304 }
1305 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001306
Daniel Veillard9ff88172002-03-11 09:15:32 +00001307 if (ctx->pos == XMLC14N_BEFORE_DOCUMENT_ELEMENT) {
1308 xmlOutputBufferWriteString(ctx->buf, "?>\x0A");
1309 } else {
1310 xmlOutputBufferWriteString(ctx->buf, "?>");
1311 }
1312 }
1313 break;
1314 case XML_COMMENT_NODE:
1315 /*
1316 * Comment Nodes
1317 * Nothing if generating canonical XML without comments. For
1318 * canonical XML with comments, generate the opening comment
1319 * symbol (<!--), the string value of the node, and the
1320 * closing comment symbol (-->). Also, a trailing #xA is rendered
1321 * after the closing comment symbol for comment children of the
1322 * root node with a lesser document order than the document
1323 * element, and a leading #xA is rendered before the opening
1324 * comment symbol of comment children of the root node with a
1325 * greater document order than the document element. (Comment
1326 * children of the root node represent comments outside of the
1327 * top-level document element and outside of the document type
1328 * declaration).
1329 */
1330 if (visible && ctx->with_comments) {
1331 if (ctx->pos == XMLC14N_AFTER_DOCUMENT_ELEMENT) {
1332 xmlOutputBufferWriteString(ctx->buf, "\x0A<!--");
1333 } else {
1334 xmlOutputBufferWriteString(ctx->buf, "<!--");
1335 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001336
Daniel Veillard9ff88172002-03-11 09:15:32 +00001337 if (cur->content != NULL) {
1338 xmlChar *buffer;
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001339
Daniel Veillard9ff88172002-03-11 09:15:32 +00001340 /* todo: do we need to normalize comment? */
1341 buffer = xmlC11NNormalizeComment(cur->content);
1342 if (buffer != NULL) {
1343 xmlOutputBufferWriteString(ctx->buf,
1344 (const char *) buffer);
1345 xmlFree(buffer);
1346 } else {
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001347#ifdef DEBUG_C14N
Daniel Veillard9ff88172002-03-11 09:15:32 +00001348 xmlGenericError(xmlGenericErrorContext,
1349 "xmlC14NProcessNode: xmlC11NNormalizeComment() failed\n");
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001350#endif
Daniel Veillard9ff88172002-03-11 09:15:32 +00001351 return (-1);
1352 }
1353 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001354
Daniel Veillard9ff88172002-03-11 09:15:32 +00001355 if (ctx->pos == XMLC14N_BEFORE_DOCUMENT_ELEMENT) {
1356 xmlOutputBufferWriteString(ctx->buf, "-->\x0A");
1357 } else {
1358 xmlOutputBufferWriteString(ctx->buf, "-->");
1359 }
1360 }
1361 break;
1362 case XML_DOCUMENT_NODE:
1363 case XML_DOCUMENT_FRAG_NODE: /* should be processed as document? */
Daniel Veillard1840ef02002-03-21 08:05:23 +00001364#ifdef LIBXML_DOCB_ENABLED
Daniel Veillard9ff88172002-03-11 09:15:32 +00001365 case XML_DOCB_DOCUMENT_NODE: /* should be processed as document? */
Daniel Veillard1840ef02002-03-21 08:05:23 +00001366#endif
1367#ifdef LIBXML_HTML_ENABLED
Daniel Veillard9ff88172002-03-11 09:15:32 +00001368 case XML_HTML_DOCUMENT_NODE: /* should be processed as document? */
Daniel Veillard1840ef02002-03-21 08:05:23 +00001369#endif
Daniel Veillard9ff88172002-03-11 09:15:32 +00001370 if (cur->children != NULL) {
1371 ctx->pos = XMLC14N_BEFORE_DOCUMENT_ELEMENT;
1372 ctx->parent_is_doc = 1;
1373 ret = xmlC14NProcessNodeList(ctx, cur->children);
1374 }
1375 break;
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001376
Daniel Veillard9ff88172002-03-11 09:15:32 +00001377 case XML_ATTRIBUTE_NODE:
Daniel Veillardd96cce12003-10-10 12:30:37 +00001378 xmlC14NErr(ctx, cur, XML_C14N_INVALID_NODE,
1379 "xmlC14NProcessNode: XML_ATTRIBUTE_NODE is illegal here\n");
Daniel Veillard9ff88172002-03-11 09:15:32 +00001380 return (-1);
1381 case XML_NAMESPACE_DECL:
Daniel Veillardd96cce12003-10-10 12:30:37 +00001382 xmlC14NErr(ctx, cur, XML_C14N_INVALID_NODE,
1383 "xmlC14NProcessNode: XML_NAMESPACE_DECL is illegal here\n");
Daniel Veillard9ff88172002-03-11 09:15:32 +00001384 return (-1);
1385 case XML_ENTITY_REF_NODE:
Daniel Veillardd96cce12003-10-10 12:30:37 +00001386 xmlC14NErr(ctx, cur, XML_C14N_INVALID_NODE,
1387 "xmlC14NProcessNode: XML_ENTITY_REF_NODE is illegal here\n");
Daniel Veillard9ff88172002-03-11 09:15:32 +00001388 return (-1);
1389 case XML_ENTITY_NODE:
Daniel Veillardd96cce12003-10-10 12:30:37 +00001390 xmlC14NErr(ctx, cur, XML_C14N_INVALID_NODE,
1391 "xmlC14NProcessNode: XML_ENTITY_NODE is illegal here\n");
Daniel Veillard9ff88172002-03-11 09:15:32 +00001392 return (-1);
1393
1394 case XML_DOCUMENT_TYPE_NODE:
1395 case XML_NOTATION_NODE:
1396 case XML_DTD_NODE:
1397 case XML_ELEMENT_DECL:
1398 case XML_ATTRIBUTE_DECL:
1399 case XML_ENTITY_DECL:
Daniel Veillard1840ef02002-03-21 08:05:23 +00001400#ifdef LIBXML_XINCLUDE_ENABLED
Daniel Veillard9ff88172002-03-11 09:15:32 +00001401 case XML_XINCLUDE_START:
1402 case XML_XINCLUDE_END:
Daniel Veillard1840ef02002-03-21 08:05:23 +00001403#endif
Daniel Veillard9ff88172002-03-11 09:15:32 +00001404 /*
1405 * should be ignored according to "W3C Canonical XML"
1406 */
1407 break;
1408 default:
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001409#ifdef DEBUG_C14N
Daniel Veillard9ff88172002-03-11 09:15:32 +00001410 xmlGenericError(xmlGenericErrorContext,
1411 "xmlC14NProcessNode: unknown node type = %d\n",
1412 cur->type);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001413#endif
Daniel Veillard9ff88172002-03-11 09:15:32 +00001414 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001415 }
1416
Daniel Veillard9ff88172002-03-11 09:15:32 +00001417 return (ret);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001418}
1419
1420/**
1421 * xmlC14NProcessNodeList:
1422 * @ctx: the pointer to C14N context object
1423 * @cur: the node to start from
1424 *
1425 * Processes all nodes in the row starting from cur.
1426 *
1427 * Returns non-negative value on success or negative value on fail
1428 */
1429static int
Daniel Veillard9ff88172002-03-11 09:15:32 +00001430xmlC14NProcessNodeList(xmlC14NCtxPtr ctx, xmlNodePtr cur)
1431{
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001432 int ret;
Daniel Veillard9ff88172002-03-11 09:15:32 +00001433
1434 if (ctx == NULL) {
1435#ifdef DEBUG_C14N
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001436 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001437 "xmlC14NProcessNodeList: Null context pointer.\n");
1438#endif
1439 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001440 }
1441
Daniel Veillard9ff88172002-03-11 09:15:32 +00001442 for (ret = 0; cur != NULL && ret >= 0; cur = cur->next) {
1443 ret = xmlC14NProcessNode(ctx, cur);
1444 }
1445 return (ret);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001446}
1447
1448
1449/**
1450 * xmlC14NFreeCtx:
1451 * @ctx: the pointer to C14N context object
1452 *
1453 * Cleanups the C14N context object.
1454 */
1455
1456static void
Daniel Veillard9ff88172002-03-11 09:15:32 +00001457xmlC14NFreeCtx(xmlC14NCtxPtr ctx)
1458{
1459 if (ctx == NULL) {
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001460#ifdef DEBUG_C14N
1461 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001462 "xmlC14NFreeCtx: ctx == NULL\n");
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001463#endif
Daniel Veillard9ff88172002-03-11 09:15:32 +00001464 return;
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001465 }
Daniel Veillard9ff88172002-03-11 09:15:32 +00001466
1467 if (ctx->ns_rendered != NULL) {
Aleksey Saninf8cb6dd2002-06-04 04:27:06 +00001468 xmlC14NVisibleNsStackDestroy(ctx->ns_rendered);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001469 }
1470 xmlFree(ctx);
1471}
1472
1473/**
1474 * xmlC14NNewCtx:
1475 * @doc: the XML document for canonization
Aleksey Sanin2c135a12002-08-01 06:31:50 +00001476 * @is_visible_callback:the function to use to determine is node visible
1477 * or not
1478 * @user_data: the first parameter for @is_visible_callback function
1479 * (in most cases, it is nodes set)
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001480 * @inclusive_ns_prefixe the list of inclusive namespace prefixes
1481 * ended with a NULL or NULL if there is no
1482 * inclusive namespaces (only for exclusive
1483 * canonicalization)
1484 * @with_comments: include comments in the result (!=0) or not (==0)
1485 * @buf: the output buffer to store canonical XML; this
1486 * buffer MUST have encoder==NULL because C14N requires
1487 * UTF-8 output
1488 *
1489 * Creates new C14N context object to store C14N parameters.
1490 *
1491 * Returns pointer to newly created object (success) or NULL (fail)
1492 */
1493static xmlC14NCtxPtr
Aleksey Sanin2c135a12002-08-01 06:31:50 +00001494xmlC14NNewCtx(xmlDocPtr doc,
1495 xmlC14NIsVisibleCallback is_visible_callback, void* user_data,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001496 int exclusive, xmlChar ** inclusive_ns_prefixes,
1497 int with_comments, xmlOutputBufferPtr buf)
1498{
Daniel Veillard659e71e2003-10-10 14:10:40 +00001499 xmlC14NCtxPtr ctx = NULL;
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001500
Daniel Veillard9ff88172002-03-11 09:15:32 +00001501 if ((doc == NULL) || (buf == NULL)) {
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001502#ifdef DEBUG_C14N
1503 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001504 "xmlC14NNewCtx: pointer to document or output buffer is NULL\n");
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001505#endif
Daniel Veillard9ff88172002-03-11 09:15:32 +00001506 return (NULL);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001507 }
1508
1509 /*
1510 * Validate the encoding output buffer encoding
1511 */
Daniel Veillard9ff88172002-03-11 09:15:32 +00001512 if (buf->encoder != NULL) {
Daniel Veillardd96cce12003-10-10 12:30:37 +00001513 xmlC14NErr(ctx, (xmlNodePtr) doc, XML_C14N_REQUIRES_UTF8,
1514"xmlC14NNewCtx: output buffer encoder != NULL but C14N requires UTF8 output\n");
Daniel Veillard9ff88172002-03-11 09:15:32 +00001515 return (NULL);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001516 }
1517
1518 /*
1519 * Validate the XML document encoding value, if provided.
1520 */
Daniel Veillard9ff88172002-03-11 09:15:32 +00001521 if (doc->charset != XML_CHAR_ENCODING_UTF8) {
Daniel Veillardd96cce12003-10-10 12:30:37 +00001522 xmlC14NErr(ctx, (xmlNodePtr) doc, XML_C14N_REQUIRES_UTF8,
1523 "xmlC14NNewCtx: source document not in UTF8\n");
Daniel Veillard9ff88172002-03-11 09:15:32 +00001524 return (NULL);
1525 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001526
1527 /*
1528 * Allocate a new xmlC14NCtxPtr and fill the fields.
1529 */
1530 ctx = (xmlC14NCtxPtr) xmlMalloc(sizeof(xmlC14NCtx));
1531 if (ctx == NULL) {
Daniel Veillardd96cce12003-10-10 12:30:37 +00001532 xmlC14NErrMemory("creating context");
Daniel Veillard9ff88172002-03-11 09:15:32 +00001533 return (NULL);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001534 }
1535 memset(ctx, 0, sizeof(xmlC14NCtx));
1536
1537 /*
1538 * initialize C14N context
Daniel Veillard9ff88172002-03-11 09:15:32 +00001539 */
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001540 ctx->doc = doc;
1541 ctx->with_comments = with_comments;
Aleksey Sanin2c135a12002-08-01 06:31:50 +00001542 ctx->is_visible_callback = is_visible_callback;
1543 ctx->user_data = user_data;
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001544 ctx->buf = buf;
1545 ctx->parent_is_doc = 1;
1546 ctx->pos = XMLC14N_BEFORE_DOCUMENT_ELEMENT;
Aleksey Saninf8cb6dd2002-06-04 04:27:06 +00001547 ctx->ns_rendered = xmlC14NVisibleNsStackCreate();
1548
1549 if(ctx->ns_rendered == NULL) {
Daniel Veillardd96cce12003-10-10 12:30:37 +00001550 xmlC14NErr(ctx, (xmlNodePtr) doc, XML_C14N_CREATE_STACK,
1551 "xmlC14NNewCtx: xmlC14NVisibleNsStackCreate failed\n");
Aleksey Saninf8cb6dd2002-06-04 04:27:06 +00001552 xmlC14NFreeCtx(ctx);
1553 return (NULL);
1554 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001555
1556 /*
1557 * Set "exclusive" flag, create a nodes set for namespaces
1558 * stack and remember list of incluseve prefixes
1559 */
Daniel Veillard9ff88172002-03-11 09:15:32 +00001560 if (exclusive) {
1561 ctx->exclusive = 1;
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001562 ctx->inclusive_ns_prefixes = inclusive_ns_prefixes;
1563 }
Daniel Veillard9ff88172002-03-11 09:15:32 +00001564 return (ctx);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001565}
1566
1567/**
Aleksey Sanin2c135a12002-08-01 06:31:50 +00001568 * xmlC14NExecute:
1569 * @doc: the XML document for canonization
1570 * @is_visible_callback:the function to use to determine is node visible
1571 * or not
1572 * @user_data: the first parameter for @is_visible_callback function
1573 * (in most cases, it is nodes set)
1574 * @exclusive: the exclusive flag (0 - non-exclusive canonicalization;
1575 * otherwise - exclusive canonicalization)
1576 * @inclusive_ns_prefixes: the list of inclusive namespace prefixes
1577 * ended with a NULL or NULL if there is no
1578 * inclusive namespaces (only for exclusive
1579 * canonicalization, ignored otherwise)
1580 * @with_comments: include comments in the result (!=0) or not (==0)
1581 * @buf: the output buffer to store canonical XML; this
1582 * buffer MUST have encoder==NULL because C14N requires
1583 * UTF-8 output
1584 *
1585 * Dumps the canonized image of given XML document into the provided buffer.
1586 * For details see "Canonical XML" (http://www.w3.org/TR/xml-c14n) or
1587 * "Exclusive XML Canonicalization" (http://www.w3.org/TR/xml-exc-c14n)
1588 *
1589 * Returns non-negative value on success or a negative value on fail
1590 */
1591int
1592xmlC14NExecute(xmlDocPtr doc, xmlC14NIsVisibleCallback is_visible_callback,
1593 void* user_data, int exclusive, xmlChar **inclusive_ns_prefixes,
1594 int with_comments, xmlOutputBufferPtr buf) {
1595
1596 xmlC14NCtxPtr ctx;
1597 int ret;
1598
1599 if ((buf == NULL) || (doc == NULL)) {
1600#ifdef DEBUG_C14N
1601 xmlGenericError(xmlGenericErrorContext,
1602 "xmlC14NExecute: null return buffer or doc pointer\n");
1603#endif
1604 return (-1);
1605 }
1606
1607 /*
1608 * Validate the encoding output buffer encoding
1609 */
1610 if (buf->encoder != NULL) {
Daniel Veillardd96cce12003-10-10 12:30:37 +00001611 xmlC14NErr(NULL, (xmlNodePtr) doc, XML_C14N_REQUIRES_UTF8,
1612"xmlC14NExecute: output buffer encoder != NULL but C14N requires UTF8 output\n");
Aleksey Sanin2c135a12002-08-01 06:31:50 +00001613 return (-1);
1614 }
1615
1616 ctx = xmlC14NNewCtx(doc, is_visible_callback, user_data,
1617 exclusive, inclusive_ns_prefixes,
1618 with_comments, buf);
1619 if (ctx == NULL) {
Daniel Veillardd96cce12003-10-10 12:30:37 +00001620 xmlC14NErr(NULL, (xmlNodePtr) doc, XML_C14N_CREATE_CTXT,
1621 "xmlC14NExecute: unable to create C14N context\n");
Aleksey Sanin2c135a12002-08-01 06:31:50 +00001622 return (-1);
1623 }
1624
1625
1626
1627 /*
1628 * Root Node
1629 * The root node is the parent of the top-level document element. The
1630 * result of processing each of its child nodes that is in the node-set
1631 * in document order. The root node does not generate a byte order mark,
1632 * XML declaration, nor anything from within the document type
1633 * declaration.
1634 */
1635 if (doc->children != NULL) {
1636 ret = xmlC14NProcessNodeList(ctx, doc->children);
1637 if (ret < 0) {
1638#ifdef DEBUG_C14N
1639 xmlGenericError(xmlGenericErrorContext,
1640 "xmlC14NExecute: process childrens' list failed.\n");
1641#endif
1642 xmlC14NFreeCtx(ctx);
1643 return (-1);
1644 }
1645 }
1646
1647 /*
1648 * Flush buffer to get number of bytes written
1649 */
1650 ret = xmlOutputBufferFlush(buf);
1651 if (ret < 0) {
1652#ifdef DEBUG_C14N
1653 xmlGenericError(xmlGenericErrorContext,
1654 "xmlC14NExecute: buffer flush failed.\n");
1655#endif
1656 xmlC14NFreeCtx(ctx);
1657 return (-1);
1658 }
1659
1660 /*
1661 * Cleanup
1662 */
1663 xmlC14NFreeCtx(ctx);
1664 return (ret);
1665}
1666
1667/**
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001668 * xmlC14NDocSaveTo:
1669 * @doc: the XML document for canonization
1670 * @nodes: the nodes set to be included in the canonized image
1671 * or NULL if all document nodes should be included
1672 * @exclusive: the exclusive flag (0 - non-exclusive canonicalization;
1673 * otherwise - exclusive canonicalization)
Daniel Veillarddb1bdba2002-03-09 14:13:11 +00001674 * @inclusive_ns_prefixes: the list of inclusive namespace prefixes
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001675 * ended with a NULL or NULL if there is no
1676 * inclusive namespaces (only for exclusive
1677 * canonicalization, ignored otherwise)
1678 * @with_comments: include comments in the result (!=0) or not (==0)
1679 * @buf: the output buffer to store canonical XML; this
1680 * buffer MUST have encoder==NULL because C14N requires
1681 * UTF-8 output
1682 *
1683 * Dumps the canonized image of given XML document into the provided buffer.
1684 * For details see "Canonical XML" (http://www.w3.org/TR/xml-c14n) or
1685 * "Exclusive XML Canonicalization" (http://www.w3.org/TR/xml-exc-c14n)
1686 *
1687 * Returns non-negative value on success or a negative value on fail
1688 */
1689int
Daniel Veillard9ff88172002-03-11 09:15:32 +00001690xmlC14NDocSaveTo(xmlDocPtr doc, xmlNodeSetPtr nodes,
1691 int exclusive, xmlChar ** inclusive_ns_prefixes,
Aleksey Sanin2c135a12002-08-01 06:31:50 +00001692 int with_comments, xmlOutputBufferPtr buf) {
1693 return(xmlC14NExecute(doc,
1694 (xmlC14NIsVisibleCallback)xmlC14NIsNodeInNodeset,
1695 nodes,
1696 exclusive,
1697 inclusive_ns_prefixes,
1698 with_comments,
1699 buf));
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001700}
1701
Aleksey Sanin2c135a12002-08-01 06:31:50 +00001702
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001703/**
1704 * xmlC14NDocDumpMemory:
1705 * @doc: the XML document for canonization
1706 * @nodes: the nodes set to be included in the canonized image
1707 * or NULL if all document nodes should be included
1708 * @exclusive: the exclusive flag (0 - non-exclusive canonicalization;
1709 * otherwise - exclusive canonicalization)
Daniel Veillarddb1bdba2002-03-09 14:13:11 +00001710 * @inclusive_ns_prefixes: the list of inclusive namespace prefixes
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001711 * ended with a NULL or NULL if there is no
1712 * inclusive namespaces (only for exclusive
1713 * canonicalization, ignored otherwise)
1714 * @with_comments: include comments in the result (!=0) or not (==0)
1715 * @doc_txt_ptr: the memory pointer for allocated canonical XML text;
1716 * the caller of this functions is responsible for calling
1717 * xmlFree() to free allocated memory
1718 *
1719 * Dumps the canonized image of given XML document into memory.
1720 * For details see "Canonical XML" (http://www.w3.org/TR/xml-c14n) or
1721 * "Exclusive XML Canonicalization" (http://www.w3.org/TR/xml-exc-c14n)
1722 *
1723 * Returns the number of bytes written on success or a negative value on fail
1724 */
1725int
Daniel Veillard9ff88172002-03-11 09:15:32 +00001726xmlC14NDocDumpMemory(xmlDocPtr doc, xmlNodeSetPtr nodes,
1727 int exclusive, xmlChar ** inclusive_ns_prefixes,
1728 int with_comments, xmlChar ** doc_txt_ptr)
1729{
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001730 int ret;
1731 xmlOutputBufferPtr buf;
1732
1733 if (doc_txt_ptr == NULL) {
Daniel Veillard9ff88172002-03-11 09:15:32 +00001734#ifdef DEBUG_C14N
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001735 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001736 "xmlC14NDocDumpMemory: null return buffer pointer\n");
1737#endif
1738 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001739 }
1740
1741 *doc_txt_ptr = NULL;
Daniel Veillard9ff88172002-03-11 09:15:32 +00001742
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001743 /*
1744 * create memory buffer with UTF8 (default) encoding
1745 */
1746 buf = xmlAllocOutputBuffer(NULL);
Daniel Veillard9ff88172002-03-11 09:15:32 +00001747 if (buf == NULL) {
1748#ifdef DEBUG_C14N
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001749 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001750 "xmlC14NDocDumpMemory: failed to allocate output buffer.\n");
1751#endif
1752 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001753 }
1754
1755 /*
1756 * canonize document and write to buffer
1757 */
Daniel Veillard9ff88172002-03-11 09:15:32 +00001758 ret = xmlC14NDocSaveTo(doc, nodes, exclusive, inclusive_ns_prefixes,
1759 with_comments, buf);
1760 if (ret < 0) {
1761#ifdef DEBUG_C14N
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001762 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001763 "xmlC14NDocDumpMemory: xmlC14NDocSaveTo failed.\n");
1764#endif
1765 (void) xmlOutputBufferClose(buf);
1766 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001767 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001768
Daniel Veillard9ff88172002-03-11 09:15:32 +00001769 ret = buf->buffer->use;
1770 if (ret > 0) {
1771 *doc_txt_ptr = xmlStrndup(buf->buffer->content, ret);
1772 }
1773 (void) xmlOutputBufferClose(buf);
1774
1775 if ((*doc_txt_ptr == NULL) && (ret > 0)) {
1776#ifdef DEBUG_C14N
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001777 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001778 "xmlC14NDocDumpMemory: failed to allocate memory for document text representation\n");
1779#endif
1780 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001781 }
Daniel Veillard9ff88172002-03-11 09:15:32 +00001782 return (ret);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001783}
1784
1785/**
1786 * xmlC14NDocSave:
1787 * @doc: the XML document for canonization
1788 * @nodes: the nodes set to be included in the canonized image
1789 * or NULL if all document nodes should be included
1790 * @exclusive: the exclusive flag (0 - non-exclusive canonicalization;
1791 * otherwise - exclusive canonicalization)
Daniel Veillarddb1bdba2002-03-09 14:13:11 +00001792 * @inclusive_ns_prefixes: the list of inclusive namespace prefixes
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001793 * ended with a NULL or NULL if there is no
1794 * inclusive namespaces (only for exclusive
1795 * canonicalization, ignored otherwise)
1796 * @with_comments: include comments in the result (!=0) or not (==0)
1797 * @filename: the filename to store canonical XML image
1798 * @compression: the compression level (zlib requred):
1799 * -1 - libxml default,
1800 * 0 - uncompressed,
1801 * >0 - compression level
1802 *
1803 * Dumps the canonized image of given XML document into the file.
1804 * For details see "Canonical XML" (http://www.w3.org/TR/xml-c14n) or
1805 * "Exclusive XML Canonicalization" (http://www.w3.org/TR/xml-exc-c14n)
1806 *
1807 * Returns the number of bytes written success or a negative value on fail
1808 */
1809int
Daniel Veillard9ff88172002-03-11 09:15:32 +00001810xmlC14NDocSave(xmlDocPtr doc, xmlNodeSetPtr nodes,
1811 int exclusive, xmlChar ** inclusive_ns_prefixes,
1812 int with_comments, const char *filename, int compression)
1813{
1814 xmlOutputBufferPtr buf;
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001815 int ret;
1816
Daniel Veillard9ff88172002-03-11 09:15:32 +00001817 if (filename == NULL) {
1818#ifdef DEBUG_C14N
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001819 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001820 "xmlC14NDocSave: filename is NULL\n");
1821#endif
1822 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001823 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001824#ifdef HAVE_ZLIB_H
Daniel Veillard9ff88172002-03-11 09:15:32 +00001825 if (compression < 0)
1826 compression = xmlGetCompressMode();
1827#endif
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001828
1829 /*
1830 * save the content to a temp buffer, use default UTF8 encoding.
1831 */
1832 buf = xmlOutputBufferCreateFilename(filename, NULL, compression);
1833 if (buf == NULL) {
Daniel Veillard9ff88172002-03-11 09:15:32 +00001834#ifdef DEBUG_C14N
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001835 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001836 "xmlC14NDocSave: unable to create buffer for file=\"%s\" with compressin=%d\n",
1837 filename, compression);
1838#endif
1839 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001840 }
Daniel Veillard9ff88172002-03-11 09:15:32 +00001841
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001842 /*
1843 * canonize document and write to buffer
1844 */
Daniel Veillard9ff88172002-03-11 09:15:32 +00001845 ret = xmlC14NDocSaveTo(doc, nodes, exclusive, inclusive_ns_prefixes,
1846 with_comments, buf);
1847 if (ret < 0) {
1848#ifdef DEBUG_C14N
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001849 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard9ff88172002-03-11 09:15:32 +00001850 "xmlC14NDocSave: xmlC14NDocSaveTo failed.\n");
1851#endif
1852 (void) xmlOutputBufferClose(buf);
1853 return (-1);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001854 }
Daniel Veillard9ff88172002-03-11 09:15:32 +00001855
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001856 /*
1857 * get the numbers of bytes written
1858 */
1859 ret = xmlOutputBufferClose(buf);
Daniel Veillard9ff88172002-03-11 09:15:32 +00001860 return (ret);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001861}
1862
1863
1864
1865/*
1866 * Macro used to grow the current buffer.
1867 */
1868#define growBufferReentrant() { \
1869 buffer_size *= 2; \
1870 buffer = (xmlChar *) \
1871 xmlRealloc(buffer, buffer_size * sizeof(xmlChar)); \
1872 if (buffer == NULL) { \
Daniel Veillardd96cce12003-10-10 12:30:37 +00001873 xmlC14NErrMemory("growing buffer"); \
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001874 return(NULL); \
1875 } \
1876}
1877
1878/**
1879 * xmlC11NNormalizeString:
1880 * @input: the input string
1881 * @mode: the normalization mode (attribute, comment, PI or text)
1882 *
1883 * Converts a string to a canonical (normalized) format. The code is stolen
1884 * from xmlEncodeEntitiesReentrant(). Added normalization of \x09, \x0a, \x0A
1885 * and the @mode parameter
1886 *
1887 * Returns a normalized string (caller is responsible for calling xmlFree())
1888 * or NULL if an error occurs
1889 */
1890static xmlChar *
Daniel Veillard9ff88172002-03-11 09:15:32 +00001891xmlC11NNormalizeString(const xmlChar * input,
1892 xmlC14NNormalizationMode mode)
1893{
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001894 const xmlChar *cur = input;
1895 xmlChar *buffer = NULL;
1896 xmlChar *out = NULL;
1897 int buffer_size = 0;
1898
Daniel Veillard9ff88172002-03-11 09:15:32 +00001899 if (input == NULL)
1900 return (NULL);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001901
1902 /*
1903 * allocate an translation buffer.
1904 */
1905 buffer_size = 1000;
Daniel Veillard3c908dc2003-04-19 00:07:51 +00001906 buffer = (xmlChar *) xmlMallocAtomic(buffer_size * sizeof(xmlChar));
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001907 if (buffer == NULL) {
Daniel Veillardd96cce12003-10-10 12:30:37 +00001908 xmlC14NErrMemory("allocating buffer");
Daniel Veillard9ff88172002-03-11 09:15:32 +00001909 return (NULL);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001910 }
1911 out = buffer;
1912
1913 while (*cur != '\0') {
Daniel Veillard9ff88172002-03-11 09:15:32 +00001914 if ((out - buffer) > (buffer_size - 10)) {
1915 int indx = out - buffer;
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001916
Daniel Veillard9ff88172002-03-11 09:15:32 +00001917 growBufferReentrant();
1918 out = &buffer[indx];
1919 }
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001920
Daniel Veillard9ff88172002-03-11 09:15:32 +00001921 if ((*cur == '<') && ((mode == XMLC14N_NORMALIZE_ATTR) ||
1922 (mode == XMLC14N_NORMALIZE_TEXT))) {
1923 *out++ = '&';
1924 *out++ = 'l';
1925 *out++ = 't';
1926 *out++ = ';';
1927 } else if ((*cur == '>') && (mode == XMLC14N_NORMALIZE_TEXT)) {
1928 *out++ = '&';
1929 *out++ = 'g';
1930 *out++ = 't';
1931 *out++ = ';';
1932 } else if ((*cur == '&') && ((mode == XMLC14N_NORMALIZE_ATTR) ||
1933 (mode == XMLC14N_NORMALIZE_TEXT))) {
1934 *out++ = '&';
1935 *out++ = 'a';
1936 *out++ = 'm';
1937 *out++ = 'p';
1938 *out++ = ';';
1939 } else if ((*cur == '"') && (mode == XMLC14N_NORMALIZE_ATTR)) {
1940 *out++ = '&';
1941 *out++ = 'q';
1942 *out++ = 'u';
1943 *out++ = 'o';
1944 *out++ = 't';
1945 *out++ = ';';
1946 } else if ((*cur == '\x09') && (mode == XMLC14N_NORMALIZE_ATTR)) {
1947 *out++ = '&';
1948 *out++ = '#';
1949 *out++ = 'x';
1950 *out++ = '9';
1951 *out++ = ';';
1952 } else if ((*cur == '\x0A') && (mode == XMLC14N_NORMALIZE_ATTR)) {
1953 *out++ = '&';
1954 *out++ = '#';
1955 *out++ = 'x';
1956 *out++ = 'A';
1957 *out++ = ';';
1958 } else if ((*cur == '\x0D') && ((mode == XMLC14N_NORMALIZE_ATTR) ||
1959 (mode == XMLC14N_NORMALIZE_TEXT) ||
1960 (mode == XMLC14N_NORMALIZE_COMMENT) ||
1961 (mode == XMLC14N_NORMALIZE_PI))) {
1962 *out++ = '&';
1963 *out++ = '#';
1964 *out++ = 'x';
1965 *out++ = 'D';
1966 *out++ = ';';
1967 } else {
1968 /*
1969 * Works because on UTF-8, all extended sequences cannot
1970 * result in bytes in the ASCII range.
1971 */
1972 *out++ = *cur;
1973 }
1974 cur++;
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001975 }
1976 *out++ = 0;
Daniel Veillard9ff88172002-03-11 09:15:32 +00001977 return (buffer);
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001978}
Daniel Veillarda9cce9c2003-09-29 13:20:24 +00001979#endif /* LIBXML_OUTPUT_ENABLED */
Daniel Veillard044fc6b2002-03-04 17:09:44 +00001980#endif /* LIBXML_C14N_ENABLED */