#include "apr.h"
#include "apr_private.h"
#include "apr_arch_file_io.h"
#include "apr_strings.h"
#include "apr_lib.h"
#include <string.h>
#include <ctype.h>
#ifdef NETWARE
#include <unistd.h>
#include <fsio.h>
#endif
APR_DECLARE(apr_status_t) apr_filepath_root(const char **rootpath,
const char **inpath,
apr_int32_t flags,
apr_pool_t *p)
{
const char *testpath = *inpath;
char *newpath;
#ifdef NETWARE
char seperator[2] = { 0, 0};
char server[APR_PATH_MAX+1];
char volume[APR_PATH_MAX+1];
char file[APR_PATH_MAX+1];
char *volsep = NULL;
int elements;
if (inpath && *inpath)
volsep = strchr (*inpath, ':');
else
return APR_EBADPATH;
if (strlen(*inpath) > APR_PATH_MAX) {
return APR_EBADPATH;
}
seperator[0] = (flags & APR_FILEPATH_NATIVE) ? '\\' : '/';
server[0] = volume[0] = file[0] = '\0';
if (volsep) {
deconstruct(testpath, server, volume, NULL, file, NULL, &elements, PATH_UNDEF);
if (volume && strlen(volume) > 0) {
newpath = apr_pcalloc(p, strlen(server)+strlen(volume)+5);
construct(newpath, server, volume, NULL, NULL, NULL, PATH_NETWARE);
strcat(newpath, seperator);
*rootpath = newpath;
newpath = volsep;
do {
++newpath;
} while (*newpath && ((*newpath == '/') || (*newpath == '\\')));
*inpath = newpath;
return APR_SUCCESS;
}
else
return APR_EBADPATH;
}
else if ((**inpath == '/') || (**inpath == '\\')) {
*rootpath = apr_pstrdup(p, seperator);
do {
++(*inpath);
} while ((**inpath == '/') || (**inpath == '\\'));
}
else
return APR_ERELATIVE;
return APR_EINCOMPLETE;
#else
char seperator[2];
const char *delim1;
const char *delim2;
seperator[0] = (flags & APR_FILEPATH_NATIVE) ? '\\' : '/';
seperator[1] = 0;
if (testpath[0] == '/' || testpath[0] == '\\') {
if (testpath[1] == '/' || testpath[1] == '\\') {
#ifdef WIN32
if ((testpath[2] == '?' || testpath[2] == '.')
&& (testpath[3] == '/' || testpath[3] == '\\')) {
if (IS_FNCHAR(testpath[4]) && testpath[5] == ':')
{
apr_status_t rv;
testpath += 4;
rv = apr_filepath_root(rootpath, &testpath, flags, p);
if (!rv || rv == APR_EINCOMPLETE)
*inpath = testpath;
return rv;
}
else if (strncasecmp(testpath + 4, "UNC", 3) == 0
&& (testpath[7] == '/' || testpath[7] == '\\')
&& (testpath[2] == '?')) {
testpath += 6;
}
else
return APR_EBADPATH;
}
#endif
delim1 = testpath + 2;
do {
if (*delim1 && !IS_FNCHAR(*(delim1++)))
return APR_EBADPATH;
} while (*delim1 && *delim1 != '/' && *delim1 != '\\');
if (*delim1) {
apr_status_t rv;
delim2 = delim1 + 1;
while (*delim2 && *delim2 != '/' && *delim2 != '\\') {
if (!IS_FNCHAR(*(delim2++)))
return APR_EBADPATH;
}
newpath = apr_pstrmemdup(p, testpath, delim2 - testpath + 1);
if (delim2 == delim1 + 1) {
*rootpath = newpath;
*inpath = delim2;
return APR_EINCOMPLETE;
}
if (flags & APR_FILEPATH_TRUENAME) {
newpath[0] = '\\';
newpath[1] = '\\';
newpath[delim1 - testpath] = '\\';
newpath[delim2 - testpath] = '\\';
rv = filepath_root_test(newpath, p);
if (rv)
return rv;
rv = filepath_root_case(&newpath, newpath, p);
if (rv)
return rv;
newpath[0] = seperator[0];
newpath[1] = seperator[0];
newpath[delim1 - testpath] = seperator[0];
newpath[delim2 - testpath] = (*delim2 ? seperator[0] : '\0');
}
else {
newpath[0] = testpath[0];
newpath[1] = testpath[1];
newpath[delim1 - testpath] = *delim1;
newpath[delim2 - testpath] = *delim2;
}
if (*delim2) {
*inpath = delim2 + 1;
while (**inpath == '/' || **inpath == '\\')
++*inpath;
}
else {
*inpath = delim2;
}
*rootpath = newpath;
return APR_SUCCESS;
}
delim1 = strchr(testpath, '\0');
if (delim1 > testpath + 2) {
newpath = apr_pstrndup(p, testpath, delim1 - testpath + 1);
if (flags & APR_FILEPATH_TRUENAME)
newpath[delim1 - testpath] = seperator[0];
else
newpath[delim1 - testpath] = newpath[0];
newpath[delim1 - testpath + 1] = '\0';
}
else {
newpath = apr_pstrndup(p, testpath, delim1 - testpath);
}
if (flags & APR_FILEPATH_TRUENAME) {
newpath[0] = seperator[0];
newpath[1] = seperator[0];
}
*rootpath = newpath;
*inpath = delim1;
return APR_EINCOMPLETE;
}
*inpath = testpath + 1;
newpath = apr_palloc(p, 2);
if (flags & APR_FILEPATH_TRUENAME)
newpath[0] = seperator[0];
else
newpath[0] = testpath[0];
newpath[1] = '\0';
*rootpath = newpath;
return APR_EINCOMPLETE;
}
if (IS_FNCHAR(*testpath) && testpath[1] == ':')
{
apr_status_t rv;
newpath = apr_palloc(p, 4);
newpath[0] = testpath[0];
newpath[1] = testpath[1];
newpath[2] = seperator[0];
newpath[3] = '\0';
if (flags & APR_FILEPATH_TRUENAME) {
newpath[0] = apr_toupper(newpath[0]);
rv = filepath_root_test(newpath, p);
if (rv)
return rv;
}
if (testpath[2] != '/' && testpath[2] != '\\') {
newpath[2] = '\0';
*rootpath = newpath;
*inpath = testpath + 2;
return APR_EINCOMPLETE;
}
*inpath = testpath + 3;
while (**inpath == '/' || **inpath == '\\')
++*inpath;
if (!(flags & APR_FILEPATH_TRUENAME))
newpath[2] = testpath[2];
*rootpath = newpath;
return APR_SUCCESS;
}
return APR_ERELATIVE;
#endif
}
#if !defined(NETWARE)
static int same_drive(const char *path1, const char *path2)
{
char drive1 = path1[0];
char drive2 = path2[0];
if (!drive1 || !drive2 || path1[1] != ':' || path2[1] != ':')
return FALSE;
if (drive1 == drive2)
return TRUE;
if (drive1 >= 'a' && drive1 <= 'z')
drive1 += 'A' - 'a';
if (drive2 >= 'a' && drive2 <= 'z')
drive2 += 'A' - 'a';
return (drive1 == drive2);
}
#endif
APR_DECLARE(apr_status_t) apr_filepath_merge(char **newpath,
const char *basepath,
const char *addpath,
apr_int32_t flags,
apr_pool_t *p)
{
char path[APR_PATH_MAX];
const char *baseroot = NULL;
const char *addroot;
apr_size_t rootlen;
apr_size_t baselen;
apr_size_t keptlen;
apr_size_t pathlen;
apr_size_t segend;
apr_size_t seglen;
apr_status_t basetype = 0;
apr_status_t addtype;
apr_status_t rv;
#ifndef NETWARE
int fixunc = 0;
#endif
if (!addpath) {
addpath = addroot = "";
addtype = APR_ERELATIVE;
}
else {
addtype = apr_filepath_root(&addroot, &addpath,
APR_FILEPATH_TRUENAME
| (flags & APR_FILEPATH_NATIVE),
p);
if (addtype == APR_SUCCESS) {
addtype = APR_EABSOLUTE;
}
else if (addtype == APR_ERELATIVE) {
addroot = "";
}
else if (addtype != APR_EINCOMPLETE) {
return addtype;
}
}
if (addtype == APR_EABSOLUTE || addtype == APR_EINCOMPLETE)
{
if (flags & APR_FILEPATH_SECUREROOTTEST)
return APR_EABOVEROOT;
if (flags & APR_FILEPATH_NOTABSOLUTE)
return addtype;
}
if (!basepath) {
if (addtype == APR_EABSOLUTE && !(flags & APR_FILEPATH_NOTABOVEROOT)) {
basepath = baseroot = "";
basetype = APR_ERELATIVE;
}
if (addtype == APR_ERELATIVE && (flags & APR_FILEPATH_NOTABSOLUTE)) {
basepath = baseroot = "";
basetype = APR_ERELATIVE;
}
}
if (!basepath)
{
char *getpath;
#ifndef NETWARE
if (addtype == APR_EINCOMPLETE && addroot[1] == ':')
rv = filepath_drive_get(&getpath, addroot[0], flags, p);
else
#endif
rv = apr_filepath_get(&getpath, flags, p);
if (rv != APR_SUCCESS)
return rv;
basepath = getpath;
}
if (!baseroot) {
basetype = apr_filepath_root(&baseroot, &basepath,
(flags & APR_FILEPATH_NATIVE), p);
if (basetype == APR_SUCCESS) {
basetype = APR_EABSOLUTE;
}
else if (basetype == APR_ERELATIVE) {
baseroot = "";
}
else if (basetype != APR_EINCOMPLETE) {
return basetype;
}
}
baselen = strlen(basepath);
if ((flags & APR_FILEPATH_NOTABSOLUTE) && basetype != APR_ERELATIVE)
return basetype;
if (addtype == APR_EABSOLUTE)
{
if ((flags & APR_FILEPATH_NOTABOVEROOT)
&& strncmp(baseroot, addroot, strlen(baseroot)))
return APR_EABOVEROOT;
keptlen = 0;
rootlen = pathlen = strlen(addroot);
memcpy(path, addroot, pathlen);
}
else if (addtype == APR_EINCOMPLETE)
{
#ifndef NETWARE
if ((addroot[0] == '/' || addroot[0] == '\\') &&
(addroot[1] == '/' || addroot[1] == '\\'))
{
if (flags & APR_FILEPATH_NOTRELATIVE)
return addtype;
if ((flags & APR_FILEPATH_NOTABOVEROOT)
&& strncmp(baseroot, addroot, strlen(baseroot)))
return APR_EABOVEROOT;
fixunc = 1;
keptlen = 0;
rootlen = pathlen = strlen(addroot);
memcpy(path, addroot, pathlen);
}
else
#endif
if ((addroot[0] == '/' || addroot[0] == '\\') && !addroot[1])
{
if (basetype != APR_EABSOLUTE && (flags & APR_FILEPATH_NOTRELATIVE))
return basetype;
if (basetype != APR_ERELATIVE) {
#ifndef NETWARE
if (basetype == APR_INCOMPLETE
&& (baseroot[0] == '/' || baseroot[0] == '\\')
&& (baseroot[1] == '/' || baseroot[1] == '\\'))
fixunc = 1;
#endif
keptlen = rootlen = pathlen = strlen(baseroot);
memcpy(path, baseroot, pathlen);
}
else {
if (flags & APR_FILEPATH_NOTABOVEROOT)
return APR_EABOVEROOT;
keptlen = 0;
rootlen = pathlen = strlen(addroot);
memcpy(path, addroot, pathlen);
}
}
#ifdef NETWARE
else if (filepath_has_drive(addroot, DRIVE_ONLY, p))
{
if (!filepath_compare_drive(addroot, baseroot, p) &&
filepath_has_drive(baseroot, 0, p)) {
#else
else if (addroot[0] && addroot[1] == ':' && !addroot[2])
{
if (same_drive(addroot, baseroot)) {
#endif
if (basetype != APR_EABSOLUTE && (flags & APR_FILEPATH_NOTRELATIVE))
return basetype;
rootlen = strlen(baseroot);
keptlen = pathlen = rootlen + baselen;
if (keptlen >= sizeof(path))
return APR_ENAMETOOLONG;
memcpy(path, baseroot, rootlen);
memcpy(path + rootlen, basepath, baselen);
}
else {
if (flags & APR_FILEPATH_NOTRELATIVE)
return addtype;
if (flags & APR_FILEPATH_NOTABOVEROOT)
return APR_EABOVEROOT;
keptlen = 0;
rootlen = pathlen = strlen(addroot);
memcpy(path, addroot, pathlen);
}
}
else {
return APR_EBADPATH;
}
}
else {
if (basetype != APR_EABSOLUTE && (flags & APR_FILEPATH_NOTRELATIVE))
return basetype;
#ifndef NETWARE
if (basetype == APR_INCOMPLETE
&& (baseroot[0] == '/' || baseroot[0] == '\\')
&& (baseroot[1] == '/' || baseroot[1] == '\\'))
fixunc = 1;
#endif
rootlen = strlen(baseroot);
keptlen = pathlen = rootlen + baselen;
if (keptlen >= sizeof(path))
return APR_ENAMETOOLONG;
memcpy(path, baseroot, rootlen);
memcpy(path + rootlen, basepath, baselen);
}
if (pathlen && path[pathlen - 1] != ':') {
if (path[pathlen - 1] != '/' && path[pathlen - 1] != '\\') {
if (pathlen + 1 >= sizeof(path))
return APR_ENAMETOOLONG;
path[pathlen++] = ((flags & APR_FILEPATH_NATIVE) ? '\\' : '/');
}
}
while (*addpath)
{
seglen = 0;
while (addpath[seglen] && addpath[seglen] != '/'
&& addpath[seglen] != '\\')
++seglen;
segend = seglen;
while (seglen && (addpath[seglen - 1] == ' '
|| addpath[seglen - 1] == '.')) {
if (seglen > 2 || addpath[seglen - 1] != '.' || addpath[0] != '.')
--seglen;
else
break;
}
if (seglen == 0 || (seglen == 1 && addpath[0] == '.'))
{
if (seglen < segend)
return APR_EBADPATH;
#ifndef NETWARE
if (fixunc)
return APR_EBADPATH;
#endif
}
else if (seglen == 2 && addpath[0] == '.' && addpath[1] == '.')
{
if (seglen < segend && (seglen != 3 || addpath[2] != '.'))
return APR_EBADPATH;
#ifndef NETWARE
if (fixunc)
return APR_EBADPATH;
#endif
if (rootlen && (pathlen <= rootlen))
{
if (flags & APR_FILEPATH_SECUREROOTTEST)
return APR_EABOVEROOT;
}
else if (pathlen == 0
|| (pathlen >= 3
&& (pathlen == 3
|| path[pathlen - 4] == ':'
|| path[pathlen - 4] == '/'
|| path[pathlen - 4] == '\\')
&& path[pathlen - 3] == '.'
&& path[pathlen - 2] == '.'
&& (path[pathlen - 1] == '/'
|| path[pathlen - 1] == '\\')))
{
if (flags & APR_FILEPATH_SECUREROOTTEST)
return APR_EABOVEROOT;
if (pathlen + 3 >= sizeof(path))
return APR_ENAMETOOLONG;
path[pathlen++] = '.';
path[pathlen++] = '.';
if (addpath[segend]) {
path[pathlen++] = ((flags & APR_FILEPATH_NATIVE)
? '\\' : ((flags & APR_FILEPATH_TRUENAME)
? '/' : addpath[segend]));
}
keptlen = pathlen;
}
else
{
do {
--pathlen;
} while (pathlen && path[pathlen - 1] != '/'
&& path[pathlen - 1] != '\\');
if (pathlen < keptlen)
{
if (flags & APR_FILEPATH_SECUREROOTTEST)
return APR_EABOVEROOT;
keptlen = pathlen;
}
}
}
else
{
#ifndef NETWARE
if (fixunc) {
const char *testpath = path;
const char *testroot;
apr_status_t testtype;
apr_size_t i = (addpath[segend] != '\0');
if (seglen < segend)
return APR_EBADPATH;
if (pathlen + seglen + 1 >= sizeof(path))
return APR_ENAMETOOLONG;
memcpy(path + pathlen, addpath, seglen + i);
path[pathlen + seglen] = ((flags & APR_FILEPATH_NATIVE)
? '\\' : '/');
pathlen += seglen + 1;
path[pathlen] = '\0';
testtype = apr_filepath_root(&testroot, &testpath,
APR_FILEPATH_TRUENAME
| (flags & APR_FILEPATH_NATIVE),
p);
if (testtype == APR_SUCCESS) {
rootlen = pathlen = (testpath - path);
memcpy(path, testroot, pathlen);
fixunc = 0;
}
else if (testtype != APR_EINCOMPLETE) {
return testtype;
}
}
else
#endif
{
apr_size_t i = (addpath[segend] != '\0');
if (pathlen + seglen + i >= sizeof(path))
return APR_ENAMETOOLONG;
memcpy(path + pathlen, addpath, seglen + i);
if (i)
path[pathlen + seglen] = ((flags & APR_FILEPATH_NATIVE)
? '\\' : '/');
pathlen += seglen + i;
}
}
if (addpath[segend])
++segend;
addpath += segend;
}
if ((flags & APR_FILEPATH_NOTABOVEROOT) && baselen) {
if (memcmp(basepath, path + rootlen, baselen) != 0)
return APR_EABOVEROOT;
if (basepath[baselen - 1] != '/' && basepath[baselen - 1] != '\\'
&& path[rootlen + baselen] && path[rootlen + baselen] != '/'
&& path[rootlen + baselen] != '\\')
return APR_EABOVEROOT;
}
if (addpath && (flags & APR_FILEPATH_TRUENAME)) {
if (rootlen > keptlen)
keptlen = rootlen;
if ((path[keptlen] == '/') || (path[keptlen] == '\\')) {
++keptlen;
}
while (keptlen < pathlen) {
apr_finfo_t finfo;
char saveslash = 0;
seglen = 0;
for (seglen = 0; keptlen + seglen < pathlen; ++seglen) {
if ((path[keptlen + seglen] == '/') ||
(path[keptlen + seglen] == '\\')) {
saveslash = path[keptlen + seglen];
break;
}
}
path[keptlen + seglen] = '\0';
if ((rv = apr_stat(&finfo, path,
APR_FINFO_LINK | APR_FINFO_TYPE | APR_FINFO_NAME, p))
== APR_SUCCESS) {
apr_size_t namelen = strlen(finfo.name);
#if defined(OS2)
if (memcmp(finfo.name, path + keptlen, seglen) != 0) {
memcpy(path + keptlen, finfo.name, namelen);
}
#else
if ((namelen != seglen) ||
(memcmp(finfo.name, path + keptlen, seglen) != 0))
{
if (namelen <= seglen) {
memcpy(path + keptlen, finfo.name, namelen);
if ((namelen < seglen) && saveslash) {
memmove(path + keptlen + namelen + 1,
path + keptlen + seglen + 1,
pathlen - keptlen - seglen);
pathlen += namelen - seglen;
seglen = namelen;
}
}
else {
if (pathlen + namelen - seglen >= sizeof(path))
return APR_ENAMETOOLONG;
if (saveslash) {
memmove(path + keptlen + namelen + 1,
path + keptlen + seglen + 1,
pathlen - keptlen - seglen);
}
memcpy(path + keptlen, finfo.name, namelen);
pathlen += namelen - seglen;
seglen = namelen;
}
}
#endif
if ((finfo.filetype != APR_DIR) &&
(finfo.filetype != APR_LNK) && saveslash)
rv = APR_ENOTDIR;
#ifdef XXX_FIGURE_THIS_OUT
{
if (saveslash) {
keptlen += seglen;
path[keptlen] = saveslash;
if (pathlen + 1 >= sizeof(path))
return APR_ENAMETOOLONG;
memmove(path + keptlen + 1,
path + keptlen,
pathlen - keptlen);
path[keptlen] = '\0';
++pathlen;
break;
}
}
#endif
}
if (saveslash) {
path[keptlen + seglen] = saveslash;
++seglen;
}
keptlen += seglen;
if (rv != APR_SUCCESS) {
if (APR_STATUS_IS_ENOENT(rv))
break;
if (APR_STATUS_IS_EPATHWILD(rv))
break;
else if (APR_STATUS_IS_ENOTDIR(rv))
break;
else
return rv;
}
}
}
*newpath = apr_pstrmemdup(p, path, pathlen);
return APR_SUCCESS;
}
APR_DECLARE(apr_status_t) apr_filepath_list_split(apr_array_header_t **pathelts,
const char *liststr,
apr_pool_t *p)
{
return apr_filepath_list_split_impl(pathelts, liststr, ';', p);
}
APR_DECLARE(apr_status_t) apr_filepath_list_merge(char **liststr,
apr_array_header_t *pathelts,
apr_pool_t *p)
{
return apr_filepath_list_merge_impl(liststr, pathelts, ';', p);
}
APR_DECLARE(apr_status_t) apr_filepath_encoding(int *style, apr_pool_t *p)
{
#if APR_HAS_UNICODE_FS
IF_WIN_OS_IS_UNICODE
{
*style = APR_FILEPATH_ENCODING_UTF8;
return APR_SUCCESS;
}
#endif
*style = APR_FILEPATH_ENCODING_LOCALE;
return APR_SUCCESS;
}