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 "testutil.h"
18#include "apr_file_io.h"
19#include "apr_file_info.h"
20#include "apr_errno.h"
21#include "apr_general.h"
22#include "apr_pools.h"
23#include "apr_lib.h"
24#include "apr_strings.h"
25
26#if defined(WIN32)
27#include <direct.h>
28#endif
29
30#if defined(WIN32) || defined(OS2)
31#define ABS_ROOT "C:/"
32#elif defined(NETWARE)
33#define ABS_ROOT "SYS:/"
34#else
35#define ABS_ROOT "/"
36#endif
37
38static void merge_aboveroot(abts_case *tc, void *data)
39{
40    apr_status_t rv;
41    char *dstpath = NULL;
42    char errmsg[256];
43
44    rv = apr_filepath_merge(&dstpath, ABS_ROOT"foo", ABS_ROOT"bar", APR_FILEPATH_NOTABOVEROOT,
45                            p);
46    apr_strerror(rv, errmsg, sizeof(errmsg));
47    ABTS_INT_EQUAL(tc, 1, APR_STATUS_IS_EABOVEROOT(rv));
48    ABTS_PTR_EQUAL(tc, NULL, dstpath);
49    ABTS_STR_EQUAL(tc, "The given path was above the root path", errmsg);
50}
51
52static void merge_belowroot(abts_case *tc, void *data)
53{
54    apr_status_t rv;
55    char *dstpath = NULL;
56
57    rv = apr_filepath_merge(&dstpath, ABS_ROOT"foo", ABS_ROOT"foo/bar",
58                            APR_FILEPATH_NOTABOVEROOT, p);
59    ABTS_PTR_NOTNULL(tc, dstpath);
60    ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
61    ABTS_STR_EQUAL(tc, ABS_ROOT"foo/bar", dstpath);
62}
63
64static void merge_noflag(abts_case *tc, void *data)
65{
66    apr_status_t rv;
67    char *dstpath = NULL;
68
69    rv = apr_filepath_merge(&dstpath, ABS_ROOT"foo", ABS_ROOT"foo/bar", 0, p);
70    ABTS_PTR_NOTNULL(tc, dstpath);
71    ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
72    ABTS_STR_EQUAL(tc, ABS_ROOT"foo/bar", dstpath);
73}
74
75static void merge_dotdot(abts_case *tc, void *data)
76{
77    apr_status_t rv;
78    char *dstpath = NULL;
79
80    rv = apr_filepath_merge(&dstpath, ABS_ROOT"foo/bar", "../baz", 0, p);
81    ABTS_PTR_NOTNULL(tc, dstpath);
82    ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
83    ABTS_STR_EQUAL(tc, ABS_ROOT"foo/baz", dstpath);
84
85    rv = apr_filepath_merge(&dstpath, "", "../test", 0, p);
86    ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
87    ABTS_STR_EQUAL(tc, "../test", dstpath);
88
89    /* Very dangerous assumptions here about what the cwd is.  However, let's assume
90     * that the testall is invoked from within apr/test/ so the following test should
91     * return ../test unless a previously fixed bug remains or the developer changes
92     * the case of the test directory:
93     */
94    rv = apr_filepath_merge(&dstpath, "", "../test", APR_FILEPATH_TRUENAME, p);
95    ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
96    ABTS_STR_EQUAL(tc, "../test", dstpath);
97}
98
99static void merge_dotdot_dotdot_dotdot(abts_case *tc, void *data)
100{
101    apr_status_t rv;
102    char *dstpath = NULL;
103
104    rv = apr_filepath_merge(&dstpath, "",
105                            "../../..", APR_FILEPATH_TRUENAME, p);
106    ABTS_PTR_NOTNULL(tc, dstpath);
107    ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
108    ABTS_STR_EQUAL(tc, "../../..", dstpath);
109
110    rv = apr_filepath_merge(&dstpath, "",
111                            "../../../", APR_FILEPATH_TRUENAME, p);
112    ABTS_PTR_NOTNULL(tc, dstpath);
113    ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
114    ABTS_STR_EQUAL(tc, "../../../", dstpath);
115}
116
117static void merge_secure(abts_case *tc, void *data)
118{
119    apr_status_t rv;
120    char *dstpath = NULL;
121
122    rv = apr_filepath_merge(&dstpath, ABS_ROOT"foo/bar", "../bar/baz", 0, p);
123    ABTS_PTR_NOTNULL(tc, dstpath);
124    ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
125    ABTS_STR_EQUAL(tc, ABS_ROOT"foo/bar/baz", dstpath);
126}
127
128static void merge_notrel(abts_case *tc, void *data)
129{
130    apr_status_t rv;
131    char *dstpath = NULL;
132
133    rv = apr_filepath_merge(&dstpath, ABS_ROOT"foo/bar", "../baz",
134                            APR_FILEPATH_NOTRELATIVE, p);
135    ABTS_PTR_NOTNULL(tc, dstpath);
136    ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
137    ABTS_STR_EQUAL(tc, ABS_ROOT"foo/baz", dstpath);
138}
139
140static void merge_notrelfail(abts_case *tc, void *data)
141{
142    apr_status_t rv;
143    char *dstpath = NULL;
144    char errmsg[256];
145
146    rv = apr_filepath_merge(&dstpath, "foo/bar", "../baz",
147                            APR_FILEPATH_NOTRELATIVE, p);
148    apr_strerror(rv, errmsg, sizeof(errmsg));
149
150    ABTS_PTR_EQUAL(tc, NULL, dstpath);
151    ABTS_INT_EQUAL(tc, 1, APR_STATUS_IS_ERELATIVE(rv));
152    ABTS_STR_EQUAL(tc, "The given path is relative", errmsg);
153}
154
155static void merge_notabsfail(abts_case *tc, void *data)
156{
157    apr_status_t rv;
158    char *dstpath = NULL;
159    char errmsg[256];
160
161    rv = apr_filepath_merge(&dstpath, ABS_ROOT"foo/bar", "../baz",
162                            APR_FILEPATH_NOTABSOLUTE, p);
163    apr_strerror(rv, errmsg, sizeof(errmsg));
164
165    ABTS_PTR_EQUAL(tc, NULL, dstpath);
166    ABTS_INT_EQUAL(tc, 1, APR_STATUS_IS_EABSOLUTE(rv));
167    ABTS_STR_EQUAL(tc, "The given path is absolute", errmsg);
168}
169
170static void merge_notabs(abts_case *tc, void *data)
171{
172    apr_status_t rv;
173    char *dstpath = NULL;
174
175    rv = apr_filepath_merge(&dstpath, "foo/bar", "../baz",
176                            APR_FILEPATH_NOTABSOLUTE, p);
177
178    ABTS_PTR_NOTNULL(tc, dstpath);
179    ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
180    ABTS_STR_EQUAL(tc, "foo/baz", dstpath);
181}
182
183#if defined (WIN32)
184static void merge_lowercasedrive(abts_case *tc, void *data)
185{
186  char current_dir[1024];
187  char current_dir_on_C[1024];
188  char *dir_on_c;
189  char *testdir;
190  apr_status_t rv;
191
192  /* Change the current directory on C: from something like "C:\dir"
193     to something like "c:\dir" to replicate the failing case. */
194  ABTS_PTR_NOTNULL(tc, _getcwd(current_dir, sizeof(current_dir)));
195
196   /* 3 stands for drive C: */
197  ABTS_PTR_NOTNULL(tc, _getdcwd(3, current_dir_on_C,
198	                            sizeof(current_dir_on_C)));
199
200  /* Use the same path, but now with a lower case driveletter */
201  dir_on_c = apr_pstrdup(p, current_dir_on_C);
202  dir_on_c[0] = (char)tolower(dir_on_c[0]);
203
204  chdir(dir_on_c);
205
206  /* Now merge a drive relative path with an upper case drive letter. */
207  rv = apr_filepath_merge(&testdir, NULL, "C:hi",
208                          APR_FILEPATH_NOTRELATIVE, p);
209
210  /* Change back to original directory for next tests */
211  chdir("C:\\"); /* Switch to upper case */
212  chdir(current_dir_on_C); /* Switch cwd on C: */
213  chdir(current_dir); /* Switch back to original cwd */
214
215  ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
216}
217#endif
218
219static void root_absolute(abts_case *tc, void *data)
220{
221    apr_status_t rv;
222    const char *root = NULL;
223    const char *path = ABS_ROOT"foo/bar";
224
225    rv = apr_filepath_root(&root, &path, 0, p);
226
227    ABTS_PTR_NOTNULL(tc, root);
228    ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
229    ABTS_STR_EQUAL(tc, ABS_ROOT, root);
230}
231
232static void root_relative(abts_case *tc, void *data)
233{
234    apr_status_t rv;
235    const char *root = NULL;
236    const char *path = "foo/bar";
237    char errmsg[256];
238
239    rv = apr_filepath_root(&root, &path, 0, p);
240    apr_strerror(rv, errmsg, sizeof(errmsg));
241
242    ABTS_PTR_EQUAL(tc, NULL, root);
243    ABTS_INT_EQUAL(tc, 1, APR_STATUS_IS_ERELATIVE(rv));
244    ABTS_STR_EQUAL(tc, "The given path is relative", errmsg);
245}
246
247static void root_from_slash(abts_case *tc, void *data)
248{
249    apr_status_t rv;
250    const char *root = NULL;
251    const char *path = "//";
252
253    rv = apr_filepath_root(&root, &path, APR_FILEPATH_TRUENAME, p);
254
255#if defined(WIN32) || defined(OS2)
256    ABTS_INT_EQUAL(tc, APR_EINCOMPLETE, rv);
257    ABTS_STR_EQUAL(tc, "//", root);
258#else
259    ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
260    ABTS_STR_EQUAL(tc, "/", root);
261#endif
262    ABTS_STR_EQUAL(tc, "", path);
263}
264
265static void root_from_cwd_and_back(abts_case *tc, void *data)
266{
267    apr_status_t rv;
268    const char *root = NULL;
269    const char *path = "//";
270    char *origpath;
271    char *testpath;
272#if defined(WIN32) || defined(OS2) || defined(NETWARE)
273    int hadfailed;
274#endif
275
276    ABTS_INT_EQUAL(tc, APR_SUCCESS, apr_filepath_get(&origpath, 0, p));
277    path = origpath;
278    rv = apr_filepath_root(&root, &path, APR_FILEPATH_TRUENAME, p);
279
280#if defined(WIN32) || defined(OS2)
281    hadfailed = tc->failed;
282    /* It appears some mingw/cygwin and more modern builds can return
283     * a lowercase drive designation, but we canonicalize to uppercase
284     */
285    ABTS_INT_EQUAL(tc, toupper(origpath[0]), root[0]);
286    ABTS_INT_EQUAL(tc, ':', root[1]);
287    ABTS_INT_EQUAL(tc, '/', root[2]);
288    ABTS_INT_EQUAL(tc, 0, root[3]);
289    ABTS_STR_EQUAL(tc, origpath + 3, path);
290#elif defined(NETWARE)
291    ABTS_INT_EQUAL(tc, origpath[0], root[0]);
292    {
293    char *pt = strchr(root, ':');
294    ABTS_PTR_NOTNULL(tc, pt);
295    ABTS_INT_EQUAL(tc, ':', pt[0]);
296    ABTS_INT_EQUAL(tc, '/', pt[1]);
297    ABTS_INT_EQUAL(tc, 0, pt[2]);
298    pt = strchr(origpath, ':');
299    ABTS_PTR_NOTNULL(tc, pt);
300    ABTS_STR_EQUAL(tc, (pt+2), path);
301    }
302#else
303    ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
304    ABTS_STR_EQUAL(tc, "/", root);
305    ABTS_STR_EQUAL(tc, origpath + 1, path);
306#endif
307
308    rv = apr_filepath_merge(&testpath, root, path,
309                            APR_FILEPATH_TRUENAME
310                          | APR_FILEPATH_NOTABOVEROOT
311                          | APR_FILEPATH_NOTRELATIVE, p);
312    ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
313#if defined(WIN32) || defined(OS2) || defined(NETWARE)
314    hadfailed = tc->failed;
315#endif
316    /* The API doesn't promise equality!!!
317     * apr_filepath_get never promised a canonical filepath.
318     * We'll emit noise under verbose so the user is aware,
319     * but translate this back to success.
320     */
321    ABTS_STR_EQUAL(tc, origpath, testpath);
322#if defined(WIN32) || defined(OS2) || defined(NETWARE)
323    if (!hadfailed) tc->failed = 0;
324#endif
325}
326
327
328abts_suite *testnames(abts_suite *suite)
329{
330    suite = ADD_SUITE(suite)
331
332    abts_run_test(suite, merge_aboveroot, NULL);
333    abts_run_test(suite, merge_belowroot, NULL);
334    abts_run_test(suite, merge_noflag, NULL);
335    abts_run_test(suite, merge_dotdot, NULL);
336    abts_run_test(suite, merge_secure, NULL);
337    abts_run_test(suite, merge_notrel, NULL);
338    abts_run_test(suite, merge_notrelfail, NULL);
339    abts_run_test(suite, merge_notabs, NULL);
340    abts_run_test(suite, merge_notabsfail, NULL);
341    abts_run_test(suite, merge_dotdot_dotdot_dotdot, NULL);
342#if defined(WIN32)
343    abts_run_test(suite, merge_lowercasedrive, NULL);
344#endif
345
346    abts_run_test(suite, root_absolute, NULL);
347    abts_run_test(suite, root_relative, NULL);
348    abts_run_test(suite, root_from_slash, NULL);
349    abts_run_test(suite, root_from_cwd_and_back, NULL);
350
351    return suite;
352}
353
354