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