c14n.c 69.1 KB
Newer Older
1
/*
Daniel Veillard's avatar
Daniel Veillard committed
2
 * "Canonical XML" implementation
3
 * http://www.w3.org/TR/xml-c14n
Daniel Veillard's avatar
Daniel Veillard committed
4
 *
5 6 7 8
 * "Exclusive XML Canonicalization" implementation
 * http://www.w3.org/TR/xml-exc-c14n
 *
 * See Copyright for the status of this software.
Daniel Veillard's avatar
Daniel Veillard committed
9
 *
10 11
 * Author: Aleksey Sanin <aleksey@aleksey.com>
 */
12
#define IN_LIBXML
13 14
#include "libxml.h"
#ifdef LIBXML_C14N_ENABLED
15
#ifdef LIBXML_OUTPUT_ENABLED
16 17 18 19 20 21 22 23

#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
#include <string.h>

#include <libxml/tree.h>
#include <libxml/parser.h>
24
#include <libxml/uri.h>
25 26 27 28 29
#include <libxml/xmlerror.h>
#include <libxml/globals.h>
#include <libxml/xpathInternals.h>
#include <libxml/c14n.h>

30 31
#include "buf.h"

32 33 34 35 36 37
/************************************************************************
 *									*
 *		Some declaration better left private ATM		*
 *									*
 ************************************************************************/

38 39 40 41
typedef enum {
    XMLC14N_BEFORE_DOCUMENT_ELEMENT = 0,
    XMLC14N_INSIDE_DOCUMENT_ELEMENT = 1,
    XMLC14N_AFTER_DOCUMENT_ELEMENT = 2
42 43
} xmlC14NPosition;

44
typedef struct _xmlC14NVisibleNsStack {
45
    int nsCurEnd;           /* number of nodes in the set */
Jared Yanovich's avatar
Jared Yanovich committed
46
    int nsPrevStart;        /* the beginning of the stack for previous visible node */
47 48
    int nsPrevEnd;          /* the end of the stack for previous visible node */
    int nsMax;              /* size of the array as allocated */
Daniel Veillard's avatar
Daniel Veillard committed
49
    xmlNsPtr	*nsTab;	    /* array of ns in no particular order */
50
    xmlNodePtr	*nodeTab;   /* array of nodes in no particular order */
51
} xmlC14NVisibleNsStack, *xmlC14NVisibleNsStackPtr;
52

53 54
typedef struct _xmlC14NCtx {
    /* input parameters */
55
    xmlDocPtr doc;
56
    xmlC14NIsVisibleCallback is_visible_callback;
Daniel Veillard's avatar
Daniel Veillard committed
57
    void* user_data;
58 59
    int with_comments;
    xmlOutputBufferPtr buf;
60 61

    /* position in the XML document */
62 63
    xmlC14NPosition pos;
    int parent_is_doc;
64
    xmlC14NVisibleNsStackPtr ns_rendered;
Daniel Veillard's avatar
Daniel Veillard committed
65

66 67
    /* C14N mode */
    xmlC14NMode mode;
68 69

    /* exclusive canonicalization */
70
    xmlChar **inclusive_ns_prefixes;
71 72 73

    /* error number */
    int error;
74 75
} xmlC14NCtx, *xmlC14NCtxPtr;

76
static xmlC14NVisibleNsStackPtr	xmlC14NVisibleNsStackCreate	(void);
77
static void     xmlC14NVisibleNsStackDestroy	(xmlC14NVisibleNsStackPtr cur);
Daniel Veillard's avatar
Daniel Veillard committed
78
static void     xmlC14NVisibleNsStackAdd	    (xmlC14NVisibleNsStackPtr cur,
79 80
                                                 xmlNsPtr ns,
                                                 xmlNodePtr node);
Daniel Veillard's avatar
Daniel Veillard committed
81
static void			xmlC14NVisibleNsStackSave	(xmlC14NVisibleNsStackPtr cur,
82
								 xmlC14NVisibleNsStackPtr state);
Daniel Veillard's avatar
Daniel Veillard committed
83
static void			xmlC14NVisibleNsStackRestore	(xmlC14NVisibleNsStackPtr cur,
84
								 xmlC14NVisibleNsStackPtr state);
Daniel Veillard's avatar
Daniel Veillard committed
85 86
static void			xmlC14NVisibleNsStackShift	(xmlC14NVisibleNsStackPtr cur);
static int			xmlC14NVisibleNsStackFind	(xmlC14NVisibleNsStackPtr cur,
87
								 xmlNsPtr ns);
Daniel Veillard's avatar
Daniel Veillard committed
88
static int			xmlExcC14NVisibleNsStackFind	(xmlC14NVisibleNsStackPtr cur,
89 90 91
								 xmlNsPtr ns,
								 xmlC14NCtxPtr ctx);

Nick Wellnhofer's avatar
Nick Wellnhofer committed
92
static int			xmlC14NIsNodeInNodeset		(void *user_data,
93 94 95 96
								 xmlNodePtr node,
								 xmlNodePtr parent);


97

98 99
static int xmlC14NProcessNode(xmlC14NCtxPtr ctx, xmlNodePtr cur);
static int xmlC14NProcessNodeList(xmlC14NCtxPtr ctx, xmlNodePtr cur);
100
typedef enum {
101 102 103 104 105
    XMLC14N_NORMALIZE_ATTR = 0,
    XMLC14N_NORMALIZE_COMMENT = 1,
    XMLC14N_NORMALIZE_PI = 2,
    XMLC14N_NORMALIZE_TEXT = 3
} xmlC14NNormalizationMode;
106

