Coverage Report

Created: 2026-05-30 06:20

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libxml2/c14n.c
Line
Count
Source
1
/*
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
11
 */
12
#define IN_LIBXML
13
#include "libxml.h"
14
#ifdef LIBXML_C14N_ENABLED
15
16
#include <stdlib.h>
17
#include <string.h>
18
19
#include <libxml/tree.h>
20
#include <libxml/parser.h>
21
#include <libxml/uri.h>
22
#include <libxml/xmlerror.h>
23
#include <libxml/xpathInternals.h>
24
#include <libxml/c14n.h>
25
26
#include "private/error.h"
27
#include "private/io.h"
28
#include "private/memory.h"
29
30
/************************************************************************
31
 *                  *
32
 *    Some declaration better left private ATM    *
33
 *                  *
34
 ************************************************************************/
35
36
typedef enum {
37
    XMLC14N_BEFORE_DOCUMENT_ELEMENT = 0,
38
    XMLC14N_INSIDE_DOCUMENT_ELEMENT = 1,
39
    XMLC14N_AFTER_DOCUMENT_ELEMENT = 2
40
} xmlC14NPosition;
41
42
typedef struct _xmlC14NVisibleNsStack {
43
    int nsCurEnd;           /* number of nodes in the set */
44
    int nsPrevStart;        /* the beginning 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 */
49
} xmlC14NVisibleNsStack, *xmlC14NVisibleNsStackPtr;
50
51
typedef struct _xmlC14NCtx {
52
    /* input parameters */
53
    xmlDocPtr doc;
54
    xmlC14NIsVisibleCallback is_visible_callback;
55
    void* user_data;
56
    int with_comments;
57
    xmlOutputBufferPtr buf;
58
59
    /* position in the XML document */
60
    xmlC14NPosition pos;
61
    int parent_is_doc;
62
    xmlC14NVisibleNsStackPtr ns_rendered;
63
64
    /* C14N mode */
65
    xmlC14NMode mode;
66
67
    /* exclusive canonicalization */
68
    xmlChar **inclusive_ns_prefixes;
69
70
    /* error number */
71
    int error;
72
} xmlC14NCtx, *xmlC14NCtxPtr;
73
74
static xmlC14NVisibleNsStackPtr xmlC14NVisibleNsStackCreate (void);
75
static void     xmlC14NVisibleNsStackDestroy  (xmlC14NVisibleNsStackPtr cur);
76
static int      xmlC14NVisibleNsStackAdd      (xmlC14NVisibleNsStackPtr cur,
77
                                                 xmlNsPtr ns,
78
                                                 xmlNodePtr node);
79
static void     xmlC14NVisibleNsStackSave (xmlC14NVisibleNsStackPtr cur,
80
                 xmlC14NVisibleNsStackPtr state);
81
static void     xmlC14NVisibleNsStackRestore  (xmlC14NVisibleNsStackPtr cur,
82
                 xmlC14NVisibleNsStackPtr state);
83
static void     xmlC14NVisibleNsStackShift  (xmlC14NVisibleNsStackPtr cur);
84
static int      xmlC14NVisibleNsStackFind (xmlC14NVisibleNsStackPtr cur,
85
                 xmlNsPtr ns);
86
static int      xmlExcC14NVisibleNsStackFind  (xmlC14NVisibleNsStackPtr cur,
87
                 xmlNsPtr ns,
88
                 xmlC14NCtxPtr ctx);
89
90
static int      xmlC14NIsNodeInNodeset    (void *user_data,
91
                 xmlNodePtr node,
92
                 xmlNodePtr parent);
93
94
95
96
static int xmlC14NProcessNode(xmlC14NCtxPtr ctx, xmlNodePtr cur);
97
static int xmlC14NProcessNodeList(xmlC14NCtxPtr ctx, xmlNodePtr cur);
98
typedef enum {
99
    XMLC14N_NORMALIZE_ATTR = 0,
100
    XMLC14N_NORMALIZE_COMMENT = 1,
101
    XMLC14N_NORMALIZE_PI = 2,
102
    XMLC14N_NORMALIZE_TEXT = 3
103
} xmlC14NNormalizationMode;
104
105
static xmlChar *xmlC11NNormalizeString(const xmlChar * input,
106
                                       xmlC14NNormalizationMode mode);
107
108
#define xmlC11NNormalizeAttr( a ) \
109
0
    xmlC11NNormalizeString((a), XMLC14N_NORMALIZE_ATTR)
110
#define xmlC11NNormalizeComment( a ) \
111
0
    xmlC11NNormalizeString((a), XMLC14N_NORMALIZE_COMMENT)
112
#define xmlC11NNormalizePI( a ) \
113
0
    xmlC11NNormalizeString((a), XMLC14N_NORMALIZE_PI)
114
#define xmlC11NNormalizeText( a ) \
115
0
    xmlC11NNormalizeString((a), XMLC14N_NORMALIZE_TEXT)
116
117
#define xmlC14NIsVisible( ctx, node, parent ) \
118
0
     (((ctx)->is_visible_callback != NULL) ? \
119
0
  (ctx)->is_visible_callback((ctx)->user_data, \
120
0
    (xmlNodePtr)(node), (xmlNodePtr)(parent)) : 1)
121
122
#define xmlC14NIsExclusive( ctx ) \
123
0
    ( (ctx)->mode == XML_C14N_EXCLUSIVE_1_0 )
124
125
/************************************************************************
126
 *                  *
127
 *    Some factorized error routines        *
128
 *                  *
129
 ************************************************************************/
130
131
/**
132
 * Handle a redefinition of memory error
133
 *
134
 * @param ctxt  a C14N evaluation context
135
 */
136
static void
137
xmlC14NErrMemory(xmlC14NCtxPtr ctxt)
138
0
{
139
0
    if (ctxt != NULL)
140
0
        ctxt->error = XML_ERR_NO_MEMORY;
141
142
0
    xmlRaiseMemoryError(NULL, NULL, NULL, XML_FROM_C14N, NULL);
143
0
}
144
145
static void
146
xmlC14NErrFull(xmlC14NCtxPtr ctxt, xmlNodePtr node, int code, const char *str1,
147
               const char *msg, ...)
148
0
{
149
0
    va_list ap;
150
0
    int res;
151
152
0
    if (ctxt != NULL)
153
0
        ctxt->error = code;
154
155
0
    va_start(ap, msg);
156
0
    res = xmlVRaiseError(NULL, NULL, NULL, ctxt, node,
157
0
                         XML_FROM_C14N, code, XML_ERR_ERROR, NULL, 0,
158
0
                         str1, NULL, NULL, 0, 0,
159
0
                         msg, ap);
160
0
    va_end(ap);
161
0
    if (res < 0)
162
0
        xmlC14NErrMemory(ctxt);
163
0
}
164
165
/**
166
 * Handle a param error
167
 *
168
 * @param ctxt  a C14N evaluation context
169
 */
170
static void
171
xmlC14NErrParam(xmlC14NCtxPtr ctxt)
172
0
{
173
0
    xmlC14NErrFull(ctxt, NULL, XML_ERR_ARGUMENT, NULL,
174
0
       "Invalid argument\n", NULL);
175
0
}
176
177
/**
178
 * Handle an invalid node error
179
 *
180
 * @param ctxt  a C14N evaluation context
181
 * @param node_type  node type
182
 * @param extra  extra information
183
 */
184
static void
185
xmlC14NErrInvalidNode(xmlC14NCtxPtr ctxt, const char *node_type,
186
                      const char *extra)
187
0
{
188
0
    xmlC14NErrFull(ctxt, NULL, XML_C14N_INVALID_NODE, extra,
189
0
       "Node %s is invalid here : %s\n", node_type, extra);
190
0
}
191
192
/**
193
 * Handle an unknown node error
194
 *
195
 * @param ctxt  a C14N evaluation context
196
 * @param node_type  node type
197
 * @param extra  extra information
198
 */
199
static void
200
xmlC14NErrUnknownNode(xmlC14NCtxPtr ctxt, int node_type, const char *extra)
201
0
{
202
0
    xmlC14NErrFull(ctxt, NULL, XML_C14N_UNKNOW_NODE, extra,
203
0
       "Unknown node type %d found : %s\n", node_type, extra);
204
0
}
205
206
/**
207
 * Handle a relative namespace error
208
 *
209
 * @param ctxt  a C14N evaluation context
210
 * @param ns_uri  namespace URI
211
 */
212
static void
213
xmlC14NErrRelativeNamespace(xmlC14NCtxPtr ctxt, const char *ns_uri)
214
0
{
215
0
    xmlC14NErrFull(ctxt, NULL, XML_C14N_RELATIVE_NAMESPACE, ns_uri,
216
0
       "Relative namespace UR is invalid here : %s\n", ns_uri);
217
0
}
218
219
220
221
/**
222
 * Handle an error
223
 *
224
 * @param ctxt  a C14N evaluation context
225
 * @param node  the context node
226
 * @param error  the error code
227
 * @param msg  the message
228
 */
229
static void
230
xmlC14NErr(xmlC14NCtxPtr ctxt, xmlNodePtr node, int error,
231
           const char * msg)
232
0
{
233
0
    xmlC14NErrFull(ctxt, node, error, NULL, "%s", msg);
234
0
}
235
236
/************************************************************************
237
 *                  *
238
 *    The implementation internals        *
239
 *                  *
240
 ************************************************************************/
