1251875Speter/* Licensed to the Apache Software Foundation (ASF) under one or more
2251875Speter * contributor license agreements.  See the NOTICE file distributed with
3251875Speter * this work for additional information regarding copyright ownership.
4251875Speter * The ASF licenses this file to You under the Apache License, Version 2.0
5251875Speter * (the "License"); you may not use this file except in compliance with
6251875Speter * the License.  You may obtain a copy of the License at
7251875Speter *
8251875Speter *     http://www.apache.org/licenses/LICENSE-2.0
9251875Speter *
10251875Speter * Unless required by applicable law or agreed to in writing, software
11251875Speter * distributed under the License is distributed on an "AS IS" BASIS,
12251875Speter * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13251875Speter * See the License for the specific language governing permissions and
14251875Speter * limitations under the License.
15251875Speter */
16251875Speter
17251875Speter#include "apr.h"
18251875Speter#include "apr_private.h"
19251875Speter#include "apr_arch_file_io.h"
20251875Speter#include "apr_file_io.h"
21251875Speter#include "apr_strings.h"
22251875Speter#define APR_WANT_STRFUNC
23251875Speter#include "apr_want.h"
24251875Speter#if APR_HAVE_UNISTD_H
25251875Speter#include <unistd.h>
26251875Speter#endif
27251875Speter
28251875Speter/* Win32 malpropism that can go away once everyone believes this
29251875Speter * code is golden, and I'm not testing it anymore :-)
30251875Speter */
31251875Speter#if APR_HAVE_DIRENT_H
32251875Speter#include <dirent.h>
33251875Speter#endif
34251875Speter
35251875Speter/* Any OS that requires/refuses trailing slashes should be dealt with here.
36251875Speter */
37251875SpeterAPR_DECLARE(apr_status_t) apr_filepath_get(char **defpath, apr_int32_t flags,
38251875Speter                                           apr_pool_t *p)
39251875Speter{
40251875Speter    char path[APR_PATH_MAX];
41251875Speter
42251875Speter    if (!getcwd(path, sizeof(path))) {
43251875Speter        if (errno == ERANGE)
44251875Speter            return APR_ENAMETOOLONG;
45251875Speter        else
46251875Speter            return errno;
47251875Speter    }
48251875Speter    *defpath = apr_pstrdup(p, path);
49251875Speter
50251875Speter    return APR_SUCCESS;
51251875Speter}
52251875Speter
53251875Speter
54251875Speter/* Any OS that requires/refuses trailing slashes should be dealt with here
55251875Speter */
56251875SpeterAPR_DECLARE(apr_status_t) apr_filepath_set(const char *path, apr_pool_t *p)
57251875Speter{
58251875Speter    if (chdir(path) != 0)
59251875Speter        return errno;
60251875Speter
61251875Speter    return APR_SUCCESS;
62251875Speter}
63251875Speter
64251875SpeterAPR_DECLARE(apr_status_t) apr_filepath_root(const char **rootpath,
65251875Speter                                            const char **inpath,
66251875Speter                                            apr_int32_t flags,
67251875Speter                                            apr_pool_t *p)
68251875Speter{
69251875Speter    if (**inpath == '/') {
70251875Speter        *rootpath = apr_pstrdup(p, "/");
71251875Speter        do {
72251875Speter            ++(*inpath);
73251875Speter        } while (**inpath == '/');
74251875Speter
75251875Speter        return APR_SUCCESS;
76251875Speter    }
77251875Speter
78251875Speter    return APR_ERELATIVE;
79251875Speter}
80251875Speter
81251875SpeterAPR_DECLARE(apr_status_t) apr_filepath_merge(char **newpath,
82251875Speter                                             const char *rootpath,
83251875Speter                                             const char *addpath,
84251875Speter                                             apr_int32_t flags,
85251875Speter                                             apr_pool_t *p)
86251875Speter{
87251875Speter    char *path;
88251875Speter    apr_size_t rootlen; /* is the length of the src rootpath */
89251875Speter    apr_size_t maxlen;  /* maximum total path length */
90251875Speter    apr_size_t keptlen; /* is the length of the retained rootpath */
91251875Speter    apr_size_t pathlen; /* is the length of the result path */
92251875Speter    apr_size_t seglen;  /* is the end of the current segment */
93251875Speter    apr_status_t rv;
94251875Speter
95251875Speter    /* Treat null as an empty path.
96251875Speter     */
97251875Speter    if (!addpath)
98251875Speter        addpath = "";
99251875Speter
100251875Speter    if (addpath[0] == '/') {
101251875Speter        /* If addpath is rooted, then rootpath is unused.
102251875Speter         * Ths violates any APR_FILEPATH_SECUREROOTTEST and
103251875Speter         * APR_FILEPATH_NOTABSOLUTE flags specified.
104251875Speter         */
105251875Speter        if (flags & APR_FILEPATH_SECUREROOTTEST)
106251875Speter            return APR_EABOVEROOT;
107251875Speter        if (flags & APR_FILEPATH_NOTABSOLUTE)
108251875Speter            return APR_EABSOLUTE;
109251875Speter
110251875Speter        /* If APR_FILEPATH_NOTABOVEROOT wasn't specified,
111251875Speter         * we won't test the root again, it's ignored.
112251875Speter         * Waste no CPU retrieving the working path.
113251875Speter         */
114251875Speter        if (!rootpath && !(flags & APR_FILEPATH_NOTABOVEROOT))
115251875Speter            rootpath = "";
116251875Speter    }
117251875Speter    else {
118251875Speter        /* If APR_FILEPATH_NOTABSOLUTE is specified, the caller
119251875Speter         * requires a relative result.  If the rootpath is
120251875Speter         * ommitted, we do not retrieve the working path,
121251875Speter         * if rootpath was supplied as absolute then fail.
122251875Speter         */
123251875Speter        if (flags & APR_FILEPATH_NOTABSOLUTE) {
124251875Speter            if (!rootpath)
125251875Speter                rootpath = "";
126251875Speter            else if (rootpath[0] == '/')
127251875Speter                return APR_EABSOLUTE;
128251875Speter        }
129251875Speter    }
130251875Speter
131251875Speter    if (!rootpath) {
132251875Speter        /* Start with the current working path.  This is bass akwards,
133251875Speter         * but required since the compiler (at least vc) doesn't like
134251875Speter         * passing the address of a char const* for a char** arg.
135251875Speter         */
136251875Speter        char *getpath;
137251875Speter        rv = apr_filepath_get(&getpath, flags, p);
138251875Speter        rootpath = getpath;
139251875Speter        if (rv != APR_SUCCESS)
140251875Speter            return errno;
141251875Speter
142251875Speter        /* XXX: Any kernel subject to goofy, uncanonical results
143251875Speter         * must run the rootpath against the user's given flags.
144251875Speter         * Simplest would be a recursive call to apr_filepath_merge
145251875Speter         * with an empty (not null) rootpath and addpath of the cwd.
146251875Speter         */
147251875Speter    }
148251875Speter
149251875Speter    rootlen = strlen(rootpath);
150251875Speter    maxlen = rootlen + strlen(addpath) + 4; /* 4 for slashes at start, after
151251875Speter                                             * root, and at end, plus trailing
152251875Speter                                             * null */
153251875Speter    if (maxlen > APR_PATH_MAX) {
154251875Speter        return APR_ENAMETOOLONG;
155251875Speter    }
156251875Speter    path = (char *)apr_palloc(p, maxlen);
157251875Speter
158251875Speter    if (addpath[0] == '/') {
159251875Speter        /* Ignore the given root path, strip off leading
160251875Speter         * '/'s to a single leading '/' from the addpath,
161251875Speter         * and leave addpath at the first non-'/' character.
162251875Speter         */
163251875Speter        keptlen = 0;
164251875Speter        while (addpath[0] == '/')
165251875Speter            ++addpath;
166251875Speter        path[0] = '/';
167251875Speter        pathlen = 1;
168251875Speter    }
169251875Speter    else {
170251875Speter        /* If both paths are relative, fail early
171251875Speter         */
172251875Speter        if (rootpath[0] != '/' && (flags & APR_FILEPATH_NOTRELATIVE))
173251875Speter            return APR_ERELATIVE;
174251875Speter
175251875Speter        /* Base the result path on the rootpath
176251875Speter         */
177251875Speter        keptlen = rootlen;
178251875Speter        memcpy(path, rootpath, rootlen);
179251875Speter
180251875Speter        /* Always '/' terminate the given root path
181251875Speter         */
182251875Speter        if (keptlen && path[keptlen - 1] != '/') {
183251875Speter            path[keptlen++] = '/';
184251875Speter        }
185251875Speter        pathlen = keptlen;
186251875Speter    }
187251875Speter
188251875Speter    while (*addpath) {
189251875Speter        /* Parse each segment, find the closing '/'
190251875Speter         */
191251875Speter        const char *next = addpath;
192251875Speter        while (*next && (*next != '/')) {
193251875Speter            ++next;
194251875Speter        }
195251875Speter        seglen = next - addpath;
196251875Speter
197251875Speter        if (seglen == 0 || (seglen == 1 && addpath[0] == '.')) {
198251875Speter            /* noop segment (/ or ./) so skip it
199251875Speter             */
200251875Speter        }
201251875Speter        else if (seglen == 2 && addpath[0] == '.' && addpath[1] == '.') {
202251875Speter            /* backpath (../) */
203251875Speter            if (pathlen == 1 && path[0] == '/') {
204251875Speter                /* Attempt to move above root.  Always die if the
205251875Speter                 * APR_FILEPATH_SECUREROOTTEST flag is specified.
206251875Speter                 */
207251875Speter                if (flags & APR_FILEPATH_SECUREROOTTEST) {
208251875Speter                    return APR_EABOVEROOT;
209251875Speter                }
210251875Speter
211251875Speter                /* Otherwise this is simply a noop, above root is root.
212251875Speter                 * Flag that rootpath was entirely replaced.
213251875Speter                 */
214251875Speter                keptlen = 0;
215251875Speter            }
216251875Speter            else if (pathlen == 0
217251875Speter                     || (pathlen == 3
218251875Speter                         && !memcmp(path + pathlen - 3, "../", 3))
219251875Speter                     || (pathlen  > 3
220251875Speter                         && !memcmp(path + pathlen - 4, "/../", 4))) {
221251875Speter                /* Path is already backpathed or empty, if the
222251875Speter                 * APR_FILEPATH_SECUREROOTTEST.was given die now.
223251875Speter                 */
224251875Speter                if (flags & APR_FILEPATH_SECUREROOTTEST) {
225251875Speter                    return APR_EABOVEROOT;
226251875Speter                }
227251875Speter
228251875Speter                /* Otherwise append another backpath, including
229251875Speter                 * trailing slash if present.
230251875Speter                 */
231251875Speter                memcpy(path + pathlen, "../", *next ? 3 : 2);
232251875Speter                pathlen += *next ? 3 : 2;
233251875Speter            }
234251875Speter            else {
235251875Speter                /* otherwise crop the prior segment
236251875Speter                 */
237251875Speter                do {
238251875Speter                    --pathlen;
239251875Speter                } while (pathlen && path[pathlen - 1] != '/');
240251875Speter            }
241251875Speter
242251875Speter            /* Now test if we are above where we started and back up
243251875Speter             * the keptlen offset to reflect the added/altered path.
244251875Speter             */
245251875Speter            if (pathlen < keptlen) {
246251875Speter                if (flags & APR_FILEPATH_SECUREROOTTEST) {
247251875Speter                    return APR_EABOVEROOT;
248251875Speter                }
249251875Speter                keptlen = pathlen;
250251875Speter            }
251251875Speter        }
252251875Speter        else {
253251875Speter            /* An actual segment, append it to the destination path
254251875Speter             */
255251875Speter            if (*next) {
256251875Speter                seglen++;
257251875Speter            }
258251875Speter            memcpy(path + pathlen, addpath, seglen);
259251875Speter            pathlen += seglen;
260251875Speter        }
261251875Speter
262251875Speter        /* Skip over trailing slash to the next segment
263251875Speter         */
264251875Speter        if (*next) {
265251875Speter            ++next;
266251875Speter        }
267251875Speter
268251875Speter        addpath = next;
269251875Speter    }
270251875Speter    path[pathlen] = '\0';
271251875Speter
272251875Speter    /* keptlen will be the rootlen unless the addpath contained
273251875Speter     * backpath elements.  If so, and APR_FILEPATH_NOTABOVEROOT
274251875Speter     * is specified (APR_FILEPATH_SECUREROOTTEST was caught above),
275251875Speter     * compare the original root to assure the result path is
276251875Speter     * still within given root path.
277251875Speter     */
278251875Speter    if ((flags & APR_FILEPATH_NOTABOVEROOT) && keptlen < rootlen) {
279251875Speter        if (strncmp(rootpath, path, rootlen)) {
280251875Speter            return APR_EABOVEROOT;
281251875Speter        }
282251875Speter        if (rootpath[rootlen - 1] != '/'
283251875Speter            && path[rootlen] && path[rootlen] != '/') {
284251875Speter            return APR_EABOVEROOT;
285251875Speter        }
286251875Speter    }
287251875Speter
288251875Speter    *newpath = path;
289251875Speter    return APR_SUCCESS;
290251875Speter}
291251875Speter
292251875SpeterAPR_DECLARE(apr_status_t) apr_filepath_list_split(apr_array_header_t **pathelts,
293251875Speter                                                  const char *liststr,
294251875Speter                                                  apr_pool_t *p)
295251875Speter{
296251875Speter    return apr_filepath_list_split_impl(pathelts, liststr, ':', p);
297251875Speter}
298251875Speter
299251875SpeterAPR_DECLARE(apr_status_t) apr_filepath_list_merge(char **liststr,
300251875Speter                                                  apr_array_header_t *pathelts,
301251875Speter                                                  apr_pool_t *p)
302251875Speter{
303251875Speter    return apr_filepath_list_merge_impl(liststr, pathelts, ':', p);
304251875Speter}
305251875Speter
306251875SpeterAPR_DECLARE(apr_status_t) apr_filepath_encoding(int *style, apr_pool_t *p)
307251875Speter{
308251875Speter#if defined(DARWIN)
309251875Speter    *style = APR_FILEPATH_ENCODING_UTF8;
310251875Speter#else
311251875Speter    *style = APR_FILEPATH_ENCODING_LOCALE;
312251875Speter#endif
313251875Speter    return APR_SUCCESS;
314251875Speter}
315