107 108
static xmlChar *xmlC11NNormalizeString(const xmlChar * input,
                                       xmlC14NNormalizationMode mode);
109

Daniel Veillard's avatar
Daniel Veillard committed
110
#define	xmlC11NNormalizeAttr( a ) \
111
    xmlC11NNormalizeString((a), XMLC14N_NORMALIZE_ATTR)
Daniel Veillard's avatar
Daniel Veillard committed
112
#define	xmlC11NNormalizeComment( a ) \
113
    xmlC11NNormalizeString((a), XMLC14N_NORMALIZE_COMMENT)
Daniel Veillard's avatar
Daniel Veillard committed
114
#define	xmlC11NNormalizePI( a )	\
115
    xmlC11NNormalizeString((a), XMLC14N_NORMALIZE_PI)
Daniel Veillard's avatar
Daniel Veillard committed
116
#define	xmlC11NNormalizeText( a ) \
117
    xmlC11NNormalizeString((a), XMLC14N_NORMALIZE_TEXT)
118

Daniel Veillard's avatar
Daniel Veillard committed
119
#define	xmlC14NIsVisible( ctx, node, parent ) \
120 121 122
     (((ctx)->is_visible_callback != NULL) ? \
	(ctx)->is_visible_callback((ctx)->user_data, \
		(xmlNodePtr)(node), (xmlNodePtr)(parent)) : 1)
123

Daniel Veillard's avatar
Daniel Veillard committed
124
#define	xmlC14NIsExclusive( ctx ) \
125 126
    ( (ctx)->mode == XML_C14N_EXCLUSIVE_1_0 )

127 128
/************************************************************************
 *									*
Daniel Veillard's avatar
Daniel Veillard committed
129
 *		Some factorized error routines				*
130 131 132 133 134 135 136
 *									*
 ************************************************************************/

/**
 * xmlC14NErrMemory:
 * @extra:  extra informations
 *
137
 * Handle a redefinition of memory error
138 139 140 141
 */
static void
xmlC14NErrMemory(const char *extra)
{
142
    __xmlRaiseError(NULL, NULL, NULL, NULL, NULL, XML_FROM_C14N,
143 144 145 146 147
		    XML_ERR_NO_MEMORY, XML_ERR_ERROR, NULL, 0, extra,
		    NULL, NULL, 0, 0,
		    "Memory allocation failed : %s\n", extra);
}

148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224
/**
 * xmlC14NErrParam:
 * @extra:  extra informations
 *
 * Handle a redefinition of param error
 */
static void
xmlC14NErrParam(const char *extra)
{
    __xmlRaiseError(NULL, NULL, NULL, NULL, NULL, XML_FROM_C14N,
		    XML_ERR_INTERNAL_ERROR, XML_ERR_ERROR, NULL, 0, extra,
		    NULL, NULL, 0, 0,
		    "Invalid parameter : %s\n", extra);
}

/**
 * xmlC14NErrInternal:
 * @extra:  extra informations
 *
 * Handle a redefinition of internal error
 */
static void
xmlC14NErrInternal(const char *extra)
{
    __xmlRaiseError(NULL, NULL, NULL, NULL, NULL, XML_FROM_C14N,
		    XML_ERR_INTERNAL_ERROR, XML_ERR_ERROR, NULL, 0, extra,
		    NULL, NULL, 0, 0,
		    "Internal error : %s\n", extra);
}

/**
 * xmlC14NErrInvalidNode:
 * @extra:  extra informations
 *
 * Handle a redefinition of invalid node error
 */
static void
xmlC14NErrInvalidNode(const char *node_type, const char *extra)
{
    __xmlRaiseError(NULL, NULL, NULL, NULL, NULL, XML_FROM_C14N,
		    XML_C14N_INVALID_NODE, XML_ERR_ERROR, NULL, 0, extra,
		    NULL, NULL, 0, 0,
		    "Node %s is invalid here : %s\n", node_type, extra);
}

/**
 * xmlC14NErrUnknownNode:
 * @extra:  extra informations
 *
 * Handle a redefinition of unknown node error
 */
static void
xmlC14NErrUnknownNode(int node_type, const char *extra)
{
    __xmlRaiseError(NULL, NULL, NULL, NULL, NULL, XML_FROM_C14N,
		    XML_C14N_UNKNOW_NODE, XML_ERR_ERROR, NULL, 0, extra,
		    NULL, NULL, 0, 0,
		    "Unknown node type %d found : %s\n", node_type, extra);
}

/**
 * xmlC14NErrRelativeNamespace:
 * @extra:  extra informations
 *
 * Handle a redefinition of relative namespace error
 */
static void
xmlC14NErrRelativeNamespace(const char *ns_uri)
{
    __xmlRaiseError(NULL, NULL, NULL, NULL, NULL, XML_FROM_C14N,
		    XML_C14N_RELATIVE_NAMESPACE, XML_ERR_ERROR, NULL, 0, NULL,
		    NULL, NULL, 0, 0,
		    "Relative namespace UR is invalid here : %s\n", ns_uri);
}



225 226 227 228
/**
 * xmlC14NErr:
 * @ctxt:  a C14N evaluation context
 * @node:  the context node
Jared Yanovich's avatar
Jared Yanovich committed
229
 * @error:  the error code
230 231 232 233 234 235 236 237 238 239 240
 * @msg:  the message
 * @extra:  extra informations
 *
 * Handle a redefinition of attribute error
 */
static void
xmlC14NErr(xmlC14NCtxPtr ctxt, xmlNodePtr node, int error,
           const char * msg)
{
    if (ctxt != NULL)
        ctxt->error = error;
241
    __xmlRaiseError(NULL, NULL, NULL,
242 243
		    ctxt, node, XML_FROM_C14N, error,
		    XML_ERR_ERROR, NULL, 0,
244
		    NULL, NULL, NULL, 0, 0, "%s", msg);
245 246
}

