1/* Licensed to the Apache Software Foundation (ASF) under one or more 2 * contributor license agreements. See the NOTICE file distributed with 3 * this work for additional information regarding copyright ownership. 4 * The ASF licenses this file to You under the Apache License, Version 2.0 5 * (the "License"); you may not use this file except in compliance with 6 * the License. You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17#include "apr.h" 18#include "apr_private.h" 19#include "apr_arch_file_io.h" 20#include "apr_strings.h" 21#include "apr_lib.h" 22#include <string.h> 23#include <ctype.h> 24 25#ifdef NETWARE 26#include <unistd.h> 27#include <fsio.h> 28#endif 29 30 /* WinNT accepts several odd forms of a 'root' path. Under Unicode 31 * calls (ApiFunctionW) the //?/C:/foo or //?/UNC/mach/share/foo forms 32 * are accepted. Ansi and Unicode functions both accept the //./C:/foo 33 * form under WinNT/2K. Since these forms are handled in the utf-8 to 34 * unicode translation phase, we don't want the user confused by them, so 35 * we will accept them but always return the canonical C:/ or //mach/share/ 36 * 37 * OS2 appears immune from the nonsense :) 38 */ 39 40APR_DECLARE(apr_status_t) apr_filepath_root(const char **rootpath, 41 const char **inpath, 42 apr_int32_t flags, 43 apr_pool_t *p) 44{ 45 const char *testpath = *inpath; 46 char *newpath; 47#ifdef NETWARE 48 char seperator[2] = { 0, 0}; 49 char server[APR_PATH_MAX+1]; 50 char volume[APR_PATH_MAX+1]; 51 char file[APR_PATH_MAX+1]; 52 char *volsep = NULL; 53 int elements; 54 55 if (inpath && *inpath) 56 volsep = strchr (*inpath, ':'); 57 else 58 return APR_EBADPATH; 59 60 if (strlen(*inpath) > APR_PATH_MAX) { 61 return APR_EBADPATH; 62 } 63 64 seperator[0] = (flags & APR_FILEPATH_NATIVE) ? '\\' : '/'; 65 66 /* Allocate and initialize each of the segment buffers 67 */ 68 server[0] = volume[0] = file[0] = '\0'; 69 70 /* If we don't have a volume separator then don't bother deconstructing 71 the path since we won't use the deconstructed information anyway. 72 */ 73 if (volsep) { 74 /* Split the inpath into its separate parts. */ 75 deconstruct(testpath, server, volume, NULL, file, NULL, &elements, PATH_UNDEF); 76 77 /* If we got a volume part then continue splitting out the root. 78 Otherwise we either have an incomplete or relative path 79 */ 80 if (volume && strlen(volume) > 0) { 81 newpath = apr_pcalloc(p, strlen(server)+strlen(volume)+5); 82 construct(newpath, server, volume, NULL, NULL, NULL, PATH_NETWARE); 83 84 /* NetWare doesn't add the root slash so we need to add it manually. 85 */ 86 strcat(newpath, seperator); 87 *rootpath = newpath; 88 89 /* Skip the inpath pointer down to the first non-root character 90 */ 91 newpath = volsep; 92 do { 93 ++newpath; 94 } while (*newpath && ((*newpath == '/') || (*newpath == '\\'))); 95 *inpath = newpath; 96 97 /* Need to handle APR_FILEPATH_TRUENAME checking here. */ 98 99 return APR_SUCCESS; 100 } 101 else 102 return APR_EBADPATH; 103 } 104 else if ((**inpath == '/') || (**inpath == '\\')) { 105 /* if we have a root path without a volume then just split 106 in same manner as unix although this path will be 107 incomplete. 108 */ 109 *rootpath = apr_pstrdup(p, seperator); 110 do { 111 ++(*inpath); 112 } while ((**inpath == '/') || (**inpath == '\\')); 113 } 114 else 115 return APR_ERELATIVE; 116 117 return APR_EINCOMPLETE; 118 119#else /* ndef(NETWARE) */ 120 121 char seperator[2]; 122 const char *delim1; 123 const char *delim2; 124 125 seperator[0] = (flags & APR_FILEPATH_NATIVE) ? '\\' : '/'; 126 seperator[1] = 0; 127 128 if (testpath[0] == '/' || testpath[0] == '\\') { 129 if (testpath[1] == '/' || testpath[1] == '\\') { 130 131#ifdef WIN32 /* //server/share isn't the only // delimited syntax */ 132 if ((testpath[2] == '?' || testpath[2] == '.') 133 && (testpath[3] == '/' || testpath[3] == '\\')) { 134 if (IS_FNCHAR(testpath[4]) && testpath[5] == ':') 135 { 136 apr_status_t rv; 137 testpath += 4; 138 /* given '//?/C: or //./C: let us try this 139 * all over again from the drive designator 140 */ 141 rv = apr_filepath_root(rootpath, &testpath, flags, p); 142 if (!rv || rv == APR_EINCOMPLETE) 143 *inpath = testpath; 144 return rv; 145 } 146 else if (strncasecmp(testpath + 4, "UNC", 3) == 0 147 && (testpath[7] == '/' || testpath[7] == '\\') 148 && (testpath[2] == '?')) { 149 /* given '//?/UNC/machine/share, a little magic 150 * at the end makes this all work out by using 151 * 'C/machine' as the starting point and replacing 152 * the UNC delimiters with \'s, including the 'C' 153 */ 154 testpath += 6; 155 } 156 else 157 /* This must not be a path to a file, but rather 158 * a volume or device. Die for now. 159 */ 160 return APR_EBADPATH; 161 } 162#endif /* WIN32 (non - //server/share syntax) */ 163 164 /* Evaluate path of '//[machine/[share[/]]]' */ 165 delim1 = testpath + 2; 166 do { 167 /* Protect against //X/ where X is illegal */ 168 if (*delim1 && !IS_FNCHAR(*(delim1++))) 169 return APR_EBADPATH; 170 } while (*delim1 && *delim1 != '/' && *delim1 != '\\'); 171 172 if (*delim1) { 173 apr_status_t rv; 174 delim2 = delim1 + 1; 175 while (*delim2 && *delim2 != '/' && *delim2 != '\\') { 176 /* Protect against //machine/X/ where X is illegal */ 177 if (!IS_FNCHAR(*(delim2++))) 178 return APR_EBADPATH; 179 } 180 181 /* Copy the '//machine/[share[/]]' path, always providing 182 * an extra byte for the trailing slash. 183 */ 184 newpath = apr_pstrmemdup(p, testpath, delim2 - testpath + 1); 185 186 if (delim2 == delim1 + 1) { 187 /* We found simply \\machine\, so give up already 188 */ 189 *rootpath = newpath; 190 *inpath = delim2; 191 return APR_EINCOMPLETE; 192 } 193 194 if (flags & APR_FILEPATH_TRUENAME) { 195 /* Validate the \\Machine\Share\ designation, 196 * Win32 will argue about slashed in UNC paths, 197 * so use backslashes till we finish testing, 198 * and add the trailing backslash [required]. 199 * apr_pstrmemdup above guarentees us the new 200 * trailing null character. 201 */ 202 newpath[0] = '\\'; 203 newpath[1] = '\\'; 204 newpath[delim1 - testpath] = '\\'; 205 newpath[delim2 - testpath] = '\\'; 206 207 rv = filepath_root_test(newpath, p); 208 if (rv) 209 return rv; 210 rv = filepath_root_case(&newpath, newpath, p); 211 if (rv) 212 return rv; 213 newpath[0] = seperator[0]; 214 newpath[1] = seperator[0]; 215 newpath[delim1 - testpath] = seperator[0]; 216 newpath[delim2 - testpath] = (*delim2 ? seperator[0] : '\0'); 217 } 218 else { 219 /* Give back the caller's own choice of delimiters 220 */ 221 newpath[0] = testpath[0]; 222 newpath[1] = testpath[1]; 223 newpath[delim1 - testpath] = *delim1; 224 newpath[delim2 - testpath] = *delim2; 225 } 226 227 /* If this root included the trailing / or \ designation 228 * then lop off multiple trailing slashes and give back 229 * appropriate delimiters. 230 */ 231 if (*delim2) { 232 *inpath = delim2 + 1; 233 while (**inpath == '/' || **inpath == '\\') 234 ++*inpath; 235 } 236 else { 237 *inpath = delim2; 238 } 239 240 *rootpath = newpath; 241 return APR_SUCCESS; 242 } 243 244 /* Have path of '\\[machine]', if the machine is given, 245 * append same trailing slash as the leading slash 246 */ 247 delim1 = strchr(testpath, '\0'); 248 if (delim1 > testpath + 2) { 249 newpath = apr_pstrndup(p, testpath, delim1 - testpath + 1); 250 if (flags & APR_FILEPATH_TRUENAME) 251 newpath[delim1 - testpath] = seperator[0]; 252 else 253 newpath[delim1 - testpath] = newpath[0]; 254 newpath[delim1 - testpath + 1] = '\0'; 255 } 256 else { 257 newpath = apr_pstrndup(p, testpath, delim1 - testpath); 258 } 259 if (flags & APR_FILEPATH_TRUENAME) { 260 newpath[0] = seperator[0]; 261 newpath[1] = seperator[0]; 262 } 263 *rootpath = newpath; 264 *inpath = delim1; 265 return APR_EINCOMPLETE; 266 } 267 268 /* Left with a path of '/', what drive are we asking about? 269 */ 270 *inpath = testpath + 1; 271 newpath = apr_palloc(p, 2); 272 if (flags & APR_FILEPATH_TRUENAME) 273 newpath[0] = seperator[0]; 274 else 275 newpath[0] = testpath[0]; 276 newpath[1] = '\0'; 277 *rootpath = newpath; 278 return APR_EINCOMPLETE; 279 } 280 281 /* Evaluate path of 'd:[/]' */ 282 if (IS_FNCHAR(*testpath) && testpath[1] == ':') 283 { 284 apr_status_t rv; 285 /* Validate that D:\ drive exists, test must be rooted 286 * Note that posix/win32 insists a drive letter is upper case, 287 * so who are we to argue with a 'feature'. 288 * It is a safe fold, since only A-Z is legal, and has no 289 * side effects of legal mis-mapped non-us-ascii codes. 290 */ 291 newpath = apr_palloc(p, 4); 292 newpath[0] = testpath[0]; 293 newpath[1] = testpath[1]; 294 newpath[2] = seperator[0]; 295 newpath[3] = '\0'; 296 if (flags & APR_FILEPATH_TRUENAME) { 297 newpath[0] = apr_toupper(newpath[0]); 298 rv = filepath_root_test(newpath, p); 299 if (rv) 300 return rv; 301 } 302 /* Just give back the root the user handed to us. 303 */ 304 if (testpath[2] != '/' && testpath[2] != '\\') { 305 newpath[2] = '\0'; 306 *rootpath = newpath; 307 *inpath = testpath + 2; 308 return APR_EINCOMPLETE; 309 } 310 311 /* strip off remaining slashes that designate the root, 312 * give the caller back their original choice of slash 313 * unless this is TRUENAME'ed 314 */ 315 *inpath = testpath + 3; 316 while (**inpath == '/' || **inpath == '\\') 317 ++*inpath; 318 if (!(flags & APR_FILEPATH_TRUENAME)) 319 newpath[2] = testpath[2]; 320 *rootpath = newpath; 321 return APR_SUCCESS; 322 } 323 324 /* Nothing interesting */ 325 return APR_ERELATIVE; 326 327#endif /* ndef(NETWARE) */ 328} 329 330#if !defined(NETWARE) 331static int same_drive(const char *path1, const char *path2) 332{ 333 char drive1 = path1[0]; 334 char drive2 = path2[0]; 335 336 if (!drive1 || !drive2 || path1[1] != ':' || path2[1] != ':') 337 return FALSE; 338 339 if (drive1 == drive2) 340 return TRUE; 341 342 if (drive1 >= 'a' && drive1 <= 'z') 343 drive1 += 'A' - 'a'; 344 345 if (drive2 >= 'a' && drive2 <= 'z') 346 drive2 += 'A' - 'a'; 347 348 return (drive1 == drive2); 349} 350#endif 351 352APR_DECLARE(apr_status_t) apr_filepath_merge(char **newpath, 353 const char *basepath, 354 const char *addpath, 355 apr_int32_t flags, 356 apr_pool_t *p) 357{ 358 char path[APR_PATH_MAX]; /* isn't null term */ 359 const char *baseroot = NULL; 360 const char *addroot; 361 apr_size_t rootlen; /* the length of the root portion of path, d:/ is 3 */ 362 apr_size_t baselen; /* the length of basepath (excluding baseroot) */ 363 apr_size_t keptlen; /* the length of the retained basepath (incl root) */ 364 apr_size_t pathlen; /* the length of the result path */ 365 apr_size_t segend; /* the end of the current segment */ 366 apr_size_t seglen; /* the length of the segment (excl trailing chars) */ 367 apr_status_t basetype = 0; /* from parsing the basepath's baseroot */ 368 apr_status_t addtype; /* from parsing the addpath's addroot */ 369 apr_status_t rv; 370#ifndef NETWARE 371 int fixunc = 0; /* flag to complete an incomplete UNC basepath */ 372#endif 373 374 /* Treat null as an empty path, otherwise split addroot from the addpath 375 */ 376 if (!addpath) { 377 addpath = addroot = ""; 378 addtype = APR_ERELATIVE; 379 } 380 else { 381 /* This call _should_ test the path 382 */ 383 addtype = apr_filepath_root(&addroot, &addpath, 384 APR_FILEPATH_TRUENAME 385 | (flags & APR_FILEPATH_NATIVE), 386 p); 387 if (addtype == APR_SUCCESS) { 388 addtype = APR_EABSOLUTE; 389 } 390 else if (addtype == APR_ERELATIVE) { 391 addroot = ""; 392 } 393 else if (addtype != APR_EINCOMPLETE) { 394 /* apr_filepath_root was incomprehensible so fail already 395 */ 396 return addtype; 397 } 398 } 399 400 /* If addpath is (even partially) rooted, then basepath is 401 * unused. Ths violates any APR_FILEPATH_SECUREROOTTEST 402 * and APR_FILEPATH_NOTABSOLUTE flags specified. 403 */ 404 if (addtype == APR_EABSOLUTE || addtype == APR_EINCOMPLETE) 405 { 406 if (flags & APR_FILEPATH_SECUREROOTTEST) 407 return APR_EABOVEROOT; 408 if (flags & APR_FILEPATH_NOTABSOLUTE) 409 return addtype; 410 } 411 412 /* Optimized tests before we query the current working path 413 */ 414 if (!basepath) { 415 416 /* If APR_FILEPATH_NOTABOVEROOT wasn't specified, 417 * we won't test the root again, it's ignored. 418 * Waste no CPU retrieving the working path. 419 */ 420 if (addtype == APR_EABSOLUTE && !(flags & APR_FILEPATH_NOTABOVEROOT)) { 421 basepath = baseroot = ""; 422 basetype = APR_ERELATIVE; 423 } 424 425 /* If APR_FILEPATH_NOTABSOLUTE is specified, the caller 426 * requires an absolutely relative result, So do not retrieve 427 * the working path. 428 */ 429 if (addtype == APR_ERELATIVE && (flags & APR_FILEPATH_NOTABSOLUTE)) { 430 basepath = baseroot = ""; 431 basetype = APR_ERELATIVE; 432 } 433 } 434 435 if (!basepath) 436 { 437 /* Start with the current working path. This is bass akwards, 438 * but required since the compiler (at least vc) doesn't like 439 * passing the address of a char const* for a char** arg. 440 * We must grab the current path of the designated drive 441 * if addroot is given in drive-relative form (e.g. d:foo) 442 */ 443 char *getpath; 444#ifndef NETWARE 445 if (addtype == APR_EINCOMPLETE && addroot[1] == ':') 446 rv = filepath_drive_get(&getpath, addroot[0], flags, p); 447 else 448#endif 449 rv = apr_filepath_get(&getpath, flags, p); 450 if (rv != APR_SUCCESS) 451 return rv; 452 basepath = getpath; 453 } 454 455 if (!baseroot) { 456 /* This call should _not_ test the path 457 */ 458 basetype = apr_filepath_root(&baseroot, &basepath, 459 (flags & APR_FILEPATH_NATIVE), p); 460 if (basetype == APR_SUCCESS) { 461 basetype = APR_EABSOLUTE; 462 } 463 else if (basetype == APR_ERELATIVE) { 464 baseroot = ""; 465 } 466 else if (basetype != APR_EINCOMPLETE) { 467 /* apr_filepath_root was incomprehensible so fail already 468 */ 469 return basetype; 470 } 471 } 472 baselen = strlen(basepath); 473 474 /* If APR_FILEPATH_NOTABSOLUTE is specified, the caller 475 * requires an absolutely relative result. If the given 476 * basepath is not relative then fail. 477 */ 478 if ((flags & APR_FILEPATH_NOTABSOLUTE) && basetype != APR_ERELATIVE) 479 return basetype; 480 481 /* The Win32 nightmare on unc street... start combining for 482 * many possible root combinations. 483 */ 484 if (addtype == APR_EABSOLUTE) 485 { 486 /* Ignore the given root path, and start with the addroot 487 */ 488 if ((flags & APR_FILEPATH_NOTABOVEROOT) 489 && strncmp(baseroot, addroot, strlen(baseroot))) 490 return APR_EABOVEROOT; 491 keptlen = 0; 492 rootlen = pathlen = strlen(addroot); 493 memcpy(path, addroot, pathlen); 494 } 495 else if (addtype == APR_EINCOMPLETE) 496 { 497 /* There are several types of incomplete paths, 498 * incomplete UNC paths (//foo/ or //), 499 * drives without rooted paths (d: as in d:foo), 500 * and simple roots (/ as in /foo). 501 * Deal with these in significantly different manners... 502 */ 503#ifndef NETWARE 504 if ((addroot[0] == '/' || addroot[0] == '\\') && 505 (addroot[1] == '/' || addroot[1] == '\\')) 506 { 507 /* Ignore the given root path if the incomplete addpath is UNC, 508 * (note that the final result will be incomplete). 509 */ 510 if (flags & APR_FILEPATH_NOTRELATIVE) 511 return addtype; 512 if ((flags & APR_FILEPATH_NOTABOVEROOT) 513 && strncmp(baseroot, addroot, strlen(baseroot))) 514 return APR_EABOVEROOT; 515 fixunc = 1; 516 keptlen = 0; 517 rootlen = pathlen = strlen(addroot); 518 memcpy(path, addroot, pathlen); 519 } 520 else 521#endif 522 if ((addroot[0] == '/' || addroot[0] == '\\') && !addroot[1]) 523 { 524 /* Bring together the drive or UNC root from the baseroot 525 * if the addpath is a simple root and basepath is rooted, 526 * otherwise disregard the basepath entirely. 527 */ 528 if (basetype != APR_EABSOLUTE && (flags & APR_FILEPATH_NOTRELATIVE)) 529 return basetype; 530 if (basetype != APR_ERELATIVE) { 531#ifndef NETWARE 532 if (basetype == APR_INCOMPLETE 533 && (baseroot[0] == '/' || baseroot[0] == '\\') 534 && (baseroot[1] == '/' || baseroot[1] == '\\')) 535 fixunc = 1; 536#endif 537 keptlen = rootlen = pathlen = strlen(baseroot); 538 memcpy(path, baseroot, pathlen); 539 } 540 else { 541 if (flags & APR_FILEPATH_NOTABOVEROOT) 542 return APR_EABOVEROOT; 543 keptlen = 0; 544 rootlen = pathlen = strlen(addroot); 545 memcpy(path, addroot, pathlen); 546 } 547 } 548#ifdef NETWARE 549 else if (filepath_has_drive(addroot, DRIVE_ONLY, p)) 550 { 551 /* If the addroot is a drive (without a volume root) 552 * use the basepath _if_ it matches this drive letter! 553 * Otherwise we must discard the basepath. 554 */ 555 if (!filepath_compare_drive(addroot, baseroot, p) && 556 filepath_has_drive(baseroot, 0, p)) { 557#else 558 else if (addroot[0] && addroot[1] == ':' && !addroot[2]) 559 { 560 /* If the addroot is a drive (without a volume root) 561 * use the basepath _if_ it matches this drive letter! 562 * Otherwise we must discard the basepath. 563 */ 564 if (same_drive(addroot, baseroot)) { 565#endif 566 /* Base the result path on the basepath 567 */ 568 if (basetype != APR_EABSOLUTE && (flags & APR_FILEPATH_NOTRELATIVE)) 569 return basetype; 570 rootlen = strlen(baseroot); 571 keptlen = pathlen = rootlen + baselen; 572 if (keptlen >= sizeof(path)) 573 return APR_ENAMETOOLONG; 574 memcpy(path, baseroot, rootlen); 575 memcpy(path + rootlen, basepath, baselen); 576 } 577 else { 578 if (flags & APR_FILEPATH_NOTRELATIVE) 579 return addtype; 580 if (flags & APR_FILEPATH_NOTABOVEROOT) 581 return APR_EABOVEROOT; 582 keptlen = 0; 583 rootlen = pathlen = strlen(addroot); 584 memcpy(path, addroot, pathlen); 585 } 586 } 587 else { 588 /* Now this is unexpected, we aren't aware of any other 589 * incomplete path forms! Fail now. 590 */ 591 return APR_EBADPATH; 592 } 593 } 594 else { /* addtype == APR_ERELATIVE */ 595 /* If both paths are relative, fail early 596 */ 597 if (basetype != APR_EABSOLUTE && (flags & APR_FILEPATH_NOTRELATIVE)) 598 return basetype; 599 600#ifndef NETWARE 601 /* An incomplete UNC path must be completed 602 */ 603 if (basetype == APR_INCOMPLETE 604 && (baseroot[0] == '/' || baseroot[0] == '\\') 605 && (baseroot[1] == '/' || baseroot[1] == '\\')) 606 fixunc = 1; 607#endif 608 609 /* Base the result path on the basepath 610 */ 611 rootlen = strlen(baseroot); 612 keptlen = pathlen = rootlen + baselen; 613 if (keptlen >= sizeof(path)) 614 return APR_ENAMETOOLONG; 615 memcpy(path, baseroot, rootlen); 616 memcpy(path + rootlen, basepath, baselen); 617 } 618 619 /* '/' terminate the given root path unless it's already terminated 620 * or is an incomplete drive root. Correct the trailing slash unless 621 * we have an incomplete UNC path still to fix. 622 */ 623 if (pathlen && path[pathlen - 1] != ':') { 624 if (path[pathlen - 1] != '/' && path[pathlen - 1] != '\\') { 625 if (pathlen + 1 >= sizeof(path)) 626 return APR_ENAMETOOLONG; 627 628 path[pathlen++] = ((flags & APR_FILEPATH_NATIVE) ? '\\' : '/'); 629 } 630 /* XXX: wrong, but gotta figure out what I intended; 631 * else if (!fixunc) 632 * path[pathlen++] = ((flags & APR_FILEPATH_NATIVE) ? '\\' : '/'); 633 */ 634 } 635 636 while (*addpath) 637 { 638 /* Parse each segment, find the closing '/' 639 */ 640 seglen = 0; 641 while (addpath[seglen] && addpath[seglen] != '/' 642 && addpath[seglen] != '\\') 643 ++seglen; 644 645 /* Truncate all trailing spaces and all but the first two dots */ 646 segend = seglen; 647 while (seglen && (addpath[seglen - 1] == ' ' 648 || addpath[seglen - 1] == '.')) { 649 if (seglen > 2 || addpath[seglen - 1] != '.' || addpath[0] != '.') 650 --seglen; 651 else 652 break; 653 } 654 655 if (seglen == 0 || (seglen == 1 && addpath[0] == '.')) 656 { 657 /* NOTE: win32 _hates_ '/ /' and '/. /' (yes, with spaces in there) 658 * so eliminate all preconceptions that it is valid. 659 */ 660 if (seglen < segend) 661 return APR_EBADPATH; 662 663#ifndef NETWARE 664 /* This isn't legal unless the unc path is completed 665 */ 666 if (fixunc) 667 return APR_EBADPATH; 668#endif 669 670 /* Otherwise, this is a noop segment (/ or ./) so ignore it 671 */ 672 } 673 else if (seglen == 2 && addpath[0] == '.' && addpath[1] == '.') 674 { 675 /* NOTE: win32 _hates_ '/.. /' (yes, with a space in there) 676 * and '/..../', some functions treat it as ".", and some 677 * fail! Eliminate all preconceptions that they are valid. 678 */ 679 if (seglen < segend && (seglen != 3 || addpath[2] != '.')) 680 return APR_EBADPATH; 681 682#ifndef NETWARE 683 /* This isn't legal unless the unc path is completed 684 */ 685 if (fixunc) 686 return APR_EBADPATH; 687#endif 688 689 /* backpath (../) when an absolute path is given */ 690 if (rootlen && (pathlen <= rootlen)) 691 { 692 /* Attempt to move above root. Always die if the 693 * APR_FILEPATH_SECUREROOTTEST flag is specified. 694 */ 695 if (flags & APR_FILEPATH_SECUREROOTTEST) 696 return APR_EABOVEROOT; 697 698 /* Otherwise this is simply a noop, above root is root. 699 */ 700 } 701 else if (pathlen == 0 702 || (pathlen >= 3 703 && (pathlen == 3 704 || path[pathlen - 4] == ':' 705 || path[pathlen - 4] == '/' 706 || path[pathlen - 4] == '\\') 707 && path[pathlen - 3] == '.' 708 && path[pathlen - 2] == '.' 709 && (path[pathlen - 1] == '/' 710 || path[pathlen - 1] == '\\'))) 711 { 712 /* Verified path is empty, exactly "..[/\]", or ends 713 * in "[:/\]..[/\]" - these patterns we will not back 714 * over since they aren't 'prior segements'. 715 * 716 * If APR_FILEPATH_SECUREROOTTEST.was given, die now. 717 */ 718 if (flags & APR_FILEPATH_SECUREROOTTEST) 719 return APR_EABOVEROOT; 720 721 /* Otherwise append another backpath. 722 */ 723 if (pathlen + 3 >= sizeof(path)) 724 return APR_ENAMETOOLONG; 725 path[pathlen++] = '.'; 726 path[pathlen++] = '.'; 727 if (addpath[segend]) { 728 path[pathlen++] = ((flags & APR_FILEPATH_NATIVE) 729 ? '\\' : ((flags & APR_FILEPATH_TRUENAME) 730 ? '/' : addpath[segend])); 731 } 732 /* The 'root' part of this path now includes the ../ path, 733 * because that backpath will not be parsed by the truename 734 * code below. 735 */ 736 keptlen = pathlen; 737 } 738 else 739 { 740 /* otherwise crop the prior segment 741 */ 742 do { 743 --pathlen; 744 } while (pathlen && path[pathlen - 1] != '/' 745 && path[pathlen - 1] != '\\'); 746 747 /* Now test if we are above where we started and back up 748 * the keptlen offset to reflect the added/altered path. 749 */ 750 if (pathlen < keptlen) 751 { 752 if (flags & APR_FILEPATH_SECUREROOTTEST) 753 return APR_EABOVEROOT; 754 keptlen = pathlen; 755 } 756 } 757 } 758 else /* not empty or dots */ 759 { 760#ifndef NETWARE 761 if (fixunc) { 762 const char *testpath = path; 763 const char *testroot; 764 apr_status_t testtype; 765 apr_size_t i = (addpath[segend] != '\0'); 766 767 /* This isn't legal unless the unc path is complete! 768 */ 769 if (seglen < segend) 770 return APR_EBADPATH; 771 if (pathlen + seglen + 1 >= sizeof(path)) 772 return APR_ENAMETOOLONG; 773 memcpy(path + pathlen, addpath, seglen + i); 774 775 /* Always add the trailing slash to a UNC segment 776 */ 777 path[pathlen + seglen] = ((flags & APR_FILEPATH_NATIVE) 778 ? '\\' : '/'); 779 pathlen += seglen + 1; 780 781 /* Recanonicalize the UNC root with the new UNC segment, 782 * and if we succeed, reset this test and the rootlen, 783 * and replace our path with the canonical UNC root path 784 */ 785 path[pathlen] = '\0'; 786 /* This call _should_ test the path 787 */ 788 testtype = apr_filepath_root(&testroot, &testpath, 789 APR_FILEPATH_TRUENAME 790 | (flags & APR_FILEPATH_NATIVE), 791 p); 792 if (testtype == APR_SUCCESS) { 793 rootlen = pathlen = (testpath - path); 794 memcpy(path, testroot, pathlen); 795 fixunc = 0; 796 } 797 else if (testtype != APR_EINCOMPLETE) { 798 /* apr_filepath_root was very unexpected so fail already 799 */ 800 return testtype; 801 } 802 } 803 else 804#endif 805 { 806 /* An actual segment, append it to the destination path 807 */ 808 apr_size_t i = (addpath[segend] != '\0'); 809 if (pathlen + seglen + i >= sizeof(path)) 810 return APR_ENAMETOOLONG; 811 memcpy(path + pathlen, addpath, seglen + i); 812 if (i) 813 path[pathlen + seglen] = ((flags & APR_FILEPATH_NATIVE) 814 ? '\\' : '/'); 815 pathlen += seglen + i; 816 } 817 } 818 819 /* Skip over trailing slash to the next segment 820 */ 821 if (addpath[segend]) 822 ++segend; 823 824 addpath += segend; 825 } 826 827 /* keptlen will be the baselen unless the addpath contained 828 * backpath elements. If so, and APR_FILEPATH_NOTABOVEROOT 829 * is specified (APR_FILEPATH_SECUREROOTTEST was caught above), 830 * compare the string beyond the root to assure the result path 831 * is still within given basepath. Note that the root path 832 * segment is thoroughly tested prior to path parsing. 833 */ 834 if ((flags & APR_FILEPATH_NOTABOVEROOT) && baselen) { 835 if (memcmp(basepath, path + rootlen, baselen) != 0) 836 return APR_EABOVEROOT; 837 838 /* Ahem... if we have a basepath without a trailing slash, 839 * we better be sure that /foo wasn't replaced with /foobar! 840 */ 841 if (basepath[baselen - 1] != '/' && basepath[baselen - 1] != '\\' 842 && path[rootlen + baselen] && path[rootlen + baselen] != '/' 843 && path[rootlen + baselen] != '\\') 844 return APR_EABOVEROOT; 845 } 846 847 if (addpath && (flags & APR_FILEPATH_TRUENAME)) { 848 /* We can always skip the root, it's already true-named. */ 849 if (rootlen > keptlen) 850 keptlen = rootlen; 851 if ((path[keptlen] == '/') || (path[keptlen] == '\\')) { 852 /* By rights, keptlen may grown longer than pathlen. 853 * we wont' use it again (in that case) so we don't care. 854 */ 855 ++keptlen; 856 } 857 /* Go through all the new segments */ 858 while (keptlen < pathlen) { 859 apr_finfo_t finfo; 860 char saveslash = 0; 861 seglen = 0; 862 /* find any slash and set it aside for a minute. */ 863 for (seglen = 0; keptlen + seglen < pathlen; ++seglen) { 864 if ((path[keptlen + seglen] == '/') || 865 (path[keptlen + seglen] == '\\')) { 866 saveslash = path[keptlen + seglen]; 867 break; 868 } 869 } 870 /* Null term for stat! */ 871 path[keptlen + seglen] = '\0'; 872 if ((rv = apr_stat(&finfo, path, 873 APR_FINFO_LINK | APR_FINFO_TYPE | APR_FINFO_NAME, p)) 874 == APR_SUCCESS) { 875 apr_size_t namelen = strlen(finfo.name); 876 877#if defined(OS2) /* only has case folding, never aliases that change the length */ 878 879 if (memcmp(finfo.name, path + keptlen, seglen) != 0) { 880 memcpy(path + keptlen, finfo.name, namelen); 881 } 882#else /* WIN32 || NETWARE; here there be aliases that gire and gimble and change length */ 883 884 if ((namelen != seglen) || 885 (memcmp(finfo.name, path + keptlen, seglen) != 0)) 886 { 887 if (namelen <= seglen) { 888 memcpy(path + keptlen, finfo.name, namelen); 889 if ((namelen < seglen) && saveslash) { 890 memmove(path + keptlen + namelen + 1, 891 path + keptlen + seglen + 1, 892 pathlen - keptlen - seglen); 893 pathlen += namelen - seglen; 894 seglen = namelen; 895 } 896 } 897 else { /* namelen > seglen */ 898 if (pathlen + namelen - seglen >= sizeof(path)) 899 return APR_ENAMETOOLONG; 900 if (saveslash) { 901 memmove(path + keptlen + namelen + 1, 902 path + keptlen + seglen + 1, 903 pathlen - keptlen - seglen); 904 } 905 memcpy(path + keptlen, finfo.name, namelen); 906 pathlen += namelen - seglen; 907 seglen = namelen; 908 } 909 } 910#endif /* !OS2 (Whatever that alias was we're over it) */ 911 912 /* That's it, the rest is path info. 913 * I don't know how we aught to handle this. Should 914 * we define a new error to indicate 'more info'? 915 * Should we split out the rest of the path? 916 */ 917 if ((finfo.filetype != APR_DIR) && 918 (finfo.filetype != APR_LNK) && saveslash) 919 rv = APR_ENOTDIR; 920#ifdef XXX_FIGURE_THIS_OUT 921 { 922 /* the example inserts a null between the end of 923 * the filename and the next segment, and increments 924 * the path length so we would return both segments. 925 */ 926 if (saveslash) { 927 keptlen += seglen; 928 path[keptlen] = saveslash; 929 if (pathlen + 1 >= sizeof(path)) 930 return APR_ENAMETOOLONG; 931 memmove(path + keptlen + 1, 932 path + keptlen, 933 pathlen - keptlen); 934 path[keptlen] = '\0'; 935 ++pathlen; 936 break; 937 } 938 } 939#endif 940 } 941 942 /* put back the '/' */ 943 if (saveslash) { 944 path[keptlen + seglen] = saveslash; 945 ++seglen; 946 } 947 keptlen += seglen; 948 949 if (rv != APR_SUCCESS) { 950 if (APR_STATUS_IS_ENOENT(rv)) 951 break; 952 if (APR_STATUS_IS_EPATHWILD(rv)) 953 /* This path included wildcards. The path elements 954 * that did not contain wildcards are canonicalized, 955 * so we will return the path, although later elements 956 * don't necessarily exist, and aren't canonical. 957 */ 958 break; 959 else if (APR_STATUS_IS_ENOTDIR(rv)) 960 /* This is a little more serious, we just added a name 961 * onto a filename (think http's PATH_INFO) 962 * If the caller is foolish enough to do this, we expect 963 * the've already canonicalized the root) that they knew 964 * what they are doing :( 965 */ 966 break; 967 else 968 return rv; 969 } 970 } 971 } 972 973 *newpath = apr_pstrmemdup(p, path, pathlen); 974 return APR_SUCCESS; 975} 976 977 978APR_DECLARE(apr_status_t) apr_filepath_list_split(apr_array_header_t **pathelts, 979 const char *liststr, 980 apr_pool_t *p) 981{ 982 return apr_filepath_list_split_impl(pathelts, liststr, ';', p); 983} 984 985APR_DECLARE(apr_status_t) apr_filepath_list_merge(char **liststr, 986 apr_array_header_t *pathelts, 987 apr_pool_t *p) 988{ 989 return apr_filepath_list_merge_impl(liststr, pathelts, ';', p); 990} 991 992 993APR_DECLARE(apr_status_t) apr_filepath_encoding(int *style, apr_pool_t *p) 994{ 995#if APR_HAS_UNICODE_FS 996 IF_WIN_OS_IS_UNICODE 997 { 998 *style = APR_FILEPATH_ENCODING_UTF8; 999 return APR_SUCCESS; 1000 } 1001#endif 1002 1003 *style = APR_FILEPATH_ENCODING_LOCALE; 1004 return APR_SUCCESS; 1005} 1006