Coverage Report

Created: 2026-09-18 06:12

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
2.46M
    xmlC11NNormalizeString((a), XMLC14N_NORMALIZE_ATTR)
110
#define xmlC11NNormalizeComment( a ) \
111
0
    xmlC11NNormalizeString((a), XMLC14N_NORMALIZE_COMMENT)
112
#define xmlC11NNormalizePI( a ) \
113
83.8k
    xmlC11NNormalizeString((a), XMLC14N_NORMALIZE_PI)
114
#define xmlC11NNormalizeText( a ) \
115
767k
    xmlC11NNormalizeString((a), XMLC14N_NORMALIZE_TEXT)
116
117
#define xmlC14NIsVisible( ctx, node, parent ) \
118
10.3M
     (((ctx)->is_visible_callback != NULL) ? \
119
10.3M
  (ctx)->is_visible_callback((ctx)->user_data, \
120
10.3M
    (xmlNodePtr)(node), (xmlNodePtr)(parent)) : 1)
121
122
#define xmlC14NIsExclusive( ctx ) \
123
2.26M
    ( (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
65
{
149
65
    va_list ap;
150
65
    int res;
151
152
65
    if (ctxt != NULL)
153
65
        ctxt->error = code;
154
155
65
    va_start(ap, msg);
156
65
    res = xmlVRaiseError(NULL, NULL, NULL, ctxt, node,
157
65
                         XML_FROM_C14N, code, XML_ERR_ERROR, NULL, 0,
158
65
                         str1, NULL, NULL, 0, 0,
159
65
                         msg, ap);
160
65
    va_end(ap);
161
65
    if (res < 0)
162
0
        xmlC14NErrMemory(ctxt);
163
65
}
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
15
{
215
15
    xmlC14NErrFull(ctxt, NULL, XML_C14N_RELATIVE_NAMESPACE, ns_uri,
216
15
       "Relative namespace UR is invalid here : %s\n", ns_uri);
217
15
}
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
50
{
233
50
    xmlC14NErrFull(ctxt, node, error, NULL, "%s", msg);
234
50
}
235
236
/************************************************************************
237
 *                  *
238
 *    The implementation internals        *
239
 *                  *
240
 ************************************************************************/
241
52.2k
#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
18.4k
xmlC14NVisibleNsStackCreate(void) {
273
18.4k
    xmlC14NVisibleNsStackPtr ret;
274
275
18.4k
    ret = (xmlC14NVisibleNsStackPtr) xmlMalloc(sizeof(xmlC14NVisibleNsStack));
276
18.4k
    if (ret == NULL)
277
0
  return(NULL);
278
18.4k
    memset(ret, 0, sizeof(xmlC14NVisibleNsStack));
279
18.4k
    return(ret);
280
18.4k
}
281
282
static void
283
18.4k
xmlC14NVisibleNsStackDestroy(xmlC14NVisibleNsStackPtr cur) {
284
18.4k
    if(cur == NULL) {
285
0
        xmlC14NErrParam(NULL);
286
0
        return;
287
0
    }
288
18.4k
    if(cur->nsTab != NULL) {
289
18.3k
  memset(cur->nsTab, 0, cur->nsMax * sizeof(xmlNsPtr));
290
18.3k
  xmlFree(cur->nsTab);
291
18.3k
    }
292
18.4k
    if(cur->nodeTab != NULL) {
293
18.3k
  memset(cur->nodeTab, 0, cur->nsMax * sizeof(xmlNodePtr));
294
18.3k
  xmlFree(cur->nodeTab);
295
18.3k
    }
296
18.4k
    memset(cur, 0, sizeof(xmlC14NVisibleNsStack));
297
18.4k
    xmlFree(cur);
298
299
18.4k
}
300
301
static int
302
2.17M
xmlC14NVisibleNsStackAdd(xmlC14NVisibleNsStackPtr cur, xmlNsPtr ns, xmlNodePtr node) {
303
2.17M
    if((cur == NULL) ||
304
2.17M
       ((cur->nsTab == NULL) && (cur->nodeTab != NULL)) ||
305
2.17M
       ((cur->nsTab != NULL) && (cur->nodeTab == NULL)))
306
0
  return (1);
307
308
2.17M
    if (cur->nsMax <= cur->nsCurEnd) {
309
52.2k
  xmlNsPtr *tmp1;
310
52.2k
        xmlNodePtr *tmp2;
311
52.2k
  int newSize;
312
313
52.2k
        newSize = xmlGrowCapacity(cur->nsMax,
314
52.2k
                                  sizeof(tmp1[0]) + sizeof(tmp2[0]),
315
52.2k
                                  XML_NAMESPACES_DEFAULT, XML_MAX_ITEMS);
316
52.2k
        if (newSize < 0)
317
0
            return (-1);
318
319
52.2k
  tmp1 = xmlRealloc(cur->nsTab, newSize * sizeof(tmp1[0]));
320
52.2k
  if (tmp1 == NULL)
321
0
      return (-1);
322
52.2k
  cur->nsTab = tmp1;
323
324
52.2k
  tmp2 = xmlRealloc(cur->nodeTab, newSize * sizeof(tmp2[0]));
325
52.2k
  if (tmp2 == NULL)
326
0
      return (-1);
327
52.2k
  cur->nodeTab = tmp2;
328
329
52.2k
  cur->nsMax = newSize;
330
52.2k
    }
331
2.17M
    cur->nsTab[cur->nsCurEnd] = ns;
332
2.17M
    cur->nodeTab[cur->nsCurEnd] = node;
333
334
2.17M
    ++cur->nsCurEnd;
335
336
2.17M
    return (0);
337
2.17M
}
338
339
static void
340
2.24M
xmlC14NVisibleNsStackSave(xmlC14NVisibleNsStackPtr cur, xmlC14NVisibleNsStackPtr state) {
341
2.24M
    if((cur == NULL) || (state == NULL)) {
342
0
        xmlC14NErrParam(NULL);
343
0
  return;
344
0
    }
345
346
2.24M
    state->nsCurEnd = cur->nsCurEnd;
347
2.24M
    state->nsPrevStart = cur->nsPrevStart;
348
2.24M
    state->nsPrevEnd = cur->nsPrevEnd;
349
2.24M
}
350
351
static void
352
2.24M
xmlC14NVisibleNsStackRestore(xmlC14NVisibleNsStackPtr cur, xmlC14NVisibleNsStackPtr state) {
353
2.24M
    if((cur == NULL) || (state == NULL)) {
354
0
        xmlC14NErrParam(NULL);
355
0
  return;
356
0
    }
357
2.24M
    cur->nsCurEnd = state->nsCurEnd;
358
2.24M
    cur->nsPrevStart = state->nsPrevStart;
359
2.24M
    cur->nsPrevEnd = state->nsPrevEnd;
360
2.24M
}
361
362
static void
363
2.12M
xmlC14NVisibleNsStackShift(xmlC14NVisibleNsStackPtr cur) {
364
2.12M
    if(cur == NULL) {
365
0
        xmlC14NErrParam(NULL);
366
0
  return;
367
0
    }
368
2.12M
    cur->nsPrevStart = cur->nsPrevEnd;
369
2.12M
    cur->nsPrevEnd = cur->nsCurEnd;
370
2.12M
}
371
372
static int
373
8.44M
xmlC14NStrEqual(const xmlChar *str1, const xmlChar *str2) {
374
8.44M
    if (str1 == str2) return(1);
375
6.51M
    if (str1 == NULL) return((*str2) == '\0');
376
6.51M
    if (str2 == NULL) return((*str1) == '\0');
377
1.67M
    do {
378
1.67M
  if (*str1++ != *str2) return(0);
379
1.67M
    } while (*str2++);
380
2.96k
    return(1);
381
144k
}
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
2.18M
{
393
2.18M
    int i;
394
2.18M
    const xmlChar *prefix;
395
2.18M
    const xmlChar *href;
396
2.18M
    int has_empty_ns;
397
398
2.18M
    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
2.18M
    prefix = ((ns == NULL) || (ns->prefix == NULL)) ? BAD_CAST "" : ns->prefix;
408
2.18M
    href = ((ns == NULL) || (ns->href == NULL)) ? BAD_CAST "" : ns->href;
409
2.18M
    has_empty_ns = (xmlC14NStrEqual(prefix, NULL) && xmlC14NStrEqual(href, NULL));
410
411
2.18M
    if (cur->nsTab != NULL) {
412
2.15M
  int start = (has_empty_ns) ? 0 : cur->nsPrevStart;
413
2.29M
        for (i = cur->nsCurEnd - 1; i >= start; --i) {
414
2.13M
            xmlNsPtr ns1 = cur->nsTab[i];
415
416
2.13M
      if(xmlC14NStrEqual(prefix, (ns1 != NULL) ? ns1->prefix : NULL)) {
417
1.99M
    return(xmlC14NStrEqual(href, (ns1 != NULL) ? ns1->href : NULL));
418
1.99M
      }
419
2.13M
        }
420
2.15M
    }
421
186k
    return(has_empty_ns);
422
2.18M
}
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
2.31M
{
476
2.31M
    return ((ns != NULL) &&
477
2.31M
            (xmlStrEqual(ns->prefix, BAD_CAST "xml")) &&
478
29.6k
            (xmlStrEqual(ns->href, XML_XML_NAMESPACE)));
479
2.31M
}
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
25.2k
{
492
25.2k
    const xmlNs *ns1 = data1;
493
25.2k
    const xmlNs *ns2 = data2;
494
25.2k
    if (ns1 == ns2)
495
0
        return (0);
496
25.2k
    if (ns1 == NULL)
497
0
        return (-1);
498
25.2k
    if (ns2 == NULL)
499
0
        return (1);
500
501
25.2k
    return (xmlStrcmp(ns1->prefix, ns2->prefix));
502
25.2k
}
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
237k
{
515
516
237k
    if ((ns == NULL) || (ctx == NULL)) {
517
0
        xmlC14NErrParam(ctx);
518
0
        return 0;
519
0
    }
520
521
237k
    if (ns->prefix != NULL) {
522
52.4k
        xmlOutputBufferWriteString(ctx->buf, " xmlns:");
523
52.4k
        xmlOutputBufferWriteString(ctx->buf, (const char *) ns->prefix);
524
52.4k
        xmlOutputBufferWriteString(ctx->buf, "=");
525
184k
    } else {
526
184k
        xmlOutputBufferWriteString(ctx->buf, " xmlns=");
527
184k
    }
528
237k
    if(ns->href != NULL) {
529
237k
  xmlOutputBufferWriteQuotedString(ctx->buf, ns->href);
530
237k
    } else {
531
0
      xmlOutputBufferWriteString(ctx->buf, "\"\"");
532
0
    }
533
237k
    return (1);
534
237k
}
535
536
static int
537
237k
xmlC14NPrintNamespacesWalker(const void *ns, void *ctx) {
538
237k
    return xmlC14NPrintNamespaces(ns, ctx);
539
237k
}
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
2.24M
{
582
2.24M
    xmlNodePtr n;
583
2.24M
    xmlNsPtr ns, tmp;
584
2.24M
    xmlListPtr list;
585
2.24M
    int already_rendered;
586
2.24M
    int has_empty_ns = 0;
587
588
2.24M
    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
2.24M
    list = xmlListCreate(NULL, xmlC14NNsCompare);
597
2.24M
    if (list == NULL) {
598
0
        xmlC14NErrMemory(ctx);
599
0
        return (-1);
600
0
    }
601
602
    /* check all namespaces */
603
7.96M
    for(n = cur; n != NULL && n->type == XML_ELEMENT_NODE; n = n->parent) {
604
8.38M
  for(ns = n->nsDef; ns != NULL; ns = ns->next) {
605
2.66M
      tmp = xmlSearchNs(cur->doc, cur, ns->prefix);
606
607
2.66M
      if((tmp == ns) && !xmlC14NIsXmlNs(ns) && xmlC14NIsVisible(ctx, ns, cur)) {
608
2.17M
    already_rendered = xmlC14NVisibleNsStackFind(ctx->ns_rendered, ns);
609
2.17M
    if(visible) {
610
2.17M
              if (xmlC14NVisibleNsStackAdd(ctx->ns_rendered, ns, cur) < 0) {
611
0
                        xmlC14NErrMemory(ctx);
612
0
                        goto error;
613
0
                    }
614
2.17M
    }
615
2.17M
    if(!already_rendered) {
616
237k
        xmlListInsert(list, ns);
617
237k
    }
618
2.17M
    if(xmlStrlen(ns->prefix) == 0) {
619
2.11M
        has_empty_ns = 1;
620
2.11M
    }
621
2.17M
      }
622
2.66M
  }
623
5.71M
    }
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
2.24M
    if(visible && !has_empty_ns) {
635
11.6k
        xmlNs ns_default;
636
637
11.6k
        memset(&ns_default, 0, sizeof(ns_default));
638
11.6k
        if(!xmlC14NVisibleNsStackFind(ctx->ns_rendered, &ns_default)) {
639
0
      xmlC14NPrintNamespaces(&ns_default, ctx);
640
0
  }
641
11.6k
    }
642
643
644
    /*
645
     * print out all elements from list
646
     */
647
2.24M
    xmlListWalk(list, xmlC14NPrintNamespacesWalker, (void *) ctx);
648
649
    /*
650
     * Cleanup
651
     */
652
2.24M
error:
653
2.24M
    xmlListDelete(list);
654
2.24M
    return (0);
655
2.24M
}
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
48.7k
{
856
48.7k
    return ((attr->ns != NULL) &&
857
29.6k
           (xmlC14NIsXmlNs(attr->ns) != 0));
858
48.7k
}
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
823k
{
871
823k
    const xmlAttr *attr1 = data1;
872
823k
    const xmlAttr *attr2 = data2;
873
823k
    int ret = 0;
874
875
    /*
876
     * Simple cases
877
     */
878
823k
    if (attr1 == attr2)
879
0
        return (0);
880
823k
    if (attr1 == NULL)
881
0
        return (-1);
882
823k
    if (attr2 == NULL)
883
0
        return (1);
884
823k
    if (attr1->ns == attr2->ns) {
885
604k
        return (xmlStrcmp(attr1->name, attr2->name));
886
604k
    }
887
888
    /*
889
     * Attributes in the default namespace are first
890
     * because the default namespace is not applied to
891
     * unqualified attributes
892
     */
893
218k
    if (attr1->ns == NULL)
894
208k
        return (-1);
895
9.33k
    if (attr2->ns == NULL)
896
9.33k
        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
2.46M
{
923
2.46M
    const xmlAttr *attr = data;
924
2.46M
    xmlC14NCtxPtr ctx = (xmlC14NCtxPtr) user;
925
2.46M
    xmlChar *value;
926
2.46M
    xmlChar *buffer;
927
928
2.46M
    if ((attr == NULL) || (ctx == NULL)) {
929
0
        xmlC14NErrParam(ctx);
930
0
        return (0);
931
0
    }
932
933
2.46M
    xmlOutputBufferWriteString(ctx->buf, " ");
934
2.46M
    if (attr->ns != NULL && xmlStrlen(attr->ns->prefix) > 0) {
935
217k
        xmlOutputBufferWriteString(ctx->buf,
936
217k
                                   (const char *) attr->ns->prefix);
937
217k
        xmlOutputBufferWriteString(ctx->buf, ":");
938
217k
    }
939
2.46M
    xmlOutputBufferWriteString(ctx->buf, (const char *) attr->name);
940
2.46M
    xmlOutputBufferWriteString(ctx->buf, "=\"");
941
942
2.46M
    value = xmlNodeListGetString(ctx->doc, attr->children, 1);
943
    /* todo: should we log an error if value==NULL ? */
944
2.46M
    if (value != NULL) {
945
2.46M
        buffer = xmlC11NNormalizeAttr(value);
946
2.46M
        xmlFree(value);
947
2.46M
        if (buffer != NULL) {
948
2.46M
            xmlOutputBufferWriteString(ctx->buf, (const char *) buffer);
949
2.46M
            xmlFree(buffer);
950
2.46M
        } else {
951
0
            xmlC14NErrMemory(ctx);
952
0
            return (0);
953
0
        }
954
2.46M
    }
955
2.46M
    xmlOutputBufferWriteString(ctx->buf, "\"");
956
2.46M
    return (1);
957
2.46M
}
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
2.24M
{
1114
2.24M
    xmlAttrPtr attr;
1115
2.24M
    xmlListPtr list;
1116
2.24M
    xmlAttrPtr attrs_to_delete = NULL;
1117
1118
    /* special processing for 1.1 spec */
1119
2.24M
    xmlAttrPtr xml_base_attr = NULL;
1120
2.24M
    xmlAttrPtr xml_lang_attr = NULL;
1121
2.24M
    xmlAttrPtr xml_space_attr = NULL;
1122
1123
2.24M
    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
2.24M
    list = xmlListCreate(NULL, xmlC14NAttrsCompare);
1132
2.24M
    if (list == NULL) {
1133
0
        xmlC14NErrMemory(ctx);
1134
0
        return (-1);
1135
0
    }
1136
1137
2.24M
    switch(ctx->mode) {
1138
2.24M
    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
2.24M
        attr = cur->properties;
1154
4.88M
        while (attr != NULL) {
1155
            /* check that attribute is visible */
1156
2.63M
            if (xmlC14NIsVisible(ctx, attr, cur)) {
1157
2.43M
                xmlListInsert(list, attr);
1158
2.43M
            }
1159
2.63M
            attr = attr->next;
1160
2.63M
        }
1161
1162
        /*
1163
         * Handle xml attributes
1164
         */
1165
2.24M
        if (parent_visible && (cur->parent != NULL) &&
1166
2.12M
            (!xmlC14NIsVisible(ctx, cur->parent, cur->parent->parent)))
1167
121k
        {
1168
121k
            xmlNodePtr tmp;
1169
1170
            /*
1171
             * If XPath node-set is not specified then the parent is always
1172
             * visible!
1173
             */
1174
121k
            tmp = cur->parent;
1175
228k
            while (tmp != NULL && tmp->type == XML_ELEMENT_NODE) {
1176
106k
                attr = tmp->properties;
1177
155k
                while (attr != NULL) {
1178
48.7k
                    if (xmlC14NIsXmlAttr(attr) != 0) {
1179
29.6k
                        if (xmlListSearch(list, attr) == NULL) {
1180
29.3k
                            xmlListInsert(list, attr);
1181
29.3k
                        }
1182
29.6k
                    }
1183
48.7k
                    attr = attr->next;
1184
48.7k
                }
1185
106k
                tmp = tmp->parent;
1186
106k
            }
1187
121k
        }
1188
1189
        /* done */
1190
2.24M
        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
2.24M
    }
1316
1317
    /*
1318
     * print out all elements from list
1319
     */
1320
2.24M
    xmlListWalk(list, xmlC14NPrintAttrs, (void *) ctx);
1321
1322
    /*
1323
     * Cleanup
1324
     */
1325
2.24M
    xmlFreePropList(attrs_to_delete);
1326
2.24M
    xmlListDelete(list);
1327
2.24M
    return (0);
1328
2.24M
}
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
2.24M
{
1340
2.24M
    xmlNsPtr ns;
1341
1342
2.24M
    if ((ctx == NULL) || (cur == NULL) || (cur->type != XML_ELEMENT_NODE)) {
1343
0
        xmlC14NErrParam(ctx);
1344
0
        return (-1);
1345
0
    }
1346
1347
2.24M
    ns = cur->nsDef;
1348
2.38M
    while (ns != NULL) {
1349
140k
        if (xmlStrlen(ns->href) > 0) {
1350
140k
            xmlURIPtr uri;
1351
140k
            int code;
1352
1353
140k
            code = xmlParseURISafe((const char *) ns->href, &uri);
1354
140k
            if (uri == NULL) {
1355
50
                if (code < 0)
1356
0
                    xmlC14NErrMemory(ctx);
1357
50
                else
1358
50
                    xmlC14NErr(ctx, cur, XML_ERR_INVALID_URI,
1359
50
                               "parsing namespace uri");
1360
50
                return (-1);
1361
50
            }
1362
140k
            if (xmlStrlen((const xmlChar *) uri->scheme) == 0) {
1363
15
                xmlC14NErrRelativeNamespace(ctx, uri->scheme);
1364
15
                xmlFreeURI(uri);
1365
15
                return (-1);
1366
15
            }
1367
140k
            xmlFreeURI(uri);
1368
140k
        }
1369
140k
        ns = ns->next;
1370
140k
    }
1371
2.24M
    return (0);
1372
2.24M
}
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
2.24M
{
1397
2.24M
    int ret;
1398
2.24M
    xmlC14NVisibleNsStack state;
1399
2.24M
    int parent_is_doc = 0;
1400
1401
2.24M
    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
2.24M
    if (xmlC14NCheckForRelativeNamespaces(ctx, cur) < 0)
1412
65
        return (-1);
1413
1414
    /*
1415
     * Save ns_rendered stack position
1416
     */
1417
2.24M
    memset(&state, 0, sizeof(state));
1418
2.24M
    xmlC14NVisibleNsStackSave(ctx->ns_rendered, &state);
1419
1420
2.24M
    if (visible) {
1421
2.12M
        if (ctx->parent_is_doc) {
1422
      /* save this flag into the stack */
1423
122k
      parent_is_doc = ctx->parent_is_doc;
1424
122k
      ctx->parent_is_doc = 0;
1425
122k
            ctx->pos = XMLC14N_INSIDE_DOCUMENT_ELEMENT;
1426
122k
        }
1427
2.12M
        xmlOutputBufferWriteString(ctx->buf, "<");
1428
1429
2.12M
        if ((cur->ns != NULL) && (xmlStrlen(cur->ns->prefix) > 0)) {
1430
399
            xmlOutputBufferWriteString(ctx->buf,
1431
399
                                       (const char *) cur->ns->prefix);
1432
399
            xmlOutputBufferWriteString(ctx->buf, ":");
1433
399
        }
1434
2.12M
        xmlOutputBufferWriteString(ctx->buf, (const char *) cur->name);
1435
2.12M
    }
1436
1437
2.24M
    if (!xmlC14NIsExclusive(ctx)) {
1438
2.24M
        ret = xmlC14NProcessNamespacesAxis(ctx, cur, visible);
1439
2.24M
    } else {
1440
0
        ret = xmlExcC14NProcessNamespacesAxis(ctx, cur, visible);
1441
0
    }
1442
2.24M
    if (ret < 0)
1443
0
        return (-1);
1444
    /* todo: shouldn't this go to "visible only"? */
1445
2.24M
    if(visible) {
1446
2.12M
  xmlC14NVisibleNsStackShift(ctx->ns_rendered);
1447
2.12M
    }
1448
1449
2.24M
    ret = xmlC14NProcessAttrsAxis(ctx, cur, visible);
1450
2.24M
    if (ret < 0)
1451
0
  return (-1);
1452
1453
2.24M
    if (visible) {
1454
2.12M
        xmlOutputBufferWriteString(ctx->buf, ">");
1455
2.12M
    }
1456
2.24M
    if (cur->children != NULL) {
1457
138k
        ret = xmlC14NProcessNodeList(ctx, cur->children);
1458
138k
        if (ret < 0)
1459
192
            return (-1);
1460
138k
    }
1461
2.24M
    if (visible) {
1462
2.12M
        xmlOutputBufferWriteString(ctx->buf, "</");
1463
2.12M
        if ((cur->ns != NULL) && (xmlStrlen(cur->ns->prefix) > 0)) {
1464
399
            xmlOutputBufferWriteString(ctx->buf,
1465
399
                                       (const char *) cur->ns->prefix);
1466
399
            xmlOutputBufferWriteString(ctx->buf, ":");
1467
399
        }
1468
2.12M
        xmlOutputBufferWriteString(ctx->buf, (const char *) cur->name);
1469
2.12M
        xmlOutputBufferWriteString(ctx->buf, ">");
1470
2.12M
        if (parent_is_doc) {
1471
      /* restore this flag from the stack for next node */
1472
121k
            ctx->parent_is_doc = parent_is_doc;
1473
121k
      ctx->pos = XMLC14N_AFTER_DOCUMENT_ELEMENT;
1474
121k
        }
1475
2.12M
    }
1476
1477
    /*
1478
     * Restore ns_rendered stack position
1479
     */
1480
2.24M
    xmlC14NVisibleNsStackRestore(ctx->ns_rendered, &state);
1481
2.24M
    return (0);
1482
2.24M
}
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
3.25M
{
1494
3.25M
    int ret = 0;
1495
3.25M
    int visible;
1496
1497
3.25M
    if ((ctx == NULL) || (cur == NULL)) {
1498
0
        xmlC14NErrParam(ctx);
1499
0
        return (-1);
1500
0
    }
1501
1502
3.25M
    visible = xmlC14NIsVisible(ctx, cur, cur->parent);
1503
3.25M
    switch (cur->type) {
1504
2.24M
        case XML_ELEMENT_NODE:
1505
2.24M
            ret = xmlC14NProcessElementNode(ctx, cur, visible);
1506
2.24M
            break;
1507
729
        case XML_CDATA_SECTION_NODE:
1508
856k
        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
856k
            if ((visible) && (cur->content != NULL)) {
1519
767k
                xmlChar *buffer;
1520
1521
767k
                buffer = xmlC11NNormalizeText(cur->content);
1522
767k
                if (buffer != NULL) {
1523
767k
                    xmlOutputBufferWriteString(ctx->buf,
1524
767k
                                               (const char *) buffer);
1525
767k
                    xmlFree(buffer);
1526
767k
                } else {
1527
0
                    xmlC14NErrMemory(ctx);
1528
0
                    return (-1);
1529
0
                }
1530
767k
            }
1531
856k
            break;
1532
856k
        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
149k
            if (visible) {
1546
148k
                if (ctx->pos == XMLC14N_AFTER_DOCUMENT_ELEMENT) {
1547
1.17k
                    xmlOutputBufferWriteString(ctx->buf, "\x0A<?");
1548
147k
                } else {
1549
147k
                    xmlOutputBufferWriteString(ctx->buf, "<?");
1550
147k
                }
1551
1552
148k
                xmlOutputBufferWriteString(ctx->buf,
1553
148k
                                           (const char *) cur->name);
1554
148k
                if ((cur->content != NULL) && (*(cur->content) != '\0')) {
1555
83.8k
                    xmlChar *buffer;
1556
1557
83.8k
                    xmlOutputBufferWriteString(ctx->buf, " ");
1558
1559
                    /* todo: do we need to normalize pi? */
1560
83.8k
                    buffer = xmlC11NNormalizePI(cur->content);
1561
83.8k
                    if (buffer != NULL) {
1562
83.8k
                        xmlOutputBufferWriteString(ctx->buf,
1563
83.8k
                                                   (const char *) buffer);
1564
83.8k
                        xmlFree(buffer);
1565
83.8k
                    } else {
1566
0
                        xmlC14NErrMemory(ctx);
1567
0
                        return (-1);
1568
0
                    }
1569
83.8k
                }
1570
1571
148k
                if (ctx->pos == XMLC14N_BEFORE_DOCUMENT_ELEMENT) {
1572
39.0k
                    xmlOutputBufferWriteString(ctx->buf, "?>\x0A");
1573
109k
                } else {
1574
109k
                    xmlOutputBufferWriteString(ctx->buf, "?>");
1575
109k
                }
1576
148k
            }
1577
149k
            break;
1578
149k
        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
1.04k
            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
1.04k
            break;
1623
1.04k
        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
3.75k
        case XML_DTD_NODE:
1651
3.75k
        case XML_ELEMENT_DECL:
1652
3.75k
        case XML_ATTRIBUTE_DECL:
1653
3.75k
        case XML_ENTITY_DECL:
1654
3.75k
#ifdef LIBXML_XINCLUDE_ENABLED
1655
3.75k
        case XML_XINCLUDE_START:
1656
3.75k
        case XML_XINCLUDE_END:
1657
3.75k
#endif
1658
            /*
1659
             * should be ignored according to "W3C Canonical XML"
1660
             */
1661
3.75k
            break;
1662
0
        default:
1663
0
            xmlC14NErrUnknownNode(ctx, cur->type, "processing node");
1664
0
            return (-1);
1665
3.25M
    }
1666
1667
3.25M
    return (ret);
1668
3.25M
}
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
156k
{
1680
156k
    int ret;
1681
1682
156k
    if (ctx == NULL) {
1683
0
        xmlC14NErrParam(ctx);
1684
0
        return (-1);
1685
0
    }
1686
1687
3.41M
    for (ret = 0; cur != NULL && ret >= 0; cur = cur->next) {
1688
3.25M
        ret = xmlC14NProcessNode(ctx, cur);
1689
3.25M
    }
1690
156k
    return (ret);
1691
156k
}
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
18.4k
{
1703
18.4k
    if (ctx == NULL) {
1704
0
        xmlC14NErrParam(ctx);
1705
0
        return;
1706
0
    }
1707
1708
18.4k
    if (ctx->ns_rendered != NULL) {
1709
18.4k
        xmlC14NVisibleNsStackDestroy(ctx->ns_rendered);
1710
18.4k
    }
1711
18.4k
    xmlFree(ctx);
1712
18.4k
}
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 canonicalization)
1726
 * @param with_comments   include comments in the result (!=0) or not (==0)
1727
 * @param buf     the output buffer to store canonical XML; this
1728
 *      buffer MUST have encoder==NULL because C14N requires
1729
 *      UTF-8 output
1730
 * @returns pointer to newly created object (success) or NULL (fail)
1731
 */
1732
static xmlC14NCtxPtr
1733
xmlC14NNewCtx(xmlDocPtr doc,
1734
        xmlC14NIsVisibleCallback is_visible_callback, void* user_data,
1735
              xmlC14NMode mode, xmlChar ** inclusive_ns_prefixes,
1736
              int with_comments, xmlOutputBufferPtr buf)
1737
18.4k
{
1738
18.4k
    xmlC14NCtxPtr ctx = NULL;
1739
1740
18.4k
    if ((doc == NULL) || (buf == NULL)) {
1741
0
        xmlC14NErrParam(ctx);
1742
0
        return (NULL);
1743
0
    }
1744
1745
    /*
1746
     *  Validate the encoding output buffer encoding
1747
     */
1748
18.4k
    if (buf->encoder != NULL) {
1749
0
        xmlC14NErr(ctx, (xmlNodePtr) doc, XML_C14N_REQUIRES_UTF8,
1750
0
"xmlC14NNewCtx: output buffer encoder != NULL but C14N requires UTF8 output\n");
1751
0
        return (NULL);
1752
0
    }
1753
1754
    /*
1755
     * Allocate a new xmlC14NCtx and fill the fields.
1756
     */
1757
18.4k
    ctx = (xmlC14NCtxPtr) xmlMalloc(sizeof(xmlC14NCtx));
1758
18.4k
    if (ctx == NULL) {
1759
0
  xmlC14NErrMemory(ctx);
1760
0
        return (NULL);
1761
0
    }
1762
18.4k
    memset(ctx, 0, sizeof(xmlC14NCtx));
1763
1764
    /*
1765
     * initialize C14N context
1766
     */
1767
18.4k
    ctx->doc = doc;
1768
18.4k
    ctx->with_comments = with_comments;
1769
18.4k
    ctx->is_visible_callback = is_visible_callback;
1770
18.4k
    ctx->user_data = user_data;
1771
18.4k
    ctx->buf = buf;
1772
18.4k
    ctx->parent_is_doc = 1;
1773
18.4k
    ctx->pos = XMLC14N_BEFORE_DOCUMENT_ELEMENT;
1774
18.4k
    ctx->ns_rendered = xmlC14NVisibleNsStackCreate();
1775
1776
18.4k
    if(ctx->ns_rendered == NULL) {
1777
0
        xmlC14NErrMemory(ctx);
1778
0
  xmlC14NFreeCtx(ctx);
1779
0
        return (NULL);
1780
0
    }
1781
1782
    /*
1783
     * Set "mode" flag and remember list of inclusive prefixes
1784
     * for exclusive c14n
1785
     */
1786
18.4k
    ctx->mode = mode;
1787
18.4k
    if(xmlC14NIsExclusive(ctx)) {
1788
0
        ctx->inclusive_ns_prefixes = inclusive_ns_prefixes;
1789
0
    }
1790
1791
18.4k
    return (ctx);
1792
18.4k
}
1793
1794
/**
1795
 * Dumps the canonized image of given XML document into the provided buffer.
1796
 * For details see "Canonical XML" (http://www.w3.org/TR/xml-c14n) or
1797
 * "Exclusive XML Canonicalization" (http://www.w3.org/TR/xml-exc-c14n)
1798
 *
1799
 * @param doc     the XML document for canonization
1800
 * @param is_visible_callback  the function to use to determine is node visible
1801
 *      or not
1802
 * @param user_data     the first parameter for `is_visible_callback` function
1803
 *      (in most cases, it is nodes set)
1804
 * @param mode    the c14n mode (see `xmlC14NMode`)
1805
 * @param inclusive_ns_prefixes  the list of inclusive namespace prefixes
1806
 *      ended with a NULL or NULL if there is no
1807
 *      inclusive namespaces (only for exclusive
1808
 *      canonicalization, ignored otherwise)
1809
 * @param with_comments   include comments in the result (!=0) or not (==0)
1810
 * @param buf     the output buffer to store canonical XML; this
1811
 *      buffer MUST have encoder==NULL because C14N requires
1812
 *      UTF-8 output
1813
 * @returns non-negative value on success or a negative value on fail
1814
 */
1815
int
1816
xmlC14NExecute(xmlDoc *doc, xmlC14NIsVisibleCallback is_visible_callback,
1817
   void* user_data, int mode, xmlChar **inclusive_ns_prefixes,
1818
18.4k
   int with_comments, xmlOutputBuffer *buf) {
1819
1820
18.4k
    xmlC14NCtxPtr ctx;
1821
18.4k
    xmlC14NMode c14n_mode = XML_C14N_1_0;
1822
18.4k
    int ret;
1823
1824
18.4k
    if ((buf == NULL) || (doc == NULL)) {
1825
0
        xmlC14NErrParam(NULL);
1826
0
        return (-1);
1827
0
    }
1828
1829
    /* for backward compatibility, we have to have "mode" as "int"
1830
       and here we check that user gives valid value */
1831
18.4k
    switch(mode) {
1832
18.4k
    case XML_C14N_1_0:
1833
18.4k
    case XML_C14N_EXCLUSIVE_1_0:
1834
18.4k
    case XML_C14N_1_1:
1835
18.4k
         c14n_mode = (xmlC14NMode)mode;
1836
18.4k
         break;
1837
0
    default:
1838
0
        xmlC14NErrParam(NULL);
1839
0
        return (-1);
1840
18.4k
    }
1841
1842
    /*
1843
     *  Validate the encoding output buffer encoding
1844
     */
1845
18.4k
    if (buf->encoder != NULL) {
1846
0
        xmlC14NErr(NULL, (xmlNodePtr) doc, XML_C14N_REQUIRES_UTF8,
1847
0
"xmlC14NExecute: output buffer encoder != NULL but C14N requires UTF8 output\n");
1848
0
        return (-1);
1849
0
    }
1850
1851
18.4k
    ctx = xmlC14NNewCtx(doc, is_visible_callback, user_data,
1852
18.4k
              c14n_mode, inclusive_ns_prefixes,
1853
18.4k
                    with_comments, buf);
1854
18.4k
    if (ctx == NULL) {
1855
0
        xmlC14NErr(NULL, (xmlNodePtr) doc, XML_C14N_CREATE_CTXT,
1856
0
       "xmlC14NExecute: unable to create C14N context\n");
1857
0
        return (-1);
1858
0
    }
1859
1860
1861
1862
    /*
1863
     * Root Node
1864
     * The root node is the parent of the top-level document element. The
1865
     * result of processing each of its child nodes that is in the node-set
1866
     * in document order. The root node does not generate a byte order mark,
1867
     * XML declaration, nor anything from within the document type
1868
     * declaration.
1869
     */
1870
18.4k
    if (doc->children != NULL) {
1871
18.4k
        ret = xmlC14NProcessNodeList(ctx, doc->children);
1872
18.4k
        if (ret < 0) {
1873
65
            xmlC14NFreeCtx(ctx);
1874
65
            return (-1);
1875
65
        }
1876
18.4k
    }
1877
1878
    /*
1879
     * Flush buffer to get number of bytes written
1880
     */
1881
18.4k
    ret = xmlOutputBufferFlush(buf);
1882
18.4k
    if (ret < 0) {
1883
0
        xmlC14NErr(ctx, NULL, buf->error, "flushing output buffer");
1884
0
        xmlC14NFreeCtx(ctx);
1885
0
        return (-1);
1886
0
    }
1887
1888
    /*
1889
     * Cleanup
1890
     */
1891
18.4k
    xmlC14NFreeCtx(ctx);
1892
18.4k
    return (ret);
1893
18.4k
}
1894
1895
/**
1896
 * Dumps the canonized image of given XML document into the provided buffer.
1897
 * For details see "Canonical XML" (http://www.w3.org/TR/xml-c14n) or
1898
 * "Exclusive XML Canonicalization" (http://www.w3.org/TR/xml-exc-c14n)
1899
 *
1900
 * @param doc     the XML document for canonization
1901
 * @param nodes     the nodes set to be included in the canonized image
1902
 *    or NULL if all document nodes should be included
1903
 * @param mode      the c14n mode (see `xmlC14NMode`)
1904
 * @param inclusive_ns_prefixes  the list of inclusive namespace prefixes
1905
 *      ended with a NULL or NULL if there is no
1906
 *      inclusive namespaces (only for exclusive
1907
 *      canonicalization, ignored otherwise)
1908
 * @param with_comments   include comments in the result (!=0) or not (==0)
1909
 * @param buf     the output buffer to store canonical XML; this
1910
 *      buffer MUST have encoder==NULL because C14N requires
1911
 *      UTF-8 output
1912
 * @returns non-negative value on success or a negative value on fail
1913
 */
1914
int
1915
xmlC14NDocSaveTo(xmlDoc *doc, xmlNodeSet *nodes,
1916
                 int mode, xmlChar ** inclusive_ns_prefixes,
1917
0
                 int with_comments, xmlOutputBuffer *buf) {
1918
0
    return(xmlC14NExecute(doc,
1919
0
      xmlC14NIsNodeInNodeset,
1920
0
      nodes,
1921
0
      mode,
1922
0
      inclusive_ns_prefixes,
1923
0
      with_comments,
1924
0
      buf));
1925
0
}
1926
1927
1928
/**
1929
 * Dumps the canonized image of given XML document into memory.
1930
 * For details see "Canonical XML" (http://www.w3.org/TR/xml-c14n) or
1931
 * "Exclusive XML Canonicalization" (http://www.w3.org/TR/xml-exc-c14n)
1932
 *
1933
 * @param doc     the XML document for canonization
1934
 * @param nodes     the nodes set to be included in the canonized image
1935
 *    or NULL if all document nodes should be included
1936
 * @param mode      the c14n mode (see `xmlC14NMode`)
1937
 * @param inclusive_ns_prefixes  the list of inclusive namespace prefixes
1938
 *      ended with a NULL or NULL if there is no
1939
 *      inclusive namespaces (only for exclusive
1940
 *      canonicalization, ignored otherwise)
1941
 * @param with_comments   include comments in the result (!=0) or not (==0)
1942
 * @param doc_txt_ptr   the memory pointer for allocated canonical XML text;
1943
 *      the caller of this functions is responsible for calling
1944
 *      xmlFree() to free allocated memory
1945
 * @returns the number of bytes written on success or a negative value on fail
1946
 */
1947
int
1948
xmlC14NDocDumpMemory(xmlDoc *doc, xmlNodeSet *nodes,
1949
                     int mode, xmlChar ** inclusive_ns_prefixes,
1950
                     int with_comments, xmlChar ** doc_txt_ptr)
1951
0
{
1952
0
    int ret;
1953
0
    xmlOutputBufferPtr buf;
1954
1955
0
    if (doc_txt_ptr == NULL) {
1956
0
        xmlC14NErrParam(NULL);
1957
0
        return (-1);
1958
0
    }
1959
1960
0
    *doc_txt_ptr = NULL;
1961
1962
    /*
1963
     * create memory buffer with UTF8 (default) encoding
1964
     */
1965
0
    buf = xmlAllocOutputBuffer(NULL);
1966
0
    if (buf == NULL) {
1967
0
        xmlC14NErrMemory(NULL);
1968
0
        return (-1);
1969
0
    }
1970
1971
    /*
1972
     * canonize document and write to buffer
1973
     */
1974
0
    ret = xmlC14NDocSaveTo(doc, nodes, mode, inclusive_ns_prefixes,
1975
0
                           with_comments, buf);
1976
0
    if (ret < 0) {
1977
0
        (void) xmlOutputBufferClose(buf);
1978
0
        return (-1);
1979
0
    }
1980
1981
0
    ret = xmlBufUse(buf->buffer);
1982
0
    if (ret >= 0) {
1983
0
        *doc_txt_ptr = xmlStrndup(xmlBufContent(buf->buffer), ret);
1984
0
    }
1985
0
    (void) xmlOutputBufferClose(buf);
1986
1987
0
    if ((*doc_txt_ptr == NULL) && (ret >= 0)) {
1988
0
        xmlC14NErrMemory(NULL);
1989
0
        return (-1);
1990
0
    }
1991
0
    return (ret);
1992
0
}
1993
1994
/**
1995
 * Dumps the canonized image of given XML document into the file.
1996
 * For details see "Canonical XML" (http://www.w3.org/TR/xml-c14n) or
1997
 * "Exclusive XML Canonicalization" (http://www.w3.org/TR/xml-exc-c14n)
1998
 *
1999
 * @param doc     the XML document for canonization
2000
 * @param nodes     the nodes set to be included in the canonized image
2001
 *    or NULL if all document nodes should be included
2002
 * @param mode      the c14n mode (see `xmlC14NMode`)
2003
 * @param inclusive_ns_prefixes  the list of inclusive namespace prefixes
2004
 *      ended with a NULL or NULL if there is no
2005
 *      inclusive namespaces (only for exclusive
2006
 *      canonicalization, ignored otherwise)
2007
 * @param with_comments   include comments in the result (!=0) or not (==0)
2008
 * @param filename      the filename to store canonical XML image
2009
 * @param compression   the compression level (zlib required):
2010
 *        -1 - libxml default,
2011
 *         0 - uncompressed,
2012
 *        >0 - compression level
2013
 * @returns the number of bytes written success or a negative value on fail
2014
 */
2015
int
2016
xmlC14NDocSave(xmlDoc *doc, xmlNodeSet *nodes,
2017
               int mode, xmlChar ** inclusive_ns_prefixes,
2018
               int with_comments, const char *filename, int compression)
2019
0
{
2020
0
    xmlOutputBufferPtr buf;
2021
0
    int ret;
2022
2023
0
    if (filename == NULL) {
2024
0
        xmlC14NErrParam(NULL);
2025
0
        return (-1);
2026
0
    }
2027
#ifdef LIBXML_ZLIB_ENABLED
2028
    if (compression < 0)
2029
        compression = xmlGetCompressMode();
2030
#endif
2031
2032
    /*
2033
     * save the content to a temp buffer, use default UTF8 encoding.
2034
     */
2035
0
    buf = xmlOutputBufferCreateFilename(filename, NULL, compression);
2036
0
    if (buf == NULL) {
2037
0
        xmlC14NErr(NULL, NULL, XML_IO_UNKNOWN, "creating temporary filename");
2038
0
        return (-1);
2039
0
    }
2040
2041
    /*
2042
     * canonize document and write to buffer
2043
     */
2044
0
    ret = xmlC14NDocSaveTo(doc, nodes, mode, inclusive_ns_prefixes,
2045
0
                           with_comments, buf);
2046
0
    if (ret < 0) {
2047
0
        (void) xmlOutputBufferClose(buf);
2048
0
        return (-1);
2049
0
    }
2050
2051
    /*
2052
     * get the numbers of bytes written
2053
     */
2054
0
    ret = xmlOutputBufferClose(buf);
2055
0
    return (ret);
2056
0
}
2057
2058
/**
2059
 * Converts a string to a canonical (normalized) format. The code is stolen
2060
 * from xmlEscapeText. Added normalization of `\x09`, `\x0a`,
2061
 * `\x0A` and the `mode` parameter.
2062
 *
2063
 * @param input     the input string
2064
 * @param mode      the normalization mode (attribute, comment, PI or text)
2065
 * @returns a normalized string (caller is responsible for calling #xmlFree)
2066
 * or NULL if an error occurs
2067
 */
2068
static xmlChar *
2069
xmlC11NNormalizeString(const xmlChar * input,
2070
                       xmlC14NNormalizationMode mode)
2071
3.31M
{
2072
3.31M
    const xmlChar *cur = input;
2073
3.31M
    xmlChar *buffer = NULL;
2074
3.31M
    xmlChar *out = NULL;
2075
3.31M
    int buffer_size = 0;
2076
2077
3.31M
    if (input == NULL)
2078
0
        return (NULL);
2079
2080
    /*
2081
     * allocate an translation buffer.
2082
     */
2083
3.31M
    buffer_size = 1000;
2084
3.31M
    buffer = xmlMalloc(buffer_size);
2085
3.31M
    if (buffer == NULL)
2086
0
        return (NULL);
2087
3.31M
    out = buffer;
2088
2089
216M
    while (*cur != '\0') {
2090
213M
        if ((out - buffer) > (buffer_size - 10)) {
2091
31.8k
            xmlChar *tmp;
2092
31.8k
            int indx = out - buffer;
2093
31.8k
            int newSize;
2094
2095
31.8k
            newSize = xmlGrowCapacity(buffer_size, 1, 1, XML_MAX_ITEMS);
2096
31.8k
            if (newSize < 0) {
2097
0
                xmlFree(buffer);
2098
0
                return(NULL);
2099
0
            }
2100
31.8k
            tmp = xmlRealloc(buffer, newSize);
2101
31.8k
            if (tmp == NULL) {
2102
0
                xmlFree(buffer);
2103
0
                return(NULL);
2104
0
            }
2105
31.8k
            buffer = tmp;
2106
31.8k
            buffer_size = newSize;
2107
31.8k
            out = &buffer[indx];
2108
31.8k
        }
2109
2110
213M
        if ((*cur == '<') && ((mode == XMLC14N_NORMALIZE_ATTR) ||
2111
106k
                              (mode == XMLC14N_NORMALIZE_TEXT))) {
2112
36.4k
            *out++ = '&';
2113
36.4k
            *out++ = 'l';
2114
36.4k
            *out++ = 't';
2115
36.4k
            *out++ = ';';
2116
213M
        } else if ((*cur == '>') && (mode == XMLC14N_NORMALIZE_TEXT)) {
2117
19.4M
            *out++ = '&';
2118
19.4M
            *out++ = 'g';
2119
19.4M
            *out++ = 't';
2120
19.4M
            *out++ = ';';
2121
193M
        } else if ((*cur == '&') && ((mode == XMLC14N_NORMALIZE_ATTR) ||
2122
20.6M
                                     (mode == XMLC14N_NORMALIZE_TEXT))) {
2123
20.6M
            *out++ = '&';
2124
20.6M
            *out++ = 'a';
2125
20.6M
            *out++ = 'm';
2126
20.6M
            *out++ = 'p';
2127
20.6M
            *out++ = ';';
2128
173M
        } else if ((*cur == '"') && (mode == XMLC14N_NORMALIZE_ATTR)) {
2129
860
            *out++ = '&';
2130
860
            *out++ = 'q';
2131
860
            *out++ = 'u';
2132
860
            *out++ = 'o';
2133
860
            *out++ = 't';
2134
860
            *out++ = ';';
2135
173M
        } else if ((*cur == '\x09') && (mode == XMLC14N_NORMALIZE_ATTR)) {
2136
65.5k
            *out++ = '&';
2137
65.5k
            *out++ = '#';
2138
65.5k
            *out++ = 'x';
2139
65.5k
            *out++ = '9';
2140
65.5k
            *out++ = ';';
2141
173M
        } else if ((*cur == '\x0A') && (mode == XMLC14N_NORMALIZE_ATTR)) {
2142
6.55k
            *out++ = '&';
2143
6.55k
            *out++ = '#';
2144
6.55k
            *out++ = 'x';
2145
6.55k
            *out++ = 'A';
2146
6.55k
            *out++ = ';';
2147
173M
        } else if ((*cur == '\x0D') && ((mode == XMLC14N_NORMALIZE_ATTR) ||
2148
20.4k
                                        (mode == XMLC14N_NORMALIZE_TEXT) ||
2149
0
                                        (mode == XMLC14N_NORMALIZE_COMMENT) ||
2150
25.3k
          (mode == XMLC14N_NORMALIZE_PI))) {
2151
25.3k
            *out++ = '&';
2152
25.3k
            *out++ = '#';
2153
25.3k
            *out++ = 'x';
2154
25.3k
            *out++ = 'D';
2155
25.3k
            *out++ = ';';
2156
173M
        } else {
2157
            /*
2158
             * Works because on UTF-8, all extended sequences cannot
2159
             * result in bytes in the ASCII range.
2160
             */
2161
173M
            *out++ = *cur;
2162
173M
        }
2163
213M
        cur++;
2164
213M
    }
2165
3.31M
    *out = 0;
2166
3.31M
    return (buffer);
2167
3.31M
}
2168
2169
#endif /* LIBXML_C14N_ENABLED */