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