247 248 249 250 251
/************************************************************************
 *									*
 *		The implementation internals				*
 *									*
 ************************************************************************/
252 253
#define XML_NAMESPACES_DEFAULT		16

Daniel Veillard's avatar
Daniel Veillard committed
254
static int
Nick Wellnhofer's avatar
Nick Wellnhofer committed
255 256
xmlC14NIsNodeInNodeset(void *user_data, xmlNodePtr node, xmlNodePtr parent) {
    xmlNodeSetPtr nodes = (xmlNodeSetPtr) user_data;
257 258 259 260 261
    if((nodes != NULL) && (node != NULL)) {
	if(node->type != XML_NAMESPACE_DECL) {
	    return(xmlXPathNodeSetContains(nodes, node));
	} else {
	    xmlNs ns;
Daniel Veillard's avatar
Daniel Veillard committed
262 263 264

	    memcpy(&ns, node, sizeof(ns));

265 266 267 268
	    /* this is a libxml hack! check xpath.c for details */
	    if((parent != NULL) && (parent->type == XML_ATTRIBUTE_NODE)) {
		ns.next = (xmlNsPtr)parent->parent;
	    } else {
Daniel Veillard's avatar
Daniel Veillard committed
269
		ns.next = (xmlNsPtr)parent;
270
	    }
271

Daniel Veillard's avatar
Daniel Veillard committed
272 273
	    /*
	     * If the input is an XPath node-set, then the node-set must explicitly
274 275 276 277 278 279 280 281
	     * contain every node to be rendered to the canonical form.
	     */
	    return(xmlXPathNodeSetContains(nodes, (xmlNodePtr)&ns));
	}
    }
    return(1);
}

282 283 284
static xmlC14NVisibleNsStackPtr
xmlC14NVisibleNsStackCreate(void) {
    xmlC14NVisibleNsStackPtr ret;
285

286
    ret = (xmlC14NVisibleNsStackPtr) xmlMalloc(sizeof(xmlC14NVisibleNsStack));
287
    if (ret == NULL) {
288
        xmlC14NErrMemory("creating namespaces stack");
289 290
	return(NULL);
    }
291
    memset(ret, 0 , (size_t) sizeof(xmlC14NVisibleNsStack));
292 293 294 295
    return(ret);
}

static void
296
xmlC14NVisibleNsStackDestroy(xmlC14NVisibleNsStackPtr cur) {
297
    if(cur == NULL) {
298 299
        xmlC14NErrParam("destroying namespaces stack");
        return;
300 301 302 303 304
    }
    if(cur->nsTab != NULL) {
	memset(cur->nsTab, 0, cur->nsMax * sizeof(xmlNsPtr));
	xmlFree(cur->nsTab);
    }
Aleksey Sanin's avatar
Aleksey Sanin committed
305 306 307 308
    if(cur->nodeTab != NULL) {
	memset(cur->nodeTab, 0, cur->nsMax * sizeof(xmlNodePtr));
	xmlFree(cur->nodeTab);
    }
309
    memset(cur, 0, sizeof(xmlC14NVisibleNsStack));
310
    xmlFree(cur);
Daniel Veillard's avatar
Daniel Veillard committed
311

312 313
}

Daniel Veillard's avatar
Daniel Veillard committed
314
static void
315
xmlC14NVisibleNsStackAdd(xmlC14NVisibleNsStackPtr cur, xmlNsPtr ns, xmlNodePtr node) {
Daniel Veillard's avatar
Daniel Veillard committed
316
    if((cur == NULL) ||
317 318
       ((cur->nsTab == NULL) && (cur->nodeTab != NULL)) ||
       ((cur->nsTab != NULL) && (cur->nodeTab == NULL))) {
319
        xmlC14NErrParam("adding namespace to stack");
320 321 322
	return;
    }

323
    if ((cur->nsTab == NULL) && (cur->nodeTab == NULL)) {
324
        cur->nsTab = (xmlNsPtr*) xmlMalloc(XML_NAMESPACES_DEFAULT * sizeof(xmlNsPtr));
325 326
        cur->nodeTab = (xmlNodePtr*) xmlMalloc(XML_NAMESPACES_DEFAULT * sizeof(xmlNodePtr));
	if ((cur->nsTab == NULL) || (cur->nodeTab == NULL)) {
327
	    xmlC14NErrMemory("adding node to stack");
328 329 330
	    return;
	}
	memset(cur->nsTab, 0 , XML_NAMESPACES_DEFAULT * sizeof(xmlNsPtr));
331
	memset(cur->nodeTab, 0 , XML_NAMESPACES_DEFAULT * sizeof(xmlNodePtr));
332
        cur->nsMax = XML_NAMESPACES_DEFAULT;
333
    } else if(cur->nsMax == cur->nsCurEnd) {
Daniel Veillard's avatar
Daniel Veillard committed
334
	void *tmp;
335
	int tmpSize;
Daniel Veillard's avatar
Daniel Veillard committed
336

337
	tmpSize = 2 * cur->nsMax;
338
	tmp = xmlRealloc(cur->nsTab, tmpSize * sizeof(xmlNsPtr));
339
	if (tmp == NULL) {
340
	    xmlC14NErrMemory("adding node to stack");
341 342
	    return;
	}
343 344 345 346
	cur->nsTab = (xmlNsPtr*)tmp;

	tmp = xmlRealloc(cur->nodeTab, tmpSize * sizeof(xmlNodePtr));
	if (tmp == NULL) {
347
	    xmlC14NErrMemory("adding node to stack");
348 349 350 351
	    return;
	}
	cur->nodeTab = (xmlNodePtr*)tmp;

352 353
	cur->nsMax = tmpSize;
    }
354 355 356 357
    cur->nsTab[cur->nsCurEnd] = ns;
    cur->nodeTab[cur->nsCurEnd] = node;

    ++cur->nsCurEnd;
358 359 360 361 362
}

