Coverage Report

Created: 2026-04-12 06:44

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libxml2/c14n.c
Line
Count
Source
1
/*
2
 * "Canonical XML" implementation
3
 * http://www.w3.org/TR/xml-c14n
4
 *
5
 * "Exclusive XML Canonicalization" implementation
6
 * http://www.w3.org/TR/xml-exc-c14n
7
 *
8
 * See Copyright for the status of this software.
9
 *
10
 * Author: Aleksey Sanin
11
 */
12
#define IN_LIBXML
13
#include "libxml.h"
14
#ifdef LIBXML_C14N_ENABLED
15
16
#include <stdlib.h>
17
#include <string.h>
18
19
#include <libxml/tree.h>
20
#include <libxml/parser.h>
21
#include <libxml/uri.h>
22
#include <libxml/xmlerror.h>
23
#include <libxml/xpathInternals.h>
24
#include <libxml/c14n.h>
25
26
#include "private/error.h"
27
#include "private/io.h"
28
#include "private/memory.h"
29
30
/************************************************************************
31
 *                  *
32
 *    Some declaration better left private ATM    *
33
 *                  *
34
 ************************************************************************/
35
36
typedef enum {
37
    XMLC14N_BEFORE_DOCUMENT_ELEMENT = 0,
38
    XMLC14N_INSIDE_DOCUMENT_ELEMENT = 1,
39
    XMLC14N_AFTER_DOCUMENT_ELEMENT = 2
40
} xmlC14NPosition;
41
42
typedef struct _xmlC14NVisibleNsStack {
43
    int nsCurEnd;           /* number of nodes in the set */
44
    int nsPrevStart;        /* the beginning of the stack for previous visible node */
45
    int nsPrevEnd;          /* the end of the stack for previous visible node */
46
    int nsMax;              /* size of the array as allocated */
47
    xmlNsPtr  *nsTab;     /* array of ns in no particular order */
48
    xmlNodePtr  *nodeTab;   /* array of nodes in no particular order */
49
} xmlC14NVisibleNsStack, *xmlC14NVisibleNsStackPtr;
50
51
typedef struct _xmlC14NCtx {
52
    /* input parameters */
53
    xmlDocPtr doc;
54
    xmlC14NIsVisibleCallback is_visible_callback;
55
    void* user_data;
56
    int with_comments;
57
    xmlOutputBufferPtr buf;
58
59
    /* position in the XML document */
60
    xmlC14NPosition pos;
61
    int parent_is_doc;
62
    xmlC14NVisibleNsStackPtr ns_rendered;
63
64
    /* C14N mode */
65
    xmlC14NMode mode;
66
67
    /* exclusive canonicalization */
68
    xmlChar **inclusive_ns_prefixes;
69
70
    /* error number */
71
    int error;
72
} xmlC14NCtx, *xmlC14NCtxPtr;
73
74
static xmlC14NVisibleNsStackPtr xmlC14NVisibleNsStackCreate (void);
75
static void     xmlC14NVisibleNsStackDestroy  (xmlC14NVisibleNsStackPtr cur);
76
static int      xmlC14NVisibleNsStackAdd      (xmlC14NVisibleNsStackPtr cur,
77
                                                 xmlNsPtr ns,
78
                                                 xmlNodePtr node);
79
static void     xmlC14NVisibleNsStackSave (xmlC14NVisibleNsStackPtr cur,
80
                 xmlC14NVisibleNsStackPtr state);
81
static void     xmlC14NVisibleNsStackRestore  (xmlC14NVisibleNsStackPtr cur,
82
                 xmlC14NVisibleNsStackPtr state);
83
static void     xmlC14NVisibleNsStackShift  (xmlC14NVisibleNsStackPtr cur);
84
static int      xmlC14NVisibleNsStackFind (xmlC14NVisibleNsStackPtr cur,
85
                 xmlNsPtr ns);
86
static int      xmlExcC14NVisibleNsStackFind  (xmlC14NVisibleNsStackPtr cur,
87
                 xmlNsPtr ns,
88
                 xmlC14NCtxPtr ctx);
89
90
static int      xmlC14NIsNodeInNodeset    (void *user_data,
91
                 xmlNodePtr node,
92
                 xmlNodePtr parent);
93
94
95
96
static int xmlC14NProcessNode(xmlC14NCtxPtr ctx, xmlNodePtr cur);
97
static int xmlC14NProcessNodeList(xmlC14NCtxPtr ctx, xmlNodePtr cur);
98
typedef enum {
99
    XMLC14N_NORMALIZE_ATTR = 0,
100
    XMLC14N_NORMALIZE_COMMENT = 1,
101
    XMLC14N_NORMALIZE_PI = 2,
102
    XMLC14N_NORMALIZE_TEXT = 3
103
} xmlC14NNormalizationMode;
104
105
static xmlChar *xmlC11NNormalizeString(const xmlChar * input,
106
                                       xmlC14NNormalizationMode mode);
107
108
#define xmlC11NNormalizeAttr( a ) \
109
0
    xmlC11NNormalizeString((a), XMLC14N_NORMALIZE_ATTR)
110
#define xmlC11NNormalizeComment( a ) \
111
0
    xmlC11NNormalizeString((a), XMLC14N_NORMALIZE_COMMENT)
112
#define xmlC11NNormalizePI( a ) \
113
0
    xmlC11NNormalizeString((a), XMLC14N_NORMALIZE_PI)
114
#define xmlC11NNormalizeText( a ) \
115
0
    xmlC11NNormalizeString((a), XMLC14N_NORMALIZE_TEXT)
116
117
#define xmlC14NIsVisible( ctx, node, parent ) \
118
0
     (((ctx)->is_visible_callback != NULL) ? \
119
0
  (ctx)->is_visible_callback((ctx)->user_data, \
120
0
    (xmlNodePtr)(node), (xmlNodePtr)(parent)) : 1)
121
122
#define xmlC14NIsExclusive( ctx ) \
123
0
    ( (ctx)->mode == XML_C14N_EXCLUSIVE_1_0 )
124
125
/************************************************************************
126
 *                  *
127
 *    Some factorized error routines        *
128
 *                  *
129
 ************************************************************************/
130
131
/**
132
 * Handle a redefinition of memory error
133
 *
134
 * @param ctxt  a C14N evaluation context
135
 */
136
static void
137
xmlC14NErrMemory(xmlC14NCtxPtr ctxt)
138
0
{
139
0
    if (ctxt != NULL)
140
0
        ctxt->error = XML_ERR_NO_MEMORY;
141
142
0
    xmlRaiseMemoryError(NULL, NULL, NULL, XML_FROM_C14N, NULL);
143
0
}
144
145
static void
146
xmlC14NErrFull(xmlC14NCtxPtr ctxt, xmlNodePtr node, int code, const char *str1,
147
               const char *msg, ...)
148
0
{
149
0
    va_list ap;
150
0
    int res;
151
152
0
    if (ctxt != NULL)
153
0
        ctxt->error = code;
154
155
0
    va_start(ap, msg);
156
0
    res = xmlVRaiseError(NULL, NULL, NULL, ctxt, node,
157
0
                         XML_FROM_C14N, code, XML_ERR_ERROR, NULL, 0,
158
0
                         str1, NULL, NULL, 0, 0,
159
0
                         msg, ap);
160
0
    va_end(ap);
161
0
    if (res < 0)
162
0
        xmlC14NErrMemory(ctxt);
163
0
}
164
165
/**
166
 * Handle a param error
167
 *
168
 * @param ctxt  a C14N evaluation context
169
 */
170
static void
171
xmlC14NErrParam(xmlC14NCtxPtr ctxt)
172
0
{
173
0
    xmlC14NErrFull(ctxt, NULL, XML_ERR_ARGUMENT, NULL,
174
0
       "Invalid argument\n", NULL);
175
0
}
176
177
/**
178
 * Handle an invalid node error
179
 *
180
 * @param ctxt  a C14N evaluation context
181
 * @param node_type  node type
182
 * @param extra  extra information
183
 */
184
static void
185
xmlC14NErrInvalidNode(xmlC14NCtxPtr ctxt, const char *node_type,
186
                      const char *extra)
187
0
{
188
0
    xmlC14NErrFull(ctxt, NULL, XML_C14N_INVALID_NODE, extra,
189
0
       "Node %s is invalid here : %s\n", node_type, extra);
190
0
}
191
192
/**
193
 * Handle an unknown node error
194
 *
195
 * @param ctxt  a C14N evaluation context
196
 * @param node_type  node type
197
 * @param extra  extra information
198
 */
199
static void
200
xmlC14NErrUnknownNode(xmlC14NCtxPtr ctxt, int node_type, const char *extra)
201
0
{
202
0
    xmlC14NErrFull(ctxt, NULL, XML_C14N_UNKNOW_NODE, extra,
203
0
       "Unknown node type %d found : %s\n", node_type, extra);
204
0
}
205
206
/**
207
 * Handle a relative namespace error
208
 *
209
 * @param ctxt  a C14N evaluation context
210
 * @param ns_uri  namespace URI
211
 */
