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