static void
xmlC14NVisibleNsStackSave(xmlC14NVisibleNsStackPtr cur, xmlC14NVisibleNsStackPtr state) {
    if((cur == NULL) || (state == NULL)) {
363
        xmlC14NErrParam("saving namespaces stack");
364 365
	return;
    }
Daniel Veillard's avatar
Daniel Veillard committed
366

367 368 369 370 371 372 373 374
    state->nsCurEnd = cur->nsCurEnd;
    state->nsPrevStart = cur->nsPrevStart;
    state->nsPrevEnd = cur->nsPrevEnd;
}

static void
xmlC14NVisibleNsStackRestore(xmlC14NVisibleNsStackPtr cur, xmlC14NVisibleNsStackPtr state) {
    if((cur == NULL) || (state == NULL)) {
375
        xmlC14NErrParam("restoring namespaces stack");
376 377 378 379 380 381 382
	return;
    }
    cur->nsCurEnd = state->nsCurEnd;
    cur->nsPrevStart = state->nsPrevStart;
    cur->nsPrevEnd = state->nsPrevEnd;
}

Daniel Veillard's avatar
Daniel Veillard committed
383
static void
384 385
xmlC14NVisibleNsStackShift(xmlC14NVisibleNsStackPtr cur) {
    if(cur == NULL) {
386
        xmlC14NErrParam("shifting namespaces stack");
387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405
	return;
    }
    cur->nsPrevStart = cur->nsPrevEnd;
    cur->nsPrevEnd = cur->nsCurEnd;
}

static int
xmlC14NStrEqual(const xmlChar *str1, const xmlChar *str2) {
    if (str1 == str2) return(1);
    if (str1 == NULL) return((*str2) == '\0');
    if (str2 == NULL) return((*str1) == '\0');
    do {
	if (*str1++ != *str2) return(0);
    } while (*str2++);
    return(1);
}

/**
 * xmlC14NVisibleNsStackFind:
Daniel Veillard's avatar
Daniel Veillard committed
406
 * @ctx:		the C14N context
407
 * @ns:			the namespace to check
408 409 410 411 412 413
 *
 * Checks whether the given namespace was already rendered or not
 *
 * Returns 1 if we already wrote this namespace or 0 otherwise
 */
static int
414
xmlC14NVisibleNsStackFind(xmlC14NVisibleNsStackPtr cur, xmlNsPtr ns)
415 416 417 418
{
    int i;
    const xmlChar *prefix;
    const xmlChar *href;
419
    int has_empty_ns;
Daniel Veillard's avatar
Daniel Veillard committed
420

421
    if(cur == NULL) {
422
        xmlC14NErrParam("searching namespaces stack (c14n)");
423 424 425 426
        return (0);
    }

    /*
Daniel Veillard's avatar
Daniel Veillard committed
427
     * if the default namespace xmlns="" is not defined yet then
428 429 430 431
     * we do not want to print it out
     */
    prefix = ((ns == NULL) || (ns->prefix == NULL)) ? BAD_CAST "" : ns->prefix;
    href = ((ns == NULL) || (ns->href == NULL)) ? BAD_CAST "" : ns->href;
432 433
    has_empty_ns = (xmlC14NStrEqual(prefix, NULL) && xmlC14NStrEqual(href, NULL));

434
    if (cur->nsTab != NULL) {
435
	int start = (has_empty_ns) ? 0 : cur->nsPrevStart;
436 437
        for (i = cur->nsCurEnd - 1; i >= start; --i) {
            xmlNsPtr ns1 = cur->nsTab[i];
Daniel Veillard's avatar
Daniel Veillard committed
438

439 440 441 442 443
	    if(xmlC14NStrEqual(prefix, (ns1 != NULL) ? ns1->prefix : NULL)) {
		return(xmlC14NStrEqual(href, (ns1 != NULL) ? ns1->href : NULL));
	    }
        }
    }
444
    return(has_empty_ns);
445 446
}

Daniel Veillard's avatar
Daniel Veillard committed
447
static int
448 449 450 451 452
xmlExcC14NVisibleNsStackFind(xmlC14NVisibleNsStackPtr cur, xmlNsPtr ns, xmlC14NCtxPtr ctx) {
    int i;
    const xmlChar *prefix;
    const xmlChar *href;
    int has_empty_ns;
Daniel Veillard's avatar
Daniel Veillard committed
453

454
    if(cur == NULL) {
455
        xmlC14NErrParam("searching namespaces stack (exc c14n)");
456 457
        return (0);
    }
458

459
    /*
Daniel Veillard's avatar
Daniel Veillard committed
460
     * if the default namespace xmlns="" is not defined yet then
461
     * we do not want to print it out
462
     */
463 464 465
    prefix = ((ns == NULL) || (ns->prefix == NULL)) ? BAD_CAST "" : ns->prefix;
    href = ((ns == NULL) || (ns->href == NULL)) ? BAD_CAST "" : ns->href;
    has_empty_ns = (xmlC14NStrEqual(prefix, NULL) && xmlC14NStrEqual(href, NULL));
466

467 468 469 470
    if (cur->nsTab != NULL) {
	int start = 0;
        for (i = cur->nsCurEnd - 1; i >= start; --i) {
            xmlNsPtr ns1 = cur->nsTab[i];
Daniel Veillard's avatar
Daniel Veillard committed
471

472 473
	    if(xmlC14NStrEqual(prefix, (ns1 != NULL) ? ns1->prefix : NULL)) {
		if(xmlC14NStrEqual(href, (ns1 != NULL) ? ns1->href : NULL)) {
Daniel Veillard's avatar
Daniel Veillard committed
474
		    return(xmlC14NIsVisible(ctx, ns1, cur->nodeTab[i]));
475 476 477 478 479 480 481
		} else {
		    return(0);
		}
	    }
        }
    }
    return(has_empty_ns);
482 483
}