212
static void
213
xmlC14NErrRelativeNamespace(xmlC14NCtxPtr ctxt, const char *ns_uri)
214
0
{
215
0
    xmlC14NErrFull(ctxt, NULL, XML_C14N_RELATIVE_NAMESPACE, ns_uri,
216
0
       "Relative namespace UR is invalid here : %s\n", ns_uri);
217
0
}
218
219
220
221
/**
222
 * Handle an error
223
 *
224
 * @param ctxt  a C14N evaluation context
225
 * @param node  the context node
226
 * @param error  the error code
227
 * @param msg  the message
228
 */
229
static void
230
xmlC14NErr(xmlC14NCtxPtr ctxt, xmlNodePtr node, int error,
231
           const char * msg)
232
0
{
233
0
    xmlC14NErrFull(ctxt, node, error, NULL, "%s", msg);
234
0
}
235
236
/************************************************************************
237
 *                  *
238
 *    The implementation internals        *
239
 *                  *
240
 ************************************************************************/
241
0
#define XML_NAMESPACES_DEFAULT    16
242
243
static int
244
0
xmlC14NIsNodeInNodeset(void *user_data, xmlNodePtr node, xmlNodePtr parent) {
245
0
    xmlNodeSetPtr nodes = (xmlNodeSetPtr) user_data;
246
0
    if((nodes != NULL) && (node != NULL)) {
247
0
  if(node->type != XML_NAMESPACE_DECL) {
248
0
      return(xmlXPathNodeSetContains(nodes, node));
249
0
  } else {
250
0
      xmlNs ns;
251
252
0
      memcpy(&ns, node, sizeof(ns));
253
254
      /* this is a libxml hack! check xpath.c for details */
255
0
      if((parent != NULL) && (parent->type == XML_ATTRIBUTE_NODE)) {
256
0
    ns.next = (xmlNsPtr)parent->parent;
257
0
      } else {
258
0
    ns.next = (xmlNsPtr)parent;
259
0
      }
260
261
      /*
262
       * If the input is an XPath node-set, then the node-set must explicitly
263
       * contain every node to be rendered to the canonical form.
264
       */
265
0
      return(xmlXPathNodeSetContains(nodes, (xmlNodePtr)&ns));
266
0
  }
267
0
    }
268
0
    return(1);
269
0
}
270
271
static xmlC14NVisibleNsStackPtr
272
0
xmlC14NVisibleNsStackCreate(void) {
273
0
    xmlC14NVisibleNsStackPtr ret;
274
275
0
    ret = (xmlC14NVisibleNsStackPtr) xmlMalloc(sizeof(xmlC14NVisibleNsStack));
276
0
    if (ret == NULL)
277
0
  return(NULL);
278
0
    memset(ret, 0, sizeof(xmlC14NVisibleNsStack));
279
0
    return(ret);
280
0
}
281
282
static void
283
0
xmlC14NVisibleNsStackDestroy(xmlC14NVisibleNsStackPtr cur) {
284
0
    if(cur == NULL) {
285
0
        xmlC14NErrParam(NULL);
286
0
        return;
287
0
    }
288
0
    if(cur->nsTab != NULL) {
289
0
  memset(cur->nsTab, 0, cur->nsMax * sizeof(xmlNsPtr));
290
0
  xmlFree(cur->nsTab);
291
0
    }
292
0
    if(cur->nodeTab != NULL) {
293
0
  memset(cur->nodeTab, 0, cur->nsMax * sizeof(xmlNodePtr));
294
0
  xmlFree(cur->nodeTab);
295
0
    }
296
0
    memset(cur, 0, sizeof(xmlC14NVisibleNsStack));
297
0
    xmlFree(cur);
298
299
0
}
300
301
static int
302
0
xmlC14NVisibleNsStackAdd(xmlC14NVisibleNsStackPtr cur, xmlNsPtr ns, xmlNodePtr node) {
303
0
    if((cur == NULL) ||
304
0
       ((cur->nsTab == NULL) && (cur->nodeTab != NULL)) ||
305
0
       ((cur->nsTab != NULL) && (cur->nodeTab == NULL)))
306
0
  return (1);
307
308
0
    if (cur->nsMax <= cur->nsCurEnd) {
309
0
  xmlNsPtr *tmp1;
310
0
        xmlNodePtr *tmp2;
311
0
  int newSize;
312
313
0
        newSize = xmlGrowCapacity(cur->nsMax,
314
0
                                  sizeof(tmp1[0]) + sizeof(tmp2[0]),
315
0
                                  XML_NAMESPACES_DEFAULT, XML_MAX_ITEMS);
316
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
0
xmlC14NVisibleNsStackSave(xmlC14NVisibleNsStackPtr cur, xmlC14NVisibleNsStackPtr state) {
339
0
    if((cur == NULL) || (state == NULL)) {
340
0
        xmlC14NErrParam(NULL);
341
0
  return;
342
0
    }
343
344
0
    state->nsCurEnd = cur->nsCurEnd;
345
0
    state->nsPrevStart = cur->nsPrevStart;
346
0
    state->nsPrevEnd = cur->nsPrevEnd;
347
0
}
348
349
static void
350
0
xmlC14NVisibleNsStackRestore(xmlC14NVisibleNsStackPtr cur, xmlC14NVisibleNsStackPtr state) {
351
0
    if((cur == NULL) || (state == NULL)) {
352
0
        xmlC14NErrParam(NULL);
353
0
  return;
354
0
    }
355
0
    cur->nsCurEnd = state->nsCurEnd;
356
0
    cur->nsPrevStart = state->nsPrevStart;
357
0
    cur->nsPrevEnd = state->nsPrevEnd;
358
0
}
359
360
static void
361
0
xmlC14NVisibleNsStackShift(xmlC14NVisibleNsStackPtr cur) {
362
0
    if(cur == NULL) {
363
0
        xmlC14NErrParam(NULL);
364
0
  return;
365
0
    }
366
0
    cur->nsPrevStart = cur->nsPrevEnd;
367
0
    cur->nsPrevEnd = cur->nsCurEnd;
368
0
}
369
370
static int
371
0
xmlC14NStrEqual(const xmlChar *str1, const xmlChar *str2) {
372
0
    if (str1 == str2) return(1);
373
0
    if (str1 == NULL) return((*str2) == '\0');
374
0
    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
0
{
391
0
    int i;
392
0
    const xmlChar *prefix;
393
0
    const xmlChar *href;
394
0
    int has_empty_ns;
395
396
0
    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
0
    prefix = ((ns == NULL) || (ns->prefix == NULL)) ? BAD_CAST "" : ns->prefix;
406
0
    href = ((ns == NULL) || (ns->href == NULL)) ? BAD_CAST "" : ns->href;
407
0
    has_empty_ns = (xmlC14NStrEqual(prefix, NULL) && xmlC14NStrEqual(href, NULL));
408
409
0
    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
0
    return(has_empty_ns);
420
0
}
421
422
static int
423
0
xmlExcC14NVisibleNsStackFind(xmlC14NVisibleNsStackPtr cur, xmlNsPtr ns, xmlC14NCtxPtr ctx) {
424
0
    int i;
425
0
    const xmlChar *prefix;
426
0
    const xmlChar *href;
427
0
    int has_empty_ns;
428
429
0
    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
0
    prefix = ((ns == NULL) || (ns->prefix == NULL)) ? BAD_CAST "" : ns->prefix;
439
0
    href = ((ns == NULL) || (ns->href == NULL)) ? BAD_CAST "" : ns->href;
440
0
    has_empty_ns = (xmlC14NStrEqual(prefix, NULL) && xmlC14NStrEqual(href, NULL));
441
442
0
    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
0
    return(has_empty_ns);
457
0
}
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
0
{
580
0
    xmlNodePtr n;
581
0
    xmlNsPtr ns, tmp;
582
0
    xmlListPtr list;
583
0
    int already_rendered;
584
0
    int has_empty_ns = 0;
585
586
0
    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
0
    list = xmlListCreate(NULL, xmlC14NNsCompare);
595
0
    if (list == NULL) {
596
0
        xmlC14NErrMemory(ctx);
597
0
        return (-1);
598
0
    }
599
600
    /* check all namespaces */
601
0
    for(n = cur; n != NULL && n->type == XML_ELEMENT_NODE; n = n->parent) {
602
0
  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
0
    }
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
0
    if(visible && !has_empty_ns) {
633
0
        xmlNs ns_default;
634
635
0
        memset(&ns_default, 0, sizeof(ns_default));
636
0
        if(!xmlC14NVisibleNsStackFind(ctx->ns_rendered, &ns_default)) {
637
0
      xmlC14NPrintNamespaces(&ns_default, ctx);
638
0
  }
639
0
    }
640
641
642
    /*
643
     * print out all elements from list
644
     */
645
0
    xmlListWalk(list, xmlC14NPrintNamespacesWalker, (void *) ctx);
646
647
    /*
648
     * Cleanup
649
     */
650
0
error:
651
0
    xmlListDelete(list);
652
0
    return (0);
653
0
}
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
0
{
688
0
    xmlNsPtr ns;
689
0
    xmlListPtr list;
690
0
    xmlAttrPtr attr;
691
0
    int already_rendered;
692
0
    int has_empty_ns = 0;
693
0
    int has_visibly_utilized_empty_ns = 0;
694
0
    int has_empty_ns_in_inclusive_list = 0;
695
696
0
    if ((ctx == NULL) || (cur == NULL) || (cur->type != XML_ELEMENT_NODE)) {
697
0
        xmlC14NErrParam(ctx);
698
0
        return (-1);
699
0
    }
700
701
0
    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
0
    list = xmlListCreate(NULL, xmlC14NNsCompare);
711
0
    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
0
    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
0
    if(cur->ns != NULL) {
757
0
  ns = cur->ns;
758
0
    } else {
759
0
        ns = xmlSearchNs(cur->doc, cur, NULL);
760
0
  has_visibly_utilized_empty_ns = 1;
761
0
    }
762
0
    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
0
    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
0
    if(visible && has_visibly_utilized_empty_ns &&
808
0
      !has_empty_ns && !has_empty_ns_in_inclusive_list) {
809
0
        xmlNs ns_default;
810
811
0
        memset(&ns_default, 0, sizeof(ns_default));
812
813
0
        already_rendered = xmlExcC14NVisibleNsStackFind(ctx->ns_rendered, &ns_default, ctx);
814
0
  if(!already_rendered) {
815
0
      xmlC14NPrintNamespaces(&ns_default, ctx);
816
0
  }
817
0
    } 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
0
    xmlListWalk(list, xmlC14NPrintNamespacesWalker, (void *) ctx);
832
833
    /*
834
     * Cleanup
835
     */
836
0
error:
837
0
    xmlListDelete(list);
838
0
    return (0);
839
0
}
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
0
{
965
0
    xmlAttrPtr res;
966
0
    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
0
    return NULL;
976
0
}
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_str = xmlStrcat(tmp_str, BAD_CAST "/");
1026
0
                if(tmp_str == NULL) {
1027
0
                    xmlFree(res);
1028
1029
0
                    xmlC14NErrMemory(ctx);
1030
0
                    return (NULL);
1031
0
                }
1032
0
            }
1033
1034
            /* build uri */
1035
0
            code = xmlBuildURISafe(res, tmp_str, &tmp_str2);
1036
0
            if (code != 0) {
1037
0
                xmlFree(tmp_str);
1038
0
                xmlFree(res);
1039
1040
0
                if (code < 0)
1041
0
                    xmlC14NErrMemory(ctx);
1042
0
                else
1043
0
                    xmlC14NErr(ctx, cur, XML_ERR_INVALID_URI,
1044
0
                               "processing xml:base attribute - "
1045
0
                               "can't construct uri");
1046
0
                return (NULL);
1047
0
            }
1048
1049
            /* cleanup and set the new res */
1050
0
            xmlFree(tmp_str);
1051
0
            xmlFree(res);
1052
0
            res = tmp_str2;
1053
0
        }
1054
1055
        /* next */
1056
0
        cur = cur->parent;
1057
0
    }
