1 /** 2 * uri.c: set of generic URI related routines 3 * 4 * Reference: RFCs 3986, 2732 and 2373 5 * 6 * See Copyright for the status of this software. 7 * 8 * daniel (at) veillard.com 9 */ 10 11 #define IN_LIBXML 12 #include "libxml.h" 13 14 #include <string.h> 15 16 #include <libxml/xmlmemory.h> 17 #include <libxml/uri.h> 18 #include <libxml/globals.h> 19 #include <libxml/xmlerror.h> 20 21 /** 22 * MAX_URI_LENGTH: 23 * 24 * The definition of the URI regexp in the above RFC has no size limit 25 * In practice they are usually relativey short except for the 26 * data URI scheme as defined in RFC 2397. Even for data URI the usual 27 * maximum size before hitting random practical limits is around 64 KB 28 * and 4KB is usually a maximum admitted limit for proper operations. 29 * The value below is more a security limit than anything else and 30 * really should never be hit by 'normal' operations 31 * Set to 1 MByte in 2012, this is only enforced on output 32 */ 33 #define MAX_URI_LENGTH 1024 * 1024 34 35 static void 36 xmlURIErrMemory(const char *extra) 37 { 38 if (extra) 39 __xmlRaiseError(NULL, NULL, NULL, 40 NULL, NULL, XML_FROM_URI, 41 XML_ERR_NO_MEMORY, XML_ERR_FATAL, NULL, 0, 42 extra, NULL, NULL, 0, 0, 43 "Memory allocation failed : %s\n", extra); 44 else 45 __xmlRaiseError(NULL, NULL, NULL, 46 NULL, NULL, XML_FROM_URI, 47 XML_ERR_NO_MEMORY, XML_ERR_FATAL, NULL, 0, 48 NULL, NULL, NULL, 0, 0, 49 "Memory allocation failed\n"); 50 } 51 52 static void xmlCleanURI(xmlURIPtr uri); 53 54 /* 55 * Old rule from 2396 used in legacy handling code 56 * alpha = lowalpha | upalpha 57 */ 58 #define IS_ALPHA(x) (IS_LOWALPHA(x) || IS_UPALPHA(x)) 59 60 61 /* 62 * lowalpha = "a" | "b" | "c" | "d" | "e" | "f" | "g" | "h" | "i" | "j" | 63 * "k" | "l" | "m" | "n" | "o" | "p" | "q" | "r" | "s" | "t" | 64 * "u" | "v" | "w" | "x" | "y" | "z" 65 */ 66 67 #define IS_LOWALPHA(x) (((x) >= 'a') && ((x) <= 'z')) 68 69 /* 70 * upalpha = "A" | "B" | "C" | "D" | "E" | "F" | "G" | "H" | "I" | "J" | 71 * "K" | "L" | "M" | "N" | "O" | "P" | "Q" | "R" | "S" | "T" | 72 * "U" | "V" | "W" | "X" | "Y" | "Z" 73 */ 74 #define IS_UPALPHA(x) (((x) >= 'A') && ((x) <= 'Z')) 75 76 #ifdef IS_DIGIT 77 #undef IS_DIGIT 78 #endif 79 /* 80 * digit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" 81 */ 82 #define IS_DIGIT(x) (((x) >= '0') && ((x) <= '9')) 83 84 /* 85 * alphanum = alpha | digit 86 */ 87 88 #define IS_ALPHANUM(x) (IS_ALPHA(x) || IS_DIGIT(x)) 89 90 /* 91 * mark = "-" | "_" | "." | "!" | "~" | "*" | "'" | "(" | ")" 92 */ 93 94 #define IS_MARK(x) (((x) == '-') || ((x) == '_') || ((x) == '.') || \ 95 ((x) == '!') || ((x) == '~') || ((x) == '*') || ((x) == '\'') || \ 96 ((x) == '(') || ((x) == ')')) 97 98 /* 99 * unwise = "{" | "}" | "|" | "\" | "^" | "`" 100 */ 101 102 #define IS_UNWISE(p) \ 103 (((*(p) == '{')) || ((*(p) == '}')) || ((*(p) == '|')) || \ 104 ((*(p) == '\\')) || ((*(p) == '^')) || ((*(p) == '[')) || \ 105 ((*(p) == ']')) || ((*(p) == '`'))) 106 /* 107 * reserved = ";" | "/" | "?" | ":" | "@" | "&" | "=" | "+" | "$" | "," | 108 * "[" | "]" 109 */ 110 111 #define IS_RESERVED(x) (((x) == ';') || ((x) == '/') || ((x) == '?') || \ 112 ((x) == ':') || ((x) == '@') || ((x) == '&') || ((x) == '=') || \ 113 ((x) == '+') || ((x) == '$') || ((x) == ',') || ((x) == '[') || \ 114 ((x) == ']')) 115 116 /* 117 * unreserved = alphanum | mark 118 */ 119 120 #define IS_UNRESERVED(x) (IS_ALPHANUM(x) || IS_MARK(x)) 121 122 /* 123 * Skip to next pointer char, handle escaped sequences 124 */ 125 126 #define NEXT(p) ((*p == '%')? p += 3 : p++) 127 128 /* 129 * Productions from the spec. 130 * 131 * authority = server | reg_name 132 * reg_name = 1*( unreserved | escaped | "$" | "," | 133 * ";" | ":" | "@" | "&" | "=" | "+" ) 134 * 135 * path = [ abs_path | opaque_part ] 136 */ 137 138 #define STRNDUP(s, n) (char *) xmlStrndup((const xmlChar *)(s), (n)) 139 140 /************************************************************************ 141 * * 142 * RFC 3986 parser * 143 * * 144 ************************************************************************/ 145 146 #define ISA_DIGIT(p) ((*(p) >= '0') && (*(p) <= '9')) 147 #define ISA_ALPHA(p) (((*(p) >= 'a') && (*(p) <= 'z')) || \ 148 ((*(p) >= 'A') && (*(p) <= 'Z'))) 149 #define ISA_HEXDIG(p) \ 150 (ISA_DIGIT(p) || ((*(p) >= 'a') && (*(p) <= 'f')) || \ 151 ((*(p) >= 'A') && (*(p) <= 'F'))) 152 153 /* 154 * sub-delims = "!" / "$" / "&" / "'" / "(" / ")" 155 * / "*" / "+" / "," / ";" / "=" 156 */ 157 #define ISA_SUB_DELIM(p) \ 158 (((*(p) == '!')) || ((*(p) == '$')) || ((*(p) == '&')) || \ 159 ((*(p) == '(')) || ((*(p) == ')')) || ((*(p) == '*')) || \ 160 ((*(p) == '+')) || ((*(p) == ',')) || ((*(p) == ';')) || \ 161 ((*(p) == '=')) || ((*(p) == '\''))) 162 163 /* 164 * gen-delims = ":" / "/" / "?" / "#" / "[" / "]" / "@" 165 */ 166 #define ISA_GEN_DELIM(p) \ 167 (((*(p) == ':')) || ((*(p) == '/')) || ((*(p) == '?')) || \ 168 ((*(p) == '#')) || ((*(p) == '[')) || ((*(p) == ']')) || \ 169 ((*(p) == '@'))) 170 171 /* 172 * reserved = gen-delims / sub-delims 173 */ 174 #define ISA_RESERVED(p) (ISA_GEN_DELIM(p) || (ISA_SUB_DELIM(p))) 175 176 /* 177 * unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~" 178 */ 179 #define ISA_UNRESERVED(p) \ 180 ((ISA_ALPHA(p)) || (ISA_DIGIT(p)) || ((*(p) == '-')) || \ 181 ((*(p) == '.')) || ((*(p) == '_')) || ((*(p) == '~'))) 182 183 /* 184 * pct-encoded = "%" HEXDIG HEXDIG 185 */ 186 #define ISA_PCT_ENCODED(p) \ 187 ((*(p) == '%') && (ISA_HEXDIG(p + 1)) && (ISA_HEXDIG(p + 2))) 188 189 /* 190 * pchar = unreserved / pct-encoded / sub-delims / ":" / "@" 191 */ 192 #define ISA_PCHAR(p) \ 193 (ISA_UNRESERVED(p) || ISA_PCT_ENCODED(p) || ISA_SUB_DELIM(p) || \ 194 ((*(p) == ':')) || ((*(p) == '@'))) 195 196 /** 197 * xmlParse3986Scheme: 198 * @uri: pointer to an URI structure 199 * @str: pointer to the string to analyze 200 * 201 * Parse an URI scheme 202 * 203 * ALPHA *( ALPHA / DIGIT / "+" / "-" / "." ) 204 * 205 * Returns 0 or the error code 206 */ 207 static int 208 xmlParse3986Scheme(xmlURIPtr uri, const char **str) { 209 const char *cur; 210 211 if (str == NULL) 212 return(-1); 213 214 cur = *str; 215 if (!ISA_ALPHA(cur)) 216 return(2); 217 cur++; 218 while (ISA_ALPHA(cur) || ISA_DIGIT(cur) || 219 (*cur == '+') || (*cur == '-') || (*cur == '.')) cur++; 220 if (uri != NULL) { 221 if (uri->scheme != NULL) xmlFree(uri->scheme); 222 uri->scheme = STRNDUP(*str, cur - *str); 223 } 224 *str = cur; 225 return(0); 226 } 227 228 /** 229 * xmlParse3986Fragment: 230 * @uri: pointer to an URI structure 231 * @str: pointer to the string to analyze 232 * 233 * Parse the query part of an URI 234 * 235 * fragment = *( pchar / "/" / "?" ) 236 * NOTE: the strict syntax as defined by 3986 does not allow '[' and ']' 237 * in the fragment identifier but this is used very broadly for 238 * xpointer scheme selection, so we are allowing it here to not break 239 * for example all the DocBook processing chains. 240 * 241 * Returns 0 or the error code 242 */ 243 static int 244 xmlParse3986Fragment(xmlURIPtr uri, const char **str) 245 { 246 const char *cur; 247 248 if (str == NULL) 249 return (-1); 250 251 cur = *str; 252 253 while ((ISA_PCHAR(cur)) || (*cur == '/') || (*cur == '?') || 254 (*cur == '[') || (*cur == ']') || 255 ((uri != NULL) && (uri->cleanup & 1) && (IS_UNWISE(cur)))) 256 NEXT(cur); 257 if (uri != NULL) { 258 if (uri->fragment != NULL) 259 xmlFree(uri->fragment); 260 if (uri->cleanup & 2) 261 uri->fragment = STRNDUP(*str, cur - *str); 262 else 263 uri->fragment = xmlURIUnescapeString(*str, cur - *str, NULL); 264 } 265 *str = cur; 266 return (0); 267 } 268 269 /** 270 * xmlParse3986Query: 271 * @uri: pointer to an URI structure 272 * @str: pointer to the string to analyze 273 * 274 * Parse the query part of an URI 275 * 276 * query = *uric 277 * 278 * Returns 0 or the error code 279 */ 280 static int 281 xmlParse3986Query(xmlURIPtr uri, const char **str) 282 { 283 const char *cur; 284 285 if (str == NULL) 286 return (-1); 287 288 cur = *str; 289 290 while ((ISA_PCHAR(cur)) || (*cur == '/') || (*cur == '?') || 291 ((uri != NULL) && (uri->cleanup & 1) && (IS_UNWISE(cur)))) 292 NEXT(cur); 293 if (uri != NULL) { 294 if (uri->query != NULL) 295 xmlFree(uri->query); 296 if (uri->cleanup & 2) 297 uri->query = STRNDUP(*str, cur - *str); 298 else 299 uri->query = xmlURIUnescapeString(*str, cur - *str, NULL); 300 301 /* Save the raw bytes of the query as well. 302 * See: http://mail.gnome.org/archives/xml/2007-April/thread.html#00114 303 */ 304 if (uri->query_raw != NULL) 305 xmlFree (uri->query_raw); 306 uri->query_raw = STRNDUP (*str, cur - *str); 307 } 308 *str = cur; 309 return (0); 310 } 311 312 /** 313 * xmlParse3986Port: 314 * @uri: pointer to an URI structure 315 * @str: the string to analyze 316 * 317 * Parse a port part and fills in the appropriate fields 318 * of the @uri structure 319 * 320 * port = *DIGIT 321 * 322 * Returns 0 or the error code 323 */ 324 static int 325 xmlParse3986Port(xmlURIPtr uri, const char **str) 326 { 327 const char *cur = *str; 328 329 if (ISA_DIGIT(cur)) { 330 if (uri != NULL) 331 uri->port = 0; 332 while (ISA_DIGIT(cur)) { 333 if (uri != NULL) 334 uri->port = uri->port * 10 + (*cur - '0'); 335 cur++; 336 } 337 *str = cur; 338 return(0); 339 } 340 return(1); 341 } 342 343 /** 344 * xmlParse3986Userinfo: 345 * @uri: pointer to an URI structure 346 * @str: the string to analyze 347 * 348 * Parse an user informations part and fills in the appropriate fields 349 * of the @uri structure 350 * 351 * userinfo = *( unreserved / pct-encoded / sub-delims / ":" ) 352 * 353 * Returns 0 or the error code 354 */ 355 static int 356 xmlParse3986Userinfo(xmlURIPtr uri, const char **str) 357 { 358 const char *cur; 359 360 cur = *str; 361 while (ISA_UNRESERVED(cur) || ISA_PCT_ENCODED(cur) || 362 ISA_SUB_DELIM(cur) || (*cur == ':')) 363 NEXT(cur); 364 if (*cur == '@') { 365 if (uri != NULL) { 366 if (uri->user != NULL) xmlFree(uri->user); 367 if (uri->cleanup & 2) 368 uri->user = STRNDUP(*str, cur - *str); 369 else 370 uri->user = xmlURIUnescapeString(*str, cur - *str, NULL); 371 } 372 *str = cur; 373 return(0); 374 } 375 return(1); 376 } 377 378 /** 379 * xmlParse3986DecOctet: 380 * @str: the string to analyze 381 * 382 * dec-octet = DIGIT ; 0-9 383 * / %x31-39 DIGIT ; 10-99 384 * / "1" 2DIGIT ; 100-199 385 * / "2" %x30-34 DIGIT ; 200-249 386 * / "25" %x30-35 ; 250-255 387 * 388 * Skip a dec-octet. 389 * 390 * Returns 0 if found and skipped, 1 otherwise 391 */ 392 static int 393 xmlParse3986DecOctet(const char **str) { 394 const char *cur = *str; 395 396 if (!(ISA_DIGIT(cur))) 397 return(1); 398 if (!ISA_DIGIT(cur+1)) 399 cur++; 400 else if ((*cur != '0') && (ISA_DIGIT(cur + 1)) && (!ISA_DIGIT(cur+2))) 401 cur += 2; 402 else if ((*cur == '1') && (ISA_DIGIT(cur + 1)) && (ISA_DIGIT(cur + 2))) 403 cur += 3; 404 else if ((*cur == '2') && (*(cur + 1) >= '0') && 405 (*(cur + 1) <= '4') && (ISA_DIGIT(cur + 2))) 406 cur += 3; 407 else if ((*cur == '2') && (*(cur + 1) == '5') && 408 (*(cur + 2) >= '0') && (*(cur + 1) <= '5')) 409 cur += 3; 410 else 411 return(1); 412 *str = cur; 413 return(0); 414 } 415 /** 416 * xmlParse3986Host: 417 * @uri: pointer to an URI structure 418 * @str: the string to analyze 419 * 420 * Parse an host part and fills in the appropriate fields 421 * of the @uri structure 422 * 423 * host = IP-literal / IPv4address / reg-name 424 * IP-literal = "[" ( IPv6address / IPvFuture ) "]" 425 * IPv4address = dec-octet "." dec-octet "." dec-octet "." dec-octet 426 * reg-name = *( unreserved / pct-encoded / sub-delims ) 427 * 428 * Returns 0 or the error code 429 */ 430 static int 431 xmlParse3986Host(xmlURIPtr uri, const char **str) 432 { 433 const char *cur = *str; 434 const char *host; 435 436 host = cur; 437 /* 438 * IPv6 and future adressing scheme are enclosed between brackets 439 */ 440 if (*cur == '[') { 441 cur++; 442 while ((*cur != ']') && (*cur != 0)) 443 cur++; 444 if (*cur != ']') 445 return(1); 446 cur++; 447 goto found; 448 } 449 /* 450 * try to parse an IPv4 451 */ 452 if (ISA_DIGIT(cur)) { 453 if (xmlParse3986DecOctet(&cur) != 0) 454 goto not_ipv4; 455 if (*cur != '.') 456 goto not_ipv4; 457 cur++; 458 if (xmlParse3986DecOctet(&cur) != 0) 459 goto not_ipv4; 460 if (*cur != '.') 461 goto not_ipv4; 462 if (xmlParse3986DecOctet(&cur) != 0) 463 goto not_ipv4; 464 if (*cur != '.') 465 goto not_ipv4; 466 if (xmlParse3986DecOctet(&cur) != 0) 467 goto not_ipv4; 468 goto found; 469 not_ipv4: 470 cur = *str; 471 } 472 /* 473 * then this should be a hostname which can be empty 474 */ 475 while (ISA_UNRESERVED(cur) || ISA_PCT_ENCODED(cur) || ISA_SUB_DELIM(cur)) 476 NEXT(cur); 477 found: 478 if (uri != NULL) { 479 if (uri->authority != NULL) xmlFree(uri->authority); 480 uri->authority = NULL; 481 if (uri->server != NULL) xmlFree(uri->server); 482 if (cur != host) { 483 if (uri->cleanup & 2) 484 uri->server = STRNDUP(host, cur - host); 485 else 486 uri->server = xmlURIUnescapeString(host, cur - host, NULL); 487 } else 488 uri->server = NULL; 489 } 490 *str = cur; 491 return(0); 492 } 493 494 /** 495 * xmlParse3986Authority: 496 * @uri: pointer to an URI structure 497 * @str: the string to analyze 498 * 499 * Parse an authority part and fills in the appropriate fields 500 * of the @uri structure 501 * 502 * authority = [ userinfo "@" ] host [ ":" port ] 503 * 504 * Returns 0 or the error code 505 */ 506 static int 507 xmlParse3986Authority(xmlURIPtr uri, const char **str) 508 { 509 const char *cur; 510 int ret; 511 512 cur = *str; 513 /* 514 * try to parse an userinfo and check for the trailing @ 515 */ 516 ret = xmlParse3986Userinfo(uri, &cur); 517 if ((ret != 0) || (*cur != '@')) 518 cur = *str; 519 else 520 cur++; 521 ret = xmlParse3986Host(uri, &cur); 522 if (ret != 0) return(ret); 523 if (*cur == ':') { 524 cur++; 525 ret = xmlParse3986Port(uri, &cur); 526 if (ret != 0) return(ret); 527 } 528 *str = cur; 529 return(0); 530 } 531 532 /** 533 * xmlParse3986Segment: 534 * @str: the string to analyze 535 * @forbid: an optional forbidden character 536 * @empty: allow an empty segment 537 * 538 * Parse a segment and fills in the appropriate fields 539 * of the @uri structure 540 * 541 * segment = *pchar 542 * segment-nz = 1*pchar 543 * segment-nz-nc = 1*( unreserved / pct-encoded / sub-delims / "@" ) 544 * ; non-zero-length segment without any colon ":" 545 * 546 * Returns 0 or the error code 547 */ 548 static int 549 xmlParse3986Segment(const char **str, char forbid, int empty) 550 { 551 const char *cur; 552 553 cur = *str; 554 if (!ISA_PCHAR(cur)) { 555 if (empty) 556 return(0); 557 return(1); 558 } 559 while (ISA_PCHAR(cur) && (*cur != forbid)) 560 NEXT(cur); 561 *str = cur; 562 return (0); 563 } 564 565 /** 566 * xmlParse3986PathAbEmpty: 567 * @uri: pointer to an URI structure 568 * @str: the string to analyze 569 * 570 * Parse an path absolute or empty and fills in the appropriate fields 571 * of the @uri structure 572 * 573 * path-abempty = *( "/" segment ) 574 * 575 * Returns 0 or the error code 576 */ 577 static int 578 xmlParse3986PathAbEmpty(xmlURIPtr uri, const char **str) 579 { 580 const char *cur; 581 int ret; 582 583 cur = *str; 584 585 while (*cur == '/') { 586 cur++; 587 ret = xmlParse3986Segment(&cur, 0, 1); 588 if (ret != 0) return(ret); 589 } 590 if (uri != NULL) { 591 if (uri->path != NULL) xmlFree(uri->path); 592 if (*str != cur) { 593 if (uri->cleanup & 2) 594 uri->path = STRNDUP(*str, cur - *str); 595 else 596 uri->path = xmlURIUnescapeString(*str, cur - *str, NULL); 597 } else { 598 uri->path = NULL; 599 } 600 } 601 *str = cur; 602 return (0); 603 } 604 605 /** 606 * xmlParse3986PathAbsolute: 607 * @uri: pointer to an URI structure 608 * @str: the string to analyze 609 * 610 * Parse an path absolute and fills in the appropriate fields 611 * of the @uri structure 612 * 613 * path-absolute = "/" [ segment-nz *( "/" segment ) ] 614 * 615 * Returns 0 or the error code 616 */ 617 static int 618 xmlParse3986PathAbsolute(xmlURIPtr uri, const char **str) 619 { 620 const char *cur; 621 int ret; 622 623 cur = *str; 624 625 if (*cur != '/') 626 return(1); 627 cur++; 628 ret = xmlParse3986Segment(&cur, 0, 0); 629 if (ret == 0) { 630 while (*cur == '/') { 631 cur++; 632 ret = xmlParse3986Segment(&cur, 0, 1); 633 if (ret != 0) return(ret); 634 } 635 } 636 if (uri != NULL) { 637 if (uri->path != NULL) xmlFree(uri->path); 638 if (cur != *str) { 639 if (uri->cleanup & 2) 640 uri->path = STRNDUP(*str, cur - *str); 641 else 642 uri->path = xmlURIUnescapeString(*str, cur - *str, NULL); 643 } else { 644 uri->path = NULL; 645 } 646 } 647 *str = cur; 648 return (0); 649 } 650 651 /** 652 * xmlParse3986PathRootless: 653 * @uri: pointer to an URI structure 654 * @str: the string to analyze 655 * 656 * Parse an path without root and fills in the appropriate fields 657 * of the @uri structure 658 * 659 * path-rootless = segment-nz *( "/" segment ) 660 * 661 * Returns 0 or the error code 662 */ 663 static int 664 xmlParse3986PathRootless(xmlURIPtr uri, const char **str) 665 { 666 const char *cur; 667 int ret; 668 669 cur = *str; 670 671 ret = xmlParse3986Segment(&cur, 0, 0); 672 if (ret != 0) return(ret); 673 while (*cur == '/') { 674 cur++; 675 ret = xmlParse3986Segment(&cur, 0, 1); 676 if (ret != 0) return(ret); 677 } 678 if (uri != NULL) { 679 if (uri->path != NULL) xmlFree(uri->path); 680 if (cur != *str) { 681 if (uri->cleanup & 2) 682 uri->path = STRNDUP(*str, cur - *str); 683 else 684 uri->path = xmlURIUnescapeString(*str, cur - *str, NULL); 685 } else { 686 uri->path = NULL; 687 } 688 } 689 *str = cur; 690 return (0); 691 } 692 693 /** 694 * xmlParse3986PathNoScheme: 695 * @uri: pointer to an URI structure 696 * @str: the string to analyze 697 * 698 * Parse an path which is not a scheme and fills in the appropriate fields 699 * of the @uri structure 700 * 701 * path-noscheme = segment-nz-nc *( "/" segment ) 702 * 703 * Returns 0 or the error code 704 */ 705 static int 706 xmlParse3986PathNoScheme(xmlURIPtr uri, const char **str) 707 { 708 const char *cur; 709 int ret; 710 711 cur = *str; 712 713 ret = xmlParse3986Segment(&cur, ':', 0); 714 if (ret != 0) return(ret); 715 while (*cur == '/') { 716 cur++; 717 ret = xmlParse3986Segment(&cur, 0, 1); 718 if (ret != 0) return(ret); 719 } 720 if (uri != NULL) { 721 if (uri->path != NULL) xmlFree(uri->path); 722 if (cur != *str) { 723 if (uri->cleanup & 2) 724 uri->path = STRNDUP(*str, cur - *str); 725 else 726 uri->path = xmlURIUnescapeString(*str, cur - *str, NULL); 727 } else { 728 uri->path = NULL; 729 } 730 } 731 *str = cur; 732 return (0); 733 } 734 735 /** 736 * xmlParse3986HierPart: 737 * @uri: pointer to an URI structure 738 * @str: the string to analyze 739 * 740 * Parse an hierarchical part and fills in the appropriate fields 741 * of the @uri structure 742 * 743 * hier-part = "//" authority path-abempty 744 * / path-absolute 745 * / path-rootless 746 * / path-empty 747 * 748 * Returns 0 or the error code 749 */ 750 static int 751 xmlParse3986HierPart(xmlURIPtr uri, const char **str) 752 { 753 const char *cur; 754 int ret; 755 756 cur = *str; 757 758 if ((*cur == '/') && (*(cur + 1) == '/')) { 759 cur += 2; 760 ret = xmlParse3986Authority(uri, &cur); 761 if (ret != 0) return(ret); 762 ret = xmlParse3986PathAbEmpty(uri, &cur); 763 if (ret != 0) return(ret); 764 *str = cur; 765 return(0); 766 } else if (*cur == '/') { 767 ret = xmlParse3986PathAbsolute(uri, &cur); 768 if (ret != 0) return(ret); 769 } else if (ISA_PCHAR(cur)) { 770 ret = xmlParse3986PathRootless(uri, &cur); 771 if (ret != 0) return(ret); 772 } else { 773 /* path-empty is effectively empty */ 774 if (uri != NULL) { 775 if (uri->path != NULL) xmlFree(uri->path); 776 uri->path = NULL; 777 } 778 } 779 *str = cur; 780 return (0); 781 } 782 783 /** 784 * xmlParse3986RelativeRef: 785 * @uri: pointer to an URI structure 786 * @str: the string to analyze 787 * 788 * Parse an URI string and fills in the appropriate fields 789 * of the @uri structure 790 * 791 * relative-ref = relative-part [ "?" query ] [ "#" fragment ] 792 * relative-part = "//" authority path-abempty 793 * / path-absolute 794 * / path-noscheme 795 * / path-empty 796 * 797 * Returns 0 or the error code 798 */ 799 static int 800 xmlParse3986RelativeRef(xmlURIPtr uri, const char *str) { 801 int ret; 802 803 if ((*str == '/') && (*(str + 1) == '/')) { 804 str += 2; 805 ret = xmlParse3986Authority(uri, &str); 806 if (ret != 0) return(ret); 807 ret = xmlParse3986PathAbEmpty(uri, &str); 808 if (ret != 0) return(ret); 809 } else if (*str == '/') { 810 ret = xmlParse3986PathAbsolute(uri, &str); 811 if (ret != 0) return(ret); 812 } else if (ISA_PCHAR(str)) { 813 ret = xmlParse3986PathNoScheme(uri, &str); 814 if (ret != 0) return(ret); 815 } else { 816 /* path-empty is effectively empty */ 817 if (uri != NULL) { 818 if (uri->path != NULL) xmlFree(uri->path); 819 uri->path = NULL; 820 } 821 } 822 823 if (*str == '?') { 824 str++; 825 ret = xmlParse3986Query(uri, &str); 826 if (ret != 0) return(ret); 827 } 828 if (*str == '#') { 829 str++; 830 ret = xmlParse3986Fragment(uri, &str); 831 if (ret != 0) return(ret); 832 } 833 if (*str != 0) { 834 xmlCleanURI(uri); 835 return(1); 836 } 837 return(0); 838 } 839 840 841 /** 842 * xmlParse3986URI: 843 * @uri: pointer to an URI structure 844 * @str: the string to analyze 845 * 846 * Parse an URI string and fills in the appropriate fields 847 * of the @uri structure 848 * 849 * scheme ":" hier-part [ "?" query ] [ "#" fragment ] 850 * 851 * Returns 0 or the error code 852 */ 853 static int 854 xmlParse3986URI(xmlURIPtr uri, const char *str) { 855 int ret; 856 857 ret = xmlParse3986Scheme(uri, &str); 858 if (ret != 0) return(ret); 859 if (*str != ':') { 860 return(1); 861 } 862 str++; 863 ret = xmlParse3986HierPart(uri, &str); 864 if (ret != 0) return(ret); 865 if (*str == '?') { 866 str++; 867 ret = xmlParse3986Query(uri, &str); 868 if (ret != 0) return(ret); 869 } 870 if (*str == '#') { 871 str++; 872 ret = xmlParse3986Fragment(uri, &str); 873 if (ret != 0) return(ret); 874 } 875 if (*str != 0) { 876 xmlCleanURI(uri); 877 return(1); 878 } 879 return(0); 880 } 881 882 /** 883 * xmlParse3986URIReference: 884 * @uri: pointer to an URI structure 885 * @str: the string to analyze 886 * 887 * Parse an URI reference string and fills in the appropriate fields 888 * of the @uri structure 889 * 890 * URI-reference = URI / relative-ref 891 * 892 * Returns 0 or the error code 893 */ 894 static int 895 xmlParse3986URIReference(xmlURIPtr uri, const char *str) { 896 int ret; 897 898 if (str == NULL) 899 return(-1); 900 xmlCleanURI(uri); 901 902 /* 903 * Try first to parse absolute refs, then fallback to relative if 904 * it fails. 905 */ 906 ret = xmlParse3986URI(uri, str); 907 if (ret != 0) { 908 xmlCleanURI(uri); 909 ret = xmlParse3986RelativeRef(uri, str); 910 if (ret != 0) { 911 xmlCleanURI(uri); 912 return(ret); 913 } 914 } 915 return(0); 916 } 917 918 /** 919 * xmlParseURI: 920 * @str: the URI string to analyze 921 * 922 * Parse an URI based on RFC 3986 923 * 924 * URI-reference = [ absoluteURI | relativeURI ] [ "#" fragment ] 925 * 926 * Returns a newly built xmlURIPtr or NULL in case of error 927 */ 928 xmlURIPtr 929 xmlParseURI(const char *str) { 930 xmlURIPtr uri; 931 int ret; 932 933 if (str == NULL) 934 return(NULL); 935 uri = xmlCreateURI(); 936 if (uri != NULL) { 937 ret = xmlParse3986URIReference(uri, str); 938 if (ret) { 939 xmlFreeURI(uri); 940 return(NULL); 941 } 942 } 943 return(uri); 944 } 945 946 /** 947 * xmlParseURIReference: 948 * @uri: pointer to an URI structure 949 * @str: the string to analyze 950 * 951 * Parse an URI reference string based on RFC 3986 and fills in the 952 * appropriate fields of the @uri structure 953 * 954 * URI-reference = URI / relative-ref 955 * 956 * Returns 0 or the error code 957 */ 958 int 959 xmlParseURIReference(xmlURIPtr uri, const char *str) { 960 return(xmlParse3986URIReference(uri, str)); 961 } 962 963 /** 964 * xmlParseURIRaw: 965 * @str: the URI string to analyze 966 * @raw: if 1 unescaping of URI pieces are disabled 967 * 968 * Parse an URI but allows to keep intact the original fragments. 969 * 970 * URI-reference = URI / relative-ref 971 * 972 * Returns a newly built xmlURIPtr or NULL in case of error 973 */ 974 xmlURIPtr 975 xmlParseURIRaw(const char *str, int raw) { 976 xmlURIPtr uri; 977 int ret; 978 979 if (str == NULL) 980 return(NULL); 981 uri = xmlCreateURI(); 982 if (uri != NULL) { 983 if (raw) { 984 uri->cleanup |= 2; 985 } 986 ret = xmlParseURIReference(uri, str); 987 if (ret) { 988 xmlFreeURI(uri); 989 return(NULL); 990 } 991 } 992 return(uri); 993 } 994 995 /************************************************************************ 996 * * 997 * Generic URI structure functions * 998 * * 999 ************************************************************************/ 1000 1001 /** 1002 * xmlCreateURI: 1003 * 1004 * Simply creates an empty xmlURI 1005 * 1006 * Returns the new structure or NULL in case of error 1007 */ 1008 xmlURIPtr 1009 xmlCreateURI(void) { 1010 xmlURIPtr ret; 1011 1012 ret = (xmlURIPtr) xmlMalloc(sizeof(xmlURI)); 1013 if (ret == NULL) { 1014 xmlURIErrMemory("creating URI structure\n"); 1015 return(NULL); 1016 } 1017 memset(ret, 0, sizeof(xmlURI)); 1018 return(ret); 1019 } 1020 1021 /** 1022 * xmlSaveUriRealloc: 1023 * 1024 * Function to handle properly a reallocation when saving an URI 1025 * Also imposes some limit on the length of an URI string output 1026 */ 1027 static xmlChar * 1028 xmlSaveUriRealloc(xmlChar *ret, int *max) { 1029 xmlChar *temp; 1030 int tmp; 1031 1032 if (*max > MAX_URI_LENGTH) { 1033 xmlURIErrMemory("reaching arbitrary MAX_URI_LENGTH limit\n"); 1034 return(NULL); 1035 } 1036 tmp = *max * 2; 1037 temp = (xmlChar *) xmlRealloc(ret, (tmp + 1)); 1038 if (temp == NULL) { 1039 xmlURIErrMemory("saving URI\n"); 1040 return(NULL); 1041 } 1042 *max = tmp; 1043 return(temp); 1044 } 1045 1046 /** 1047 * xmlSaveUri: 1048 * @uri: pointer to an xmlURI 1049 * 1050 * Save the URI as an escaped string 1051 * 1052 * Returns a new string (to be deallocated by caller) 1053 */ 1054 xmlChar * 1055 xmlSaveUri(xmlURIPtr uri) { 1056 xmlChar *ret = NULL; 1057 xmlChar *temp; 1058 const char *p; 1059 int len; 1060 int max; 1061 1062 if (uri == NULL) return(NULL); 1063 1064 1065 max = 80; 1066 ret = (xmlChar *) xmlMallocAtomic((max + 1) * sizeof(xmlChar)); 1067 if (ret == NULL) { 1068 xmlURIErrMemory("saving URI\n"); 1069 return(NULL); 1070 } 1071 len = 0; 1072 1073 if (uri->scheme != NULL) { 1074 p = uri->scheme; 1075 while (*p != 0) { 1076 if (len >= max) { 1077 temp = xmlSaveUriRealloc(ret, &max); 1078 if (temp == NULL) goto mem_error; 1079 ret = temp; 1080 } 1081 ret[len++] = *p++; 1082 } 1083 if (len >= max) { 1084 temp = xmlSaveUriRealloc(ret, &max); 1085 if (temp == NULL) goto mem_error; 1086 ret = temp; 1087 } 1088 ret[len++] = ':'; 1089 } 1090 if (uri->opaque != NULL) { 1091 p = uri->opaque; 1092 while (*p != 0) { 1093 if (len + 3 >= max) { 1094 temp = xmlSaveUriRealloc(ret, &max); 1095 if (temp == NULL) goto mem_error; 1096 ret = temp; 1097 } 1098 if (IS_RESERVED(*(p)) || IS_UNRESERVED(*(p))) 1099 ret[len++] = *p++; 1100 else { 1101 int val = *(unsigned char *)p++; 1102 int hi = val / 0x10, lo = val % 0x10; 1103 ret[len++] = '%'; 1104 ret[len++] = hi + (hi > 9? 'A'-10 : '0'); 1105 ret[len++] = lo + (lo > 9? 'A'-10 : '0'); 1106 } 1107 } 1108 } else { 1109 if (uri->server != NULL) { 1110 if (len + 3 >= max) { 1111 temp = xmlSaveUriRealloc(ret, &max); 1112 if (temp == NULL) goto mem_error; 1113 ret = temp; 1114 } 1115 ret[len++] = '/'; 1116 ret[len++] = '/'; 1117 if (uri->user != NULL) { 1118 p = uri->user; 1119 while (*p != 0) { 1120 if (len + 3 >= max) { 1121 temp = xmlSaveUriRealloc(ret, &max); 1122 if (temp == NULL) goto mem_error; 1123 ret = temp; 1124 } 1125 if ((IS_UNRESERVED(*(p))) || 1126 ((*(p) == ';')) || ((*(p) == ':')) || 1127 ((*(p) == '&')) || ((*(p) == '=')) || 1128 ((*(p) == '+')) || ((*(p) == '$')) || 1129 ((*(p) == ','))) 1130 ret[len++] = *p++; 1131 else { 1132 int val = *(unsigned char *)p++; 1133 int hi = val / 0x10, lo = val % 0x10; 1134 ret[len++] = '%'; 1135 ret[len++] = hi + (hi > 9? 'A'-10 : '0'); 1136 ret[len++] = lo + (lo > 9? 'A'-10 : '0'); 1137 } 1138 } 1139 if (len + 3 >= max) { 1140 temp = xmlSaveUriRealloc(ret, &max); 1141 if (temp == NULL) goto mem_error; 1142 ret = temp; 1143 } 1144 ret[len++] = '@'; 1145 } 1146 p = uri->server; 1147 while (*p != 0) { 1148 if (len >= max) { 1149 temp = xmlSaveUriRealloc(ret, &max); 1150 if (temp == NULL) goto mem_error; 1151 ret = temp; 1152 } 1153 ret[len++] = *p++; 1154 } 1155 if (uri->port > 0) { 1156 if (len + 10 >= max) { 1157 temp = xmlSaveUriRealloc(ret, &max); 1158 if (temp == NULL) goto mem_error; 1159 ret = temp; 1160 } 1161 len += snprintf((char *) &ret[len], max - len, ":%d", uri->port); 1162 } 1163 } else if (uri->authority != NULL) { 1164 if (len + 3 >= max) { 1165 temp = xmlSaveUriRealloc(ret, &max); 1166 if (temp == NULL) goto mem_error; 1167 ret = temp; 1168 } 1169 ret[len++] = '/'; 1170 ret[len++] = '/'; 1171 p = uri->authority; 1172 while (*p != 0) { 1173 if (len + 3 >= max) { 1174 temp = xmlSaveUriRealloc(ret, &max); 1175 if (temp == NULL) goto mem_error; 1176 ret = temp; 1177 } 1178 if ((IS_UNRESERVED(*(p))) || 1179 ((*(p) == '$')) || ((*(p) == ',')) || ((*(p) == ';')) || 1180 ((*(p) == ':')) || ((*(p) == '@')) || ((*(p) == '&')) || 1181 ((*(p) == '=')) || ((*(p) == '+'))) 1182 ret[len++] = *p++; 1183 else { 1184 int val = *(unsigned char *)p++; 1185 int hi = val / 0x10, lo = val % 0x10; 1186 ret[len++] = '%'; 1187 ret[len++] = hi + (hi > 9? 'A'-10 : '0'); 1188 ret[len++] = lo + (lo > 9? 'A'-10 : '0'); 1189 } 1190 } 1191 } else if (uri->scheme != NULL) { 1192 if (len + 3 >= max) { 1193 temp = xmlSaveUriRealloc(ret, &max); 1194 if (temp == NULL) goto mem_error; 1195 ret = temp; 1196 } 1197 ret[len++] = '/'; 1198 ret[len++] = '/'; 1199 } 1200 if (uri->path != NULL) { 1201 p = uri->path; 1202 /* 1203 * the colon in file:///d: should not be escaped or 1204 * Windows accesses fail later. 1205 */ 1206 if ((uri->scheme != NULL) && 1207 (p[0] == '/') && 1208 (((p[1] >= 'a') && (p[1] <= 'z')) || 1209 ((p[1] >= 'A') && (p[1] <= 'Z'))) && 1210 (p[2] == ':') && 1211 (xmlStrEqual(BAD_CAST uri->scheme, BAD_CAST "file"))) { 1212 if (len + 3 >= max) { 1213 temp = xmlSaveUriRealloc(ret, &max); 1214 if (temp == NULL) goto mem_error; 1215 ret = temp; 1216 } 1217 ret[len++] = *p++; 1218 ret[len++] = *p++; 1219 ret[len++] = *p++; 1220 } 1221 while (*p != 0) { 1222 if (len + 3 >= max) { 1223 temp = xmlSaveUriRealloc(ret, &max); 1224 if (temp == NULL) goto mem_error; 1225 ret = temp; 1226 } 1227 if ((IS_UNRESERVED(*(p))) || ((*(p) == '/')) || 1228 ((*(p) == ';')) || ((*(p) == '@')) || ((*(p) == '&')) || 1229 ((*(p) == '=')) || ((*(p) == '+')) || ((*(p) == '$')) || 1230 ((*(p) == ','))) 1231 ret[len++] = *p++; 1232 else { 1233 int val = *(unsigned char *)p++; 1234 int hi = val / 0x10, lo = val % 0x10; 1235 ret[len++] = '%'; 1236 ret[len++] = hi + (hi > 9? 'A'-10 : '0'); 1237 ret[len++] = lo + (lo > 9? 'A'-10 : '0'); 1238 } 1239 } 1240 } 1241 if (uri->query_raw != NULL) { 1242 if (len + 1 >= max) { 1243 temp = xmlSaveUriRealloc(ret, &max); 1244 if (temp == NULL) goto mem_error; 1245 ret = temp; 1246 } 1247 ret[len++] = '?'; 1248 p = uri->query_raw; 1249 while (*p != 0) { 1250 if (len + 1 >= max) { 1251 temp = xmlSaveUriRealloc(ret, &max); 1252 if (temp == NULL) goto mem_error; 1253 ret = temp; 1254 } 1255 ret[len++] = *p++; 1256 } 1257 } else if (uri->query != NULL) { 1258 if (len + 3 >= max) { 1259 temp = xmlSaveUriRealloc(ret, &max); 1260 if (temp == NULL) goto mem_error; 1261 ret = temp; 1262 } 1263 ret[len++] = '?'; 1264 p = uri->query; 1265 while (*p != 0) { 1266 if (len + 3 >= max) { 1267 temp = xmlSaveUriRealloc(ret, &max); 1268 if (temp == NULL) goto mem_error; 1269 ret = temp; 1270 } 1271 if ((IS_UNRESERVED(*(p))) || (IS_RESERVED(*(p)))) 1272 ret[len++] = *p++; 1273 else { 1274 int val = *(unsigned char *)p++; 1275 int hi = val / 0x10, lo = val % 0x10; 1276 ret[len++] = '%'; 1277 ret[len++] = hi + (hi > 9? 'A'-10 : '0'); 1278 ret[len++] = lo + (lo > 9? 'A'-10 : '0'); 1279 } 1280 } 1281 } 1282 } 1283 if (uri->fragment != NULL) { 1284 if (len + 3 >= max) { 1285 temp = xmlSaveUriRealloc(ret, &max); 1286 if (temp == NULL) goto mem_error; 1287 ret = temp; 1288 } 1289 ret[len++] = '#'; 1290 p = uri->fragment; 1291 while (*p != 0) { 1292 if (len + 3 >= max) { 1293 temp = xmlSaveUriRealloc(ret, &max); 1294 if (temp == NULL) goto mem_error; 1295 ret = temp; 1296 } 1297 if ((IS_UNRESERVED(*(p))) || (IS_RESERVED(*(p)))) 1298 ret[len++] = *p++; 1299 else { 1300 int val = *(unsigned char *)p++; 1301 int hi = val / 0x10, lo = val % 0x10; 1302 ret[len++] = '%'; 1303 ret[len++] = hi + (hi > 9? 'A'-10 : '0'); 1304 ret[len++] = lo + (lo > 9? 'A'-10 : '0'); 1305 } 1306 } 1307 } 1308 if (len >= max) { 1309 temp = xmlSaveUriRealloc(ret, &max); 1310 if (temp == NULL) goto mem_error; 1311 ret = temp; 1312 } 1313 ret[len] = 0; 1314 return(ret); 1315 1316 mem_error: 1317 xmlFree(ret); 1318 return(NULL); 1319 } 1320 1321 /** 1322 * xmlPrintURI: 1323 * @stream: a FILE* for the output 1324 * @uri: pointer to an xmlURI 1325 * 1326 * Prints the URI in the stream @stream. 1327 */ 1328 void 1329 xmlPrintURI(FILE *stream, xmlURIPtr uri) { 1330 xmlChar *out; 1331 1332 out = xmlSaveUri(uri); 1333 if (out != NULL) { 1334 fprintf(stream, "%s", (char *) out); 1335 xmlFree(out); 1336 } 1337 } 1338 1339 /** 1340 * xmlCleanURI: 1341 * @uri: pointer to an xmlURI 1342 * 1343 * Make sure the xmlURI struct is free of content 1344 */ 1345 static void 1346 xmlCleanURI(xmlURIPtr uri) { 1347 if (uri == NULL) return; 1348 1349 if (uri->scheme != NULL) xmlFree(uri->scheme); 1350 uri->scheme = NULL; 1351 if (uri->server != NULL) xmlFree(uri->server); 1352 uri->server = NULL; 1353 if (uri->user != NULL) xmlFree(uri->user); 1354 uri->user = NULL; 1355 if (uri->path != NULL) xmlFree(uri->path); 1356 uri->path = NULL; 1357 if (uri->fragment != NULL) xmlFree(uri->fragment); 1358 uri->fragment = NULL; 1359 if (uri->opaque != NULL) xmlFree(uri->opaque); 1360 uri->opaque = NULL; 1361 if (uri->authority != NULL) xmlFree(uri->authority); 1362 uri->authority = NULL; 1363 if (uri->query != NULL) xmlFree(uri->query); 1364 uri->query = NULL; 1365 if (uri->query_raw != NULL) xmlFree(uri->query_raw); 1366 uri->query_raw = NULL; 1367 } 1368 1369 /** 1370 * xmlFreeURI: 1371 * @uri: pointer to an xmlURI 1372 * 1373 * Free up the xmlURI struct 1374 */ 1375 void 1376 xmlFreeURI(xmlURIPtr uri) { 1377 if (uri == NULL) return; 1378 1379 if (uri->scheme != NULL) xmlFree(uri->scheme); 1380 if (uri->server != NULL) xmlFree(uri->server); 1381 if (uri->user != NULL) xmlFree(uri->user); 1382 if (uri->path != NULL) xmlFree(uri->path); 1383 if (uri->fragment != NULL) xmlFree(uri->fragment); 1384 if (uri->opaque != NULL) xmlFree(uri->opaque); 1385 if (uri->authority != NULL) xmlFree(uri->authority); 1386 if (uri->query != NULL) xmlFree(uri->query); 1387 if (uri->query_raw != NULL) xmlFree(uri->query_raw); 1388 xmlFree(uri); 1389 } 1390 1391 /************************************************************************ 1392 * * 1393 * Helper functions * 1394 * * 1395 ************************************************************************/ 1396 1397 /** 1398 * xmlNormalizeURIPath: 1399 * @path: pointer to the path string 1400 * 1401 * Applies the 5 normalization steps to a path string--that is, RFC 2396 1402 * Section 5.2, steps 6.c through 6.g. 1403 * 1404 * Normalization occurs directly on the string, no new allocation is done 1405 * 1406 * Returns 0 or an error code 1407 */ 1408 int 1409 xmlNormalizeURIPath(char *path) { 1410 char *cur, *out; 1411 1412 if (path == NULL) 1413 return(-1); 1414 1415 /* Skip all initial "/" chars. We want to get to the beginning of the 1416 * first non-empty segment. 1417 */ 1418 cur = path; 1419 while (cur[0] == '/') 1420 ++cur; 1421 if (cur[0] == '\0') 1422 return(0); 1423 1424 /* Keep everything we've seen so far. */ 1425 out = cur; 1426 1427 /* 1428 * Analyze each segment in sequence for cases (c) and (d). 1429 */ 1430 while (cur[0] != '\0') { 1431 /* 1432 * c) All occurrences of "./", where "." is a complete path segment, 1433 * are removed from the buffer string. 1434 */ 1435 if ((cur[0] == '.') && (cur[1] == '/')) { 1436 cur += 2; 1437 /* '//' normalization should be done at this point too */ 1438 while (cur[0] == '/') 1439 cur++; 1440 continue; 1441 } 1442 1443 /* 1444 * d) If the buffer string ends with "." as a complete path segment, 1445 * that "." is removed. 1446 */ 1447 if ((cur[0] == '.') && (cur[1] == '\0')) 1448 break; 1449 1450 /* Otherwise keep the segment. */ 1451 while (cur[0] != '/') { 1452 if (cur[0] == '\0') 1453 goto done_cd; 1454 (out++)[0] = (cur++)[0]; 1455 } 1456 /* nomalize // */ 1457 while ((cur[0] == '/') && (cur[1] == '/')) 1458 cur++; 1459 1460 (out++)[0] = (cur++)[0]; 1461 } 1462 done_cd: 1463 out[0] = '\0'; 1464 1465 /* Reset to the beginning of the first segment for the next sequence. */ 1466 cur = path; 1467 while (cur[0] == '/') 1468 ++cur; 1469 if (cur[0] == '\0') 1470 return(0); 1471 1472 /* 1473 * Analyze each segment in sequence for cases (e) and (f). 1474 * 1475 * e) All occurrences of "<segment>/../", where <segment> is a 1476 * complete path segment not equal to "..", are removed from the 1477 * buffer string. Removal of these path segments is performed 1478 * iteratively, removing the leftmost matching pattern on each 1479 * iteration, until no matching pattern remains. 1480 * 1481 * f) If the buffer string ends with "<segment>/..", where <segment> 1482 * is a complete path segment not equal to "..", that 1483 * "<segment>/.." is removed. 1484 * 1485 * To satisfy the "iterative" clause in (e), we need to collapse the 1486 * string every time we find something that needs to be removed. Thus, 1487 * we don't need to keep two pointers into the string: we only need a 1488 * "current position" pointer. 1489 */ 1490 while (1) { 1491 char *segp, *tmp; 1492 1493 /* At the beginning of each iteration of this loop, "cur" points to 1494 * the first character of the segment we want to examine. 1495 */ 1496 1497 /* Find the end of the current segment. */ 1498 segp = cur; 1499 while ((segp[0] != '/') && (segp[0] != '\0')) 1500 ++segp; 1501 1502 /* If this is the last segment, we're done (we need at least two 1503 * segments to meet the criteria for the (e) and (f) cases). 1504 */ 1505 if (segp[0] == '\0') 1506 break; 1507 1508 /* If the first segment is "..", or if the next segment _isn't_ "..", 1509 * keep this segment and try the next one. 1510 */ 1511 ++segp; 1512 if (((cur[0] == '.') && (cur[1] == '.') && (segp == cur+3)) 1513 || ((segp[0] != '.') || (segp[1] != '.') 1514 || ((segp[2] != '/') && (segp[2] != '\0')))) { 1515 cur = segp; 1516 continue; 1517 } 1518 1519 /* If we get here, remove this segment and the next one and back up 1520 * to the previous segment (if there is one), to implement the 1521 * "iteratively" clause. It's pretty much impossible to back up 1522 * while maintaining two pointers into the buffer, so just compact 1523 * the whole buffer now. 1524 */ 1525 1526 /* If this is the end of the buffer, we're done. */ 1527 if (segp[2] == '\0') { 1528 cur[0] = '\0'; 1529 break; 1530 } 1531 /* Valgrind complained, strcpy(cur, segp + 3); */ 1532 /* string will overlap, do not use strcpy */ 1533 tmp = cur; 1534 segp += 3; 1535 while ((*tmp++ = *segp++) != 0) 1536 ; 1537 1538 /* If there are no previous segments, then keep going from here. */ 1539 segp = cur; 1540 while ((segp > path) && ((--segp)[0] == '/')) 1541 ; 1542 if (segp == path) 1543 continue; 1544 1545 /* "segp" is pointing to the end of a previous segment; find it's 1546 * start. We need to back up to the previous segment and start 1547 * over with that to handle things like "foo/bar/../..". If we 1548 * don't do this, then on the first pass we'll remove the "bar/..", 1549 * but be pointing at the second ".." so we won't realize we can also 1550 * remove the "foo/..". 1551 */ 1552 cur = segp; 1553 while ((cur > path) && (cur[-1] != '/')) 1554 --cur; 1555 } 1556 out[0] = '\0'; 1557 1558 /* 1559 * g) If the resulting buffer string still begins with one or more 1560 * complete path segments of "..", then the reference is 1561 * considered to be in error. Implementations may handle this 1562 * error by retaining these components in the resolved path (i.e., 1563 * treating them as part of the final URI), by removing them from 1564 * the resolved path (i.e., discarding relative levels above the 1565 * root), or by avoiding traversal of the reference. 1566 * 1567 * We discard them from the final path. 1568 */ 1569 if (path[0] == '/') { 1570 cur = path; 1571 while ((cur[0] == '/') && (cur[1] == '.') && (cur[2] == '.') 1572 && ((cur[3] == '/') || (cur[3] == '\0'))) 1573 cur += 3; 1574 1575 if (cur != path) { 1576 out = path; 1577 while (cur[0] != '\0') 1578 (out++)[0] = (cur++)[0]; 1579 out[0] = 0; 1580 } 1581 } 1582 1583 return(0); 1584 } 1585 1586 static int is_hex(char c) { 1587 if (((c >= '0') && (c <= '9')) || 1588 ((c >= 'a') && (c <= 'f')) || 1589 ((c >= 'A') && (c <= 'F'))) 1590 return(1); 1591 return(0); 1592 } 1593 1594 /** 1595 * xmlURIUnescapeString: 1596 * @str: the string to unescape 1597 * @len: the length in bytes to unescape (or <= 0 to indicate full string) 1598 * @target: optional destination buffer 1599 * 1600 * Unescaping routine, but does not check that the string is an URI. The 1601 * output is a direct unsigned char translation of %XX values (no encoding) 1602 * Note that the length of the result can only be smaller or same size as 1603 * the input string. 1604 * 1605 * Returns a copy of the string, but unescaped, will return NULL only in case 1606 * of error 1607 */ 1608 char * 1609 xmlURIUnescapeString(const char *str, int len, char *target) { 1610 char *ret, *out; 1611 const char *in; 1612 1613 if (str == NULL) 1614 return(NULL); 1615 if (len <= 0) len = strlen(str); 1616 if (len < 0) return(NULL); 1617 1618 if (target == NULL) { 1619 ret = (char *) xmlMallocAtomic(len + 1); 1620 if (ret == NULL) { 1621 xmlURIErrMemory("unescaping URI value\n"); 1622 return(NULL); 1623 } 1624 } else 1625 ret = target; 1626 in = str; 1627 out = ret; 1628 while(len > 0) { 1629 if ((len > 2) && (*in == '%') && (is_hex(in[1])) && (is_hex(in[2]))) { 1630 in++; 1631 if ((*in >= '0') && (*in <= '9')) 1632 *out = (*in - '0'); 1633 else if ((*in >= 'a') && (*in <= 'f')) 1634 *out = (*in - 'a') + 10; 1635 else if ((*in >= 'A') && (*in <= 'F')) 1636 *out = (*in - 'A') + 10; 1637 in++; 1638 if ((*in >= '0') && (*in <= '9')) 1639 *out = *out * 16 + (*in - '0'); 1640 else if ((*in >= 'a') && (*in <= 'f')) 1641 *out = *out * 16 + (*in - 'a') + 10; 1642 else if ((*in >= 'A') && (*in <= 'F')) 1643 *out = *out * 16 + (*in - 'A') + 10; 1644 in++; 1645 len -= 3; 1646 out++; 1647 } else { 1648 *out++ = *in++; 1649 len--; 1650 } 1651 } 1652 *out = 0; 1653 return(ret); 1654 } 1655 1656 /** 1657 * xmlURIEscapeStr: 1658 * @str: string to escape 1659 * @list: exception list string of chars not to escape 1660 * 1661 * This routine escapes a string to hex, ignoring reserved characters (a-z) 1662 * and the characters in the exception list. 1663 * 1664 * Returns a new escaped string or NULL in case of error. 1665 */ 1666 xmlChar * 1667 xmlURIEscapeStr(const xmlChar *str, const xmlChar *list) { 1668 xmlChar *ret, ch; 1669 xmlChar *temp; 1670 const xmlChar *in; 1671 int len, out; 1672 1673 if (str == NULL) 1674 return(NULL); 1675 if (str[0] == 0) 1676 return(xmlStrdup(str)); 1677 len = xmlStrlen(str); 1678 if (!(len > 0)) return(NULL); 1679 1680 len += 20; 1681 ret = (xmlChar *) xmlMallocAtomic(len); 1682 if (ret == NULL) { 1683 xmlURIErrMemory("escaping URI value\n"); 1684 return(NULL); 1685 } 1686 in = (const xmlChar *) str; 1687 out = 0; 1688 while(*in != 0) { 1689 if (len - out <= 3) { 1690 temp = xmlSaveUriRealloc(ret, &len); 1691 if (temp == NULL) { 1692 xmlURIErrMemory("escaping URI value\n"); 1693 xmlFree(ret); 1694 return(NULL); 1695 } 1696 ret = temp; 1697 } 1698 1699 ch = *in; 1700 1701 if ((ch != '@') && (!IS_UNRESERVED(ch)) && (!xmlStrchr(list, ch))) { 1702 unsigned char val; 1703 ret[out++] = '%'; 1704 val = ch >> 4; 1705 if (val <= 9) 1706 ret[out++] = '0' + val; 1707 else 1708 ret[out++] = 'A' + val - 0xA; 1709 val = ch & 0xF; 1710 if (val <= 9) 1711 ret[out++] = '0' + val; 1712 else 1713 ret[out++] = 'A' + val - 0xA; 1714 in++; 1715 } else { 1716 ret[out++] = *in++; 1717 } 1718 1719 } 1720 ret[out] = 0; 1721 return(ret); 1722 } 1723 1724 /** 1725 * xmlURIEscape: 1726 * @str: the string of the URI to escape 1727 * 1728 * Escaping routine, does not do validity checks ! 1729 * It will try to escape the chars needing this, but this is heuristic 1730 * based it's impossible to be sure. 1731 * 1732 * Returns an copy of the string, but escaped 1733 * 1734 * 25 May 2001 1735 * Uses xmlParseURI and xmlURIEscapeStr to try to escape correctly 1736 * according to RFC2396. 1737 * - Carl Douglas 1738 */ 1739 xmlChar * 1740 xmlURIEscape(const xmlChar * str) 1741 { 1742 xmlChar *ret, *segment = NULL; 1743 xmlURIPtr uri; 1744 int ret2; 1745 1746 #define NULLCHK(p) if(!p) { \ 1747 xmlURIErrMemory("escaping URI value\n"); \ 1748 xmlFreeURI(uri); \ 1749 return NULL; } \ 1750 1751 if (str == NULL) 1752 return (NULL); 1753 1754 uri = xmlCreateURI(); 1755 if (uri != NULL) { 1756 /* 1757 * Allow escaping errors in the unescaped form 1758 */ 1759 uri->cleanup = 1; 1760 ret2 = xmlParseURIReference(uri, (const char *)str); 1761 if (ret2) { 1762 xmlFreeURI(uri); 1763 return (NULL); 1764 } 1765 } 1766 1767 if (!uri) 1768 return NULL; 1769 1770 ret = NULL; 1771 1772 if (uri->scheme) { 1773 segment = xmlURIEscapeStr(BAD_CAST uri->scheme, BAD_CAST "+-."); 1774 NULLCHK(segment) 1775 ret = xmlStrcat(ret, segment); 1776 ret = xmlStrcat(ret, BAD_CAST ":"); 1777 xmlFree(segment); 1778 } 1779 1780 if (uri->authority) { 1781 segment = 1782 xmlURIEscapeStr(BAD_CAST uri->authority, BAD_CAST "/?;:@"); 1783 NULLCHK(segment) 1784 ret = xmlStrcat(ret, BAD_CAST "//"); 1785 ret = xmlStrcat(ret, segment); 1786 xmlFree(segment); 1787 } 1788 1789 if (uri->user) { 1790 segment = xmlURIEscapeStr(BAD_CAST uri->user, BAD_CAST ";:&=+$,"); 1791 NULLCHK(segment) 1792 ret = xmlStrcat(ret,BAD_CAST "//"); 1793 ret = xmlStrcat(ret, segment); 1794 ret = xmlStrcat(ret, BAD_CAST "@"); 1795 xmlFree(segment); 1796 } 1797 1798 if (uri->server) { 1799 segment = xmlURIEscapeStr(BAD_CAST uri->server, BAD_CAST "/?;:@"); 1800 NULLCHK(segment) 1801 if (uri->user == NULL) 1802 ret = xmlStrcat(ret, BAD_CAST "//"); 1803 ret = xmlStrcat(ret, segment); 1804 xmlFree(segment); 1805 } 1806 1807 if (uri->port) { 1808 xmlChar port[10]; 1809 1810 snprintf((char *) port, 10, "%d", uri->port); 1811 ret = xmlStrcat(ret, BAD_CAST ":"); 1812 ret = xmlStrcat(ret, port); 1813 } 1814 1815 if (uri->path) { 1816 segment = 1817 xmlURIEscapeStr(BAD_CAST uri->path, BAD_CAST ":@&=+$,/?;"); 1818 NULLCHK(segment) 1819 ret = xmlStrcat(ret, segment); 1820 xmlFree(segment); 1821 } 1822 1823 if (uri->query_raw) { 1824 ret = xmlStrcat(ret, BAD_CAST "?"); 1825 ret = xmlStrcat(ret, BAD_CAST uri->query_raw); 1826 } 1827 else if (uri->query) { 1828 segment = 1829 xmlURIEscapeStr(BAD_CAST uri->query, BAD_CAST ";/?:@&=+,$"); 1830 NULLCHK(segment) 1831 ret = xmlStrcat(ret, BAD_CAST "?"); 1832 ret = xmlStrcat(ret, segment); 1833 xmlFree(segment); 1834 } 1835 1836 if (uri->opaque) { 1837 segment = xmlURIEscapeStr(BAD_CAST uri->opaque, BAD_CAST ""); 1838 NULLCHK(segment) 1839 ret = xmlStrcat(ret, segment); 1840 xmlFree(segment); 1841 } 1842 1843 if (uri->fragment) { 1844 segment = xmlURIEscapeStr(BAD_CAST uri->fragment, BAD_CAST "#"); 1845 NULLCHK(segment) 1846 ret = xmlStrcat(ret, BAD_CAST "#"); 1847 ret = xmlStrcat(ret, segment); 1848 xmlFree(segment); 1849 } 1850 1851 xmlFreeURI(uri); 1852 #undef NULLCHK 1853 1854 return (ret); 1855 } 1856 1857 /************************************************************************ 1858 * * 1859 * Public functions * 1860 * * 1861 ************************************************************************/ 1862 1863 /** 1864 * xmlBuildURI: 1865 * @URI: the URI instance found in the document 1866 * @base: the base value 1867 * 1868 * Computes he final URI of the reference done by checking that 1869 * the given URI is valid, and building the final URI using the 1870 * base URI. This is processed according to section 5.2 of the 1871 * RFC 2396 1872 * 1873 * 5.2. Resolving Relative References to Absolute Form 1874 * 1875 * Returns a new URI string (to be freed by the caller) or NULL in case 1876 * of error. 1877 */ 1878 xmlChar * 1879 xmlBuildURI(const xmlChar *URI, const xmlChar *base) { 1880 xmlChar *val = NULL; 1881 int ret, len, indx, cur, out; 1882 xmlURIPtr ref = NULL; 1883 xmlURIPtr bas = NULL; 1884 xmlURIPtr res = NULL; 1885 1886 /* 1887 * 1) The URI reference is parsed into the potential four components and 1888 * fragment identifier, as described in Section 4.3. 1889 * 1890 * NOTE that a completely empty URI is treated by modern browsers 1891 * as a reference to "." rather than as a synonym for the current 1892 * URI. Should we do that here? 1893 */ 1894 if (URI == NULL) 1895 ret = -1; 1896 else { 1897 if (*URI) { 1898 ref = xmlCreateURI(); 1899 if (ref == NULL) 1900 goto done; 1901 ret = xmlParseURIReference(ref, (const char *) URI); 1902 } 1903 else 1904 ret = 0; 1905 } 1906 if (ret != 0) 1907 goto done; 1908 if ((ref != NULL) && (ref->scheme != NULL)) { 1909 /* 1910 * The URI is absolute don't modify. 1911 */ 1912 val = xmlStrdup(URI); 1913 goto done; 1914 } 1915 if (base == NULL) 1916 ret = -1; 1917 else { 1918 bas = xmlCreateURI(); 1919 if (bas == NULL) 1920 goto done; 1921 ret = xmlParseURIReference(bas, (const char *) base); 1922 } 1923 if (ret != 0) { 1924 if (ref) 1925 val = xmlSaveUri(ref); 1926 goto done; 1927 } 1928 if (ref == NULL) { 1929 /* 1930 * the base fragment must be ignored 1931 */ 1932 if (bas->fragment != NULL) { 1933 xmlFree(bas->fragment); 1934 bas->fragment = NULL; 1935 } 1936 val = xmlSaveUri(bas); 1937 goto done; 1938 } 1939 1940 /* 1941 * 2) If the path component is empty and the scheme, authority, and 1942 * query components are undefined, then it is a reference to the 1943 * current document and we are done. Otherwise, the reference URI's 1944 * query and fragment components are defined as found (or not found) 1945 * within the URI reference and not inherited from the base URI. 1946 * 1947 * NOTE that in modern browsers, the parsing differs from the above 1948 * in the following aspect: the query component is allowed to be 1949 * defined while still treating this as a reference to the current 1950 * document. 1951 */ 1952 res = xmlCreateURI(); 1953 if (res == NULL) 1954 goto done; 1955 if ((ref->scheme == NULL) && (ref->path == NULL) && 1956 ((ref->authority == NULL) && (ref->server == NULL))) { 1957 if (bas->scheme != NULL) 1958 res->scheme = xmlMemStrdup(bas->scheme); 1959 if (bas->authority != NULL) 1960 res->authority = xmlMemStrdup(bas->authority); 1961 else if (bas->server != NULL) { 1962 res->server = xmlMemStrdup(bas->server); 1963 if (bas->user != NULL) 1964 res->user = xmlMemStrdup(bas->user); 1965 res->port = bas->port; 1966 } 1967 if (bas->path != NULL) 1968 res->path = xmlMemStrdup(bas->path); 1969 if (ref->query_raw != NULL) 1970 res->query_raw = xmlMemStrdup (ref->query_raw); 1971 else if (ref->query != NULL) 1972 res->query = xmlMemStrdup(ref->query); 1973 else if (bas->query_raw != NULL) 1974 res->query_raw = xmlMemStrdup(bas->query_raw); 1975 else if (bas->query != NULL) 1976 res->query = xmlMemStrdup(bas->query); 1977 if (ref->fragment != NULL) 1978 res->fragment = xmlMemStrdup(ref->fragment); 1979 goto step_7; 1980 } 1981 1982 /* 1983 * 3) If the scheme component is defined, indicating that the reference 1984 * starts with a scheme name, then the reference is interpreted as an 1985 * absolute URI and we are done. Otherwise, the reference URI's 1986 * scheme is inherited from the base URI's scheme component. 1987 */ 1988 if (ref->scheme != NULL) { 1989 val = xmlSaveUri(ref); 1990 goto done; 1991 } 1992 if (bas->scheme != NULL) 1993 res->scheme = xmlMemStrdup(bas->scheme); 1994 1995 if (ref->query_raw != NULL) 1996 res->query_raw = xmlMemStrdup(ref->query_raw); 1997 else if (ref->query != NULL) 1998 res->query = xmlMemStrdup(ref->query); 1999 if (ref->fragment != NULL) 2000 res->fragment = xmlMemStrdup(ref->fragment); 2001 2002 /* 2003 * 4) If the authority component is defined, then the reference is a 2004 * network-path and we skip to step 7. Otherwise, the reference 2005 * URI's authority is inherited from the base URI's authority 2006 * component, which will also be undefined if the URI scheme does not 2007 * use an authority component. 2008 */ 2009 if ((ref->authority != NULL) || (ref->server != NULL)) { 2010 if (ref->authority != NULL) 2011 res->authority = xmlMemStrdup(ref->authority); 2012 else { 2013 res->server = xmlMemStrdup(ref->server); 2014 if (ref->user != NULL) 2015 res->user = xmlMemStrdup(ref->user); 2016 res->port = ref->port; 2017 } 2018 if (ref->path != NULL) 2019 res->path = xmlMemStrdup(ref->path); 2020 goto step_7; 2021 } 2022 if (bas->authority != NULL) 2023 res->authority = xmlMemStrdup(bas->authority); 2024 else if (bas->server != NULL) { 2025 res->server = xmlMemStrdup(bas->server); 2026 if (bas->user != NULL) 2027 res->user = xmlMemStrdup(bas->user); 2028 res->port = bas->port; 2029 } 2030 2031 /* 2032 * 5) If the path component begins with a slash character ("/"), then 2033 * the reference is an absolute-path and we skip to step 7. 2034 */ 2035 if ((ref->path != NULL) && (ref->path[0] == '/')) { 2036 res->path = xmlMemStrdup(ref->path); 2037 goto step_7; 2038 } 2039 2040 2041 /* 2042 * 6) If this step is reached, then we are resolving a relative-path 2043 * reference. The relative path needs to be merged with the base 2044 * URI's path. Although there are many ways to do this, we will 2045 * describe a simple method using a separate string buffer. 2046 * 2047 * Allocate a buffer large enough for the result string. 2048 */ 2049 len = 2; /* extra / and 0 */ 2050 if (ref->path != NULL) 2051 len += strlen(ref->path); 2052 if (bas->path != NULL) 2053 len += strlen(bas->path); 2054 res->path = (char *) xmlMallocAtomic(len); 2055 if (res->path == NULL) { 2056 xmlURIErrMemory("resolving URI against base\n"); 2057 goto done; 2058 } 2059 res->path[0] = 0; 2060 2061 /* 2062 * a) All but the last segment of the base URI's path component is 2063 * copied to the buffer. In other words, any characters after the 2064 * last (right-most) slash character, if any, are excluded. 2065 */ 2066 cur = 0; 2067 out = 0; 2068 if (bas->path != NULL) { 2069 while (bas->path[cur] != 0) { 2070 while ((bas->path[cur] != 0) && (bas->path[cur] != '/')) 2071 cur++; 2072 if (bas->path[cur] == 0) 2073 break; 2074 2075 cur++; 2076 while (out < cur) { 2077 res->path[out] = bas->path[out]; 2078 out++; 2079 } 2080 } 2081 } 2082 res->path[out] = 0; 2083 2084 /* 2085 * b) The reference's path component is appended to the buffer 2086 * string. 2087 */ 2088 if (ref->path != NULL && ref->path[0] != 0) { 2089 indx = 0; 2090 /* 2091 * Ensure the path includes a '/' 2092 */ 2093 if ((out == 0) && (bas->server != NULL)) 2094 res->path[out++] = '/'; 2095 while (ref->path[indx] != 0) { 2096 res->path[out++] = ref->path[indx++]; 2097 } 2098 } 2099 res->path[out] = 0; 2100 2101 /* 2102 * Steps c) to h) are really path normalization steps 2103 */ 2104 xmlNormalizeURIPath(res->path); 2105 2106 step_7: 2107 2108 /* 2109 * 7) The resulting URI components, including any inherited from the 2110 * base URI, are recombined to give the absolute form of the URI 2111 * reference. 2112 */ 2113 val = xmlSaveUri(res); 2114 2115 done: 2116 if (ref != NULL) 2117 xmlFreeURI(ref); 2118 if (bas != NULL) 2119 xmlFreeURI(bas); 2120 if (res != NULL) 2121 xmlFreeURI(res); 2122 return(val); 2123 } 2124 2125 /** 2126 * xmlBuildRelativeURI: 2127 * @URI: the URI reference under consideration 2128 * @base: the base value 2129 * 2130 * Expresses the URI of the reference in terms relative to the 2131 * base. Some examples of this operation include: 2132 * base = "http://site1.com/docs/book1.html" 2133 * URI input URI returned 2134 * docs/pic1.gif pic1.gif 2135 * docs/img/pic1.gif img/pic1.gif 2136 * img/pic1.gif ../img/pic1.gif 2137 * http://site1.com/docs/pic1.gif pic1.gif 2138 * http://site2.com/docs/pic1.gif http://site2.com/docs/pic1.gif 2139 * 2140 * base = "docs/book1.html" 2141 * URI input URI returned 2142 * docs/pic1.gif pic1.gif 2143 * docs/img/pic1.gif img/pic1.gif 2144 * img/pic1.gif ../img/pic1.gif 2145 * http://site1.com/docs/pic1.gif http://site1.com/docs/pic1.gif 2146 * 2147 * 2148 * Note: if the URI reference is really wierd or complicated, it may be 2149 * worthwhile to first convert it into a "nice" one by calling 2150 * xmlBuildURI (using 'base') before calling this routine, 2151 * since this routine (for reasonable efficiency) assumes URI has 2152 * already been through some validation. 2153 * 2154 * Returns a new URI string (to be freed by the caller) or NULL in case 2155 * error. 2156 */ 2157 xmlChar * 2158 xmlBuildRelativeURI (const xmlChar * URI, const xmlChar * base) 2159 { 2160 xmlChar *val = NULL; 2161 int ret; 2162 int ix; 2163 int pos = 0; 2164 int nbslash = 0; 2165 int len; 2166 xmlURIPtr ref = NULL; 2167 xmlURIPtr bas = NULL; 2168 xmlChar *bptr, *uptr, *vptr; 2169 int remove_path = 0; 2170 2171 if ((URI == NULL) || (*URI == 0)) 2172 return NULL; 2173 2174 /* 2175 * First parse URI into a standard form 2176 */ 2177 ref = xmlCreateURI (); 2178 if (ref == NULL) 2179 return NULL; 2180 /* If URI not already in "relative" form */ 2181 if (URI[0] != '.') { 2182 ret = xmlParseURIReference (ref, (const char *) URI); 2183 if (ret != 0) 2184 goto done; /* Error in URI, return NULL */ 2185 } else 2186 ref->path = (char *)xmlStrdup(URI); 2187 2188 /* 2189 * Next parse base into the same standard form 2190 */ 2191 if ((base == NULL) || (*base == 0)) { 2192 val = xmlStrdup (URI); 2193 goto done; 2194 } 2195 bas = xmlCreateURI (); 2196 if (bas == NULL) 2197 goto done; 2198 if (base[0] != '.') { 2199 ret = xmlParseURIReference (bas, (const char *) base); 2200 if (ret != 0) 2201 goto done; /* Error in base, return NULL */ 2202 } else 2203 bas->path = (char *)xmlStrdup(base); 2204 2205 /* 2206 * If the scheme / server on the URI differs from the base, 2207 * just return the URI 2208 */ 2209 if ((ref->scheme != NULL) && 2210 ((bas->scheme == NULL) || 2211 (xmlStrcmp ((xmlChar *)bas->scheme, (xmlChar *)ref->scheme)) || 2212 (xmlStrcmp ((xmlChar *)bas->server, (xmlChar *)ref->server)))) { 2213 val = xmlStrdup (URI); 2214 goto done; 2215 } 2216 if (xmlStrEqual((xmlChar *)bas->path, (xmlChar *)ref->path)) { 2217 val = xmlStrdup(BAD_CAST ""); 2218 goto done; 2219 } 2220 if (bas->path == NULL) { 2221 val = xmlStrdup((xmlChar *)ref->path); 2222 goto done; 2223 } 2224 if (ref->path == NULL) { 2225 ref->path = (char *) "/"; 2226 remove_path = 1; 2227 } 2228 2229 /* 2230 * At this point (at last!) we can compare the two paths 2231 * 2232 * First we take care of the special case where either of the 2233 * two path components may be missing (bug 316224) 2234 */ 2235 if (bas->path == NULL) { 2236 if (ref->path != NULL) { 2237 uptr = (xmlChar *) ref->path; 2238 if (*uptr == '/') 2239 uptr++; 2240 /* exception characters from xmlSaveUri */ 2241 val = xmlURIEscapeStr(uptr, BAD_CAST "/;&=+$,"); 2242 } 2243 goto done; 2244 } 2245 bptr = (xmlChar *)bas->path; 2246 if (ref->path == NULL) { 2247 for (ix = 0; bptr[ix] != 0; ix++) { 2248 if (bptr[ix] == '/') 2249 nbslash++; 2250 } 2251 uptr = NULL; 2252 len = 1; /* this is for a string terminator only */ 2253 } else { 2254 /* 2255 * Next we compare the two strings and find where they first differ 2256 */ 2257 if ((ref->path[pos] == '.') && (ref->path[pos+1] == '/')) 2258 pos += 2; 2259 if ((*bptr == '.') && (bptr[1] == '/')) 2260 bptr += 2; 2261 else if ((*bptr == '/') && (ref->path[pos] != '/')) 2262 bptr++; 2263 while ((bptr[pos] == ref->path[pos]) && (bptr[pos] != 0)) 2264 pos++; 2265 2266 if (bptr[pos] == ref->path[pos]) { 2267 val = xmlStrdup(BAD_CAST ""); 2268 goto done; /* (I can't imagine why anyone would do this) */ 2269 } 2270 2271 /* 2272 * In URI, "back up" to the last '/' encountered. This will be the 2273 * beginning of the "unique" suffix of URI 2274 */ 2275 ix = pos; 2276 if ((ref->path[ix] == '/') && (ix > 0)) 2277 ix--; 2278 else if ((ref->path[ix] == 0) && (ix > 1) && (ref->path[ix - 1] == '/')) 2279 ix -= 2; 2280 for (; ix > 0; ix--) { 2281 if (ref->path[ix] == '/') 2282 break; 2283 } 2284 if (ix == 0) { 2285 uptr = (xmlChar *)ref->path; 2286 } else { 2287 ix++; 2288 uptr = (xmlChar *)&ref->path[ix]; 2289 } 2290 2291 /* 2292 * In base, count the number of '/' from the differing point 2293 */ 2294 if (bptr[pos] != ref->path[pos]) {/* check for trivial URI == base */ 2295 for (; bptr[ix] != 0; ix++) { 2296 if (bptr[ix] == '/') 2297 nbslash++; 2298 } 2299 } 2300 len = xmlStrlen (uptr) + 1; 2301 } 2302 2303 if (nbslash == 0) { 2304 if (uptr != NULL) 2305 /* exception characters from xmlSaveUri */ 2306 val = xmlURIEscapeStr(uptr, BAD_CAST "/;&=+$,"); 2307 goto done; 2308 } 2309 2310 /* 2311 * Allocate just enough space for the returned string - 2312 * length of the remainder of the URI, plus enough space 2313 * for the "../" groups, plus one for the terminator 2314 */ 2315 val = (xmlChar *) xmlMalloc (len + 3 * nbslash); 2316 if (val == NULL) { 2317 xmlURIErrMemory("building relative URI\n"); 2318 goto done; 2319 } 2320 vptr = val; 2321 /* 2322 * Put in as many "../" as needed 2323 */ 2324 for (; nbslash>0; nbslash--) { 2325 *vptr++ = '.'; 2326 *vptr++ = '.'; 2327 *vptr++ = '/'; 2328 } 2329 /* 2330 * Finish up with the end of the URI 2331 */ 2332 if (uptr != NULL) { 2333 if ((vptr > val) && (len > 0) && 2334 (uptr[0] == '/') && (vptr[-1] == '/')) { 2335 memcpy (vptr, uptr + 1, len - 1); 2336 vptr[len - 2] = 0; 2337 } else { 2338 memcpy (vptr, uptr, len); 2339 vptr[len - 1] = 0; 2340 } 2341 } else { 2342 vptr[len - 1] = 0; 2343 } 2344 2345 /* escape the freshly-built path */ 2346 vptr = val; 2347 /* exception characters from xmlSaveUri */ 2348 val = xmlURIEscapeStr(vptr, BAD_CAST "/;&=+$,"); 2349 xmlFree(vptr); 2350 2351 done: 2352 /* 2353 * Free the working variables 2354 */ 2355 if (remove_path != 0) 2356 ref->path = NULL; 2357 if (ref != NULL) 2358 xmlFreeURI (ref); 2359 if (bas != NULL) 2360 xmlFreeURI (bas); 2361 2362 return val; 2363 } 2364 2365 /** 2366 * xmlCanonicPath: 2367 * @path: the resource locator in a filesystem notation 2368 * 2369 * Constructs a canonic path from the specified path. 2370 * 2371 * Returns a new canonic path, or a duplicate of the path parameter if the 2372 * construction fails. The caller is responsible for freeing the memory occupied 2373 * by the returned string. If there is insufficient memory available, or the 2374 * argument is NULL, the function returns NULL. 2375 */ 2376 #define IS_WINDOWS_PATH(p) \ 2377 ((p != NULL) && \ 2378 (((p[0] >= 'a') && (p[0] <= 'z')) || \ 2379 ((p[0] >= 'A') && (p[0] <= 'Z'))) && \ 2380 (p[1] == ':') && ((p[2] == '/') || (p[2] == '\\'))) 2381 xmlChar * 2382 xmlCanonicPath(const xmlChar *path) 2383 { 2384 /* 2385 * For Windows implementations, additional work needs to be done to 2386 * replace backslashes in pathnames with "forward slashes" 2387 */ 2388 #if defined(_WIN32) && !defined(__CYGWIN__) 2389 int len = 0; 2390 int i = 0; 2391 xmlChar *p = NULL; 2392 #endif 2393 xmlURIPtr uri; 2394 xmlChar *ret; 2395 const xmlChar *absuri; 2396 2397 if (path == NULL) 2398 return(NULL); 2399 2400 #if defined(_WIN32) 2401 /* 2402 * We must not change the backslashes to slashes if the the path 2403 * starts with \\?\ 2404 * Those paths can be up to 32k characters long. 2405 * Was added specifically for OpenOffice, those paths can't be converted 2406 * to URIs anyway. 2407 */ 2408 if ((path[0] == '\\') && (path[1] == '\\') && (path[2] == '?') && 2409 (path[3] == '\\') ) 2410 return xmlStrdup((const xmlChar *) path); 2411 #endif 2412 2413 /* sanitize filename starting with // so it can be used as URI */ 2414 if ((path[0] == '/') && (path[1] == '/') && (path[2] != '/')) 2415 path++; 2416 2417 if ((uri = xmlParseURI((const char *) path)) != NULL) { 2418 xmlFreeURI(uri); 2419 return xmlStrdup(path); 2420 } 2421 2422 /* Check if this is an "absolute uri" */ 2423 absuri = xmlStrstr(path, BAD_CAST "://"); 2424 if (absuri != NULL) { 2425 int l, j; 2426 unsigned char c; 2427 xmlChar *escURI; 2428 2429 /* 2430 * this looks like an URI where some parts have not been 2431 * escaped leading to a parsing problem. Check that the first 2432 * part matches a protocol. 2433 */ 2434 l = absuri - path; 2435 /* Bypass if first part (part before the '://') is > 20 chars */ 2436 if ((l <= 0) || (l > 20)) 2437 goto path_processing; 2438 /* Bypass if any non-alpha characters are present in first part */ 2439 for (j = 0;j < l;j++) { 2440 c = path[j]; 2441 if (!(((c >= 'a') && (c <= 'z')) || ((c >= 'A') && (c <= 'Z')))) 2442 goto path_processing; 2443 } 2444 2445 /* Escape all except the characters specified in the supplied path */ 2446 escURI = xmlURIEscapeStr(path, BAD_CAST ":/?_.#&;="); 2447 if (escURI != NULL) { 2448 /* Try parsing the escaped path */ 2449 uri = xmlParseURI((const char *) escURI); 2450 /* If successful, return the escaped string */ 2451 if (uri != NULL) { 2452 xmlFreeURI(uri); 2453 return escURI; 2454 } 2455 } 2456 } 2457 2458 path_processing: 2459 /* For Windows implementations, replace backslashes with 'forward slashes' */ 2460 #if defined(_WIN32) && !defined(__CYGWIN__) 2461 /* 2462 * Create a URI structure 2463 */ 2464 uri = xmlCreateURI(); 2465 if (uri == NULL) { /* Guard against 'out of memory' */ 2466 return(NULL); 2467 } 2468 2469 len = xmlStrlen(path); 2470 if ((len > 2) && IS_WINDOWS_PATH(path)) { 2471 /* make the scheme 'file' */ 2472 uri->scheme = xmlStrdup(BAD_CAST "file"); 2473 /* allocate space for leading '/' + path + string terminator */ 2474 uri->path = xmlMallocAtomic(len + 2); 2475 if (uri->path == NULL) { 2476 xmlFreeURI(uri); /* Guard agains 'out of memory' */ 2477 return(NULL); 2478 } 2479 /* Put in leading '/' plus path */ 2480 uri->path[0] = '/'; 2481 p = uri->path + 1; 2482 strncpy(p, path, len + 1); 2483 } else { 2484 uri->path = xmlStrdup(path); 2485 if (uri->path == NULL) { 2486 xmlFreeURI(uri); 2487 return(NULL); 2488 } 2489 p = uri->path; 2490 } 2491 /* Now change all occurences of '\' to '/' */ 2492 while (*p != '\0') { 2493 if (*p == '\\') 2494 *p = '/'; 2495 p++; 2496 } 2497 2498 if (uri->scheme == NULL) { 2499 ret = xmlStrdup((const xmlChar *) uri->path); 2500 } else { 2501 ret = xmlSaveUri(uri); 2502 } 2503 2504 xmlFreeURI(uri); 2505 #else 2506 ret = xmlStrdup((const xmlChar *) path); 2507 #endif 2508 return(ret); 2509 } 2510 2511 /** 2512 * xmlPathToURI: 2513 * @path: the resource locator in a filesystem notation 2514 * 2515 * Constructs an URI expressing the existing path 2516 * 2517 * Returns a new URI, or a duplicate of the path parameter if the 2518 * construction fails. The caller is responsible for freeing the memory 2519 * occupied by the returned string. If there is insufficient memory available, 2520 * or the argument is NULL, the function returns NULL. 2521 */ 2522 xmlChar * 2523 xmlPathToURI(const xmlChar *path) 2524 { 2525 xmlURIPtr uri; 2526 xmlURI temp; 2527 xmlChar *ret, *cal; 2528 2529 if (path == NULL) 2530 return(NULL); 2531 2532 if ((uri = xmlParseURI((const char *) path)) != NULL) { 2533 xmlFreeURI(uri); 2534 return xmlStrdup(path); 2535 } 2536 cal = xmlCanonicPath(path); 2537 if (cal == NULL) 2538 return(NULL); 2539 #if defined(_WIN32) && !defined(__CYGWIN__) 2540 /* xmlCanonicPath can return an URI on Windows (is that the intended behaviour?) 2541 If 'cal' is a valid URI allready then we are done here, as continuing would make 2542 it invalid. */ 2543 if ((uri = xmlParseURI((const char *) cal)) != NULL) { 2544 xmlFreeURI(uri); 2545 return cal; 2546 } 2547 /* 'cal' can contain a relative path with backslashes. If that is processed 2548 by xmlSaveURI, they will be escaped and the external entity loader machinery 2549 will fail. So convert them to slashes. Misuse 'ret' for walking. */ 2550 ret = cal; 2551 while (*ret != '\0') { 2552 if (*ret == '\\') 2553 *ret = '/'; 2554 ret++; 2555 } 2556 #endif 2557 memset(&temp, 0, sizeof(temp)); 2558 temp.path = (char *) cal; 2559 ret = xmlSaveUri(&temp); 2560 xmlFree(cal); 2561 return(ret); 2562 } 2563 #define bottom_uri 2564 #include "elfgcchack.h" 2565