241
0
#define XML_NAMESPACES_DEFAULT    16
242
243
static int
244
0
xmlC14NIsNodeInNodeset(void *user_data, xmlNodePtr node, xmlNodePtr parent) {
245
0
    xmlNodeSetPtr nodes = (xmlNodeSetPtr) user_data;
246
0
    if((nodes != NULL) && (node != NULL)) {
247
0
  if(node->type != XML_NAMESPACE_DECL) {
248
0
      return(xmlXPathNodeSetContains(nodes, node));
249
0
  } else {
250
0
      xmlNs ns;
251
252
0
      memcpy(&ns, node, sizeof(ns));
253
254
      /* this is a libxml hack! check xpath.c for details */
255
0
      if((parent != NULL) && (parent->type == XML_ATTRIBUTE_NODE)) {
256
0
    ns.next = (xmlNsPtr)parent->parent;
257
0
      } else {
258
0
    ns.next = (xmlNsPtr)parent;
259
0
      }
260
261
      /*
262
       * If the input is an XPath node-set, then the node-set must explicitly
263
       * contain every node to be rendered to the canonical form.
264
       */
265
0
      return(xmlXPathNodeSetContains(nodes, (xmlNodePtr)&ns));
266
0
  }
267
0
    }
268
0
    return(1);
269
0
}
270
271
static xmlC14NVisibleNsStackPtr
272
0
xmlC14NVisibleNsStackCreate(void) {
273
0
    xmlC14NVisibleNsStackPtr ret;
274
275
0
    ret = (xmlC14NVisibleNsStackPtr) xmlMalloc(sizeof(xmlC14NVisibleNsStack));
276
0
    if (ret == NULL)
277
0
  return(NULL);
278
0
    memset(ret, 0, sizeof(xmlC14NVisibleNsStack));
279
0
    return(ret);
280
0
}
281
282
static void
283
0
xmlC14NVisibleNsStackDestroy(xmlC14NVisibleNsStackPtr cur) {
284
0
    if(cur == NULL) {
285
0
        xmlC14NErrParam(NULL);
286
0
        return;
287
0
    }
288
0
    if(cur->nsTab != NULL) {
289
0
  memset(cur->nsTab, 0, cur->nsMax * sizeof(xmlNsPtr));
290
0
  xmlFree(cur->nsTab);
291
0
    }
292
0
    if(cur->nodeTab != NULL) {
293
0
  memset(cur->nodeTab, 0, cur->nsMax * sizeof(xmlNodePtr));
294
0
  xmlFree(cur->nodeTab);
295
0
    }
296
0
    memset(cur, 0, sizeof(xmlC14NVisibleNsStack));
297
0
    xmlFree(cur);
298
299
0
}
300
301
static int
302
0
xmlC14NVisibleNsStackAdd(xmlC14NVisibleNsStackPtr cur, xmlNsPtr ns, xmlNodePtr node) {
303
0
    if((cur == NULL) ||
304
0
       ((cur->nsTab == NULL) && (cur->nodeTab != NULL)) ||
305
0
       ((cur->nsTab != NULL) && (cur->nodeTab == NULL)))
306
0
  return (1);
307
308
0
    if (cur->nsMax <= cur->nsCurEnd) {
309
0
  xmlNsPtr *tmp1;
310
0
        xmlNodePtr *tmp2;
311
0
  int newSize;
312
313
0
        newSize = xmlGrowCapacity(cur->nsMax,
314
0
                                  sizeof(tmp1[0]) + sizeof(tmp2[0]),
315
0
                                  XML_NAMESPACES_DEFAULT, XML_MAX_ITEMS);
316
0
        if (newSize < 0)
317
0
            return (-1);
318
319
0
  tmp1 = xmlRealloc(cur->nsTab, newSize * sizeof(tmp1[0]));
320
0
  if (tmp1 == NULL)
321
0
      return (-1);
322
0
  cur->nsTab = tmp1;
323
324
0
  tmp2 = xmlRealloc(cur->nodeTab, newSize * sizeof(tmp2[0]));
325
0
  if (tmp2 == NULL)
326
0
      return (-1);
327
0
  cur->nodeTab = tmp2;
328
329
0
  cur->nsMax = newSize;
330
0
    }
331
0
    cur->nsTab[cur->nsCurEnd] = ns;
332
0
    cur->nodeTab[cur->nsCurEnd] = node;
333
334
0
    ++cur->nsCurEnd;
335
336
0
    return (0);
337
0
}
338
339
static void
340
0
xmlC14NVisibleNsStackSave(xmlC14NVisibleNsStackPtr cur, xmlC14NVisibleNsStackPtr state) {
341
0
    if((cur == NULL) || (state == NULL)) {
342
0
        xmlC14NErrParam(NULL);
343
0
  return;
344
0
    }
345
346
0
    state->nsCurEnd = cur->nsCurEnd;
347
0
    state->nsPrevStart = cur->nsPrevStart;
348
0
    state->nsPrevEnd = cur->nsPrevEnd;
349
0
}
350
351
static void
352
0
xmlC14NVisibleNsStackRestore(xmlC14NVisibleNsStackPtr cur, xmlC14NVisibleNsStackPtr state) {
353
0
    if((cur == NULL) || (state == NULL)) {
354
0
        xmlC14NErrParam(NULL);
355
0
  return;
356
0
    }
357
0
    cur->nsCurEnd = state->nsCurEnd;
358
0
    cur->nsPrevStart = state->nsPrevStart;
359
0
    cur->nsPrevEnd = state->nsPrevEnd;
360
0
}
361
362
static void
363
0
xmlC14NVisibleNsStackShift(xmlC14NVisibleNsStackPtr cur) {
364
0
    if(cur == NULL) {
365
0
        xmlC14NErrParam(NULL);
366
0
  return;
367
0
    }
368
0
    cur->nsPrevStart = cur->nsPrevEnd;
369
0
    cur->nsPrevEnd = cur->nsCurEnd;
370
0
}
371
372
static int
373
0
xmlC14NStrEqual(const xmlChar *str1, const xmlChar *str2) {
374
0
    if (str1 == str2) return(1);
375
0
    if (str1 == NULL) return((*str2) == '\0');
376
0
    if (str2 == NULL) return((*str1) == '\0');
377
0
    do {
378
0
  if (*str1++ != *str2) return(0);
379
0
    } while (*str2++);
380
0
    return(1);
381
0
}
382
383
/**
384
 * Checks whether the given namespace was already rendered or not
385
 *
386
 * @param cur     the visible stack
387
 * @param ns        the namespace to check
388
 * @returns 1 if we already wrote this namespace or 0 otherwise
389
 */
390
static int
391
xmlC14NVisibleNsStackFind(xmlC14NVisibleNsStackPtr cur, xmlNsPtr ns)
392
0
{
393
0
    int i;
394
0
    const xmlChar *prefix;
395
0
    const xmlChar *href;
396
0
    int has_empty_ns;
397
398
0
    if(cur == NULL) {
399
0
        xmlC14NErrParam(NULL);
400
0
        return (0);
401
0
    }
402
403
    /*
404
     * if the default namespace xmlns="" is not defined yet then
405
     * we do not want to print it out
406
     */
407
0
    prefix = ((ns == NULL) || (ns->prefix == NULL)) ? BAD_CAST "" : ns->prefix;
408
0
    href = ((ns == NULL) || (ns->href == NULL)) ? BAD_CAST "" : ns->href;
409
0
    has_empty_ns = (xmlC14NStrEqual(prefix, NULL) && xmlC14NStrEqual(href, NULL));
410
411
0
    if (cur->nsTab != NULL) {
412
0
  int start = (has_empty_ns) ? 0 : cur->nsPrevStart;
413
0
        for (i = cur->nsCurEnd - 1; i >= start; --i) {
414
0
            xmlNsPtr ns1 = cur->nsTab[i];
415
416
0
      if(xmlC14NStrEqual(prefix, (ns1 != NULL) ? ns1->prefix : NULL)) {
417
0
    return(xmlC14NStrEqual(href, (ns1 != NULL) ? ns1->href : NULL));
418
0
      }
419
0
        }
420
0
    }
421
0
    return(has_empty_ns);
422
0
}
423
424
static int
425
0
xmlExcC14NVisibleNsStackFind(xmlC14NVisibleNsStackPtr cur, xmlNsPtr ns, xmlC14NCtxPtr ctx) {
426
0
    int i;
427
0
    const xmlChar *prefix;
428
0
    const xmlChar *href;
429
0
    int has_empty_ns;
430
431
0
    if(cur == NULL) {
432
0
        xmlC14NErrParam(ctx);
433
0
        return (0);
434
0
    }
435
436
    /*
437
     * if the default namespace xmlns="" is not defined yet then
438
     * we do not want to print it out
439
     */
440
0
    prefix = ((ns == NULL) || (ns->prefix == NULL)) ? BAD_CAST "" : ns->prefix;
441
0
    href = ((ns == NULL) || (ns->href == NULL)) ? BAD_CAST "" : ns->href;
442
0
    has_empty_ns = (xmlC14NStrEqual(prefix, NULL) && xmlC14NStrEqual(href, NULL));
443
444
0
    if (cur->nsTab != NULL) {
445
0
  int start = 0;
446
0
        for (i = cur->nsCurEnd - 1; i >= start; --i) {
447
0
            xmlNsPtr ns1 = cur->nsTab[i];
448
449
0
      if(xmlC14NStrEqual(prefix, (ns1 != NULL) ? ns1->prefix : NULL)) {
450
0
    if(xmlC14NStrEqual(href, (ns1 != NULL) ? ns1->href : NULL)) {
451
0
        return(xmlC14NIsVisible(ctx, ns1, cur->nodeTab[i]));
452
0
    } else {
453
0
        return(0);
454
0
    }
455
0
      }
456
0
        }
457
0
    }
458
0
    return(has_empty_ns);
459
0
}
460
461
462
463
464
/**
465
 * Checks whether the given namespace is a default "xml:" namespace
466
 * with href="http://www.w3.org/XML/1998/namespace"
467
 *
468
 * @param ns      the namespace to check
469
 * @returns 1 if the node is default or 0 otherwise
470
 */
471
472
/* todo: make it a define? */
473
static int
474
xmlC14NIsXmlNs(xmlNsPtr ns)
475
0
{
476
0
    return ((ns != NULL) &&
477
0
            (xmlStrEqual(ns->prefix, BAD_CAST "xml")) &&
478
0
            (xmlStrEqual(ns->href, XML_XML_NAMESPACE)));
479
0
}
480
481
482
/**
483
 * Compares the namespaces by names (prefixes).
484
 *
485
 * @param data1     the pointer to first namespace
486
 * @param data2     the pointer to second namespace
487
 * @returns -1 if ns1 < ns2, 0 if ns1 == ns2 or 1 if ns1 > ns2.
488
 */
489
static int
490
xmlC14NNsCompare(const void *data1, const void *data2)
491
0
{
492
0
    const xmlNs *ns1 = data1;
493
0
    const xmlNs *ns2 = data2;
494
0
    if (ns1 == ns2)
495
0
        return (0);
496
0
    if (ns1 == NULL)
497
0
        return (-1);
498
0
    if (ns2 == NULL)
499
0
        return (1);
500
501
0
    return (xmlStrcmp(ns1->prefix, ns2->prefix));
502
0
}
503
504
505
/**
506
 * Prints the given namespace to the output buffer from C14N context.
507
 *
508
 * @param ns        the pointer to namespace
509
 * @param ctx     the C14N context
510
 * @returns 1 on success or 0 on fail.
511
 */
512
static int
513
xmlC14NPrintNamespaces(const xmlNs *ns, xmlC14NCtxPtr ctx)
514
0
{
515
516
0
    if ((ns == NULL) || (ctx == NULL)) {
517
0
        xmlC14NErrParam(ctx);
518
0
        return 0;
519
0
    }
520
521
0
    if (ns->prefix != NULL) {
522
0
        xmlOutputBufferWriteString(ctx->buf, " xmlns:");
523
0
        xmlOutputBufferWriteString(ctx->buf, (const char *) ns->prefix);
524
0
        xmlOutputBufferWriteString(ctx->buf, "=");
525
0
    } else {
526
0
        xmlOutputBufferWriteString(ctx->buf, " xmlns=");
527
0
    }
528
0
    if(ns->href != NULL) {
529
0
  xmlOutputBufferWriteQuotedString(ctx->buf, ns->href);
530
0
    } else {
531
0
      xmlOutputBufferWriteString(ctx->buf, "\"\"");
532
0
    }
533
0
    return (1);
534
0
}
535
536
static int
537
0
xmlC14NPrintNamespacesWalker(const void *ns, void *ctx) {
538
0
    return xmlC14NPrintNamespaces(ns, ctx);
539
0
}
540
541
/**
542
 * Prints out canonical namespace axis of the current node to the
543
 * buffer from C14N context as follows
544
 *
545
 * Canonical XML v 1.0 (http://www.w3.org/TR/xml-c14n)
546
 *
547
 * Namespace Axis
548
 * Consider a list L containing only namespace nodes in the
549
 * axis and in the node-set in lexicographic order (ascending). To begin
550
 * processing L, if the first node is not the default namespace node (a node
551
 * with no namespace URI and no local name), then generate a space followed
552
 * by xmlns="" if and only if the following conditions are met:
553
 *    - the element E that owns the axis is in the node-set
554
 *    - The nearest ancestor element of E in the node-set has a default
555
 *      namespace node in the node-set (default namespace nodes always
556
 *      have non-empty values in XPath)
557
 * The latter condition eliminates unnecessary occurrences of xmlns="" in
558
 * the canonical form since an element only receives an xmlns="" if its
559
 * default namespace is empty and if it has an immediate parent in the
560
 * canonical form that has a non-empty default namespace. To finish
561
 * processing  L, simply process every namespace node in L, except omit
562
 * namespace node with local name xml, which defines the xml prefix,
563
 * if its string value is http://www.w3.org/XML/1998/namespace.
564
 *
565
 * Exclusive XML Canonicalization v 1.0 (http://www.w3.org/TR/xml-exc-c14n)
566
 * Canonical XML applied to a document subset requires the search of the
567
 * ancestor nodes of each orphan element node for attributes in the xml
568
 * namespace, such as xml:lang and xml:space. These are copied into the
569
 * element node except if a declaration of the same attribute is already
570
 * in the attribute axis of the element (whether or not it is included in
571
 * the document subset). This search and copying are omitted from the
572
 * Exclusive XML Canonicalization method.
573
 *
574
 * @param ctx     the C14N context
575
 * @param cur     the current node
576
 * @param visible     the visibility of node
577
 * @returns 0 on success or -1 on fail.
578
 */