1058
1059
    /* check if result uri is empty or not */
1060
0
    if((res == NULL) || xmlStrEqual(res, BAD_CAST "")) {
1061
0
        xmlFree(res);
1062
0
        return (NULL);
1063
0
    }
1064
1065
    /* create and return the new attribute node */
1066
0
    attr = xmlNewNsProp(NULL, xml_base_attr->ns, BAD_CAST "base", res);
1067
0
    if(attr == NULL) {
1068
0
        xmlFree(res);
1069
1070
0
        xmlC14NErrMemory(ctx);
1071
0
        return (NULL);
1072
0
    }
1073
1074
    /* done */
1075
0
    xmlFree(res);
1076
0
    return (attr);
1077
0
}
1078
1079
/**
1080
 * Prints out canonical attribute axis of the current node to the
1081
 * buffer from C14N context as follows
1082
 *
1083
 * Canonical XML v 1.0 (http://www.w3.org/TR/xml-c14n)
1084
 *
1085
 * Attribute Axis
1086
 * In lexicographic order (ascending), process each node that
1087
 * is in the element's attribute axis and in the node-set.
1088
 *
1089
 * The processing of an element node E MUST be modified slightly
1090
 * when an XPath node-set is given as input and the element's
1091
 * parent is omitted from the node-set.
1092
 *
1093
 *
1094
 * Exclusive XML Canonicalization v 1.0 (http://www.w3.org/TR/xml-exc-c14n)
1095
 *
1096
 * Canonical XML applied to a document subset requires the search of the
1097
 * ancestor nodes of each orphan element node for attributes in the xml
1098
 * namespace, such as xml:lang and xml:space. These are copied into the
1099
 * element node except if a declaration of the same attribute is already
1100
 * in the attribute axis of the element (whether or not it is included in
1101
 * the document subset). This search and copying are omitted from the
1102
 * Exclusive XML Canonicalization method.
1103
 *
1104
 * @param ctx     the C14N context
1105
 * @param cur     the current node
1106
 * @param parent_visible    the visibility of the parent node
1107
 * @returns 0 on success or -1 on fail.
1108
 */
