~ubuntu-branches/ubuntu/feisty/apache2/feisty

« back to all changes in this revision

Viewing changes to srclib/apr/file_io/win32/filepath.c

  • Committer: Bazaar Package Importer
  • Author(s): Andreas Barth
  • Date: 2006-12-09 21:05:45 UTC
  • mfrom: (0.6.1 upstream)
  • Revision ID: james.westby@ubuntu.com-20061209210545-h70s0xaqc2v8vqr2
Tags: 2.2.3-3.2
* Non-maintainer upload.
* 043_ajp_connection_reuse: Patch from upstream Bugzilla, fixing a critical
  issue with regard to connection reuse in mod_proxy_ajp.
  Closes: #396265

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* Copyright 2000-2005 The Apache Software Foundation or its licensors, as
 
2
 * applicable.
 
3
 *
 
4
 * Licensed under the Apache License, Version 2.0 (the "License");
 
5
 * you may not use this file except in compliance with the License.
 
6
 * 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
 
 
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
 
 
331
APR_DECLARE(apr_status_t) apr_filepath_merge(char **newpath, 
 
332
                                             const char *basepath, 
 
333
                                             const char *addpath, 
 
334
                                             apr_int32_t flags,
 
335
                                             apr_pool_t *p)
 
336
{
 
337
    char path[APR_PATH_MAX]; /* isn't null term */
 
338
    const char *baseroot = NULL;
 
339
    const char *addroot;
 
340
    apr_size_t rootlen; /* the length of the root portion of path, d:/ is 3 */
 
341
    apr_size_t baselen; /* the length of basepath (excluding baseroot) */
 
342
    apr_size_t keptlen; /* the length of the retained basepath (incl root) */
 
343
    apr_size_t pathlen; /* the length of the result path */
 
344
    apr_size_t segend;  /* the end of the current segment */
 
345
    apr_size_t seglen;  /* the length of the segment (excl trailing chars) */
 
346
    apr_status_t basetype = 0; /* from parsing the basepath's baseroot */
 
347
    apr_status_t addtype;      /* from parsing the addpath's addroot */
 
348
    apr_status_t rv;
 
349
#ifndef NETWARE
 
350
    int fixunc = 0;  /* flag to complete an incomplete UNC basepath */
 
351
#endif
 
352
    
 
353
    /* Treat null as an empty path, otherwise split addroot from the addpath
 
354
     */
 
355
    if (!addpath) {
 
356
        addpath = addroot = "";
 
357
        addtype = APR_ERELATIVE;
 
358
    }
 
359
    else {
 
360
        /* This call _should_ test the path
 
361
         */
 
362
        addtype = apr_filepath_root(&addroot, &addpath, 
 
363
                                    APR_FILEPATH_TRUENAME
 
364
                                    | (flags & APR_FILEPATH_NATIVE),
 
365
                                    p);
 
366
        if (addtype == APR_SUCCESS) {
 
367
            addtype = APR_EABSOLUTE;
 
368
        }
 
369
        else if (addtype == APR_ERELATIVE) {
 
370
            addroot = "";
 
371
        }
 
372
        else if (addtype != APR_EINCOMPLETE) {
 
373
            /* apr_filepath_root was incomprehensible so fail already
 
374
             */
 
375
            return addtype;
 
376
        }
 
377
    }
 
378
 
 
379
    /* If addpath is (even partially) rooted, then basepath is
 
380
     * unused.  Ths violates any APR_FILEPATH_SECUREROOTTEST 
 
381
     * and APR_FILEPATH_NOTABSOLUTE flags specified.
 
382
     */
 
383
    if (addtype == APR_EABSOLUTE || addtype == APR_EINCOMPLETE)
 
384
    {
 
385
        if (flags & APR_FILEPATH_SECUREROOTTEST)
 
386
            return APR_EABOVEROOT;
 
387
        if (flags & APR_FILEPATH_NOTABSOLUTE)
 
388
            return addtype;
 
389
    }
 
390
 
 
391
    /* Optimized tests before we query the current working path
 
392
     */
 
393
    if (!basepath) {
 
394
 
 
395
        /* If APR_FILEPATH_NOTABOVEROOT wasn't specified,
 
396
         * we won't test the root again, it's ignored.
 
397
         * Waste no CPU retrieving the working path.
 
398
         */
 
399
        if (addtype == APR_EABSOLUTE && !(flags & APR_FILEPATH_NOTABOVEROOT)) {
 
400
            basepath = baseroot = "";
 
401
            basetype = APR_ERELATIVE;
 
402
        }
 
403
 
 
404
        /* If APR_FILEPATH_NOTABSOLUTE is specified, the caller 
 
405
         * requires an absolutely relative result, So do not retrieve 
 
406
         * the working path.
 
407
         */
 
408
        if (addtype == APR_ERELATIVE && (flags & APR_FILEPATH_NOTABSOLUTE)) {
 
409
            basepath = baseroot = "";
 
410
            basetype = APR_ERELATIVE;
 
411
        }
 
412
    }
 
413
 
 
414
    if (!basepath) 
 
415
    {
 
416
        /* Start with the current working path.  This is bass akwards,
 
417
         * but required since the compiler (at least vc) doesn't like
 
418
         * passing the address of a char const* for a char** arg.
 
419
         * We must grab the current path of the designated drive 
 
420
         * if addroot is given in drive-relative form (e.g. d:foo)
 
421
         */
 
422
        char *getpath;
 
423
#ifndef NETWARE
 
424
        if (addtype == APR_EINCOMPLETE && addroot[1] == ':')
 
425
            rv = filepath_drive_get(&getpath, addroot[0], flags, p);
 
426
        else
 
427
#endif
 
428
            rv = apr_filepath_get(&getpath, flags, p);
 
429
        if (rv != APR_SUCCESS)
 
430
            return rv;
 
431
        basepath = getpath;
 
432
    }
 
433
 
 
434
    if (!baseroot) {
 
435
        /* This call should _not_ test the path
 
436
         */
 
437
        basetype = apr_filepath_root(&baseroot, &basepath,
 
438
                                     (flags & APR_FILEPATH_NATIVE), p);
 
439
        if (basetype == APR_SUCCESS) {
 
440
            basetype = APR_EABSOLUTE;
 
441
        }
 
442
        else if (basetype == APR_ERELATIVE) {
 
443
            baseroot = "";
 
444
        }
 
445
        else if (basetype != APR_EINCOMPLETE) {
 
446
            /* apr_filepath_root was incomprehensible so fail already
 
447
             */
 
448
            return basetype;
 
449
        }
 
450
    }
 
451
    baselen = strlen(basepath);
 
452
 
 
453
    /* If APR_FILEPATH_NOTABSOLUTE is specified, the caller 
 
454
     * requires an absolutely relative result.  If the given 
 
455
     * basepath is not relative then fail.
 
456
     */
 
457
    if ((flags & APR_FILEPATH_NOTABSOLUTE) && basetype != APR_ERELATIVE)
 
458
        return basetype;
 
459
 
 
460
    /* The Win32 nightmare on unc street... start combining for
 
461
     * many possible root combinations.
 
462
     */
 
463
    if (addtype == APR_EABSOLUTE)
 
464
    {
 
465
        /* Ignore the given root path, and start with the addroot
 
466
         */
 
467
        if ((flags & APR_FILEPATH_NOTABOVEROOT) 
 
468
                && strncmp(baseroot, addroot, strlen(baseroot)))
 
469
            return APR_EABOVEROOT;
 
470
        keptlen = 0;
 
471
        rootlen = pathlen = strlen(addroot);
 
472
        memcpy(path, addroot, pathlen);
 
473
    }
 
474
    else if (addtype == APR_EINCOMPLETE)
 
475
    {
 
476
        /* There are several types of incomplete paths, 
 
477
         *     incomplete UNC paths         (//foo/ or //),
 
478
         *     drives without rooted paths  (d: as in d:foo), 
 
479
         * and simple roots                 (/ as in /foo).
 
480
         * Deal with these in significantly different manners...
 
481
         */
 
482
#ifndef NETWARE
 
483
        if ((addroot[0] == '/' || addroot[0] == '\\') &&
 
484
            (addroot[1] == '/' || addroot[1] == '\\')) 
 
485
        {
 
486
            /* Ignore the given root path if the incomplete addpath is UNC,
 
487
             * (note that the final result will be incomplete).
 
488
             */
 
489
            if (flags & APR_FILEPATH_NOTRELATIVE)
 
490
                return addtype;
 
491
            if ((flags & APR_FILEPATH_NOTABOVEROOT) 
 
492
                    && strncmp(baseroot, addroot, strlen(baseroot)))
 
493
                return APR_EABOVEROOT;
 
494
            fixunc = 1;
 
495
            keptlen = 0;
 
496
            rootlen = pathlen = strlen(addroot);
 
497
            memcpy(path, addroot, pathlen);
 
498
        }
 
499
        else
 
500
#endif            
 
501
        if ((addroot[0] == '/' || addroot[0] == '\\') && !addroot[1]) 
 
502
        {
 
503
            /* Bring together the drive or UNC root from the baseroot
 
504
             * if the addpath is a simple root and basepath is rooted,
 
505
             * otherwise disregard the basepath entirely.
 
506
             */
 
507
            if (basetype != APR_EABSOLUTE && (flags & APR_FILEPATH_NOTRELATIVE))
 
508
                return basetype;
 
509
            if (basetype != APR_ERELATIVE) {
 
510
#ifndef NETWARE
 
511
                if (basetype == APR_INCOMPLETE 
 
512
                        && (baseroot[0] == '/' || baseroot[0] == '\\')
 
513
                        && (baseroot[1] == '/' || baseroot[1] == '\\'))
 
514
                    fixunc = 1;
 
515
#endif
 
516
                keptlen = rootlen = pathlen = strlen(baseroot);
 
517
                memcpy(path, baseroot, pathlen);
 
518
            }
 
519
            else {
 
520
                if (flags & APR_FILEPATH_NOTABOVEROOT)
 
521
                    return APR_EABOVEROOT;
 
522
                keptlen = 0;
 
523
                rootlen = pathlen = strlen(addroot);
 
524
                memcpy(path, addroot, pathlen);
 
525
            }
 
526
        }
 
527
#ifdef NETWARE
 
528
        else if (filepath_has_drive(addroot, DRIVE_ONLY, p)) 
 
529
        {
 
530
            /* If the addroot is a drive (without a volume root)
 
531
             * use the basepath _if_ it matches this drive letter!
 
532
             * Otherwise we must discard the basepath.
 
533
             */
 
534
            if (!filepath_compare_drive(addroot, baseroot, p) && 
 
535
                filepath_has_drive(baseroot, 0, p)) {
 
536
#else
 
537
        else if (addroot[0] && addroot[1] == ':' && !addroot[2]) 
 
538
        {
 
539
            /* If the addroot is a drive (without a volume root)
 
540
             * use the basepath _if_ it matches this drive letter!
 
541
             * Otherwise we must discard the basepath.
 
542
             */
 
543
            if (addroot[0] == baseroot[0] && baseroot[1] == ':') {
 
544
#endif
 
545
                /* Base the result path on the basepath
 
546
                 */
 
547
                if (basetype != APR_EABSOLUTE && (flags & APR_FILEPATH_NOTRELATIVE))
 
548
                    return basetype;
 
549
                rootlen = strlen(baseroot);
 
550
                keptlen = pathlen = rootlen + baselen;
 
551
                if (keptlen >= sizeof(path))
 
552
                    return APR_ENAMETOOLONG;
 
553
                memcpy(path, baseroot, rootlen);
 
554
                memcpy(path + rootlen, basepath, baselen);
 
555
            } 
 
556
            else {
 
557
                if (flags & APR_FILEPATH_NOTRELATIVE)
 
558
                    return addtype;
 
559
                if (flags & APR_FILEPATH_NOTABOVEROOT)
 
560
                    return APR_EABOVEROOT;
 
561
                keptlen = 0;
 
562
                rootlen = pathlen = strlen(addroot);
 
563
                memcpy(path, addroot, pathlen);
 
564
            }
 
565
        }
 
566
        else {
 
567
            /* Now this is unexpected, we aren't aware of any other
 
568
             * incomplete path forms!  Fail now.
 
569
             */
 
570
            return APR_EBADPATH;
 
571
        }
 
572
    }
 
573
    else { /* addtype == APR_ERELATIVE */
 
574
        /* If both paths are relative, fail early
 
575
         */
 
576
        if (basetype != APR_EABSOLUTE && (flags & APR_FILEPATH_NOTRELATIVE))
 
577
            return basetype;
 
578
 
 
579
#ifndef NETWARE
 
580
        /* An incomplete UNC path must be completed
 
581
         */
 
582
        if (basetype == APR_INCOMPLETE 
 
583
                && (baseroot[0] == '/' || baseroot[0] == '\\')
 
584
                && (baseroot[1] == '/' || baseroot[1] == '\\'))
 
585
            fixunc = 1;
 
586
#endif
 
587
 
 
588
        /* Base the result path on the basepath
 
589
         */
 
590
        rootlen = strlen(baseroot);
 
591
        keptlen = pathlen = rootlen + baselen;
 
592
        if (keptlen >= sizeof(path))
 
593
            return APR_ENAMETOOLONG;
 
594
        memcpy(path, baseroot, rootlen);
 
595
        memcpy(path + rootlen, basepath, baselen);
 
596
    }
 
597
 
 
598
    /* '/' terminate the given root path unless it's already terminated
 
599
     * or is an incomplete drive root.  Correct the trailing slash unless
 
600
     * we have an incomplete UNC path still to fix.
 
601
     */
 
602
    if (pathlen && path[pathlen - 1] != ':') {
 
603
        if (path[pathlen - 1] != '/' && path[pathlen - 1] != '\\') {
 
604
            if (pathlen + 1 >= sizeof(path))
 
605
                return APR_ENAMETOOLONG;
 
606
        
 
607
            path[pathlen++] = ((flags & APR_FILEPATH_NATIVE) ? '\\' : '/');
 
608
        }
 
609
    /*  XXX: wrong, but gotta figure out what I intended;
 
610
     *  else if (!fixunc)
 
611
     *      path[pathlen++] = ((flags & APR_FILEPATH_NATIVE) ? '\\' : '/');
 
612
     */
 
613
    }
 
614
 
 
615
    while (*addpath) 
 
616
    {
 
617
        /* Parse each segment, find the closing '/' 
 
618
         */
 
619
        seglen = 0;
 
620
        while (addpath[seglen] && addpath[seglen] != '/'
 
621
                               && addpath[seglen] != '\\')
 
622
            ++seglen;
 
623
 
 
624
        /* Truncate all trailing spaces and all but the first two dots */
 
625
        segend = seglen;
 
626
        while (seglen && (addpath[seglen - 1] == ' ' 
 
627
                       || addpath[seglen - 1] == '.')) {
 
628
            if (seglen > 2 || addpath[seglen - 1] != '.' || addpath[0] != '.')
 
629
                --seglen;
 
630
            else
 
631
                break;
 
632
        }
 
633
 
 
634
        if (seglen == 0 || (seglen == 1 && addpath[0] == '.')) 
 
635
        {
 
636
            /* NOTE: win32 _hates_ '/ /' and '/. /' (yes, with spaces in there)
 
637
             * so eliminate all preconceptions that it is valid.
 
638
             */
 
639
            if (seglen < segend)
 
640
                return APR_EBADPATH;
 
641
 
 
642
#ifndef NETWARE
 
643
            /* This isn't legal unless the unc path is completed
 
644
             */
 
645
            if (fixunc)
 
646
                return APR_EBADPATH;
 
647
#endif
 
648
 
 
649
            /* Otherwise, this is a noop segment (/ or ./) so ignore it 
 
650
             */
 
651
        }
 
652
        else if (seglen == 2 && addpath[0] == '.' && addpath[1] == '.') 
 
653
        {
 
654
            /* NOTE: win32 _hates_ '/.. /' (yes, with a space in there)
 
655
             * and '/..../', some functions treat it as ".", and some 
 
656
             * fail! Eliminate all preconceptions that they are valid.
 
657
             */
 
658
            if (seglen < segend && (seglen != 3 || addpath[2] != '.'))
 
659
                return APR_EBADPATH;
 
660
 
 
661
#ifndef NETWARE
 
662
            /* This isn't legal unless the unc path is completed
 
663
             */
 
664
            if (fixunc)
 
665
                return APR_EBADPATH;
 
666
#endif
 
667
 
 
668
            /* backpath (../) when an absolute path is given */
 
669
            if (rootlen && (pathlen <= rootlen)) 
 
670
            {
 
671
                /* Attempt to move above root.  Always die if the 
 
672
                 * APR_FILEPATH_SECUREROOTTEST flag is specified.
 
673
                 */
 
674
                if (flags & APR_FILEPATH_SECUREROOTTEST)
 
675
                    return APR_EABOVEROOT;
 
676
                
 
677
                /* Otherwise this is simply a noop, above root is root.
 
678
                 */
 
679
            }
 
680
            else if (pathlen == 0 ||
 
681
                     (pathlen >= 3 && (pathlen == 3
 
682
                                    || path[pathlen - 4] == ':')
 
683
                                   &&  path[pathlen - 3] == '.' 
 
684
                                   &&  path[pathlen - 2] == '.' 
 
685
                                   && (path[pathlen - 1] == '/' 
 
686
                                    || path[pathlen - 1] == '\\')))
 
687
            {
 
688
                /* Path is already backpathed or empty, if the
 
689
                 * APR_FILEPATH_SECUREROOTTEST.was given die now.
 
690
                 */
 
691
                if (flags & APR_FILEPATH_SECUREROOTTEST)
 
692
                    return APR_EABOVEROOT;
 
693
 
 
694
                /* Otherwise append another backpath.
 
695
                 */
 
696
                if (pathlen + 3 >= sizeof(path))
 
697
                    return APR_ENAMETOOLONG;
 
698
                memcpy(path + pathlen, ((flags & APR_FILEPATH_NATIVE) 
 
699
                                          ? "..\\" : "../"), 3);
 
700
                pathlen += 3;
 
701
                /* The 'root' part of this path now includes the ../ path,
 
702
                 * because that backpath will not be parsed by the truename
 
703
                 * code below.
 
704
                 */
 
705
                keptlen = pathlen;
 
706
            }
 
707
            else 
 
708
            {
 
709
                /* otherwise crop the prior segment 
 
710
                 */
 
711
                do {
 
712
                    --pathlen;
 
713
                } while (pathlen && path[pathlen - 1] != '/'
 
714
                                 && path[pathlen - 1] != '\\');
 
715
 
 
716
                /* Now test if we are above where we started and back up
 
717
                 * the keptlen offset to reflect the added/altered path.
 
718
                 */
 
719
                if (pathlen < keptlen) 
 
720
                {
 
721
                    if (flags & APR_FILEPATH_SECUREROOTTEST)
 
722
                        return APR_EABOVEROOT;
 
723
                    keptlen = pathlen;
 
724
                }
 
725
            }
 
726
        }
 
727
        else /* not empty or dots */
 
728
        {
 
729
#ifndef NETWARE
 
730
            if (fixunc) {
 
731
                const char *testpath = path;
 
732
                const char *testroot;
 
733
                apr_status_t testtype;
 
734
                apr_size_t i = (addpath[segend] != '\0');
 
735
                
 
736
                /* This isn't legal unless the unc path is complete!
 
737
                 */
 
738
                if (seglen < segend)
 
739
                    return APR_EBADPATH;
 
740
                if (pathlen + seglen + 1 >= sizeof(path))
 
741
                    return APR_ENAMETOOLONG;
 
742
                memcpy(path + pathlen, addpath, seglen + i);
 
743
                
 
744
                /* Always add the trailing slash to a UNC segment
 
745
                 */
 
746
                path[pathlen + seglen] = ((flags & APR_FILEPATH_NATIVE) 
 
747
                                             ? '\\' : '/');
 
748
                pathlen += seglen + 1;
 
749
 
 
750
                /* Recanonicalize the UNC root with the new UNC segment,
 
751
                 * and if we succeed, reset this test and the rootlen,
 
752
                 * and replace our path with the canonical UNC root path
 
753
                 */
 
754
                path[pathlen] = '\0';
 
755
                /* This call _should_ test the path
 
756
                 */
 
757
                testtype = apr_filepath_root(&testroot, &testpath, 
 
758
                                             APR_FILEPATH_TRUENAME
 
759
                                             | (flags & APR_FILEPATH_NATIVE),
 
760
                                             p);
 
761
                if (testtype == APR_SUCCESS) {
 
762
                    rootlen = pathlen = (testpath - path);
 
763
                    memcpy(path, testroot, pathlen);
 
764
                    fixunc = 0;
 
765
                }
 
766
                else if (testtype != APR_EINCOMPLETE) {
 
767
                    /* apr_filepath_root was very unexpected so fail already
 
768
                     */
 
769
                    return testtype;
 
770
                }
 
771
            }
 
772
            else
 
773
#endif
 
774
            {
 
775
                /* An actual segment, append it to the destination path
 
776
                 */
 
777
                apr_size_t i = (addpath[segend] != '\0');
 
778
                if (pathlen + seglen + i >= sizeof(path))
 
779
                    return APR_ENAMETOOLONG;
 
780
                memcpy(path + pathlen, addpath, seglen + i);
 
781
                if (i)
 
782
                    path[pathlen + seglen] = ((flags & APR_FILEPATH_NATIVE) 
 
783
                                                 ? '\\' : '/');
 
784
                pathlen += seglen + i;
 
785
            }
 
786
        }
 
787
 
 
788
        /* Skip over trailing slash to the next segment
 
789
         */
 
790
        if (addpath[segend])
 
791
            ++segend;
 
792
 
 
793
        addpath += segend;
 
794
    }
 
795
    
 
796
    /* keptlen will be the baselen unless the addpath contained
 
797
     * backpath elements.  If so, and APR_FILEPATH_NOTABOVEROOT
 
798
     * is specified (APR_FILEPATH_SECUREROOTTEST was caught above),
 
799
     * compare the string beyond the root to assure the result path 
 
800
     * is still within given basepath.  Note that the root path 
 
801
     * segment is thoroughly tested prior to path parsing.
 
802
     */
 
803
    if ((flags & APR_FILEPATH_NOTABOVEROOT) && baselen) {
 
804
        if (memcmp(basepath, path + rootlen, baselen) != 0)
 
805
            return APR_EABOVEROOT;
 
806
 
 
807
         /* Ahem... if we have a basepath without a trailing slash,
 
808
          * we better be sure that /foo wasn't replaced with /foobar!
 
809
          */
 
810
        if (basepath[baselen - 1] != '/' && basepath[baselen - 1] != '\\'
 
811
              && path[rootlen + baselen] && path[rootlen + baselen] != '/' 
 
812
                                         && path[rootlen + baselen] != '\\')
 
813
            return APR_EABOVEROOT;
 
814
    }
 
815
 
 
816
    if (addpath && (flags & APR_FILEPATH_TRUENAME)) {
 
817
        /* We can always skip the root, it's already true-named. */
 
818
        if (rootlen > keptlen)
 
819
            keptlen = rootlen;
 
820
        if ((path[keptlen] == '/') || (path[keptlen] == '\\')) {
 
821
            /* By rights, keptlen may grown longer than pathlen.
 
822
             * we wont' use it again (in that case) so we don't care.
 
823
             */
 
824
            ++keptlen;
 
825
        }
 
826
        /* Go through all the new segments */
 
827
        while (keptlen < pathlen) {
 
828
            apr_finfo_t finfo;
 
829
            char saveslash = 0;
 
830
            seglen = 0;
 
831
            /* find any slash and set it aside for a minute. */
 
832
            for (seglen = 0; keptlen + seglen < pathlen; ++seglen) {
 
833
                if ((path[keptlen + seglen] == '/')  ||
 
834
                    (path[keptlen + seglen] == '\\')) {
 
835
                    saveslash = path[keptlen + seglen];
 
836
                    break;
 
837
                }
 
838
            }
 
839
            /* Null term for stat! */
 
840
            path[keptlen + seglen] = '\0';
 
841
            if ((rv = apr_stat(&finfo, path, 
 
842
                               APR_FINFO_LINK | APR_FINFO_TYPE | APR_FINFO_NAME, p))
 
843
                == APR_SUCCESS) {
 
844
                apr_size_t namelen = strlen(finfo.name);
 
845
 
 
846
#if defined(OS2) /* only has case folding, never aliases that change the length */
 
847
 
 
848
                if (memcmp(finfo.name, path + keptlen, seglen) != 0) {
 
849
                    memcpy(path + keptlen, finfo.name, namelen);
 
850
                }
 
851
#else /* WIN32 || NETWARE; here there be aliases that gire and gimble and change length */
 
852
 
 
853
                if ((namelen != seglen) || 
 
854
                    (memcmp(finfo.name, path + keptlen, seglen) != 0)) 
 
855
                {
 
856
                    if (namelen <= seglen) {
 
857
                        memcpy(path + keptlen, finfo.name, namelen);
 
858
                        if ((namelen < seglen) && saveslash) {
 
859
                            memmove(path + keptlen + namelen + 1,
 
860
                                   path + keptlen + seglen + 1,
 
861
                                   pathlen - keptlen - seglen);
 
862
                            pathlen += namelen - seglen;
 
863
                            seglen = namelen;
 
864
                        }
 
865
                    }
 
866
                    else { /* namelen > seglen */
 
867
                        if (pathlen + namelen - seglen >= sizeof(path))
 
868
                            return APR_ENAMETOOLONG;
 
869
                        if (saveslash) {
 
870
                            memmove(path + keptlen + namelen + 1,
 
871
                                   path + keptlen + seglen + 1,
 
872
                                   pathlen - keptlen - seglen);
 
873
                        }
 
874
                        memcpy(path + keptlen, finfo.name, namelen);
 
875
                        pathlen += namelen - seglen;
 
876
                        seglen = namelen;
 
877
                    }
 
878
                }
 
879
#endif /* !OS2 (Whatever that alias was we're over it) */
 
880
 
 
881
                /* That's it, the rest is path info. 
 
882
                 * I don't know how we aught to handle this.  Should
 
883
                 * we define a new error to indicate 'more info'?
 
884
                 * Should we split out the rest of the path?
 
885
                 */
 
886
                if ((finfo.filetype != APR_DIR) && 
 
887
                    (finfo.filetype != APR_LNK) && saveslash) 
 
888
                    rv = APR_ENOTDIR;
 
889
#ifdef XXX_FIGURE_THIS_OUT
 
890
                {
 
891
                    /* the example inserts a null between the end of 
 
892
                     * the filename and the next segment, and increments
 
893
                     * the path length so we would return both segments.
 
894
                     */
 
895
                    if (saveslash) {
 
896
                        keptlen += seglen;
 
897
                        path[keptlen] = saveslash;
 
898
                        if (pathlen + 1 >= sizeof(path))
 
899
                            return APR_ENAMETOOLONG;
 
900
                        memmove(path + keptlen + 1,
 
901
                               path + keptlen,
 
902
                               pathlen - keptlen);
 
903
                        path[keptlen] = '\0';
 
904
                        ++pathlen;
 
905
                        break;
 
906
                    }
 
907
                }
 
908
#endif
 
909
            }
 
910
 
 
911
            /* put back the '/' */
 
912
            if (saveslash) {
 
913
                path[keptlen + seglen] = saveslash;
 
914
                ++seglen;
 
915
            }
 
916
            keptlen += seglen;
 
917
 
 
918
            if (rv != APR_SUCCESS) {
 
919
                if (APR_STATUS_IS_ENOENT(rv))
 
920
                    break;
 
921
                if (APR_STATUS_IS_EPATHWILD(rv))
 
922
                    /* This path included wildcards.  The path elements
 
923
                     * that did not contain wildcards are canonicalized,
 
924
                     * so we will return the path, although later elements
 
925
                     * don't necessarily exist, and aren't canonical.
 
926
                     */
 
927
                    break;
 
928
                else if (APR_STATUS_IS_ENOTDIR(rv))
 
929
                    /* This is a little more serious, we just added a name
 
930
                     * onto a filename (think http's PATH_INFO)
 
931
                     * If the caller is foolish enough to do this, we expect
 
932
                     * the've already canonicalized the root) that they knew
 
933
                     * what they are doing :(
 
934
                     */
 
935
                    break;
 
936
                else
 
937
                    return rv;
 
938
            }
 
939
        }
 
940
    }
 
941
 
 
942
    *newpath = apr_pmemdup(p, path, pathlen + 1);
 
943
    (*newpath)[pathlen] = '\0';
 
944
    return APR_SUCCESS;
 
945
}
 
946
 
 
947
 
 
948
APR_DECLARE(apr_status_t) apr_filepath_list_split(apr_array_header_t **pathelts,
 
949
                                                  const char *liststr,
 
950
                                                  apr_pool_t *p)
 
951
{
 
952
    return apr_filepath_list_split_impl(pathelts, liststr, ';', p);
 
953
}
 
954
 
 
955
APR_DECLARE(apr_status_t) apr_filepath_list_merge(char **liststr,
 
956
                                                  apr_array_header_t *pathelts,
 
957
                                                  apr_pool_t *p)
 
958
{
 
959
    return apr_filepath_list_merge_impl(liststr, pathelts, ';', p);
 
960
}
 
961
 
 
962
 
 
963
APR_DECLARE(apr_status_t) apr_filepath_encoding(int *style, apr_pool_t *p)
 
964
{
 
965
#if APR_HAS_UNICODE_FS
 
966
    IF_WIN_OS_IS_UNICODE
 
967
    {
 
968
        *style = APR_FILEPATH_ENCODING_UTF8;
 
969
        return APR_SUCCESS;
 
970
    }
 
971
#endif
 
972
 
 
973
    *style = APR_FILEPATH_ENCODING_LOCALE;
 
974
    return APR_SUCCESS;
 
975
}