579
static int
580
xmlC14NProcessNamespacesAxis(xmlC14NCtxPtr ctx, xmlNodePtr cur, int visible)
581
0
{
582
0
    xmlNodePtr n;
583
0
    xmlNsPtr ns, tmp;
584
0
    xmlListPtr list;
585
0
    int already_rendered;
586
0
    int has_empty_ns = 0;
587
588
0
    if ((ctx == NULL) || (cur == NULL) || (cur->type != XML_ELEMENT_NODE)) {
589
0
        xmlC14NErrParam(ctx);
590
0
        return (-1);
591
0
    }
592
593
    /*
594
     * Create a sorted list to store element namespaces
595
     */
596
0
    list = xmlListCreate(NULL, xmlC14NNsCompare);
597
0
    if (list == NULL) {
598
0
        xmlC14NErrMemory(ctx);
599
0
        return (-1);
600
0
    }
601
602
    /* check all namespaces */
603
0
    for(n = cur; n != NULL && n->type == XML_ELEMENT_NODE; n = n->parent) {
604
0
  for(ns = n->nsDef; ns != NULL; ns = ns->next) {
605
0
      tmp = xmlSearchNs(cur->doc, cur, ns->prefix);
606
607
0
      if((tmp == ns) && !xmlC14NIsXmlNs(ns) && xmlC14NIsVisible(ctx, ns, cur)) {
608
0
    already_rendered = xmlC14NVisibleNsStackFind(ctx->ns_rendered, ns);
609
0
    if(visible) {
610
0
              if (xmlC14NVisibleNsStackAdd(ctx->ns_rendered, ns, cur) < 0) {
611
0
                        xmlC14NErrMemory(ctx);
612
0
                        goto error;
613
0
                    }
614
0
    }
615
0
    if(!already_rendered) {
616
0
        xmlListInsert(list, ns);
617
0
    }
618
0
    if(xmlStrlen(ns->prefix) == 0) {
619
0
        has_empty_ns = 1;
620
0
    }
621
0
      }
622
0
  }
623
0
    }
624
625
    /**
626
     * if the first node is not the default namespace node (a node with no
627
     * namespace URI and no local name), then generate a space followed by
628
     * xmlns="" if and only if the following conditions are met:
629
     *  - the element E that owns the axis is in the node-set
630
     *  - the nearest ancestor element of E in the node-set has a default
631
     *     namespace node in the node-set (default namespace nodes always
632
     *     have non-empty values in XPath)
633
     */
634
0
    if(visible && !has_empty_ns) {
635
0
        xmlNs ns_default;
636
637
0
        memset(&ns_default, 0, sizeof(ns_default));
638
0
        if(!xmlC14NVisibleNsStackFind(ctx->ns_rendered, &ns_default)) {
639
0
      xmlC14NPrintNamespaces(&ns_default, ctx);
640
0
  }
641
0
    }
642
643
644
    /*
645
     * print out all elements from list
646
     */
647
0
    xmlListWalk(list, xmlC14NPrintNamespacesWalker, (void *) ctx);
648
649
    /*
650
     * Cleanup
651
     */
652
0
error:
653
0
    xmlListDelete(list);
654
0
    return (0);
655
0
}
656
657
658
/**
659
 * Prints out exclusive canonical namespace axis of the current node to the
660
 * buffer from C14N context as follows
661
 *
662
 * Exclusive XML Canonicalization
663
 * http://www.w3.org/TR/xml-exc-c14n
664
 *
665
 * If the element node is in the XPath subset then output the node in
666
 * accordance with Canonical XML except for namespace nodes which are
667
 * rendered as follows:
668
 *
669
 * 1. Render each namespace node iff:
670
 *    * it is visibly utilized by the immediate parent element or one of
671
 *      its attributes, or is present in InclusiveNamespaces PrefixList, and
672
 *    * its prefix and value do not appear in ns_rendered. ns_rendered is
673
 *      obtained by popping the state stack in order to obtain a list of
674
 *      prefixes and their values which have already been rendered by
675
 *      an output ancestor of the namespace node's parent element.
676
 * 2. Append the rendered namespace node to the list ns_rendered of namespace
677
 * nodes rendered by output ancestors. Push ns_rendered on state stack and
678
 * recurse.
679
 * 3. After the recursion returns, pop thestate stack.
680
 *
681
 *
682
 * @param ctx     the C14N context
683
 * @param cur     the current node
684
 * @param visible     the visibility of node
685
 * @returns 0 on success or -1 on fail.
686
 */
687
static int
688
xmlExcC14NProcessNamespacesAxis(xmlC14NCtxPtr ctx, xmlNodePtr cur, int visible)
689
0
{
690
0
    xmlNsPtr ns;
691
0
    xmlListPtr list;
692
0
    xmlAttrPtr attr;
693
0
    int already_rendered;
694
0
    int has_empty_ns = 0;
695
0
    int has_visibly_utilized_empty_ns = 0;
696
0
    int has_empty_ns_in_inclusive_list = 0;
697
698
0
    if ((ctx == NULL) || (cur == NULL) || (cur->type != XML_ELEMENT_NODE)) {
699
0
        xmlC14NErrParam(ctx);
700
0
        return (-1);
701
0
    }
702
703
0
    if(!xmlC14NIsExclusive(ctx)) {
704
0
        xmlC14NErrParam(ctx);
705
0
        return (-1);
706
707
0
    }
708
709
    /*
710
     * Create a sorted list to store element namespaces
711
     */
712
0
    list = xmlListCreate(NULL, xmlC14NNsCompare);
713
0
    if (list == NULL) {
714
0
        xmlC14NErrMemory(ctx);
715
0
        return (-1);
716
0
    }
717
718
    /*
719
     * process inclusive namespaces:
720
     * All namespace nodes appearing on inclusive ns list are
721
     * handled as provided in Canonical XML
722
     */
723
0
    if(ctx->inclusive_ns_prefixes != NULL) {
724
0
  xmlChar *prefix;
725
0
  int i;
726
727
0
  for (i = 0; ctx->inclusive_ns_prefixes[i] != NULL; ++i) {
728
0
      prefix = ctx->inclusive_ns_prefixes[i];
729
      /*
730
       * Special values for namespace with empty prefix
731
       */
732
0
            if (xmlStrEqual(prefix, BAD_CAST "#default")
733
0
                || xmlStrEqual(prefix, BAD_CAST "")) {
734
0
                prefix = NULL;
735
0
    has_empty_ns_in_inclusive_list = 1;
736
0
            }
737
738
0
      ns = xmlSearchNs(cur->doc, cur, prefix);
739
0
      if((ns != NULL) && !xmlC14NIsXmlNs(ns) && xmlC14NIsVisible(ctx, ns, cur)) {
740
0
    already_rendered = xmlC14NVisibleNsStackFind(ctx->ns_rendered, ns);
741
0
    if(visible) {
742
0
        if (xmlC14NVisibleNsStackAdd(ctx->ns_rendered, ns, cur) < 0) {
743
0
                        xmlC14NErrMemory(ctx);
744
0
                        goto error;
745
0
                    }
746
0
    }
747
0
    if(!already_rendered) {
748
0
        xmlListInsert(list, ns);
749
0
    }
750
0
    if(xmlStrlen(ns->prefix) == 0) {
751
0
        has_empty_ns = 1;
752
0
    }
753
0
      }
754
0
  }
755
0
    }
756
757
    /* add node namespace */
758
0
    if(cur->ns != NULL) {
759
0
  ns = cur->ns;
760
0
    } else {
761
0
        ns = xmlSearchNs(cur->doc, cur, NULL);
762
0
  has_visibly_utilized_empty_ns = 1;
763
0
    }
764
0
    if((ns != NULL) && !xmlC14NIsXmlNs(ns)) {
765
0
  if(visible && xmlC14NIsVisible(ctx, ns, cur)) {
766
0
      if(!xmlExcC14NVisibleNsStackFind(ctx->ns_rendered, ns, ctx)) {
767
0
    xmlListInsert(list, ns);
768
0
      }
769
0
  }
770
0
  if(visible) {
771
0
      if (xmlC14NVisibleNsStackAdd(ctx->ns_rendered, ns, cur) < 0) {
772
0
                xmlC14NErrMemory(ctx);
773
0
                goto error;
774
0
            }
775
0
  }
776
0
  if(xmlStrlen(ns->prefix) == 0) {
777
0
      has_empty_ns = 1;
778
0
  }
779
0
    }
780
781
782
    /* add attributes */
783
0
    for(attr = cur->properties; attr != NULL; attr = attr->next) {
784
        /*
785
         * we need to check that attribute is visible and has non
786
         * default namespace (XML Namespaces: "default namespaces
787
   * do not apply directly to attributes")
788
         */
789
0
  if((attr->ns != NULL) && !xmlC14NIsXmlNs(attr->ns) && xmlC14NIsVisible(ctx, attr, cur)) {
790
0
      already_rendered = xmlExcC14NVisibleNsStackFind(ctx->ns_rendered, attr->ns, ctx);
791
0
      if (xmlC14NVisibleNsStackAdd(ctx->ns_rendered, attr->ns, cur) < 0) {
792
0
                xmlC14NErrMemory(ctx);
793
0
                goto error;
794
0
            }
795
0
      if(!already_rendered && visible) {
796
0
    xmlListInsert(list, attr->ns);
797
0
      }
798
0
      if(xmlStrlen(attr->ns->prefix) == 0) {
799
0
    has_empty_ns = 1;
800
0
      }
801
0
  } else if((attr->ns != NULL) && (xmlStrlen(attr->ns->prefix) == 0) && (xmlStrlen(attr->ns->href) == 0)) {
802
0
      has_visibly_utilized_empty_ns = 1;
803
0
  }
804
0
    }
805
806
    /*
807
     * Process xmlns=""
808
     */
809
0
    if(visible && has_visibly_utilized_empty_ns &&
810
0
      !has_empty_ns && !has_empty_ns_in_inclusive_list) {
811
0
        xmlNs ns_default;
812
813
0
        memset(&ns_default, 0, sizeof(ns_default));
814
815
0
        already_rendered = xmlExcC14NVisibleNsStackFind(ctx->ns_rendered, &ns_default, ctx);
816
0
  if(!already_rendered) {
817
0
      xmlC14NPrintNamespaces(&ns_default, ctx);
818
0
  }
819
0
    } else if(visible && !has_empty_ns && has_empty_ns_in_inclusive_list) {
820
0
        xmlNs ns_default;
821
822
0
        memset(&ns_default, 0, sizeof(ns_default));
823
0
        if(!xmlC14NVisibleNsStackFind(ctx->ns_rendered, &ns_default)) {
824
0
      xmlC14NPrintNamespaces(&ns_default, ctx);
825
0
  }
826
0
    }
827
828
829
830
    /*
831
     * print out all elements from list
832
     */
833
0
    xmlListWalk(list, xmlC14NPrintNamespacesWalker, (void *) ctx);
834
835
    /*
836
     * Cleanup
837
     */
838
0
error:
839
0
    xmlListDelete(list);
840
0
    return (0);
841
0
}
842
843
844
/**
845
 * Checks whether the given attribute is a default "xml:" namespace
846
 * with href="http://www.w3.org/XML/1998/namespace"
847
 *
848
 * @param attr      the attr to check
849
 * @returns 1 if the node is default or 0 otherwise
850
 */