1109
static int
1110
xmlC14NProcessAttrsAxis(xmlC14NCtxPtr ctx, xmlNodePtr cur, int parent_visible)
1111
0
{
1112
0
    xmlAttrPtr attr;
1113
0
    xmlListPtr list;
1114
0
    xmlAttrPtr attrs_to_delete = NULL;
1115
1116
    /* special processing for 1.1 spec */
1117
0
    xmlAttrPtr xml_base_attr = NULL;
1118
0
    xmlAttrPtr xml_lang_attr = NULL;
1119
0
    xmlAttrPtr xml_space_attr = NULL;
1120
1121
0
    if ((ctx == NULL) || (cur == NULL) || (cur->type != XML_ELEMENT_NODE)) {
1122
0
        xmlC14NErrParam(ctx);
1123
0
        return (-1);
1124
0
    }
1125
1126
    /*
1127
     * Create a sorted list to store element attributes
1128
     */
1129
0
    list = xmlListCreate(NULL, xmlC14NAttrsCompare);
1130
0
    if (list == NULL) {
1131
0
        xmlC14NErrMemory(ctx);
1132
0
        return (-1);
1133
0
    }
1134
1135
0
    switch(ctx->mode) {
1136
0
    case XML_C14N_1_0:
1137
        /* The processing of an element node E MUST be modified slightly when an XPath node-set is
1138
         * given as input and the element's parent is omitted from the node-set. The method for processing
1139
         * the attribute axis of an element E in the node-set is enhanced. All element nodes along E's
1140
         * ancestor axis are examined for nearest occurrences of attributes in the xml namespace, such
1141
         * as xml:lang and xml:space (whether or not they are in the node-set). From this list of attributes,
1142
         * remove any that are in E's attribute axis (whether or not they are in the node-set). Then,
1143
         * lexicographically merge this attribute list with the nodes of E's attribute axis that are in
1144
         * the node-set. The result of visiting the attribute axis is computed by processing the attribute
1145
         * nodes in this merged attribute list.
1146
         */
1147
1148
        /*
1149
         * Add all visible attributes from current node.
1150
         */
1151
0
        attr = cur->properties;
1152
0
        while (attr != NULL) {
1153
            /* check that attribute is visible */
1154
0
            if (xmlC14NIsVisible(ctx, attr, cur)) {
1155
0
                xmlListInsert(list, attr);
1156
0
            }
1157
0
            attr = attr->next;
1158
0
        }
1159
1160
        /*
1161
         * Handle xml attributes
1162
         */
1163
0
        if (parent_visible && (cur->parent != NULL) &&
1164
0
            (!xmlC14NIsVisible(ctx, cur->parent, cur->parent->parent)))
1165
0
        {
1166
0
            xmlNodePtr tmp;
1167
1168
            /*
1169
             * If XPath node-set is not specified then the parent is always
1170
             * visible!
1171
             */
1172
0
            tmp = cur->parent;
1173
0
            while (tmp != NULL && tmp->type == XML_ELEMENT_NODE) {
1174
0
                attr = tmp->properties;
1175
0
                while (attr != NULL) {
1176
0
                    if (xmlC14NIsXmlAttr(attr) != 0) {
1177
0
                        if (xmlListSearch(list, attr) == NULL) {
1178
0
                            xmlListInsert(list, attr);
1179
0
                        }
1180
0
                    }
1181
0
                    attr = attr->next;
1182
0
                }
1183
0
                tmp = tmp->parent;
1184
0
            }
1185
0
        }
1186
1187
        /* done */
1188
0
        break;
1189
0
    case XML_C14N_EXCLUSIVE_1_0:
1190
        /* attributes in the XML namespace, such as xml:lang and xml:space
1191
         * are not imported into orphan nodes of the document subset
1192
         */
1193
1194
        /*
1195
         * Add all visible attributes from current node.
1196
         */
1197
0
        attr = cur->properties;
1198
0
        while (attr != NULL) {
1199
            /* check that attribute is visible */
1200
0
            if (xmlC14NIsVisible(ctx, attr, cur)) {
1201
0
                xmlListInsert(list, attr);
1202
0
            }
1203
0
            attr = attr->next;
1204
0
        }
1205
1206
        /* do nothing special for xml attributes */
1207
0
        break;
1208
0
    case XML_C14N_1_1:
1209
        /* The processing of an element node E MUST be modified slightly when an XPath node-set is
1210
         * given as input and some of the element's ancestors are omitted from the node-set.
1211
         *
1212
         * Simple inheritable attributes are attributes that have a value that requires at most a simple
1213
         * redeclaration. This redeclaration is done by supplying a new value in the child axis. The
1214
         * redeclaration of a simple inheritable attribute A contained in one of E's ancestors is done
1215
         * by supplying a value to an attribute Ae inside E with the same name. Simple inheritable attributes
1216
         * are xml:lang and xml:space.
1217
         *
1218
         * The method for processing the attribute axis of an element E in the node-set is hence enhanced.
1219
         * All element nodes along E's ancestor axis are examined for the nearest occurrences of simple
1220
         * inheritable attributes in the xml namespace, such as xml:lang and xml:space (whether or not they
1221
         * are in the node-set). From this list of attributes, any simple inheritable attributes that are
1222
         * already in E's attribute axis (whether or not they are in the node-set) are removed. Then,
1223
         * lexicographically merge this attribute list with the nodes of E's attribute axis that are in
1224
         * the node-set. The result of visiting the attribute axis is computed by processing the attribute
1225
         * nodes in this merged attribute list.
1226
         *
1227
         * The xml:id attribute is not a simple inheritable attribute and no processing of these attributes is
1228
         * performed.
1229
         *
1230
         * The xml:base attribute is not a simple inheritable attribute and requires special processing beyond
1231
         * a simple redeclaration.
1232
         *
1233
         * Attributes in the XML namespace other than xml:base, xml:id, xml:lang, and xml:space MUST be processed
1234
         * as ordinary attributes.
1235
         */
1236
1237
        /*
1238
         * Add all visible attributes from current node.
1239
         */
1240
0
        attr = cur->properties;
1241
0
        while (attr != NULL) {
1242
            /* special processing for XML attribute kiks in only when we have invisible parents */
1243
0
            if ((!parent_visible) || (xmlC14NIsXmlAttr(attr) == 0)) {
1244
                /* check that attribute is visible */
1245
0
                if (xmlC14NIsVisible(ctx, attr, cur)) {
1246
0
                    xmlListInsert(list, attr);
1247
0
                }
1248
0
            } else {
1249
0
                int matched = 0;
1250
1251
                /* check for simple inheritance attributes */
1252
0
                if((!matched) && (xml_lang_attr == NULL) && xmlStrEqual(attr->name, BAD_CAST "lang")) {
1253
0
                    xml_lang_attr = attr;
1254
0
                    matched = 1;
1255
0
                }
1256
0
                if((!matched) && (xml_space_attr == NULL) && xmlStrEqual(attr->name, BAD_CAST "space")) {
1257
0
                    xml_space_attr = attr;
1258
0
                    matched = 1;
1259
0
                }
1260
1261
                /* check for base attr */
1262
0
                if((!matched) && (xml_base_attr == NULL) && xmlStrEqual(attr->name, BAD_CAST "base")) {
1263
0
                    xml_base_attr = attr;
1264
0
                    matched = 1;
1265
0
                }
1266
1267
                /* otherwise, it is a normal attribute, so just check if it is visible */
1268
0
                if((!matched) && xmlC14NIsVisible(ctx, attr, cur)) {
1269
0
                    xmlListInsert(list, attr);
1270
0
                }
1271
0
            }
1272
1273
            /* move to the next one */
1274
0
            attr = attr->next;
1275
0
        }
1276
1277
        /* special processing for XML attribute kiks in only when we have invisible parents */
1278
0
        if ((parent_visible)) {
1279
1280
            /* simple inheritance attributes - copy */
1281
0
            if(xml_lang_attr == NULL) {
1282
0
                xml_lang_attr = xmlC14NFindHiddenParentAttr(ctx, cur->parent, BAD_CAST "lang", XML_XML_NAMESPACE);
1283
0
            }
1284
0
            if(xml_lang_attr != NULL) {
1285
0
                xmlListInsert(list, xml_lang_attr);
1286
0
            }
1287
0
            if(xml_space_attr == NULL) {
1288
0
                xml_space_attr = xmlC14NFindHiddenParentAttr(ctx, cur->parent, BAD_CAST "space", XML_XML_NAMESPACE);
1289
0
            }
1290
0
            if(xml_space_attr != NULL) {
1291
0
                xmlListInsert(list, xml_space_attr);
1292
0
            }
1293
1294
            /* base uri attribute - fix up */
1295
0
            if(xml_base_attr == NULL) {
1296
                /* if we don't have base uri attribute, check if we have a "hidden" one above */
1297
0
                xml_base_attr = xmlC14NFindHiddenParentAttr(ctx, cur->parent, BAD_CAST "base", XML_XML_NAMESPACE);
1298
0
            }
1299
0
            if(xml_base_attr != NULL) {
1300
0
                xml_base_attr = xmlC14NFixupBaseAttr(ctx, xml_base_attr);
1301
0
                if(xml_base_attr != NULL) {
1302
0
                    xmlListInsert(list, xml_base_attr);
1303
1304
                    /* note that we MUST delete returned attr node ourselves! */
1305
0
                    xml_base_attr->next = attrs_to_delete;
1306
0
                    attrs_to_delete = xml_base_attr;
1307
0
                }
1308
0
            }
1309
0
        }
1310
1311
        /* done */
1312
0
        break;
1313
0
    }
1314
1315
    /*
1316
     * print out all elements from list
1317
     */
1318
0
    xmlListWalk(list, xmlC14NPrintAttrs, (void *) ctx);
1319
1320
    /*
1321
     * Cleanup
1322
     */
1323
0
    xmlFreePropList(attrs_to_delete);
1324
0
    xmlListDelete(list);
1325
0
    return (0);
1326
0
}
1327
1328
/**
1329
 * Checks that current element node has no relative namespaces defined
1330
 *
1331
 * @param ctx     the C14N context
1332
 * @param cur     the current element node
1333
 * @returns 0 if the node has no relative namespaces or -1 otherwise.
1334
 */
1335
static int
1336
xmlC14NCheckForRelativeNamespaces(xmlC14NCtxPtr ctx, xmlNodePtr cur)
1337
0
{
1338
0
    xmlNsPtr ns;
1339
1340
0
    if ((ctx == NULL) || (cur == NULL) || (cur->type != XML_ELEMENT_NODE)) {
1341
0
        xmlC14NErrParam(ctx);
1342
0
        return (-1);
1343
0
    }
1344
1345
0
    ns = cur->nsDef;
1346
0
    while (ns != NULL) {
1347
0
        if (xmlStrlen(ns->href) > 0) {
1348
0
            xmlURIPtr uri;
1349
0
            int code;
1350
1351
0
            code = xmlParseURISafe((const char *) ns->href, &uri);
1352
0
            if (uri == NULL) {
1353
0
                if (code < 0)
1354
0
                    xmlC14NErrMemory(ctx);
1355
0
                else
1356
0
                    xmlC14NErr(ctx, cur, XML_ERR_INVALID_URI,
1357
0
                               "parsing namespace uri");
1358
0
                return (-1);
1359
0
            }
1360
0
            if (xmlStrlen((const xmlChar *) uri->scheme) == 0) {
1361
0
                xmlC14NErrRelativeNamespace(ctx, uri->scheme);
1362
0
                xmlFreeURI(uri);
1363
0
                return (-1);
1364
0
            }
1365
0
            xmlFreeURI(uri);
1366
0
        }
1367
0
        ns = ns->next;
1368
0
    }
1369
0
    return (0);
1370
0
}
1371
1372
/**
1373
 * Canonical XML v 1.0 (http://www.w3.org/TR/xml-c14n)
1374
 *
1375
 * Element Nodes
1376
 * If the element is not in the node-set, then the result is obtained
1377
 * by processing the namespace axis, then the attribute axis, then
1378
 * processing the child nodes of the element that are in the node-set
1379
 * (in document order). If the element is in the node-set, then the result
1380
 * is an open angle bracket (<), the element QName, the result of
1381
 * processing the namespace axis, the result of processing the attribute
1382
 * axis, a close angle bracket (>), the result of processing the child
1383
 * nodes of the element that are in the node-set (in document order), an
1384
 * open angle bracket, a forward slash (/), the element QName, and a close
1385
 * angle bracket.
1386
 *
1387
 * @param ctx     the pointer to C14N context object
1388
 * @param cur     the node to process
1389
 * @param visible     this node is visible
1390
 * @returns non-negative value on success or negative value on fail
1391
 */
