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
apr_filepath_root(const char ** rootpath,const char ** inpath,apr_int32_t flags,apr_pool_t * p)40 APR_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)
same_drive(const char * path1,const char * path2)331 static 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
apr_filepath_merge(char ** newpath,const char * basepath,const char * addpath,apr_int32_t flags,apr_pool_t * p)352 APR_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
978 APR_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
985 APR_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
993 APR_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