851
852
/* todo: make it a define? */
853
static int
854
xmlC14NIsXmlAttr(xmlAttrPtr attr)
855
0
{
856
0
    return ((attr->ns != NULL) &&
857
0
           (xmlC14NIsXmlNs(attr->ns) != 0));
858
0
}
859
860
861
/**
862
 * Prints the given attribute to the output buffer from C14N context.
863
 *
864
 * @param data1     the pointer tls o first attr
865
 * @param data2     the pointer to second attr
866
 * @returns -1 if attr1 < attr2, 0 if attr1 == attr2 or 1 if attr1 > attr2.
867
 */
868
static int
869
xmlC14NAttrsCompare(const void *data1, const void *data2)
870
0
{
871
0
    const xmlAttr *attr1 = data1;
872
0
    const xmlAttr *attr2 = data2;
873
0
    int ret = 0;
874
875
    /*
876
     * Simple cases
877
     */
878
0
    if (attr1 == attr2)
879
0
        return (0);
880
0
    if (attr1 == NULL)
881
0
        return (-1);
882
0
    if (attr2 == NULL)
883
0
        return (1);
884
0
    if (attr1->ns == attr2->ns) {
885
0
        return (xmlStrcmp(attr1->name, attr2->name));
886
0
    }
887
888
    /*
889
     * Attributes in the default namespace are first
890
     * because the default namespace is not applied to
891
     * unqualified attributes
892
     */
893
0
    if (attr1->ns == NULL)
894
0
        return (-1);
895
0
    if (attr2->ns == NULL)
896
0
        return (1);
897
0
    if (attr1->ns->prefix == NULL)
898
0
        return (-1);
899
0
    if (attr2->ns->prefix == NULL)
900
0
        return (1);
901
902
0
    ret = xmlStrcmp(attr1->ns->href, attr2->ns->href);
903
0
    if (ret == 0) {
904
0
        ret = xmlStrcmp(attr1->name, attr2->name);
905
0
    }
906
0
    return (ret);
907
0
}
908
909
910
/**
911
 * Prints out canonical attribute urrent node to the
912
 * buffer from C14N context as follows
913
 *
914
 * Canonical XML v 1.0 (http://www.w3.org/TR/xml-c14n)
915
 *
916
 * @param data      the pointer to attr
917
 * @param user      the C14N context
918
 * @returns 1 on success or 0 on fail.
919
 */
920
static int
921
xmlC14NPrintAttrs(const void *data, void *user)
922
0
{
923
0
    const xmlAttr *attr = data;
924
0
    xmlC14NCtxPtr ctx = (xmlC14NCtxPtr) user;
925
0
    xmlChar *value;
926
0
    xmlChar *buffer;
927
928
0
    if ((attr == NULL) || (ctx == NULL)) {
929
0
        xmlC14NErrParam(ctx);
930
0
        return (0);
931
0
    }
932
933
0
    xmlOutputBufferWriteString(ctx->buf, " ");
934
0
    if (attr->ns != NULL && xmlStrlen(attr->ns->prefix) > 0) {
935
0
        xmlOutputBufferWriteString(ctx->buf,
936
0
                                   (const char *) attr->ns->prefix);
937
0
        xmlOutputBufferWriteString(ctx->buf, ":");
938
0
    }
939
0
    xmlOutputBufferWriteString(ctx->buf, (const char *) attr->name);
940
0
    xmlOutputBufferWriteString(ctx->buf, "=\"");
941
942
0
    value = xmlNodeListGetString(ctx->doc, attr->children, 1);
943
    /* todo: should we log an error if value==NULL ? */
944
0
    if (value != NULL) {
945
0
        buffer = xmlC11NNormalizeAttr(value);
946
0
        xmlFree(value);
947
0
        if (buffer != NULL) {
948
0
            xmlOutputBufferWriteString(ctx->buf, (const char *) buffer);
949
0
            xmlFree(buffer);
950
0
        } else {
951
0
            xmlC14NErrMemory(ctx);
952
0
            return (0);
953
0
        }
954
0
    }
955
0
    xmlOutputBufferWriteString(ctx->buf, "\"");
956
0
    return (1);
957
0
}
958
959
/**
960
 * Finds an attribute in a hidden parent node.
961
 *
962
 * @returns a pointer to the attribute node (if found) or NULL otherwise.
963
 */
964
static xmlAttrPtr
965
xmlC14NFindHiddenParentAttr(xmlC14NCtxPtr ctx, xmlNodePtr cur, const xmlChar * name, const xmlChar * ns)
966
0
{
967
0
    xmlAttrPtr res;
968
0
    while((cur != NULL) && (!xmlC14NIsVisible(ctx, cur, cur->parent))) {
969
0
        res = xmlHasNsProp(cur, name, ns);
970
0
        if(res != NULL) {
971
0
            return res;
972
0
        }
973
974
0
        cur = cur->parent;
975
0
    }
976
977
0
    return NULL;
978
0
}
979
980
/**
981
 * Fixes up the xml:base attribute
982
 *
983
 * @returns the newly created attribute or NULL
984
 */
985
static xmlAttrPtr
986
xmlC14NFixupBaseAttr(xmlC14NCtxPtr ctx, xmlAttrPtr xml_base_attr)
987
0
{
988
0
    xmlChar * res = NULL;
989
0
    xmlNodePtr cur;
990
0
    xmlAttrPtr attr;
991
0
    xmlChar * tmp_str;
992
0
    xmlChar * tmp_str2;
993
0
    int tmp_str_len;
994
995
0
    if ((ctx == NULL) || (xml_base_attr == NULL) || (xml_base_attr->parent == NULL)) {
996
0
        xmlC14NErrParam(ctx);
997
0
        return (NULL);
998
0
    }
999
1000
    /* start from current value */
1001
0
    res = xmlNodeListGetString(ctx->doc, xml_base_attr->children, 1);
1002
0
    if(res == NULL) {
1003
0
        xmlC14NErrMemory(ctx);
1004
0
        return (NULL);
1005
0
    }
1006
1007
    /* go up the stack until we find a node that we rendered already */
1008
0
    cur = xml_base_attr->parent->parent;
1009
0
    while((cur != NULL) && (!xmlC14NIsVisible(ctx, cur, cur->parent))) {
1010
0
        int code;
1011
1012
0
        attr = xmlHasNsProp(cur, BAD_CAST "base", XML_XML_NAMESPACE);
1013
0
        if(attr != NULL) {
1014
            /* get attr value */
1015
0
            tmp_str = xmlNodeListGetString(ctx->doc, attr->children, 1);
1016
0
            if(tmp_str == NULL) {
1017
0
                xmlFree(res);
1018
1019
0
                xmlC14NErrMemory(ctx);
1020
0
                return (NULL);
1021
0
            }
1022
1023
            /* we need to add '/' if our current base uri ends with '..' or '.'
1024
            to ensure that we are forced to go "up" all the time */
1025
0
            tmp_str_len = xmlStrlen(tmp_str);
1026
0
            if(tmp_str_len > 1 && tmp_str[tmp_str_len - 2] == '.') {
1027
0
                tmp_str = xmlStrcat(tmp_str, BAD_CAST "/");
1028
0
                if(tmp_str == NULL) {
1029
0
                    xmlFree(res);
1030
1031
0
                    xmlC14NErrMemory(ctx);
1032
0
                    return (NULL);
1033
0
                }
1034
0
            }
1035
1036
            /* build uri */
1037
0
            code = xmlBuildURISafe(res, tmp_str, &tmp_str2);
1038
0
            if (code != 0) {
1039
0
                xmlFree(tmp_str);
1040
0
                xmlFree(res);
1041
1042
0
                if (code < 0)
1043
0
                    xmlC14NErrMemory(ctx);
1044
0
                else
1045
0
                    xmlC14NErr(ctx, cur, XML_ERR_INVALID_URI,
1046
0
                               "processing xml:base attribute - "
1047
0
                               "can't construct uri");
1048
0
                return (NULL);
1049
0
            }
1050
1051
            /* cleanup and set the new res */
1052
0
            xmlFree(tmp_str);
1053
0
            xmlFree(res);
1054
0
            res = tmp_str2;
1055
0
        }
1056
1057
        /* next */
1058
0
        cur = cur->parent;
1059
0
    }
1060
1061
    /* check if result uri is empty or not */
1062
0
    if((res == NULL) || xmlStrEqual(res, BAD_CAST "")) {
1063
0
        xmlFree(res);
1064
0
        return (NULL);
1065
0
    }
1066
1067
    /* create and return the new attribute node */
1068
0
    attr = xmlNewNsProp(NULL, xml_base_attr->ns, BAD_CAST "base", res);
1069
0
    if(attr == NULL) {
1070
0
        xmlFree(res);
1071
1072
0
        xmlC14NErrMemory(ctx);
1073
0
        return (NULL);
1074
0
    }
1075
1076
    /* done */
1077
0
    xmlFree(res);
1078
0
    return (attr);
1079
0
}
1080
1081
/**
1082
 * Prints out canonical attribute axis of the current node to the
1083
 * buffer from C14N context as follows
1084
 *
1085
 * Canonical XML v 1.0 (http://www.w3.org/TR/xml-c14n)
1086
 *
1087
 * Attribute Axis
1088
 * In lexicographic order (ascending), process each node that
1089
 * is in the element's attribute axis and in the node-set.
1090
 *
1091
 * The processing of an element node E MUST be modified slightly
1092
 * when an XPath node-set is given as input and the element's
1093
 * parent is omitted from the node-set.
1094
 *
1095
 *
1096
 * Exclusive XML Canonicalization v 1.0 (http://www.w3.org/TR/xml-exc-c14n)
1097
 *
1098
 * Canonical XML applied to a document subset requires the search of the
1099
 * ancestor nodes of each orphan element node for attributes in the xml
1100
 * namespace, such as xml:lang and xml:space. These are copied into the
1101
 * element node except if a declaration of the same attribute is already
1102
 * in the attribute axis of the element (whether or not it is included in
1103
 * the document subset). This search and copying are omitted from the
1104
 * Exclusive XML Canonicalization method.
1105
 *
1106
 * @param ctx     the C14N context
1107
 * @param cur     the current node
1108
 * @param parent_visible    the visibility of the parent node
1109
 * @returns 0 on success or -1 on fail.
1110
 */