1392
static int
1393
xmlC14NProcessElementNode(xmlC14NCtxPtr ctx, xmlNodePtr cur, int visible)
1394
0
{
1395
0
    int ret;
1396
0
    xmlC14NVisibleNsStack state;
1397
0
    int parent_is_doc = 0;
1398
1399
0
    if ((ctx == NULL) || (cur == NULL) || (cur->type != XML_ELEMENT_NODE)) {
1400
0
        xmlC14NErrParam(ctx);
1401
0
        return (-1);
1402
0
    }
1403
1404
    /*
1405
     * Check relative relative namespaces:
1406
     * implementations of XML canonicalization MUST report an operation
1407
     * failure on documents containing relative namespace URIs.
1408
     */
1409
0
    if (xmlC14NCheckForRelativeNamespaces(ctx, cur) < 0)
1410
0
        return (-1);
1411
1412
    /*
1413
     * Save ns_rendered stack position
1414
     */
1415
0
    memset(&state, 0, sizeof(state));
1416
0
    xmlC14NVisibleNsStackSave(ctx->ns_rendered, &state);
1417
1418
0
    if (visible) {
1419
0
        if (ctx->parent_is_doc) {
1420
      /* save this flag into the stack */
1421
0
      parent_is_doc = ctx->parent_is_doc;
1422
0
      ctx->parent_is_doc = 0;
1423
0
            ctx->pos = XMLC14N_INSIDE_DOCUMENT_ELEMENT;
1424
0
        }
1425
0
        xmlOutputBufferWriteString(ctx->buf, "<");
1426
1427
0
        if ((cur->ns != NULL) && (xmlStrlen(cur->ns->prefix) > 0)) {
1428
0
            xmlOutputBufferWriteString(ctx->buf,
1429
0
                                       (const char *) cur->ns->prefix);
1430
0
            xmlOutputBufferWriteString(ctx->buf, ":");
1431
0
        }
1432
0
        xmlOutputBufferWriteString(ctx->buf, (const char *) cur->name);
1433
0
    }
1434
1435
0
    if (!xmlC14NIsExclusive(ctx)) {
1436
0
        ret = xmlC14NProcessNamespacesAxis(ctx, cur, visible);
1437
0
    } else {
1438
0
        ret = xmlExcC14NProcessNamespacesAxis(ctx, cur, visible);
1439
0
    }
1440
0
    if (ret < 0)
1441
0
        return (-1);
1442
    /* todo: shouldn't this go to "visible only"? */
1443
0
    if(visible) {
1444
0
  xmlC14NVisibleNsStackShift(ctx->ns_rendered);
1445
0
    }
1446
1447
0
    ret = xmlC14NProcessAttrsAxis(ctx, cur, visible);
1448
0
    if (ret < 0)
1449
0
  return (-1);
1450
1451
0
    if (visible) {
1452
0
        xmlOutputBufferWriteString(ctx->buf, ">");
1453
0
    }
1454
0
    if (cur->children != NULL) {
1455
0
        ret = xmlC14NProcessNodeList(ctx, cur->children);
1456
0
        if (ret < 0)
1457
0
            return (-1);
1458
0
    }
1459
0
    if (visible) {
1460
0
        xmlOutputBufferWriteString(ctx->buf, "</");
1461
0
        if ((cur->ns != NULL) && (xmlStrlen(cur->ns->prefix) > 0)) {
1462
0
            xmlOutputBufferWriteString(ctx->buf,
1463
0
                                       (const char *) cur->ns->prefix);
1464
0
            xmlOutputBufferWriteString(ctx->buf, ":");
1465
0
        }
1466
0
        xmlOutputBufferWriteString(ctx->buf, (const char *) cur->name);
1467
0
        xmlOutputBufferWriteString(ctx->buf, ">");
1468
0
        if (parent_is_doc) {
1469
      /* restore this flag from the stack for next node */
1470
0
            ctx->parent_is_doc = parent_is_doc;
1471
0
      ctx->pos = XMLC14N_AFTER_DOCUMENT_ELEMENT;
1472
0
        }
1473
0
    }
1474
1475
    /*
1476
     * Restore ns_rendered stack position
1477
     */
1478
0
    xmlC14NVisibleNsStackRestore(ctx->ns_rendered, &state);
1479
0
    return (0);
1480
0
}
1481
1482
/**
1483
 * Processes the given node
1484
 *
1485
 * @param ctx     the pointer to C14N context object
1486
 * @param cur     the node to process
1487
 * @returns non-negative value on success or negative value on fail
1488
 */