484 485 486



487 488
/**
 * xmlC14NIsXmlNs:
Daniel Veillard's avatar
Daniel Veillard committed
489 490
 * @ns:		the namespace to check
 *
491 492 493 494 495
 * Checks whether the given namespace is a default "xml:" namespace
 * with href="http://www.w3.org/XML/1998/namespace"
 *
 * Returns 1 if the node is default or 0 otherwise
 */
496

497 498
/* todo: make it a define? */
static int
499 500 501 502
xmlC14NIsXmlNs(xmlNsPtr ns)
{
    return ((ns != NULL) &&
            (xmlStrEqual(ns->prefix, BAD_CAST "xml")) &&
503
            (xmlStrEqual(ns->href, XML_XML_NAMESPACE)));
504 505 506 507
}


/**
508
 * xmlC14NNsCompare:
509
 * @ns1:		the pointer to first namespace
Daniel Veillard's avatar
Daniel Veillard committed
510
 * @ns2:		the pointer to second namespace
511 512 513 514 515
 *
 * Compares the namespaces by names (prefixes).
 *
 * Returns -1 if ns1 < ns2, 0 if ns1 == ns2 or 1 if ns1 > ns2.
 */
516
static int
Nick Wellnhofer's avatar
Nick Wellnhofer committed
517
xmlC14NNsCompare(const void *data1, const void *data2)
518
{
Nick Wellnhofer's avatar
Nick Wellnhofer committed
519 520
    const xmlNsPtr ns1 = (const xmlNsPtr) data1;
    const xmlNsPtr ns2 = (const xmlNsPtr) data2;
521 522 523 524 525 526 527 528
    if (ns1 == ns2)
        return (0);
    if (ns1 == NULL)
        return (-1);
    if (ns2 == NULL)
        return (1);

    return (xmlStrcmp(ns1->prefix, ns2->prefix));
529 530 531 532 533 534
}


/**
 * xmlC14NPrintNamespaces:
 * @ns:			the pointer to namespace
Daniel Veillard's avatar
Daniel Veillard committed
535
 * @ctx:		the C14N context
536 537 538 539 540 541
 *
 * Prints the given namespace to the output buffer from C14N context.
 *
 * Returns 1 on success or 0 on fail.
 */
static int
542 543
xmlC14NPrintNamespaces(const xmlNsPtr ns, xmlC14NCtxPtr ctx)
{
544

545
    if ((ns == NULL) || (ctx == NULL)) {
546
        xmlC14NErrParam("writing namespaces");
547
        return 0;
548 549
    }

550 551 552
    if (ns->prefix != NULL) {
        xmlOutputBufferWriteString(ctx->buf, " xmlns:");
        xmlOutputBufferWriteString(ctx->buf, (const char *) ns->prefix);
553
        xmlOutputBufferWriteString(ctx->buf, "=");
554
    } else {
555
        xmlOutputBufferWriteString(ctx->buf, " xmlns=");
556
    }
557
    if(ns->href != NULL) {
558 559 560
	xmlBufWriteQuotedString(ctx->buf->buffer, ns->href);
    } else {
    	xmlOutputBufferWriteString(ctx->buf, "\"\"");
561
    }
562
    return (1);
563 564
}

Nick Wellnhofer's avatar
Nick Wellnhofer committed
565 566 567 568 569
static int
xmlC14NPrintNamespacesWalker(const void *ns, void *ctx) {
    return xmlC14NPrintNamespaces((const xmlNsPtr) ns, (xmlC14NCtxPtr) ctx);
}

570 571
/**
 * xmlC14NProcessNamespacesAxis:
Daniel Veillard's avatar
Daniel Veillard committed
572
 * @ctx:		the C14N context
573 574 575
 * @node:		the current node
 *
 * Prints out canonical namespace axis of the current node to the
Daniel Veillard's avatar
Daniel Veillard committed
576
 * buffer from C14N context as follows
577 578 579 580
 *
 * Canonical XML v 1.0 (http://www.w3.org/TR/xml-c14n)
 *
 * Namespace Axis
Daniel Veillard's avatar
Daniel Veillard committed
581 582 583 584
 * Consider a list L containing only namespace nodes in the
 * axis and in the node-set in lexicographic order (ascending). To begin
 * processing L, if the first node is not the default namespace node (a node
 * with no namespace URI and no local name), then generate a space followed
585 586
 * by xmlns="" if and only if the following conditions are met:
 *    - the element E that owns the axis is in the node-set
Daniel Veillard's avatar
Daniel Veillard committed
587 588
 *    - The nearest ancestor element of E in the node-set has a default
 *	    namespace node in the node-set (default namespace nodes always
589
 *      have non-empty values in XPath)
Daniel Veillard's avatar
Daniel Veillard committed
590 591 592 593 594 595
 * The latter condition eliminates unnecessary occurrences of xmlns="" in
 * the canonical form since an element only receives an xmlns="" if its
 * default namespace is empty and if it has an immediate parent in the
 * canonical form that has a non-empty default namespace. To finish
 * processing  L, simply process every namespace node in L, except omit
 * namespace node with local name xml, which defines the xml prefix,
596 597 598
 * if its string value is http://www.w3.org/XML/1998/namespace.
 *
 * Exclusive XML Canonicalization v 1.0 (http://www.w3.org/TR/xml-exc-c14n)
Daniel Veillard's avatar
Daniel Veillard committed
599 600 601 602 603 604
 * Canonical XML applied to a document subset requires the search of the
 * ancestor nodes of each orphan element node for attributes in the xml
 * namespace, such as xml:lang and xml:space. These are copied into the
 * element node except if a declaration of the same attribute is already
 * in the attribute axis of the element (whether or not it is included in
 * the document subset). This search and copying are omitted from the
605 606 607 608 609
 * Exclusive XML Canonicalization method.
 *
 * Returns 0 on success or -1 on fail.
 */