1111
static int
1112
xmlC14NProcessAttrsAxis(xmlC14NCtxPtr ctx, xmlNodePtr cur, int parent_visible)
1113
0
{
1114
0
    xmlAttrPtr attr;
1115
0
    xmlListPtr list;
1116
0
    xmlAttrPtr attrs_to_delete = NULL;
1117
1118
    /* special processing for 1.1 spec */
1119
0
    xmlAttrPtr xml_base_attr = NULL;
1120
0
    xmlAttrPtr xml_lang_attr = NULL;
1121
0
    xmlAttrPtr xml_space_attr = NULL;
1122
1123
0
    if ((ctx == NULL) || (cur == NULL) || (cur->type != XML_ELEMENT_NODE)) {
1124
0
        xmlC14NErrParam(ctx);
1125
0
        return (-1);
1126
0
    }
1127
1128
    /*
1129
     * Create a sorted list to store element attributes
1130
     */
1131
0
    list = xmlListCreate(NULL, xmlC14NAttrsCompare);
1132
0
    if (list == NULL) {
1133
0
        xmlC14NErrMemory(ctx);
1134
0
        return (-1);
1135
0
    }
1136
1137
0
    switch(ctx->mode) {
1138
0
    case XML_C14N_1_0:
1139
        /* The processing of an element node E MUST be modified slightly when an XPath node-set is
1140
         * given as input and the element's parent is omitted from the node-set. The method for processing
1141
         * the attribute axis of an element E in the node-set is enhanced. All element nodes along E's
1142
         * ancestor axis are examined for nearest occurrences of attributes in the xml namespace, such
1143
         * as xml:lang and xml:space (whether or not they are in the node-set). From this list of attributes,
1144
         * remove any that are in E's attribute axis (whether or not they are in the node-set). Then,
1145
         * lexicographically merge this attribute list with the nodes of E's attribute axis that are in
1146
         * the node-set. The result of visiting the attribute axis is computed by processing the attribute
1147
         * nodes in this merged attribute list.
1148
         */
1149
1150
        /*
1151
         * Add all visible attributes from current node.
1152
         */
1153
0
        attr = cur->properties;
1154
0
        while (attr != NULL) {
1155
            /* check that attribute is visible */
1156
0
            if (xmlC14NIsVisible(ctx, attr, cur)) {
1157
0
                xmlListInsert(list, attr);
1158
0
            }
1159
0
            attr = attr->next;
1160
0
        }
1161
1162
        /*
1163
         * Handle xml attributes
1164
         */
1165
0
        if (parent_visible && (cur->parent != NULL) &&
1166
0
            (!xmlC14NIsVisible(ctx, cur->parent, cur->parent->parent)))
1167
0
        {
1168
0
            xmlNodePtr tmp;
1169
1170
            /*
1171
             * If XPath node-set is not specified then the parent is always
1172
             * visible!
1173
             */
1174
0
            tmp = cur->parent;
1175
0
            while (tmp != NULL && tmp->type == XML_ELEMENT_NODE) {
1176
0
                attr = tmp->properties;
1177
0
                while (attr != NULL) {
1178
0
                    if (xmlC14NIsXmlAttr(attr) != 0) {
1179
0
                        if (xmlListSearch(list, attr) == NULL) {
1180
0
                            xmlListInsert(list, attr);
1181
0
                        }
1182
0
                    }
1183
0
                    attr = attr->next;
1184
0
                }
1185
0
                tmp = tmp->parent;
1186
0
            }
1187
0
        }
1188
1189
        /* done */
1190
0
        break;
1191
0
    case XML_C14N_EXCLUSIVE_1_0:
1192
        /* attributes in the XML namespace, such as xml:lang and xml:space
1193
         * are not imported into orphan nodes of the document subset
1194
         */
1195
1196
        /*
1197
         * Add all visible attributes from current node.
1198
         */
1199
0
        attr = cur->properties;
1200
0
        while (attr != NULL) {
1201
            /* check that attribute is visible */
1202
0
            if (xmlC14NIsVisible(ctx, attr, cur)) {
1203
0
                xmlListInsert(list, attr);
1204
0
            }
1205
0
            attr = attr->next;
1206
0
        }
1207
1208
        /* do nothing special for xml attributes */
1209
0
        break;
1210
0
    case XML_C14N_1_1:
1211
        /* The processing of an element node E MUST be modified slightly when an XPath node-set is
1212
         * given as input and some of the element's ancestors are omitted from the node-set.
1213
         *
1214
         * Simple inheritable attributes are attributes that have a value that requires at most a simple
1215
         * redeclaration. This redeclaration is done by supplying a new value in the child axis. The
1216
         * redeclaration of a simple inheritable attribute A contained in one of E's ancestors is done
1217
         * by supplying a value to an attribute Ae inside E with the same name. Simple inheritable attributes
1218
         * are xml:lang and xml:space.
1219
         *
1220
         * The method for processing the attribute axis of an element E in the node-set is hence enhanced.
1221
         * All element nodes along E's ancestor axis are examined for the nearest occurrences of simple
1222
         * inheritable attributes in the xml namespace, such as xml:lang and xml:space (whether or not they
1223
         * are in the node-set). From this list of attributes, any simple inheritable attributes that are
1224
         * already in E's attribute axis (whether or not they are in the node-set) are removed. Then,
1225
         * lexicographically merge this attribute list with the nodes of E's attribute axis that are in
1226
         * the node-set. The result of visiting the attribute axis is computed by processing the attribute
1227
         * nodes in this merged attribute list.
1228
         *
1229
         * The xml:id attribute is not a simple inheritable attribute and no processing of these attributes is
1230
         * performed.
1231
         *
1232
         * The xml:base attribute is not a simple inheritable attribute and requires special processing beyond
1233
         * a simple redeclaration.
1234
         *
1235
         * Attributes in the XML namespace other than xml:base, xml:id, xml:lang, and xml:space MUST be processed
1236
         * as ordinary attributes.
1237
         */
1238
1239
        /*
1240
         * Add all visible attributes from current node.
1241
         */
1242
0
        attr = cur->properties;
1243
0
        while (attr != NULL) {
1244
            /* special processing for XML attribute kiks in only when we have invisible parents */
1245
0
            if ((!parent_visible) || (xmlC14NIsXmlAttr(attr) == 0)) {
1246
                /* check that attribute is visible */
1247
0
                if (xmlC14NIsVisible(ctx, attr, cur)) {
1248
0
                    xmlListInsert(list, attr);
1249
0
                }
1250
0
            } else {
1251
0
                int matched = 0;
1252
1253
                /* check for simple inheritance attributes */
1254
0
                if((!matched) && (xml_lang_attr == NULL) && xmlStrEqual(attr->name, BAD_CAST "lang")) {
1255
0
                    xml_lang_attr = attr;
1256
0
                    matched = 1;
1257
0
                }
1258
0
                if((!matched) && (xml_space_attr == NULL) && xmlStrEqual(attr->name, BAD_CAST "space")) {
1259
0
                    xml_space_attr = attr;
1260
0
                    matched = 1;
1261
0
                }
1262
1263
                /* check for base attr */
1264
0
                if((!matched) && (xml_base_attr == NULL) && xmlStrEqual(attr->name, BAD_CAST "base")) {
1265
0
                    xml_base_attr = attr;
1266
0
                    matched = 1;
1267
0
                }
1268
1269
                /* otherwise, it is a normal attribute, so just check if it is visible */
1270
0
                if((!matched) && xmlC14NIsVisible(ctx, attr, cur)) {
1271
0
                    xmlListInsert(list, attr);
1272
0
                }
1273
0
            }
1274
1275
            /* move to the next one */
1276
0
            attr = attr->next;
1277
0
        }
1278
1279
        /* special processing for XML attribute kiks in only when we have invisible parents */
1280
0
        if ((parent_visible)) {
1281
1282
            /* simple inheritance attributes - copy */
1283
0
            if(xml_lang_attr == NULL) {
1284
0
                xml_lang_attr = xmlC14NFindHiddenParentAttr(ctx, cur->parent, BAD_CAST "lang", XML_XML_NAMESPACE);
1285
0
            }
1286
0
            if(xml_lang_attr != NULL) {
1287
0
                xmlListInsert(list, xml_lang_attr);
1288
0
            }
1289
0
            if(xml_space_attr == NULL) {
1290
0
                xml_space_attr = xmlC14NFindHiddenParentAttr(ctx, cur->parent, BAD_CAST "space", XML_XML_NAMESPACE);
1291
0
            }
1292
0
            if(xml_space_attr != NULL) {
1293
0
                xmlListInsert(list, xml_space_attr);
1294
0
            }
1295
1296
            /* base uri attribute - fix up */
1297
0
            if(xml_base_attr == NULL) {
1298
                /* if we don't have base uri attribute, check if we have a "hidden" one above */
1299
0
                xml_base_attr = xmlC14NFindHiddenParentAttr(ctx, cur->parent, BAD_CAST "base", XML_XML_NAMESPACE);
1300
0
            }
1301
0
            if(xml_base_attr != NULL) {
1302
0
                xml_base_attr = xmlC14NFixupBaseAttr(ctx, xml_base_attr);
1303
0
                if(xml_base_attr != NULL) {
1304
0
                    xmlListInsert(list, xml_base_attr);
1305
1306
                    /* note that we MUST delete returned attr node ourselves! */
1307
0
                    xml_base_attr->next = attrs_to_delete;
1308
0
                    attrs_to_delete = xml_base_attr;
1309
0
                }
1310
0
            }
1311
0
        }
1312
1313
        /* done */
1314
0
        break;
1315
0
    }
1316
1317
    /*
1318
     * print out all elements from list
1319
     */
1320
0
    xmlListWalk(list, xmlC14NPrintAttrs, (void *) ctx);
1321
1322
    /*
1323
     * Cleanup
1324
     */
1325
0
    xmlFreePropList(attrs_to_delete);
1326
0
    xmlListDelete(list);
1327
0
    return (0);
1328
0
}
1329
1330
/**
1331
 * Checks that current element node has no relative namespaces defined
1332
 *
1333
 * @param ctx     the C14N context
1334
 * @param cur     the current element node
1335
 * @returns 0 if the node has no relative namespaces or -1 otherwise.
1336
 */
1337
static int
1338
xmlC14NCheckForRelativeNamespaces(xmlC14NCtxPtr ctx, xmlNodePtr cur)
1339
0
{
1340
0
    xmlNsPtr ns;
1341
1342
0
    if ((ctx == NULL) || (cur == NULL) || (cur->type != XML_ELEMENT_NODE)) {
1343
0
        xmlC14NErrParam(ctx);
1344
0
        return (-1);
1345
0
    }
1346
1347
0
    ns = cur->nsDef;
1348
0
    while (ns != NULL) {
1349
0
        if (xmlStrlen(ns->href) > 0) {
1350
0
            xmlURIPtr uri;
1351
0
            int code;
1352
1353
0
            code = xmlParseURISafe((const char *) ns->href, &uri);
1354
0
            if (uri == NULL) {
1355
0
                if (code < 0)
1356
0
                    xmlC14NErrMemory(ctx);
1357
0
                else
1358
0
                    xmlC14NErr(ctx, cur, XML_ERR_INVALID_URI,
1359
0
                               "parsing namespace uri");
1360
0
                return (-1);
1361
0
            }
1362
0
            if (xmlStrlen((const xmlChar *) uri->scheme) == 0) {
1363
0
                xmlC14NErrRelativeNamespace(ctx, uri->scheme);
1364
0
                xmlFreeURI(uri);
1365
0
                return (-1);
1366
0
            }
1367
0
            xmlFreeURI(uri);
1368
0
        }
1369
0
        ns = ns->next;
1370
0
    }
1371
0
    return (0);
1372
0
}
1373
1374
/**
1375
 * Canonical XML v 1.0 (http://www.w3.org/TR/xml-c14n)
1376
 *
1377
 * Element Nodes
1378
 * If the element is not in the node-set, then the result is obtained
1379
 * by processing the namespace axis, then the attribute axis, then
1380
 * processing the child nodes of the element that are in the node-set
1381
 * (in document order). If the element is in the node-set, then the result
1382
 * is an open angle bracket (<), the element QName, the result of
1383
 * processing the namespace axis, the result of processing the attribute
1384
 * axis, a close angle bracket (>), the result of processing the child
1385
 * nodes of the element that are in the node-set (in document order), an
1386
 * open angle bracket, a forward slash (/), the element QName, and a close
1387
 * angle bracket.
1388
 *
1389
 * @param ctx     the pointer to C14N context object
1390
 * @param cur     the node to process
1391
 * @param visible     this node is visible
1392
 * @returns non-negative value on success or negative value on fail
1393
 */
