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