static int
610
xmlC14NProcessNamespacesAxis(xmlC14NCtxPtr ctx, xmlNodePtr cur, int visible)
611
{
612 613
    xmlNodePtr n;
    xmlNsPtr ns, tmp;
614
    xmlListPtr list;
615 616
    int already_rendered;
    int has_empty_ns = 0;
Daniel Veillard's avatar
Daniel Veillard committed
617

618
    if ((ctx == NULL) || (cur == NULL) || (cur->type != XML_ELEMENT_NODE)) {
619
        xmlC14NErrParam("processing namespaces axis (c14n)");
620 621
        return (-1);
    }
622 623 624 625

    /*
     * Create a sorted list to store element namespaces
     */
Nick Wellnhofer's avatar
Nick Wellnhofer committed
626
    list = xmlListCreate(NULL, xmlC14NNsCompare);
627
    if (list == NULL) {
628
        xmlC14NErrInternal("creating namespaces list (c14n)");
629 630 631
        return (-1);
    }

632 633 634 635
    /* check all namespaces */
    for(n = cur; n != NULL; n = n->parent) {
	for(ns = n->nsDef; ns != NULL; ns = ns->next) {
	    tmp = xmlSearchNs(cur->doc, cur, ns->prefix);
Daniel Veillard's avatar
Daniel Veillard committed
636

637 638 639
	    if((tmp == ns) && !xmlC14NIsXmlNs(ns) && xmlC14NIsVisible(ctx, ns, cur)) {
		already_rendered = xmlC14NVisibleNsStackFind(ctx->ns_rendered, ns);
		if(visible) {
Daniel Veillard's avatar
Daniel Veillard committed
640
	    xmlC14NVisibleNsStackAdd(ctx->ns_rendered, ns, cur);
641 642
		}
		if(!already_rendered) {
Daniel Veillard's avatar
Daniel Veillard committed
643
		    xmlListInsert(list, ns);
644
		}
Daniel Veillard's avatar
Daniel Veillard committed
645
		if(xmlStrlen(ns->prefix) == 0) {
646 647
		    has_empty_ns = 1;
		}
648
	    }
649
	}
650
    }
Daniel Veillard's avatar
Daniel Veillard committed
651

652
    /**
Daniel Veillard's avatar
Daniel Veillard committed
653 654
     * if the first node is not the default namespace node (a node with no
     * namespace URI and no local name), then generate a space followed by
655 656
     * xmlns="" if and only if the following conditions are met:
     *  - the element E that owns the axis is in the node-set
Daniel Veillard's avatar
Daniel Veillard committed
657 658
     *  - the nearest ancestor element of E in the node-set has a default
     *     namespace node in the node-set (default namespace nodes always
659 660 661 662 663 664 665
     *     have non-empty values in XPath)
     */
    if(visible && !has_empty_ns) {
        static xmlNs ns_default;

        memset(&ns_default, 0, sizeof(ns_default));
        if(!xmlC14NVisibleNsStackFind(ctx->ns_rendered, &ns_default)) {
Daniel Veillard's avatar
Daniel Veillard committed
666
	    xmlC14NPrintNamespaces(&ns_default, ctx);
667
	}
668
    }
Daniel Veillard's avatar
Daniel Veillard committed
669 670 671 672


    /*
     * print out all elements from list
673
     */
Nick Wellnhofer's avatar
Nick Wellnhofer committed
674
    xmlListWalk(list, xmlC14NPrintNamespacesWalker, (void *) ctx);
675

Daniel Veillard's avatar
Daniel Veillard committed
676
    /*
677 678 679
     * Cleanup
     */
    xmlListDelete(list);
680
    return (0);
681 682
}

683

684 685
/**
 * xmlExcC14NProcessNamespacesAxis:
Daniel Veillard's avatar
Daniel Veillard committed
686
 * @ctx:		the C14N context
687 688 689
 * @node:		the current node
 *
 * Prints out exclusive canonical namespace axis of the current node to the
Daniel Veillard's avatar
Daniel Veillard committed
690
 * buffer from C14N context as follows
691 692 693 694
 *
 * Exclusive XML Canonicalization
 * http://www.w3.org/TR/xml-exc-c14n
 *
Daniel Veillard's avatar
Daniel Veillard committed
695 696
 * If the element node is in the XPath subset then output the node in
 * accordance with Canonical XML except for namespace nodes which are
697 698 699
 * rendered as follows:
 *
 * 1. Render each namespace node iff:
Daniel Veillard's avatar
Daniel Veillard committed
700
 *    * it is visibly utilized by the immediate parent element or one of
701
 *      its attributes, or is present in InclusiveNamespaces PrefixList, and
Daniel Veillard's avatar
Daniel Veillard committed
702 703 704
 *    * its prefix and value do not appear in ns_rendered. ns_rendered is
 *      obtained by popping the state stack in order to obtain a list of
 *      prefixes and their values which have already been rendered by
705
 *      an output ancestor of the namespace node's parent element.
Daniel Veillard's avatar
Daniel Veillard committed
706 707
 * 2. Append the rendered namespace node to the list ns_rendered of namespace
 * nodes rendered by output ancestors. Push ns_rendered on state stack and
708 709 710 711 712 713 714
 * recurse.
 * 3. After the recursion returns, pop thestate stack.
 *
 *
 * Returns 0 on success or -1 on fail.
 */