1489
static int
1490
xmlC14NProcessNode(xmlC14NCtxPtr ctx, xmlNodePtr cur)
1491
0
{
1492
0
    int ret = 0;
1493
0
    int visible;
1494
1495
0
    if ((ctx == NULL) || (cur == NULL)) {
1496
0
        xmlC14NErrParam(ctx);
1497
0
        return (-1);
1498
0
    }
1499
1500
0
    visible = xmlC14NIsVisible(ctx, cur, cur->parent);
1501
0
    switch (cur->type) {
1502
0
        case XML_ELEMENT_NODE:
1503
0
            ret = xmlC14NProcessElementNode(ctx, cur, visible);
1504
0
            break;
1505
0
        case XML_CDATA_SECTION_NODE:
1506
0
        case XML_TEXT_NODE:
1507
            /*
1508
             * Text Nodes
1509
             * the string value, except all ampersands are replaced
1510
             * by &amp;, all open angle brackets (<) are replaced by &lt;, all closing
1511
             * angle brackets (>) are replaced by &gt;, and all #xD characters are
1512
             * replaced by &#xD;.
1513
             */
1514
            /* cdata sections are processed as text nodes */
1515
            /* todo: verify that cdata sections are included in XPath nodes set */
1516
0
            if ((visible) && (cur->content != NULL)) {
1517
0
                xmlChar *buffer;
1518
1519
0
                buffer = xmlC11NNormalizeText(cur->content);
1520
0
                if (buffer != NULL) {
1521
0
                    xmlOutputBufferWriteString(ctx->buf,
1522
0
                                               (const char *) buffer);
1523
0
                    xmlFree(buffer);
1524
0
                } else {
1525
0
                    xmlC14NErrMemory(ctx);
1526
0
                    return (-1);
1527
0
                }
1528
0
            }
1529
0
            break;
1530
0
        case XML_PI_NODE:
1531
            /*
1532
             * Processing Instruction (PI) Nodes-
1533
             * The opening PI symbol (<?), the PI target name of the node,
1534
             * a leading space and the string value if it is not empty, and
1535
             * the closing PI symbol (?>). If the string value is empty,
1536
             * then the leading space is not added. Also, a trailing #xA is
1537
             * rendered after the closing PI symbol for PI children of the
1538
             * root node with a lesser document order than the document
1539
             * element, and a leading #xA is rendered before the opening PI
1540
             * symbol of PI children of the root node with a greater document
1541
             * order than the document element.
1542
             */
1543
0
            if (visible) {
1544
0
                if (ctx->pos == XMLC14N_AFTER_DOCUMENT_ELEMENT) {
1545
0
                    xmlOutputBufferWriteString(ctx->buf, "\x0A<?");
1546
0
                } else {
1547
0
                    xmlOutputBufferWriteString(ctx->buf, "<?");
1548
0
                }
1549
1550
0
                xmlOutputBufferWriteString(ctx->buf,
1551
0
                                           (const char *) cur->name);
1552
0
                if ((cur->content != NULL) && (*(cur->content) != '\0')) {
1553
0
                    xmlChar *buffer;
1554
1555
0
                    xmlOutputBufferWriteString(ctx->buf, " ");
1556
1557
                    /* todo: do we need to normalize pi? */
1558
0
                    buffer = xmlC11NNormalizePI(cur->content);
1559
0
                    if (buffer != NULL) {
1560
0
                        xmlOutputBufferWriteString(ctx->buf,
1561
0
                                                   (const char *) buffer);
1562
0
                        xmlFree(buffer);
1563
0
                    } else {
1564
0
                        xmlC14NErrMemory(ctx);
1565
0
                        return (-1);
1566
0
                    }
1567
0
                }
1568
1569
0
                if (ctx->pos == XMLC14N_BEFORE_DOCUMENT_ELEMENT) {
1570
0
                    xmlOutputBufferWriteString(ctx->buf, "?>\x0A");
1571
0
                } else {
1572
0
                    xmlOutputBufferWriteString(ctx->buf, "?>");
1573
0
                }
1574
0
            }
1575
0
            break;
1576
0
        case XML_COMMENT_NODE:
1577
            /*
1578
             * Comment Nodes
1579
             * Nothing if generating canonical XML without  comments. For
1580
             * canonical XML with comments, generate the opening comment
1581
             * symbol (<!--), the string value of the node, and the
1582
             * closing comment symbol (-->). Also, a trailing #xA is rendered
1583
             * after the closing comment symbol for comment children of the
1584
             * root node with a lesser document order than the document
1585
             * element, and a leading #xA is rendered before the opening
1586
             * comment symbol of comment children of the root node with a
1587
             * greater document order than the document element. (Comment
1588
             * children of the root node represent comments outside of the
1589
             * top-level document element and outside of the document type
1590
             * declaration).
1591
             */
1592
0
            if (visible && ctx->with_comments) {
1593
0
                if (ctx->pos == XMLC14N_AFTER_DOCUMENT_ELEMENT) {
1594
0
                    xmlOutputBufferWriteString(ctx->buf, "\x0A<!--");
1595
0
                } else {
1596
0
                    xmlOutputBufferWriteString(ctx->buf, "<!--");
1597
0
                }
1598
1599
0
                if (cur->content != NULL) {
1600
0
                    xmlChar *buffer;
1601
1602
                    /* todo: do we need to normalize comment? */
1603
0
                    buffer = xmlC11NNormalizeComment(cur->content);
1604
0
                    if (buffer != NULL) {
1605
0
                        xmlOutputBufferWriteString(ctx->buf,
1606
0
                                                   (const char *) buffer);
1607
0
                        xmlFree(buffer);
1608
0
                    } else {
1609
0
                        xmlC14NErrMemory(ctx);
1610
0
                        return (-1);
1611
0
                    }
1612
0
                }
1613
1614
0
                if (ctx->pos == XMLC14N_BEFORE_DOCUMENT_ELEMENT) {
1615
0
                    xmlOutputBufferWriteString(ctx->buf, "-->\x0A");
1616
0
                } else {
1617
0
                    xmlOutputBufferWriteString(ctx->buf, "-->");
1618
0
                }
1619
0
            }
1620
0
            break;
1621
0
        case XML_DOCUMENT_NODE:
1622
0
        case XML_DOCUMENT_FRAG_NODE:   /* should be processed as document? */
1623
0
#ifdef LIBXML_HTML_ENABLED
1624
0
        case XML_HTML_DOCUMENT_NODE:   /* should be processed as document? */
1625
0
#endif
1626
0
            if (cur->children != NULL) {
1627
0
                ctx->pos = XMLC14N_BEFORE_DOCUMENT_ELEMENT;
1628
0
                ctx->parent_is_doc = 1;
1629
0
                ret = xmlC14NProcessNodeList(ctx, cur->children);
1630
0
            }
1631
0
            break;
1632
1633
0
        case XML_ATTRIBUTE_NODE:
1634
0
            xmlC14NErrInvalidNode(ctx, "XML_ATTRIBUTE_NODE", "processing node");
1635
0
            return (-1);
1636
0
        case XML_NAMESPACE_DECL:
1637
0
            xmlC14NErrInvalidNode(ctx, "XML_NAMESPACE_DECL", "processing node");
1638
0
            return (-1);
1639
0
        case XML_ENTITY_REF_NODE:
1640
0
            xmlC14NErrInvalidNode(ctx, "XML_ENTITY_REF_NODE", "processing node");
1641
0
            return (-1);
1642
0
        case XML_ENTITY_NODE:
1643
0
            xmlC14NErrInvalidNode(ctx, "XML_ENTITY_NODE", "processing node");
1644
0
            return (-1);
1645
1646
0
        case XML_DOCUMENT_TYPE_NODE:
1647
0
        case XML_NOTATION_NODE:
1648
0
        case XML_DTD_NODE:
1649
0
        case XML_ELEMENT_DECL:
1650
0
        case XML_ATTRIBUTE_DECL:
1651
0
        case XML_ENTITY_DECL:
1652
0
#ifdef LIBXML_XINCLUDE_ENABLED
1653
0
        case XML_XINCLUDE_START:
1654
0
        case XML_XINCLUDE_END:
1655
0
#endif
1656
            /*
1657
             * should be ignored according to "W3C Canonical XML"
1658
             */
1659
0
            break;
1660
0
        default:
1661
0
            xmlC14NErrUnknownNode(ctx, cur->type, "processing node");
1662
0
            return (-1);
1663
0
    }
1664
1665
0
    return (ret);
1666
0
}
1667
1668
/**
1669
 * Processes all nodes in the row starting from cur.
1670
 *
1671
 * @param ctx     the pointer to C14N context object
1672
 * @param cur     the node to start from
1673
 * @returns non-negative value on success or negative value on fail
1674
 */
1675
static int
1676
xmlC14NProcessNodeList(xmlC14NCtxPtr ctx, xmlNodePtr cur)
1677
0
{
1678
0
    int ret;
1679
1680
0
    if (ctx == NULL) {
1681
0
        xmlC14NErrParam(ctx);
1682
0
        return (-1);
1683
0
    }
1684
1685
0
    for (ret = 0; cur != NULL && ret >= 0; cur = cur->next) {
1686
0
        ret = xmlC14NProcessNode(ctx, cur);
1687
0
    }
1688
0
    return (ret);
1689
0
}
1690
1691
1692
/**
1693
 * Cleanups the C14N context object.
1694
 *
1695
 * @param ctx  the pointer to C14N context object
1696
 */
1697
1698
static void
1699
xmlC14NFreeCtx(xmlC14NCtxPtr ctx)
1700
0
{
1701
0
    if (ctx == NULL) {
1702
0
        xmlC14NErrParam(ctx);
1703
0
        return;
1704
0
    }
1705
1706
0
    if (ctx->ns_rendered != NULL) {
1707
0
        xmlC14NVisibleNsStackDestroy(ctx->ns_rendered);
1708
0
    }
1709
0
    xmlFree(ctx);
1710
0
}
1711
1712
/**
1713
 * Creates new C14N context object to store C14N parameters.
1714
 *
1715
 * @param doc     the XML document for canonization
1716
 * @param is_visible_callback  the function to use to determine is node visible
1717
 *      or not
1718
 * @param user_data     the first parameter for `is_visible_callback` function
1719
 *      (in most cases, it is nodes set)
1720
 * @param mode  the c14n mode (see `xmlC14NMode`)
1721
 * @param inclusive_ns_prefixes  the list of inclusive namespace prefixes
1722
 *      ended with a NULL or NULL if there is no
1723
 *      inclusive namespaces (only for `
1724
 *      canonicalization)
1725
 * @param with_comments   include comments in the result (!=0) or not (==0)
1726
 * @param buf     the output buffer to store canonical XML; this
1727
 *      buffer MUST have encoder==NULL because C14N requires
1728
 *      UTF-8 output
1729
 * @returns pointer to newly created object (success) or NULL (fail)
1730
 */
1731
static xmlC14NCtxPtr
1732
xmlC14NNewCtx(xmlDocPtr doc,
1733
        xmlC14NIsVisibleCallback is_visible_callback, void* user_data,
1734
              xmlC14NMode mode, xmlChar ** inclusive_ns_prefixes,
1735
              int with_comments, xmlOutputBufferPtr buf)
