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