static int
715
xmlExcC14NProcessNamespacesAxis(xmlC14NCtxPtr ctx, xmlNodePtr cur, int visible)
716
{
717
    xmlNsPtr ns;
718 719
    xmlListPtr list;
    xmlAttrPtr attr;
720 721 722 723
    int already_rendered;
    int has_empty_ns = 0;
    int has_visibly_utilized_empty_ns = 0;
    int has_empty_ns_in_inclusive_list = 0;
Daniel Veillard's avatar
Daniel Veillard committed
724

725
    if ((ctx == NULL) || (cur == NULL) || (cur->type != XML_ELEMENT_NODE)) {
726
        xmlC14NErrParam("processing namespaces axis (exc c14n)");
727 728 729
        return (-1);
    }

730
    if(!xmlC14NIsExclusive(ctx)) {
731
        xmlC14NErrParam("processing namespaces axis (exc c14n)");
732 733
        return (-1);

734
    }
735

736 737 738
    /*
     * Create a sorted list to store element namespaces
     */
Nick Wellnhofer's avatar
Nick Wellnhofer committed
739
    list = xmlListCreate(NULL, xmlC14NNsCompare);
740
    if (list == NULL) {
741
        xmlC14NErrInternal("creating namespaces list (exc c14n)");
742 743
        return (-1);
    }
744

Daniel Veillard's avatar
Daniel Veillard committed
745
    /*
746
     * process inclusive namespaces:
Daniel Veillard's avatar
Daniel Veillard committed
747
     * All namespace nodes appearing on inclusive ns list are
748
     * handled as provided in Canonical XML
749
     */
750
    if(ctx->inclusive_ns_prefixes != NULL) {
Daniel Veillard's avatar
Daniel Veillard committed
751
	xmlChar *prefix;
752
	int i;
Daniel Veillard's avatar
Daniel Veillard committed
753

754 755 756 757 758 759 760 761 762 763
	for (i = 0; ctx->inclusive_ns_prefixes[i] != NULL; ++i) {
	    prefix = ctx->inclusive_ns_prefixes[i];
	    /*
	     * Special values for namespace with empty prefix
	     */
            if (xmlStrEqual(prefix, BAD_CAST "#default")
                || xmlStrEqual(prefix, BAD_CAST "")) {
                prefix = NULL;
		has_empty_ns_in_inclusive_list = 1;
            }
Daniel Veillard's avatar
Daniel Veillard committed
764 765

	    ns = xmlSearchNs(cur->doc, cur, prefix);
766 767 768
	    if((ns != NULL) && !xmlC14NIsXmlNs(ns) && xmlC14NIsVisible(ctx, ns, cur)) {
		already_rendered = xmlC14NVisibleNsStackFind(ctx->ns_rendered, ns);
		if(visible) {
Daniel Veillard's avatar
Daniel Veillard committed
769
		    xmlC14NVisibleNsStackAdd(ctx->ns_rendered, ns, cur);
770 771
		}
		if(!already_rendered) {
Daniel Veillard's avatar
Daniel Veillard committed
772
		    xmlListInsert(list, ns);
773
		}
Daniel Veillard's avatar
Daniel Veillard committed
774
		if(xmlStrlen(ns->prefix) == 0) {
775 776
		    has_empty_ns = 1;
		}
777
	    }
778 779
	}
    }
Daniel Veillard's avatar
Daniel Veillard committed
780

781 782 783 784 785 786 787 788
    /* add node namespace */
    if(cur->ns != NULL) {
	ns = cur->ns;
    } else {
        ns = xmlSearchNs(cur->doc, cur, NULL);
	has_visibly_utilized_empty_ns = 1;
    }
    if((ns != NULL) && !xmlC14NIsXmlNs(ns)) {
Daniel Veillard's avatar
Daniel Veillard committed
789
	if(visible && xmlC14NIsVisible(ctx, ns, cur)) {
790 791
	    if(!xmlExcC14NVisibleNsStackFind(ctx->ns_rendered, ns, ctx)) {
		xmlListInsert(list, ns);
792
	    }
793
	}
794
	if(visible) {
Daniel Veillard's avatar
Daniel Veillard committed
795
	    xmlC14NVisibleNsStackAdd(ctx->ns_rendered, ns, cur);
796 797 798 799
	}
	if(xmlStrlen(ns->prefix) == 0) {
	    has_empty_ns = 1;
	}
800
    }
Daniel Veillard's avatar
Daniel Veillard committed
801 802


803 804
    /* add attributes */
    for(attr = cur->properties; attr != NULL; attr = attr->next) {
Daniel Veillard's avatar
Daniel Veillard committed
805
        /*
806
         * we need to check that attribute is visible and has non
Daniel Veillard's avatar
Daniel Veillard committed
807 808
         * default namespace (XML Namespaces: "default namespaces
	 * do not apply directly to attributes")
809
         */
810
	if((attr->ns != NULL) && !xmlC14NIsXmlNs(attr->ns) && xmlC14NIsVisible(ctx, attr, cur)) {
811
	    already_rendered = xmlExcC14NVisibleNsStackFind(ctx->ns_rendered, attr->ns, ctx);
Daniel Veillard's avatar
Daniel Veillard committed
812
	    xmlC14NVisibleNsStackAdd(ctx->ns_rendered, attr->ns, cur);
813
	    if(!already_rendered && visible) {
Daniel Veillard's avatar
Daniel Veillard committed
814
		xmlListInsert(list, attr->ns);
815 816 817 818
	    }
	    if(xmlStrlen(attr->ns->prefix) == 0) {
		has_empty_ns = 1;
	    }
819
	} else if((attr->ns != NULL) && (xmlStrlen(attr->ns->prefix) == 0) && (xmlStrlen(attr->ns->href) == 0)) {
820 821
	    has_visibly_utilized_empty_ns = 1;
	}
822
    }
823

824 825
    /*
     * Process xmlns=""
826
     */
Daniel Veillard's avatar
Daniel Veillard committed
827
    if(visible && has_visibly_utilized_empty_ns &&
828 829
	    !has_empty_ns && !has_empty_ns_in_inclusive_list) {
        static xmlNs ns_default;
830

831
        memset(&ns_default, 0, sizeof(ns_default));
Daniel Veillard's avatar
Daniel Veillard committed
832

833 834
        already_rendered = xmlExcC14NVisibleNsStackFind(ctx->ns_rendered, &ns_default, ctx);
	if(!already_rendered) {
Daniel Veillard's avatar
Daniel Veillard committed
835
	    xmlC14NPrintNamespaces(&ns_default, ctx);
836 837 838 839 840 841
	}
    } else if(visible && !has_empty_ns && has_empty_ns_in_inclusive_list) {
        static xmlNs ns_default;

        memset(&ns_default, 0, sizeof(ns_default));
        if(!xmlC14NVisibleNsStackFind(ctx->ns_rendered, &ns_default)) {
Daniel Veillard's avatar
Daniel Veillard committed
842
	    xmlC14NPrintNamespaces(&ns_default, ctx);
843
	}
844
    }
845

846

Daniel Veillard's avatar
Daniel Veillard committed
847 848 849

    /*
     * print out all elements from list
850
     */
Nick Wellnhofer's avatar
Nick Wellnhofer committed
851
    xmlListWalk(list, xmlC14NPrintNamespacesWalker, (void *) ctx);
852

Daniel Veillard's avatar
Daniel Veillard committed
853
    /*
854 855 856
     * Cleanup
     */
    xmlListDelete(list);
857
    return (0);
858 859 860
}