1736
0
{
1737
0
    xmlC14NCtxPtr ctx = NULL;
1738
1739
0
    if ((doc == NULL) || (buf == NULL)) {
1740
0
        xmlC14NErrParam(ctx);
1741
0
        return (NULL);
1742
0
    }
1743
1744
    /*
1745
     *  Validate the encoding output buffer encoding
1746
     */
1747
0
    if (buf->encoder != NULL) {
1748
0
        xmlC14NErr(ctx, (xmlNodePtr) doc, XML_C14N_REQUIRES_UTF8,
1749
0
"xmlC14NNewCtx: output buffer encoder != NULL but C14N requires UTF8 output\n");
1750
0
        return (NULL);
1751
0
    }
1752
1753
    /*
1754
     * Allocate a new xmlC14NCtx and fill the fields.
1755
     */
1756
0
    ctx = (xmlC14NCtxPtr) xmlMalloc(sizeof(xmlC14NCtx));
1757
0
    if (ctx == NULL) {
1758
0
  xmlC14NErrMemory(ctx);
1759
0
        return (NULL);
1760
0
    }
1761
0
    memset(ctx, 0, sizeof(xmlC14NCtx));
1762
1763
    /*
1764
     * initialize C14N context
1765
     */
1766
0
    ctx->doc = doc;
1767
0
    ctx->with_comments = with_comments;
1768
0
    ctx->is_visible_callback = is_visible_callback;
1769
0
    ctx->user_data = user_data;
1770
0
    ctx->buf = buf;
1771
0
    ctx->parent_is_doc = 1;
1772
0
    ctx->pos = XMLC14N_BEFORE_DOCUMENT_ELEMENT;
1773
0
    ctx->ns_rendered = xmlC14NVisibleNsStackCreate();
1774
1775
0
    if(ctx->ns_rendered == NULL) {
1776
0
        xmlC14NErrMemory(ctx);
1777
0
  xmlC14NFreeCtx(ctx);
1778
0
        return (NULL);
1779
0
    }
1780
1781
    /*
1782
     * Set "mode" flag and remember list of inclusive prefixes
1783
     * for exclusive c14n
1784
     */
1785
0
    ctx->mode = mode;
1786
0
    if(xmlC14NIsExclusive(ctx)) {
1787
0
        ctx->inclusive_ns_prefixes = inclusive_ns_prefixes;
1788
0
    }
1789
1790
0
    return (ctx);
1791
0
}
1792
1793
/**
1794
 * Dumps the canonized image of given XML document into the provided buffer.
1795
 * For details see "Canonical XML" (http://www.w3.org/TR/xml-c14n) or
1796
 * "Exclusive XML Canonicalization" (http://www.w3.org/TR/xml-exc-c14n)
1797
 *
1798
 * @param doc     the XML document for canonization
1799
 * @param is_visible_callback  the function to use to determine is node visible
1800
 *      or not
1801
 * @param user_data     the first parameter for `is_visible_callback` function
1802
 *      (in most cases, it is nodes set)
1803
 * @param mode    the c14n mode (see `xmlC14NMode`)
1804
 * @param inclusive_ns_prefixes  the list of inclusive namespace prefixes
1805
 *      ended with a NULL or NULL if there is no
1806
 *      inclusive namespaces (only for exclusive
1807
 *      canonicalization, ignored otherwise)
1808
 * @param with_comments   include comments in the result (!=0) or not (==0)
1809
 * @param buf     the output buffer to store canonical XML; this
1810
 *      buffer MUST have encoder==NULL because C14N requires
1811
 *      UTF-8 output
1812
 * @returns non-negative value on success or a negative value on fail
1813
 */
1814
int
1815
xmlC14NExecute(xmlDoc *doc, xmlC14NIsVisibleCallback is_visible_callback,
1816
   void* user_data, int mode, xmlChar **inclusive_ns_prefixes,
1817
0
   int with_comments, xmlOutputBuffer *buf) {
1818
1819
0
    xmlC14NCtxPtr ctx;
1820
0
    xmlC14NMode c14n_mode = XML_C14N_1_0;
1821
0
    int ret;
1822
1823
0
    if ((buf == NULL) || (doc == NULL)) {
1824
0
        xmlC14NErrParam(NULL);
1825
0
        return (-1);
1826
0
    }
1827
1828
    /* for backward compatibility, we have to have "mode" as "int"
1829
       and here we check that user gives valid value */
1830
0
    switch(mode) {
1831
0
    case XML_C14N_1_0:
1832
0
    case XML_C14N_EXCLUSIVE_1_0:
1833
0
    case XML_C14N_1_1:
1834
0
         c14n_mode = (xmlC14NMode)mode;
1835
0
         break;
1836
0
    default:
1837
0
        xmlC14NErrParam(NULL);
1838
0
        return (-1);
1839
0
    }
1840
1841
    /*
1842
     *  Validate the encoding output buffer encoding
1843
     */
1844
0
    if (buf->encoder != NULL) {
1845
0
        xmlC14NErr(NULL, (xmlNodePtr) doc, XML_C14N_REQUIRES_UTF8,
1846
0
"xmlC14NExecute: output buffer encoder != NULL but C14N requires UTF8 output\n");
1847
0
        return (-1);
1848
0
    }
1849
1850
0
    ctx = xmlC14NNewCtx(doc, is_visible_callback, user_data,
1851
0
              c14n_mode, inclusive_ns_prefixes,
1852
0
                    with_comments, buf);
1853
0
    if (ctx == NULL) {
1854
0
        xmlC14NErr(NULL, (xmlNodePtr) doc, XML_C14N_CREATE_CTXT,
1855
0
       "xmlC14NExecute: unable to create C14N context\n");
1856
0
        return (-1);
1857
0
    }
1858
1859
1860
1861
    /*
1862
     * Root Node
1863
     * The root node is the parent of the top-level document element. The
1864
     * result of processing each of its child nodes that is in the node-set
1865
     * in document order. The root node does not generate a byte order mark,
1866
     * XML declaration, nor anything from within the document type
1867
     * declaration.
1868
     */
1869
0
    if (doc->children != NULL) {
1870
0
        ret = xmlC14NProcessNodeList(ctx, doc->children);
1871
0
        if (ret < 0) {
1872
0
            xmlC14NFreeCtx(ctx);
1873
0
            return (-1);
1874
0
        }
1875
0
    }
1876
1877
    /*
1878
     * Flush buffer to get number of bytes written
1879
     */
1880
0
    ret = xmlOutputBufferFlush(buf);
1881
0
    if (ret < 0) {
1882
0
        xmlC14NErr(ctx, NULL, buf->error, "flushing output buffer");
1883
0
        xmlC14NFreeCtx(ctx);
1884
0
        return (-1);
1885
0
    }
1886
1887
    /*
1888
     * Cleanup
1889
     */
1890
0
    xmlC14NFreeCtx(ctx);
1891
0
    return (ret);
1892
0
}
1893
1894
/**
1895
 * Dumps the canonized image of given XML document into the provided buffer.
1896
 * For details see "Canonical XML" (http://www.w3.org/TR/xml-c14n) or
1897
 * "Exclusive XML Canonicalization" (http://www.w3.org/TR/xml-exc-c14n)
1898
 *
1899
 * @param doc     the XML document for canonization
1900
 * @param nodes     the nodes set to be included in the canonized image
1901
 *    or NULL if all document nodes should be included
1902
 * @param mode      the c14n mode (see `xmlC14NMode`)
1903
 * @param inclusive_ns_prefixes  the list of inclusive namespace prefixes
1904
 *      ended with a NULL or NULL if there is no
1905
 *      inclusive namespaces (only for exclusive
1906
 *      canonicalization, ignored otherwise)
1907
 * @param with_comments   include comments in the result (!=0) or not (==0)
1908
 * @param buf     the output buffer to store canonical XML; this
1909
 *      buffer MUST have encoder==NULL because C14N requires
1910
 *      UTF-8 output
1911
 * @returns non-negative value on success or a negative value on fail
1912
 */
1913
int
1914
xmlC14NDocSaveTo(xmlDoc *doc, xmlNodeSet *nodes,
1915
                 int mode, xmlChar ** inclusive_ns_prefixes,
1916
0
                 int with_comments, xmlOutputBuffer *buf) {
1917
0
    return(xmlC14NExecute(doc,
1918
0
      xmlC14NIsNodeInNodeset,
1919
0
      nodes,
1920
0
      mode,
1921
0
      inclusive_ns_prefixes,
1922
0
      with_comments,
1923
0
      buf));
1924
0
}
1925
1926
1927
/**
1928
 * Dumps the canonized image of given XML document into memory.
1929
 * For details see "Canonical XML" (http://www.w3.org/TR/xml-c14n) or
1930
 * "Exclusive XML Canonicalization" (http://www.w3.org/TR/xml-exc-c14n)
1931
 *
1932
 * @param doc     the XML document for canonization
1933
 * @param nodes     the nodes set to be included in the canonized image
1934
 *    or NULL if all document nodes should be included
1935
 * @param mode      the c14n mode (see `xmlC14NMode`)
1936
 * @param inclusive_ns_prefixes  the list of inclusive namespace prefixes
1937
 *      ended with a NULL or NULL if there is no
1938
 *      inclusive namespaces (only for exclusive
1939
 *      canonicalization, ignored otherwise)
1940
 * @param with_comments   include comments in the result (!=0) or not (==0)
1941
 * @param doc_txt_ptr   the memory pointer for allocated canonical XML text;
1942
 *      the caller of this functions is responsible for calling
1943
 *      xmlFree() to free allocated memory
1944
 * @returns the number of bytes written on success or a negative value on fail
1945
 */
1946
int
1947
xmlC14NDocDumpMemory(xmlDoc *doc, xmlNodeSet *nodes,
1948
                     int mode, xmlChar ** inclusive_ns_prefixes,
1949
                     int with_comments, xmlChar ** doc_txt_ptr)