1394
static int
1395
xmlC14NProcessElementNode(xmlC14NCtxPtr ctx, xmlNodePtr cur, int visible)
1396
0
{
1397
0
    int ret;
1398
0
    xmlC14NVisibleNsStack state;
1399
0
    int parent_is_doc = 0;
1400
1401
0
    if ((ctx == NULL) || (cur == NULL) || (cur->type != XML_ELEMENT_NODE)) {
1402
0
        xmlC14NErrParam(ctx);
1403
0
        return (-1);
1404
0
    }
1405
1406
    /*
1407
     * Check relative relative namespaces:
1408
     * implementations of XML canonicalization MUST report an operation
1409
     * failure on documents containing relative namespace URIs.
1410
     */
1411
0
    if (xmlC14NCheckForRelativeNamespaces(ctx, cur) < 0)
1412
0
        return (-1);
1413
1414
    /*
1415
     * Save ns_rendered stack position
1416
     */
1417
0
    memset(&state, 0, sizeof(state));
1418
0
    xmlC14NVisibleNsStackSave(ctx->ns_rendered, &state);
1419
1420
0
    if (visible) {
1421
0
        if (ctx->parent_is_doc) {
1422
      /* save this flag into the stack */
1423
0
      parent_is_doc = ctx->parent_is_doc;
1424
0
      ctx->parent_is_doc = 0;
1425
0
            ctx->pos = XMLC14N_INSIDE_DOCUMENT_ELEMENT;
1426
0
        }
1427
0
        xmlOutputBufferWriteString(ctx->buf, "<");
1428
1429
0
        if ((cur->ns != NULL) && (xmlStrlen(cur->ns->prefix) > 0)) {
1430
0
            xmlOutputBufferWriteString(ctx->buf,
1431
0
                                       (const char *) cur->ns->prefix);
1432
0
            xmlOutputBufferWriteString(ctx->buf, ":");
1433
0
        }
1434
0
        xmlOutputBufferWriteString(ctx->buf, (const char *) cur->name);
1435
0
    }
1436
1437
0
    if (!xmlC14NIsExclusive(ctx)) {
1438
0
        ret = xmlC14NProcessNamespacesAxis(ctx, cur, visible);
1439
0
    } else {
1440
0
        ret = xmlExcC14NProcessNamespacesAxis(ctx, cur, visible);
1441
0
    }
1442
0
    if (ret < 0)
1443
0
        return (-1);
1444
    /* todo: shouldn't this go to "visible only"? */
1445
0
    if(visible) {
1446
0
  xmlC14NVisibleNsStackShift(ctx->ns_rendered);
1447
0
    }
1448
1449
0
    ret = xmlC14NProcessAttrsAxis(ctx, cur, visible);
1450
0
    if (ret < 0)
1451
0
  return (-1);
1452
1453
0
    if (visible) {
1454
0
        xmlOutputBufferWriteString(ctx->buf, ">");
1455
0
    }
1456
0
    if (cur->children != NULL) {
1457
0
        ret = xmlC14NProcessNodeList(ctx, cur->children);
1458
0
        if (ret < 0)
1459
0
            return (-1);
1460
0
    }
1461
0
    if (visible) {
1462
0
        xmlOutputBufferWriteString(ctx->buf, "</");
1463
0
        if ((cur->ns != NULL) && (xmlStrlen(cur->ns->prefix) > 0)) {
1464
0
            xmlOutputBufferWriteString(ctx->buf,
1465
0
                                       (const char *) cur->ns->prefix);
1466
0
            xmlOutputBufferWriteString(ctx->buf, ":");
1467
0
        }
1468
0
        xmlOutputBufferWriteString(ctx->buf, (const char *) cur->name);
1469
0
        xmlOutputBufferWriteString(ctx->buf, ">");
1470
0
        if (parent_is_doc) {
1471
      /* restore this flag from the stack for next node */
1472
0
            ctx->parent_is_doc = parent_is_doc;
1473
0
      ctx->pos = XMLC14N_AFTER_DOCUMENT_ELEMENT;
1474
0
        }
1475
0
    }
1476
1477
    /*
1478
     * Restore ns_rendered stack position
1479
     */
1480
0
    xmlC14NVisibleNsStackRestore(ctx->ns_rendered, &state);
1481
0
    return (0);
1482
0
}
1483
1484
/**
1485
 * Processes the given node
1486
 *
1487
 * @param ctx     the pointer to C14N context object
1488
 * @param cur     the node to process
1489
 * @returns non-negative value on success or negative value on fail
1490
 */
1491
static int
1492
xmlC14NProcessNode(xmlC14NCtxPtr ctx, xmlNodePtr cur)
1493
0
{
1494
0
    int ret = 0;
1495
0
    int visible;
1496
1497
0
    if ((ctx == NULL) || (cur == NULL)) {
1498
0
        xmlC14NErrParam(ctx);
1499
0
        return (-1);
1500
0
    }
1501
1502
0
    visible = xmlC14NIsVisible(ctx, cur, cur->parent);
1503
0
    switch (cur->type) {
1504
0
        case XML_ELEMENT_NODE:
1505
0
            ret = xmlC14NProcessElementNode(ctx, cur, visible);
1506
0
            break;
1507
0
        case XML_CDATA_SECTION_NODE:
1508
0
        case XML_TEXT_NODE:
1509
            /*
1510
             * Text Nodes
1511
             * the string value, except all ampersands are replaced
1512
             * by &amp;, all open angle brackets (<) are replaced by &lt;, all closing
1513
             * angle brackets (>) are replaced by &gt;, and all #xD characters are
1514
             * replaced by &#xD;.
1515
             */
1516
            /* cdata sections are processed as text nodes */
1517
            /* todo: verify that cdata sections are included in XPath nodes set */
1518
0
            if ((visible) && (cur->content != NULL)) {
1519
0
                xmlChar *buffer;
1520
1521
0
                buffer = xmlC11NNormalizeText(cur->content);
1522
0
                if (buffer != NULL) {
1523
0
                    xmlOutputBufferWriteString(ctx->buf,
1524
0
                                               (const char *) buffer);
1525
0
                    xmlFree(buffer);
1526
0
                } else {
1527
0
                    xmlC14NErrMemory(ctx);
1528
0
                    return (-1);
1529
0
                }
1530
0
            }
1531
0
            break;
1532
0
        case XML_PI_NODE:
1533
            /*
1534
             * Processing Instruction (PI) Nodes-
1535
             * The opening PI symbol (<?), the PI target name of the node,
1536
             * a leading space and the string value if it is not empty, and
1537
             * the closing PI symbol (?>). If the string value is empty,
1538
             * then the leading space is not added. Also, a trailing #xA is
1539
             * rendered after the closing PI symbol for PI children of the
1540
             * root node with a lesser document order than the document
1541
             * element, and a leading #xA is rendered before the opening PI
1542
             * symbol of PI children of the root node with a greater document
1543
             * order than the document element.
1544
             */
1545
0
            if (visible) {
1546
0
                if (ctx->pos == XMLC14N_AFTER_DOCUMENT_ELEMENT) {
1547
0
                    xmlOutputBufferWriteString(ctx->buf, "\x0A<?");
1548
0
                } else {
1549
0
                    xmlOutputBufferWriteString(ctx->buf, "<?");
1550
0
                }
1551
1552
0
                xmlOutputBufferWriteString(ctx->buf,
1553
0
                                           (const char *) cur->name);
1554
0
                if ((cur->content != NULL) && (*(cur->content) != '\0')) {
1555
0
                    xmlChar *buffer;
1556
1557
0
                    xmlOutputBufferWriteString(ctx->buf, " ");
1558
1559
                    /* todo: do we need to normalize pi? */
1560
0
                    buffer = xmlC11NNormalizePI(cur->content);
1561
0
                    if (buffer != NULL) {
1562
0
                        xmlOutputBufferWriteString(ctx->buf,
1563
0
                                                   (const char *) buffer);
1564
0
                        xmlFree(buffer);
1565
0
                    } else {
1566
0
                        xmlC14NErrMemory(ctx);
1567
0
                        return (-1);
1568
0
                    }
1569
0
                }
1570
1571
0
                if (ctx->pos == XMLC14N_BEFORE_DOCUMENT_ELEMENT) {
1572
0
                    xmlOutputBufferWriteString(ctx->buf, "?>\x0A");
1573
0
                } else {
1574
0
                    xmlOutputBufferWriteString(ctx->buf, "?>");
1575
0
                }
1576
0
            }
1577
0
            break;
1578
0
        case XML_COMMENT_NODE:
1579
            /*
1580
             * Comment Nodes
1581
             * Nothing if generating canonical XML without  comments. For
1582
             * canonical XML with comments, generate the opening comment
1583
             * symbol (<!--), the string value of the node, and the
1584
             * closing comment symbol (-->). Also, a trailing #xA is rendered
1585
             * after the closing comment symbol for comment children of the
1586
             * root node with a lesser document order than the document
1587
             * element, and a leading #xA is rendered before the opening
1588
             * comment symbol of comment children of the root node with a
1589
             * greater document order than the document element. (Comment
1590
             * children of the root node represent comments outside of the
1591
             * top-level document element and outside of the document type
1592
             * declaration).
1593
             */
1594
0
            if (visible && ctx->with_comments) {
1595
0
                if (ctx->pos == XMLC14N_AFTER_DOCUMENT_ELEMENT) {
1596
0
                    xmlOutputBufferWriteString(ctx->buf, "\x0A<!--");
1597
0
                } else {
1598
0
                    xmlOutputBufferWriteString(ctx->buf, "<!--");
1599
0
                }
1600
1601
0
                if (cur->content != NULL) {
1602
0
                    xmlChar *buffer;
1603
1604
                    /* todo: do we need to normalize comment? */
1605
0
                    buffer = xmlC11NNormalizeComment(cur->content);
1606
0
                    if (buffer != NULL) {
1607
0
                        xmlOutputBufferWriteString(ctx->buf,
1608
0
                                                   (const char *) buffer);
1609
0
                        xmlFree(buffer);
1610
0
                    } else {
1611
0
                        xmlC14NErrMemory(ctx);
1612
0
                        return (-1);
1613
0
                    }
1614
0
                }
1615
1616
0
                if (ctx->pos == XMLC14N_BEFORE_DOCUMENT_ELEMENT) {
1617
0
                    xmlOutputBufferWriteString(ctx->buf, "-->\x0A");
1618
0
                } else {
1619
0
                    xmlOutputBufferWriteString(ctx->buf, "-->");
1620
0
                }
1621
0
            }
1622
0
            break;
1623
0
        case XML_DOCUMENT_NODE:
1624
0
        case XML_DOCUMENT_FRAG_NODE:   /* should be processed as document? */
1625
0
#ifdef LIBXML_HTML_ENABLED
1626
0
        case XML_HTML_DOCUMENT_NODE:   /* should be processed as document? */
1627
0
#endif
1628
0
            if (cur->children != NULL) {
1629
0
                ctx->pos = XMLC14N_BEFORE_DOCUMENT_ELEMENT;
1630
0
                ctx->parent_is_doc = 1;
1631
0
                ret = xmlC14NProcessNodeList(ctx, cur->children);
1632
0
            }
1633
0
            break;
1634
1635
0
        case XML_ATTRIBUTE_NODE:
1636
0
            xmlC14NErrInvalidNode(ctx, "XML_ATTRIBUTE_NODE", "processing node");
1637
0
            return (-1);
1638
0
        case XML_NAMESPACE_DECL:
1639
0
            xmlC14NErrInvalidNode(ctx, "XML_NAMESPACE_DECL", "processing node");
1640
0
            return (-1);
1641
0
        case XML_ENTITY_REF_NODE:
1642
0
            xmlC14NErrInvalidNode(ctx, "XML_ENTITY_REF_NODE", "processing node");
1643
0
            return (-1);
1644
0
        case XML_ENTITY_NODE:
1645
0
            xmlC14NErrInvalidNode(ctx, "XML_ENTITY_NODE", "processing node");
1646
0
            return (-1);
1647
1648
0
        case XML_DOCUMENT_TYPE_NODE:
1649
0
        case XML_NOTATION_NODE:
1650
0
        case XML_DTD_NODE:
1651
0
        case XML_ELEMENT_DECL:
1652
0
        case XML_ATTRIBUTE_DECL:
1653
0
        case XML_ENTITY_DECL:
1654
0
#ifdef LIBXML_XINCLUDE_ENABLED
1655
0
        case XML_XINCLUDE_START:
1656
0
        case XML_XINCLUDE_END:
1657
0
#endif
1658
            /*
1659
             * should be ignored according to "W3C Canonical XML"
1660
             */
1661
0
            break;
1662
0
        default:
1663
0
            xmlC14NErrUnknownNode(ctx, cur->type, "processing node");
1664
0
            return (-1);
1665
0
    }
1666
1667
0
    return (ret);
1668
0
}
1669
1670
/**
1671
 * Processes all nodes in the row starting from cur.
1672
 *
1673
 * @param ctx     the pointer to C14N context object
1674
 * @param cur     the node to start from
1675
 * @returns non-negative value on success or negative value on fail
1676
 */
