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