Coverage Report

Created: 2025-07-01 06:27

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