1677
static int
1678
xmlC14NProcessNodeList(xmlC14NCtxPtr ctx, xmlNodePtr cur)
1679
0
{
1680
0
    int ret;
1681
1682
0
    if (ctx == NULL) {
1683
0
        xmlC14NErrParam(ctx);
1684
0
        return (-1);
1685
0
    }
1686
1687
0
    for (ret = 0; cur != NULL && ret >= 0; cur = cur->next) {
1688
0
        ret = xmlC14NProcessNode(ctx, cur);
1689
0
    }
1690
0
    return (ret);
1691
0
}
1692
1693
1694
/**
1695
 * Cleanups the C14N context object.
1696
 *
1697
 * @param ctx  the pointer to C14N context object
1698
 */
1699
1700
static void
1701
xmlC14NFreeCtx(xmlC14NCtxPtr ctx)
1702
0
{
1703
0
    if (ctx == NULL) {
1704
0
        xmlC14NErrParam(ctx);
1705
0
        return;
1706
0
    }
1707
1708
0
    if (ctx->ns_rendered != NULL) {
1709
0
        xmlC14NVisibleNsStackDestroy(ctx->ns_rendered);
1710
0
    }
1711
0
    xmlFree(ctx);
1712
0
}
1713
1714
/**
1715
 * Creates new C14N context object to store C14N parameters.
1716
 *
1717
 * @param doc     the XML document for canonization
1718
 * @param is_visible_callback  the function to use to determine is node visible
1719
 *      or not
1720
 * @param user_data     the first parameter for `is_visible_callback` function
1721
 *      (in most cases, it is nodes set)
1722
 * @param mode  the c14n mode (see `xmlC14NMode`)
1723
 * @param inclusive_ns_prefixes  the list of inclusive namespace prefixes
1724
 *      ended with a NULL or NULL if there is no
1725
 *      inclusive namespaces (only for `
1726
 *      canonicalization)
1727
 * @param with_comments   include comments in the result (!=0) or not (==0)
1728
 * @param buf     the output buffer to store canonical XML; this
1729
 *      buffer MUST have encoder==NULL because C14N requires
1730
 *      UTF-8 output
1731
 * @returns pointer to newly created object (success) or NULL (fail)
1732
 */
1733
static xmlC14NCtxPtr
1734
xmlC14NNewCtx(xmlDocPtr doc,
1735
        xmlC14NIsVisibleCallback is_visible_callback, void* user_data,
1736
              xmlC14NMode mode, xmlChar ** inclusive_ns_prefixes,
1737
              int with_comments, xmlOutputBufferPtr buf)
1738
0
{
1739
0
    xmlC14NCtxPtr ctx = NULL;
1740
1741
0
    if ((doc == NULL) || (buf == NULL)) {
1742
0
        xmlC14NErrParam(ctx);
1743
0
        return (NULL);
1744
0
    }
1745
1746
    /*
1747
     *  Validate the encoding output buffer encoding
1748
     */
1749
0
    if (buf->encoder != NULL) {
1750
0
        xmlC14NErr(ctx, (xmlNodePtr) doc, XML_C14N_REQUIRES_UTF8,
1751
0
"xmlC14NNewCtx: output buffer encoder != NULL but C14N requires UTF8 output\n");
1752
0
        return (NULL);
1753
0
    }
1754
1755
    /*
1756
     * Allocate a new xmlC14NCtx and fill the fields.
1757
     */
1758
0
    ctx = (xmlC14NCtxPtr) xmlMalloc(sizeof(xmlC14NCtx));
1759
0
    if (ctx == NULL) {
1760
0
  xmlC14NErrMemory(ctx);
1761
0
        return (NULL);
1762
0
    }
1763
0
    memset(ctx, 0, sizeof(xmlC14NCtx));
1764
1765
    /*
1766
     * initialize C14N context
1767
     */
1768
0
    ctx->doc = doc;
1769
0
    ctx->with_comments = with_comments;
1770
0
    ctx->is_visible_callback = is_visible_callback;
1771
0
    ctx->user_data = user_data;
1772
0
    ctx->buf = buf;
1773
0
    ctx->parent_is_doc = 1;
1774
0
    ctx->pos = XMLC14N_BEFORE_DOCUMENT_ELEMENT;
1775
0
    ctx->ns_rendered = xmlC14NVisibleNsStackCreate();
1776
1777
0
    if(ctx->ns_rendered == NULL) {
1778
0
        xmlC14NErrMemory(ctx);
1779
0
  xmlC14NFreeCtx(ctx);
1780
0
        return (NULL);
1781
0
    }
1782
1783
    /*
1784
     * Set "mode" flag and remember list of inclusive prefixes
1785
     * for exclusive c14n
1786
     */
1787
0
    ctx->mode = mode;
1788
0
    if(xmlC14NIsExclusive(ctx)) {
1789
0
        ctx->inclusive_ns_prefixes = inclusive_ns_prefixes;
1790
0
    }
1791
1792
0
    return (ctx);
1793
0
}
1794
1795
/**
1796
 * Dumps the canonized image of given XML document into the provided buffer.
1797
 * For details see "Canonical XML" (http://www.w3.org/TR/xml-c14n) or
1798
 * "Exclusive XML Canonicalization" (http://www.w3.org/TR/xml-exc-c14n)
1799
 *
1800
 * @param doc     the XML document for canonization
1801
 * @param is_visible_callback  the function to use to determine is node visible
1802
 *      or not
1803
 * @param user_data     the first parameter for `is_visible_callback` function
1804
 *      (in most cases, it is nodes set)
1805
 * @param mode    the c14n mode (see `xmlC14NMode`)
1806
 * @param inclusive_ns_prefixes  the list of inclusive namespace prefixes
1807
 *      ended with a NULL or NULL if there is no
1808
 *      inclusive namespaces (only for exclusive
1809
 *      canonicalization, ignored otherwise)
1810
 * @param with_comments   include comments in the result (!=0) or not (==0)
1811
 * @param buf     the output buffer to store canonical XML; this
1812
 *      buffer MUST have encoder==NULL because C14N requires
1813
 *      UTF-8 output
1814
 * @returns non-negative value on success or a negative value on fail
1815
 */
1816
int
1817
xmlC14NExecute(xmlDoc *doc, xmlC14NIsVisibleCallback is_visible_callback,
1818
   void* user_data, int mode, xmlChar **inclusive_ns_prefixes,
1819
0
   int with_comments, xmlOutputBuffer *buf) {
1820
1821
0
    xmlC14NCtxPtr ctx;
1822
0
    xmlC14NMode c14n_mode = XML_C14N_1_0;
1823
0
    int ret;
1824
1825
0
    if ((buf == NULL) || (doc == NULL)) {
1826
0
        xmlC14NErrParam(NULL);
1827
0
        return (-1);
1828
0
    }
1829
1830
    /* for backward compatibility, we have to have "mode" as "int"
1831
       and here we check that user gives valid value */
1832
0
    switch(mode) {
1833
0
    case XML_C14N_1_0:
1834
0
    case XML_C14N_EXCLUSIVE_1_0:
1835
0
    case XML_C14N_1_1:
1836
0
         c14n_mode = (xmlC14NMode)mode;
1837
0
         break;
1838
0
    default:
1839
0
        xmlC14NErrParam(NULL);
1840
0
        return (-1);
1841
0
    }
1842
1843
    /*
1844
     *  Validate the encoding output buffer encoding
1845
     */
1846
0
    if (buf->encoder != NULL) {
1847
0
        xmlC14NErr(NULL, (xmlNodePtr) doc, XML_C14N_REQUIRES_UTF8,
1848
0
"xmlC14NExecute: output buffer encoder != NULL but C14N requires UTF8 output\n");
1849
0
        return (-1);
1850
0
    }
1851
1852
0
    ctx = xmlC14NNewCtx(doc, is_visible_callback, user_data,
1853
0
              c14n_mode, inclusive_ns_prefixes,
1854
0
                    with_comments, buf);
1855
0
    if (ctx == NULL) {
1856
0
        xmlC14NErr(NULL, (xmlNodePtr) doc, XML_C14N_CREATE_CTXT,
1857
0
       "xmlC14NExecute: unable to create C14N context\n");
1858
0
        return (-1);
1859
0
    }
1860
1861
1862
1863
    /*
1864
     * Root Node
1865
     * The root node is the parent of the top-level document element. The
1866
     * result of processing each of its child nodes that is in the node-set
1867
     * in document order. The root node does not generate a byte order mark,
1868
     * XML declaration, nor anything from within the document type
1869
     * declaration.
1870
     */
1871
0
    if (doc->children != NULL) {
1872
0
        ret = xmlC14NProcessNodeList(ctx, doc->children);
1873
0
        if (ret < 0) {
1874
0
            xmlC14NFreeCtx(ctx);
1875
0
            return (-1);
1876
0
        }
1877
0
    }
1878
1879
    /*
1880
     * Flush buffer to get number of bytes written
1881
     */
1882
0
    ret = xmlOutputBufferFlush(buf);
1883
0
    if (ret < 0) {
1884
0
        xmlC14NErr(ctx, NULL, buf->error, "flushing output buffer");
1885
0
        xmlC14NFreeCtx(ctx);
1886
0
        return (-1);
1887
0
    }
1888
1889
    /*
1890
     * Cleanup
1891
     */
1892
0
    xmlC14NFreeCtx(ctx);
1893
0
    return (ret);
1894
0
}
1895
1896
/**
1897
 * Dumps the canonized image of given XML document into the provided buffer.
1898
 * For details see "Canonical XML" (http://www.w3.org/TR/xml-c14n) or
1899
 * "Exclusive XML Canonicalization" (http://www.w3.org/TR/xml-exc-c14n)
1900
 *
1901
 * @param doc     the XML document for canonization
1902
 * @param nodes     the nodes set to be included in the canonized image
1903
 *    or NULL if all document nodes should be included
1904
 * @param mode      the c14n mode (see `xmlC14NMode`)
1905
 * @param inclusive_ns_prefixes  the list of inclusive namespace prefixes
1906
 *      ended with a NULL or NULL if there is no
1907
 *      inclusive namespaces (only for exclusive
1908
 *      canonicalization, ignored otherwise)
1909
 * @param with_comments   include comments in the result (!=0) or not (==0)
1910
 * @param buf     the output buffer to store canonical XML; this
1911
 *      buffer MUST have encoder==NULL because C14N requires
1912
 *      UTF-8 output
1913
 * @returns non-negative value on success or a negative value on fail
1914
 */