861 862
/**
 * xmlC14NIsXmlAttr:
Daniel Veillard's avatar
Daniel Veillard committed
863 864
 * @attr:		the attr to check
 *
865 866 867 868 869 870 871 872 873 874
 * Checks whether the given attribute is a default "xml:" namespace
 * with href="http://www.w3.org/XML/1998/namespace"
 *
 * Returns 1 if the node is default or 0 otherwise
 */

/* todo: make it a define? */
static int
xmlC14NIsXmlAttr(xmlAttrPtr attr)
{
Daniel Veillard's avatar
Daniel Veillard committed
875
    return ((attr->ns != NULL) &&
876 877 878 879
           (xmlC14NIsXmlNs(attr->ns) != 0));
}


880 881
/**
 * xmlC14NAttrsCompare:
882
 * @attr1:		the pointer tls o first attr
Daniel Veillard's avatar
Daniel Veillard committed
883
 * @attr2:		the pointer to second attr
884 885 886 887 888 889
 *
 * Prints the given attribute to the output buffer from C14N context.
 *
 * Returns -1 if attr1 < attr2, 0 if attr1 == attr2 or 1 if attr1 > attr2.
 */
static int
Nick Wellnhofer's avatar
Nick Wellnhofer committed
890
xmlC14NAttrsCompare(const void *data1, const void *data2)
891
{
Nick Wellnhofer's avatar
Nick Wellnhofer committed
892 893
    const xmlAttrPtr attr1 = (const xmlAttrPtr) data1;
    const xmlAttrPtr attr2 = (const xmlAttrPtr) data2;
894 895 896 897 898
    int ret = 0;

    /*
     * Simple cases
     */
899 900 901 902 903 904 905 906
    if (attr1 == attr2)
        return (0);
    if (attr1 == NULL)
        return (-1);
    if (attr2 == NULL)
        return (1);
    if (attr1->ns == attr2->ns) {
        return (xmlStrcmp(attr1->name, attr2->name));
907 908
    }

Daniel Veillard's avatar
Daniel Veillard committed
909
    /*
910 911 912 913
     * Attributes in the default namespace are first
     * because the default namespace is not applied to
     * unqualified attributes
     */
914 915 916 917 918 919 920 921 922
    if (attr1->ns == NULL)
        return (-1);
    if (attr2->ns == NULL)
        return (1);
    if (attr1->ns->prefix == NULL)
        return (-1);
    if (attr2->ns->prefix == NULL)
        return (1);

923
    ret = xmlStrcmp(attr1->ns->href, attr2->ns->href);
924 925
    if (ret == 0) {
        ret = xmlStrcmp(attr1->name, attr2->name);
926
    }
927
    return (ret);
928 929 930 931 932 933
}


/**
 * xmlC14NPrintAttrs:
 * @attr:		the pointer to attr
Daniel Veillard's avatar
Daniel Veillard committed
934
 * @ctx:		the C14N context
935 936
 *
 * Prints out canonical attribute urrent node to the
Daniel Veillard's avatar
Daniel Veillard committed