1950
0
{
1951
0
    int ret;
1952
0
    xmlOutputBufferPtr buf;
1953
1954
0
    if (doc_txt_ptr == NULL) {
1955
0
        xmlC14NErrParam(NULL);
1956
0
        return (-1);
1957
0
    }
1958
1959
0
    *doc_txt_ptr = NULL;
1960
1961
    /*
1962
     * create memory buffer with UTF8 (default) encoding
1963
     */
1964
0
    buf = xmlAllocOutputBuffer(NULL);
1965
0
    if (buf == NULL) {
1966
0
        xmlC14NErrMemory(NULL);
1967
0
        return (-1);
1968
0
    }
1969
1970
    /*
1971
     * canonize document and write to buffer
1972
     */
1973
0
    ret = xmlC14NDocSaveTo(doc, nodes, mode, inclusive_ns_prefixes,
1974
0
                           with_comments, buf);
1975
0
    if (ret < 0) {
1976
0
        (void) xmlOutputBufferClose(buf);
1977
0
        return (-1);
1978
0
    }
1979
1980
0
    ret = xmlBufUse(buf->buffer);
1981
0
    if (ret >= 0) {
1982
0
        *doc_txt_ptr = xmlStrndup(xmlBufContent(buf->buffer), ret);
1983
0
    }
1984
0
    (void) xmlOutputBufferClose(buf);
1985
1986
0
    if ((*doc_txt_ptr == NULL) && (ret >= 0)) {
1987
0
        xmlC14NErrMemory(NULL);
1988
0
        return (-1);
1989
0
    }
1990
0
    return (ret);
1991
0
}
1992
1993
/**
1994
 * Dumps the canonized image of given XML document into the file.
1995
 * For details see "Canonical XML" (http://www.w3.org/TR/xml-c14n) or
1996
 * "Exclusive XML Canonicalization" (http://www.w3.org/TR/xml-exc-c14n)
1997
 *
1998
 * @param doc     the XML document for canonization
1999
 * @param nodes     the nodes set to be included in the canonized image
2000
 *    or NULL if all document nodes should be included
2001
 * @param mode      the c14n mode (see `xmlC14NMode`)
2002
 * @param inclusive_ns_prefixes  the list of inclusive namespace prefixes
2003
 *      ended with a NULL or NULL if there is no
2004
 *      inclusive namespaces (only for exclusive
2005
 *      canonicalization, ignored otherwise)
2006
 * @param with_comments   include comments in the result (!=0) or not (==0)
2007
 * @param filename      the filename to store canonical XML image
2008
 * @param compression   the compression level (zlib required):
2009
 *        -1 - libxml default,
2010
 *         0 - uncompressed,
2011
 *        >0 - compression level
2012
 * @returns the number of bytes written success or a negative value on fail
2013
 */
2014
int
2015
xmlC14NDocSave(xmlDoc *doc, xmlNodeSet *nodes,
2016
               int mode, xmlChar ** inclusive_ns_prefixes,
2017
               int with_comments, const char *filename, int compression)
2018
0
{
2019
0
    xmlOutputBufferPtr buf;
2020
0
    int ret;
2021
2022
0
    if (filename == NULL) {
2023
0
        xmlC14NErrParam(NULL);
2024
0
        return (-1);
2025
0
    }
2026
#ifdef LIBXML_ZLIB_ENABLED
2027
    if (compression < 0)
2028
        compression = xmlGetCompressMode();
2029
#endif
2030
2031
    /*
2032
     * save the content to a temp buffer, use default UTF8 encoding.
2033
     */
2034
0
    buf = xmlOutputBufferCreateFilename(filename, NULL, compression);
2035
0
    if (buf == NULL) {
2036
0
        xmlC14NErr(NULL, NULL, XML_IO_UNKNOWN, "creating temporary filename");
2037
0
        return (-1);
2038
0
    }
2039
2040
    /*
2041
     * canonize document and write to buffer
2042
     */
2043
0
    ret = xmlC14NDocSaveTo(doc, nodes, mode, inclusive_ns_prefixes,
2044
0
                           with_comments, buf);
2045
0
    if (ret < 0) {
2046
0
        (void) xmlOutputBufferClose(buf);
2047
0
        return (-1);
2048
0
    }
2049
2050
    /*
2051
     * get the numbers of bytes written
2052
     */
2053
0
    ret = xmlOutputBufferClose(buf);
2054
0
    return (ret);
2055
0
}
2056
2057
/**
2058
 * Converts a string to a canonical (normalized) format. The code is stolen
2059
 * from xmlEscapeText. Added normalization of `\x09`, `\x0a`,
2060
 * `\x0A` and the `mode` parameter.
2061
 *
2062
 * @param input     the input string
2063
 * @param mode      the normalization mode (attribute, comment, PI or text)
2064
 * @returns a normalized string (caller is responsible for calling #xmlFree)
2065
 * or NULL if an error occurs
2066
 */
2067
static xmlChar *
2068
xmlC11NNormalizeString(const xmlChar * input,
2069
                       xmlC14NNormalizationMode mode)
2070
0
{
2071
0
    const xmlChar *cur = input;
2072
0
    xmlChar *buffer = NULL;
2073
0
    xmlChar *out = NULL;
2074
0
    int buffer_size = 0;
2075
2076
0
    if (input == NULL)
2077
0
        return (NULL);
2078
2079
    /*
2080
     * allocate an translation buffer.
2081
     */
2082
0
    buffer_size = 1000;
2083
0
    buffer = xmlMalloc(buffer_size);
2084
0
    if (buffer == NULL)
2085
0
        return (NULL);
2086
0
    out = buffer;
2087
2088
0
    while (*cur != '\0') {
2089
0
        if ((out - buffer) > (buffer_size - 10)) {
2090
0
            xmlChar *tmp;
2091
0
            int indx = out - buffer;
2092
0
            int newSize;
2093
2094
0
            newSize = xmlGrowCapacity(buffer_size, 1, 1, XML_MAX_ITEMS);
2095
0
            if (newSize < 0) {
2096
0
                xmlFree(buffer);
2097
0
                return(NULL);
2098
0
            }
2099
0
            tmp = xmlRealloc(buffer, newSize);
2100
0
            if (tmp == NULL) {
2101
0
                xmlFree(buffer);
2102
0
                return(NULL);
2103
0
            }
2104
0
            buffer = tmp;
2105
0
            buffer_size = newSize;
2106
0
            out = &buffer[indx];
2107
0
        }
2108
2109
0
        if ((*cur == '<') && ((mode == XMLC14N_NORMALIZE_ATTR) ||
2110
0
                              (mode == XMLC14N_NORMALIZE_TEXT))) {
2111
0
            *out++ = '&';
2112
0
            *out++ = 'l';
2113
0
            *out++ = 't';
2114
0
            *out++ = ';';
2115
0
        } else if ((*cur == '>') && (mode == XMLC14N_NORMALIZE_TEXT)) {
2116
0
            *out++ = '&';
2117
0
            *out++ = 'g';
2118
0
            *out++ = 't';
2119
0
            *out++ = ';';
2120
0
        } else if ((*cur == '&') && ((mode == XMLC14N_NORMALIZE_ATTR) ||
2121
0
                                     (mode == XMLC14N_NORMALIZE_TEXT))) {
2122
0
            *out++ = '&';
2123
0
            *out++ = 'a';
2124
0
            *out++ = 'm';
2125
0
            *out++ = 'p';
2126
0
            *out++ = ';';
2127
0
        } else if ((*cur == '"') && (mode == XMLC14N_NORMALIZE_ATTR)) {
2128
0
            *out++ = '&';
2129
0
            *out++ = 'q';
2130
0
            *out++ = 'u';
2131
0
            *out++ = 'o';
2132
0
            *out++ = 't';
2133
0
            *out++ = ';';
2134
0
        } else if ((*cur == '\x09') && (mode == XMLC14N_NORMALIZE_ATTR)) {
2135
0
            *out++ = '&';
2136
0
            *out++ = '#';
2137
0
            *out++ = 'x';
2138
0
            *out++ = '9';
2139
0
            *out++ = ';';
2140
0
        } else if ((*cur == '\x0A') && (mode == XMLC14N_NORMALIZE_ATTR)) {
2141
0
            *out++ = '&';
2142
0
            *out++ = '#';
2143
0
            *out++ = 'x';
2144
0
            *out++ = 'A';
2145
0
            *out++ = ';';
2146
0
        } else if ((*cur == '\x0D') && ((mode == XMLC14N_NORMALIZE_ATTR) ||
2147
0
                                        (mode == XMLC14N_NORMALIZE_TEXT) ||
2148
0
                                        (mode == XMLC14N_NORMALIZE_COMMENT) ||
2149
0
          (mode == XMLC14N_NORMALIZE_PI))) {
2150
0
            *out++ = '&';
2151
0
            *out++ = '#';
2152
0
            *out++ = 'x';
2153
0
            *out++ = 'D';
2154
0
            *out++ = ';';
2155
0
        } else {
2156
            /*
2157
             * Works because on UTF-8, all extended sequences cannot
2158
             * result in bytes in the ASCII range.
2159
             */
2160
0
            *out++ = *cur;
2161
0
        }
2162
0
        cur++;
2163
0
    }
2164
0
    *out = 0;
2165
0
    return (buffer);
2166
0
}
2167
2168
#endif /* LIBXML_C14N_ENABLED */