1915
int
1916
xmlC14NDocSaveTo(xmlDoc *doc, xmlNodeSet *nodes,
1917
                 int mode, xmlChar ** inclusive_ns_prefixes,
1918
0
                 int with_comments, xmlOutputBuffer *buf) {
1919
0
    return(xmlC14NExecute(doc,
1920
0
      xmlC14NIsNodeInNodeset,
1921
0
      nodes,
1922
0
      mode,
1923
0
      inclusive_ns_prefixes,
1924
0
      with_comments,
1925
0
      buf));
1926
0
}
1927
1928
1929
/**
1930
 * Dumps the canonized image of given XML document into memory.
1931
 * For details see "Canonical XML" (http://www.w3.org/TR/xml-c14n) or
1932
 * "Exclusive XML Canonicalization" (http://www.w3.org/TR/xml-exc-c14n)
1933
 *
1934
 * @param doc     the XML document for canonization
1935
 * @param nodes     the nodes set to be included in the canonized image
1936
 *    or NULL if all document nodes should be included
1937
 * @param mode      the c14n mode (see `xmlC14NMode`)
1938
 * @param inclusive_ns_prefixes  the list of inclusive namespace prefixes
1939
 *      ended with a NULL or NULL if there is no
1940
 *      inclusive namespaces (only for exclusive
1941
 *      canonicalization, ignored otherwise)
1942
 * @param with_comments   include comments in the result (!=0) or not (==0)
1943
 * @param doc_txt_ptr   the memory pointer for allocated canonical XML text;
1944
 *      the caller of this functions is responsible for calling
1945
 *      xmlFree() to free allocated memory
1946
 * @returns the number of bytes written on success or a negative value on fail
1947
 */
1948
int
1949
xmlC14NDocDumpMemory(xmlDoc *doc, xmlNodeSet *nodes,
1950
                     int mode, xmlChar ** inclusive_ns_prefixes,
1951
                     int with_comments, xmlChar ** doc_txt_ptr)
1952
0
{
1953
0
    int ret;
1954
0
    xmlOutputBufferPtr buf;
1955
1956
0
    if (doc_txt_ptr == NULL) {
1957
0
        xmlC14NErrParam(NULL);
1958
0
        return (-1);
1959
0
    }
1960
1961
0
    *doc_txt_ptr = NULL;
1962
1963
    /*
1964
     * create memory buffer with UTF8 (default) encoding
1965
     */
1966
0
    buf = xmlAllocOutputBuffer(NULL);
1967
0
    if (buf == NULL) {
1968
0
        xmlC14NErrMemory(NULL);
1969
0
        return (-1);
1970
0
    }
1971
1972
    /*
1973
     * canonize document and write to buffer
1974
     */
1975
0
    ret = xmlC14NDocSaveTo(doc, nodes, mode, inclusive_ns_prefixes,
1976
0
                           with_comments, buf);
1977
0
    if (ret < 0) {
1978
0
        (void) xmlOutputBufferClose(buf);
1979
0
        return (-1);
1980
0
    }
1981
1982
0
    ret = xmlBufUse(buf->buffer);
1983
0
    if (ret >= 0) {
1984
0
        *doc_txt_ptr = xmlStrndup(xmlBufContent(buf->buffer), ret);
1985
0
    }
1986
0
    (void) xmlOutputBufferClose(buf);
1987
1988
0
    if ((*doc_txt_ptr == NULL) && (ret >= 0)) {
1989
0
        xmlC14NErrMemory(NULL);
1990
0
        return (-1);
1991
0
    }
1992
0
    return (ret);
1993
0
}
1994
1995
/**
1996
 * Dumps the canonized image of given XML document into the file.
1997
 * For details see "Canonical XML" (http://www.w3.org/TR/xml-c14n) or
1998
 * "Exclusive XML Canonicalization" (http://www.w3.org/TR/xml-exc-c14n)
1999
 *
2000
 * @param doc     the XML document for canonization
2001
 * @param nodes     the nodes set to be included in the canonized image
2002
 *    or NULL if all document nodes should be included
2003
 * @param mode      the c14n mode (see `xmlC14NMode`)
2004
 * @param inclusive_ns_prefixes  the list of inclusive namespace prefixes
2005
 *      ended with a NULL or NULL if there is no
2006
 *      inclusive namespaces (only for exclusive
2007
 *      canonicalization, ignored otherwise)
2008
 * @param with_comments   include comments in the result (!=0) or not (==0)
2009
 * @param filename      the filename to store canonical XML image
2010
 * @param compression   the compression level (zlib required):
2011
 *        -1 - libxml default,
2012
 *         0 - uncompressed,
2013
 *        >0 - compression level
2014
 * @returns the number of bytes written success or a negative value on fail
2015
 */
2016
int
2017
xmlC14NDocSave(xmlDoc *doc, xmlNodeSet *nodes,
2018
               int mode, xmlChar ** inclusive_ns_prefixes,
2019
               int with_comments, const char *filename, int compression)
2020
0
{
2021
0
    xmlOutputBufferPtr buf;
2022
0
    int ret;
2023
2024
0
    if (filename == NULL) {
2025
0
        xmlC14NErrParam(NULL);
2026
0
        return (-1);
2027
0
    }
2028
#ifdef LIBXML_ZLIB_ENABLED
2029
    if (compression < 0)
2030
        compression = xmlGetCompressMode();
2031
#endif
2032
2033
    /*
2034
     * save the content to a temp buffer, use default UTF8 encoding.
2035
     */
2036
0
    buf = xmlOutputBufferCreateFilename(filename, NULL, compression);
2037
0
    if (buf == NULL) {
2038
0
        xmlC14NErr(NULL, NULL, XML_IO_UNKNOWN, "creating temporary filename");
2039
0
        return (-1);
2040
0
    }
2041
2042
    /*
2043
     * canonize document and write to buffer
2044
     */
2045
0
    ret = xmlC14NDocSaveTo(doc, nodes, mode, inclusive_ns_prefixes,
2046
0
                           with_comments, buf);
2047
0
    if (ret < 0) {
2048
0
        (void) xmlOutputBufferClose(buf);
2049
0
        return (-1);
2050
0
    }
2051
2052
    /*
2053
     * get the numbers of bytes written
2054
     */
2055
0
    ret = xmlOutputBufferClose(buf);
2056
0
    return (ret);
2057
0
}
2058
2059
/**
2060
 * Converts a string to a canonical (normalized) format. The code is stolen
2061
 * from xmlEscapeText. Added normalization of `\x09`, `\x0a`,
2062
 * `\x0A` and the `mode` parameter.
2063
 *
2064
 * @param input     the input string
2065
 * @param mode      the normalization mode (attribute, comment, PI or text)
2066
 * @returns a normalized string (caller is responsible for calling #xmlFree)
2067
 * or NULL if an error occurs
2068
 */
2069
static xmlChar *
2070
xmlC11NNormalizeString(const xmlChar * input,
2071
                       xmlC14NNormalizationMode mode)
2072
0
{
2073
0
    const xmlChar *cur = input;
2074
0
    xmlChar *buffer = NULL;
2075
0
    xmlChar *out = NULL;
2076
0
    int buffer_size = 0;
2077
2078
0
    if (input == NULL)
2079
0
        return (NULL);
2080
2081
    /*
2082
     * allocate an translation buffer.
2083
     */
2084
0
    buffer_size = 1000;
2085
0
    buffer = xmlMalloc(buffer_size);
2086
0
    if (buffer == NULL)
2087
0
        return (NULL);
2088
0
    out = buffer;
2089
2090
0
    while (*cur != '\0') {
2091
0
        if ((out - buffer) > (buffer_size - 10)) {
2092
0
            xmlChar *tmp;
2093
0
            int indx = out - buffer;
2094
0
            int newSize;
2095
2096
0
            newSize = xmlGrowCapacity(buffer_size, 1, 1, XML_MAX_ITEMS);
2097
0
            if (newSize < 0) {
2098
0
                xmlFree(buffer);
2099
0
                return(NULL);
2100
0
            }
2101
0
            tmp = xmlRealloc(buffer, newSize);
2102
0
            if (tmp == NULL) {
2103
0
                xmlFree(buffer);
2104
0
                return(NULL);
2105
0
            }
2106
0
            buffer = tmp;
2107
0
            buffer_size = newSize;
2108
0
            out = &buffer[indx];
2109
0
        }
2110
2111
0
        if ((*cur == '<') && ((mode == XMLC14N_NORMALIZE_ATTR) ||
2112
0
                              (mode == XMLC14N_NORMALIZE_TEXT))) {
2113
0
            *out++ = '&';
2114
0
            *out++ = 'l';
2115
0
            *out++ = 't';
2116
0
            *out++ = ';';
2117
0
        } else if ((*cur == '>') && (mode == XMLC14N_NORMALIZE_TEXT)) {
2118
0
            *out++ = '&';
2119
0
            *out++ = 'g';
2120
0
            *out++ = 't';
2121
0
            *out++ = ';';
2122
0
        } else if ((*cur == '&') && ((mode == XMLC14N_NORMALIZE_ATTR) ||
2123
0
                                     (mode == XMLC14N_NORMALIZE_TEXT))) {
2124
0
            *out++ = '&';
2125
0
            *out++ = 'a';
2126
0
            *out++ = 'm';
2127
0
            *out++ = 'p';
2128
0
            *out++ = ';';
2129
0
        } else if ((*cur == '"') && (mode == XMLC14N_NORMALIZE_ATTR)) {
2130
0
            *out++ = '&';
2131
0
            *out++ = 'q';
2132
0
            *out++ = 'u';
2133
0
            *out++ = 'o';
2134
0
            *out++ = 't';
2135
0
            *out++ = ';';
2136
0
        } else if ((*cur == '\x09') && (mode == XMLC14N_NORMALIZE_ATTR)) {
2137
0
            *out++ = '&';
2138
0
            *out++ = '#';
2139
0
            *out++ = 'x';
2140
0
            *out++ = '9';
2141
0
            *out++ = ';';
2142
0
        } else if ((*cur == '\x0A') && (mode == XMLC14N_NORMALIZE_ATTR)) {
2143
0
            *out++ = '&';
2144
0
            *out++ = '#';
2145
0
            *out++ = 'x';
2146
0
            *out++ = 'A';
2147
0
            *out++ = ';';
2148
0
        } else if ((*cur == '\x0D') && ((mode == XMLC14N_NORMALIZE_ATTR) ||
2149
0
                                        (mode == XMLC14N_NORMALIZE_TEXT) ||
2150
0
                                        (mode == XMLC14N_NORMALIZE_COMMENT) ||
2151
0
          (mode == XMLC14N_NORMALIZE_PI))) {
2152
0
            *out++ = '&';
2153
0
            *out++ = '#';
2154
0
            *out++ = 'x';
2155
0
            *out++ = 'D';
2156
0
            *out++ = ';';
2157
0
        } else {
2158
            /*
2159
             * Works because on UTF-8, all extended sequences cannot
2160
             * result in bytes in the ASCII range.
2161
             */
2162
0
            *out++ = *cur;
2163
0
        }
2164
0
        cur++;
2165
0
    }
2166
0
    *out = 0;
2167
0
    return (buffer);
2168
0
}
2169
2170
#endif /* LIBXML_C14N_ENABLED */