Back to index

php5  5.3.10
fopen_wrappers.c
Go to the documentation of this file.
00001 /*
00002    +----------------------------------------------------------------------+
00003    | PHP Version 5                                                        |
00004    +----------------------------------------------------------------------+
00005    | Copyright (c) 1997-2012 The PHP Group                                |
00006    +----------------------------------------------------------------------+
00007    | This source file is subject to version 3.01 of the PHP license,      |
00008    | that is bundled with this package in the file LICENSE, and is        |
00009    | available through the world-wide-web at the following url:           |
00010    | http://www.php.net/license/3_01.txt                                  |
00011    | If you did not receive a copy of the PHP license and are unable to   |
00012    | obtain it through the world-wide-web, please send a note to          |
00013    | license@php.net so we can mail you a copy immediately.               |
00014    +----------------------------------------------------------------------+
00015    | Authors: Rasmus Lerdorf <rasmus@lerdorf.on.ca>                       |
00016    |          Jim Winstead <jimw@php.net>                                 |
00017    +----------------------------------------------------------------------+
00018  */
00019 
00020 /* $Id: fopen_wrappers.c 321634 2012-01-01 13:15:04Z felipe $ */
00021 
00022 /* {{{ includes
00023  */
00024 #include "php.h"
00025 #include "php_globals.h"
00026 #include "SAPI.h"
00027 
00028 #include <stdio.h>
00029 #include <stdlib.h>
00030 #include <errno.h>
00031 #include <sys/types.h>
00032 #include <sys/stat.h>
00033 #include <fcntl.h>
00034 
00035 #ifdef PHP_WIN32
00036 #define O_RDONLY _O_RDONLY
00037 #include "win32/param.h"
00038 #else
00039 #include <sys/param.h>
00040 #endif
00041 
00042 #include "safe_mode.h"
00043 #include "ext/standard/head.h"
00044 #include "ext/standard/php_standard.h"
00045 #include "zend_compile.h"
00046 #include "php_network.h"
00047 
00048 #if HAVE_PWD_H
00049 #include <pwd.h>
00050 #endif
00051 
00052 #include <sys/types.h>
00053 #if HAVE_SYS_SOCKET_H
00054 #include <sys/socket.h>
00055 #endif
00056 
00057 #ifndef S_ISREG
00058 #define S_ISREG(mode)       (((mode) & S_IFMT) == S_IFREG)
00059 #endif
00060 
00061 #ifdef PHP_WIN32
00062 #include <winsock2.h>
00063 #elif defined(NETWARE) && defined(USE_WINSOCK)
00064 #include <novsock2.h>
00065 #else
00066 #include <netinet/in.h>
00067 #include <netdb.h>
00068 #if HAVE_ARPA_INET_H
00069 #include <arpa/inet.h>
00070 #endif
00071 #endif
00072 
00073 #if defined(PHP_WIN32) || defined(__riscos__) || defined(NETWARE)
00074 #undef AF_UNIX
00075 #endif
00076 
00077 #if defined(AF_UNIX)
00078 #include <sys/un.h>
00079 #endif
00080 /* }}} */
00081 
00082 /* {{{ OnUpdateBaseDir
00083 Allows any change to open_basedir setting in during Startup and Shutdown events,
00084 or a tightening during activation/runtime/deactivation */
00085 PHPAPI ZEND_INI_MH(OnUpdateBaseDir)
00086 {
00087        char **p, *pathbuf, *ptr, *end;
00088 #ifndef ZTS
00089        char *base = (char *) mh_arg2;
00090 #else
00091        char *base = (char *) ts_resource(*((int *) mh_arg2));
00092 #endif
00093 
00094        p = (char **) (base + (size_t) mh_arg1);
00095 
00096        if (stage == PHP_INI_STAGE_STARTUP || stage == PHP_INI_STAGE_SHUTDOWN || stage == PHP_INI_STAGE_ACTIVATE || stage == PHP_INI_STAGE_DEACTIVATE) {
00097               /* We're in a PHP_INI_SYSTEM context, no restrictions */
00098               *p = new_value;
00099               return SUCCESS;
00100        }
00101 
00102        /* Otherwise we're in runtime */
00103        if (!*p || !**p) {
00104               /* open_basedir not set yet, go ahead and give it a value */
00105               *p = new_value;
00106               return SUCCESS;
00107        }
00108 
00109        /* Shortcut: When we have a open_basedir and someone tries to unset, we know it'll fail */
00110        if (!new_value || !*new_value) {
00111               return FAILURE;
00112        }
00113 
00114        /* Is the proposed open_basedir at least as restrictive as the current setting? */
00115        ptr = pathbuf = estrdup(new_value);
00116        while (ptr && *ptr) {
00117               end = strchr(ptr, DEFAULT_DIR_SEPARATOR);
00118               if (end != NULL) {
00119                      *end = '\0';
00120                      end++;
00121               }
00122               if (php_check_open_basedir_ex(ptr, 0 TSRMLS_CC) != 0) {
00123                      /* At least one portion of this open_basedir is less restrictive than the prior one, FAIL */
00124                      efree(pathbuf);
00125                      return FAILURE;
00126               }
00127               ptr = end;
00128        }
00129        efree(pathbuf);
00130 
00131        /* Everything checks out, set it */
00132        *p = new_value;
00133 
00134        return SUCCESS;
00135 }
00136 /* }}} */
00137 
00138 /* {{{ php_check_specific_open_basedir
00139        When open_basedir is not NULL, check if the given filename is located in
00140        open_basedir. Returns -1 if error or not in the open_basedir, else 0.
00141        When open_basedir is NULL, always return 0.
00142 */
00143 PHPAPI int php_check_specific_open_basedir(const char *basedir, const char *path TSRMLS_DC)
00144 {
00145        char resolved_name[MAXPATHLEN];
00146        char resolved_basedir[MAXPATHLEN];
00147        char local_open_basedir[MAXPATHLEN];
00148        char path_tmp[MAXPATHLEN];
00149        char *path_file;
00150        int resolved_basedir_len;
00151        int resolved_name_len;
00152        int path_len;
00153        int nesting_level = 0;
00154 
00155        /* Special case basedir==".": Use script-directory */
00156        if (strcmp(basedir, ".") || !VCWD_GETCWD(local_open_basedir, MAXPATHLEN)) {
00157               /* Else use the unmodified path */
00158               strlcpy(local_open_basedir, basedir, sizeof(local_open_basedir));
00159        }
00160 
00161        path_len = strlen(path);
00162        if (path_len > (MAXPATHLEN - 1)) {
00163               /* empty and too long paths are invalid */
00164               return -1;
00165        }
00166 
00167        /* normalize and expand path */
00168        if (expand_filepath(path, resolved_name TSRMLS_CC) == NULL) {
00169               return -1;
00170        }
00171 
00172        path_len = strlen(resolved_name);
00173        memcpy(path_tmp, resolved_name, path_len + 1); /* safe */
00174 
00175        while (VCWD_REALPATH(path_tmp, resolved_name) == NULL) {
00176 #if defined(PHP_WIN32) || defined(HAVE_SYMLINK)
00177 #if defined(PHP_WIN32)
00178               if (EG(windows_version_info).dwMajorVersion > 5) {
00179 #endif
00180                      if (nesting_level == 0) {
00181                             int ret;
00182                             char buf[MAXPATHLEN];
00183 
00184                             ret = php_sys_readlink(path_tmp, buf, MAXPATHLEN - 1);
00185                             if (ret < 0) {
00186                                    /* not a broken symlink, move along.. */
00187                             } else {
00188                                    /* put the real path into the path buffer */
00189                                    memcpy(path_tmp, buf, ret);
00190                                    path_tmp[ret] = '\0';
00191                             }
00192                      }
00193 #if defined(PHP_WIN32)
00194               }
00195 #endif
00196 #endif
00197 
00198 #if defined(PHP_WIN32) || defined(NETWARE)
00199               path_file = strrchr(path_tmp, DEFAULT_SLASH);
00200               if (!path_file) {
00201                      path_file = strrchr(path_tmp, '/');
00202               }
00203 #else
00204               path_file = strrchr(path_tmp, DEFAULT_SLASH);
00205 #endif
00206               if (!path_file) {
00207                      /* none of the path components exist. definitely not in open_basedir.. */
00208                      return -1;
00209               } else {
00210                      path_len = path_file - path_tmp + 1;
00211 #if defined(PHP_WIN32) || defined(NETWARE)
00212                      if (path_len > 1 && path_tmp[path_len - 2] == ':') {
00213                             if (path_len != 3) {
00214                                    return -1;
00215                             } 
00216                             /* this is c:\ */
00217                             path_tmp[path_len] = '\0';
00218                      } else {
00219                             path_tmp[path_len - 1] = '\0';
00220                      }
00221 #else
00222                      path_tmp[path_len - 1] = '\0';
00223 #endif
00224               }
00225               nesting_level++;
00226        }
00227 
00228        /* Resolve open_basedir to resolved_basedir */
00229        if (expand_filepath(local_open_basedir, resolved_basedir TSRMLS_CC) != NULL) {
00230               /* Handler for basedirs that end with a / */
00231               resolved_basedir_len = strlen(resolved_basedir);
00232 #if defined(PHP_WIN32) || defined(NETWARE)
00233               if (basedir[strlen(basedir) - 1] == PHP_DIR_SEPARATOR || basedir[strlen(basedir) - 1] == '/') {
00234 #else
00235               if (basedir[strlen(basedir) - 1] == PHP_DIR_SEPARATOR) {
00236 #endif
00237                      if (resolved_basedir[resolved_basedir_len - 1] != PHP_DIR_SEPARATOR) {
00238                             resolved_basedir[resolved_basedir_len] = PHP_DIR_SEPARATOR;
00239                             resolved_basedir[++resolved_basedir_len] = '\0';
00240                      }
00241               } else {
00242                             resolved_basedir[resolved_basedir_len++] = PHP_DIR_SEPARATOR;
00243                             resolved_basedir[resolved_basedir_len] = '\0';
00244               }
00245 
00246               resolved_name_len = strlen(resolved_name);
00247               if (path_tmp[path_len - 1] == PHP_DIR_SEPARATOR) {
00248                      if (resolved_name[resolved_name_len - 1] != PHP_DIR_SEPARATOR) {
00249                             resolved_name[resolved_name_len] = PHP_DIR_SEPARATOR;
00250                             resolved_name[++resolved_name_len] = '\0';
00251                      }
00252               }
00253 
00254               /* Check the path */
00255 #if defined(PHP_WIN32) || defined(NETWARE)
00256               if (strncasecmp(resolved_basedir, resolved_name, resolved_basedir_len) == 0) {
00257 #else
00258               if (strncmp(resolved_basedir, resolved_name, resolved_basedir_len) == 0) {
00259 #endif
00260                      if (resolved_name_len > resolved_basedir_len &&
00261                             resolved_name[resolved_basedir_len - 1] != PHP_DIR_SEPARATOR) {
00262                             return -1;
00263                      } else {
00264                             /* File is in the right directory */
00265                             return 0;
00266                      }
00267               } else {
00268                      /* /openbasedir/ and /openbasedir are the same directory */
00269                      if (resolved_basedir_len == (resolved_name_len + 1) && resolved_basedir[resolved_basedir_len - 1] == PHP_DIR_SEPARATOR) {
00270 #if defined(PHP_WIN32) || defined(NETWARE)
00271                             if (strncasecmp(resolved_basedir, resolved_name, resolved_name_len) == 0) {
00272 #else
00273                             if (strncmp(resolved_basedir, resolved_name, resolved_name_len) == 0) {
00274 #endif
00275                                    return 0;
00276                             }
00277                      }
00278                      return -1;
00279               }
00280        } else {
00281               /* Unable to resolve the real path, return -1 */
00282               return -1;
00283        }
00284 }
00285 /* }}} */
00286 
00287 PHPAPI int php_check_open_basedir(const char *path TSRMLS_DC)
00288 {
00289        return php_check_open_basedir_ex(path, 1 TSRMLS_CC);
00290 }
00291 
00292 /* {{{ php_check_open_basedir
00293  */
00294 PHPAPI int php_check_open_basedir_ex(const char *path, int warn TSRMLS_DC)
00295 {
00296        /* Only check when open_basedir is available */
00297        if (PG(open_basedir) && *PG(open_basedir)) {
00298               char *pathbuf;
00299               char *ptr;
00300               char *end;
00301 
00302               /* Check if the path is too long so we can give a more useful error
00303               * message. */
00304               if (strlen(path) > (MAXPATHLEN - 1)) {
00305                      php_error_docref(NULL TSRMLS_CC, E_WARNING, "File name is longer than the maximum allowed path length on this platform (%d): %s", MAXPATHLEN, path);
00306                      errno = EINVAL;
00307                      return -1;
00308               }
00309 
00310               pathbuf = estrdup(PG(open_basedir));
00311 
00312               ptr = pathbuf;
00313 
00314               while (ptr && *ptr) {
00315                      end = strchr(ptr, DEFAULT_DIR_SEPARATOR);
00316                      if (end != NULL) {
00317                             *end = '\0';
00318                             end++;
00319                      }
00320 
00321                      if (php_check_specific_open_basedir(ptr, path TSRMLS_CC) == 0) {
00322                             efree(pathbuf);
00323                             return 0;
00324                      }
00325 
00326                      ptr = end;
00327               }
00328               if (warn) {
00329                      php_error_docref(NULL TSRMLS_CC, E_WARNING, "open_basedir restriction in effect. File(%s) is not within the allowed path(s): (%s)", path, PG(open_basedir));
00330               }
00331               efree(pathbuf);
00332               errno = EPERM; /* we deny permission to open it */
00333               return -1;
00334        }
00335 
00336        /* Nothing to check... */
00337        return 0;
00338 }
00339 /* }}} */
00340 
00341 /* {{{ php_check_safe_mode_include_dir
00342  */
00343 PHPAPI int php_check_safe_mode_include_dir(const char *path TSRMLS_DC)
00344 {
00345        if (PG(safe_mode)) {
00346               if (PG(safe_mode_include_dir) && *PG(safe_mode_include_dir)) {
00347                      char *pathbuf;
00348                      char *ptr;
00349                      char *end;
00350                      char resolved_name[MAXPATHLEN];
00351 
00352                      /* Resolve the real path into resolved_name */
00353                      if (expand_filepath(path, resolved_name TSRMLS_CC) == NULL) {
00354                             return -1;
00355                      }
00356                      pathbuf = estrdup(PG(safe_mode_include_dir));
00357                      ptr = pathbuf;
00358 
00359                      while (ptr && *ptr) {
00360                             end = strchr(ptr, DEFAULT_DIR_SEPARATOR);
00361                             if (end != NULL) {
00362                                    *end = '\0';
00363                                    end++;
00364                             }
00365 
00366                             /* Check the path */
00367 #ifdef PHP_WIN32
00368                             if (strncasecmp(ptr, resolved_name, strlen(ptr)) == 0)
00369 #else
00370                             if (strncmp(ptr, resolved_name, strlen(ptr)) == 0)
00371 #endif
00372                             {
00373                                    /* File is in the right directory */
00374                                    efree(pathbuf);
00375                                    return 0;
00376                             }
00377 
00378                             ptr = end;
00379                      }
00380                      efree(pathbuf);
00381               }
00382               return -1;
00383        }
00384 
00385        /* Nothing to check... */
00386        return 0;
00387 }
00388 /* }}} */
00389 
00390 /* {{{ php_fopen_and_set_opened_path
00391  */
00392 static FILE *php_fopen_and_set_opened_path(const char *path, const char *mode, char **opened_path TSRMLS_DC)
00393 {
00394        FILE *fp;
00395 
00396        if (php_check_open_basedir((char *)path TSRMLS_CC)) {
00397               return NULL;
00398        }
00399        fp = VCWD_FOPEN(path, mode);
00400        if (fp && opened_path) {
00401               *opened_path = expand_filepath(path, NULL TSRMLS_CC);
00402        }
00403        return fp;
00404 }
00405 /* }}} */
00406 
00407 /* {{{ php_fopen_primary_script
00408  */
00409 PHPAPI int php_fopen_primary_script(zend_file_handle *file_handle TSRMLS_DC)
00410 {
00411        FILE *fp;
00412 #ifndef PHP_WIN32
00413        struct stat st;
00414 #endif
00415        char *path_info;
00416        char *filename = NULL;
00417        char *resolved_path = NULL;
00418        int length;
00419 
00420        path_info = SG(request_info).request_uri;
00421 #if HAVE_PWD_H
00422        if (PG(user_dir) && *PG(user_dir) && path_info && '/' == path_info[0] && '~' == path_info[1]) {
00423               char *s = strchr(path_info + 2, '/');
00424 
00425               if (s) {                    /* if there is no path name after the file, do not bother */
00426                      char user[32];                     /* to try open the directory */
00427                      struct passwd *pw;
00428 #if defined(ZTS) && defined(HAVE_GETPWNAM_R) && defined(_SC_GETPW_R_SIZE_MAX)
00429                      struct passwd pwstruc;
00430                      long pwbuflen = sysconf(_SC_GETPW_R_SIZE_MAX);
00431                      char *pwbuf;
00432 
00433                      if (pwbuflen < 1) {
00434                             return FAILURE;
00435                      }
00436 
00437                      pwbuf = emalloc(pwbuflen);
00438 #endif
00439                      length = s - (path_info + 2);
00440                      if (length > (int)sizeof(user) - 1) {
00441                             length = sizeof(user) - 1;
00442                      }
00443                      memcpy(user, path_info + 2, length);
00444                      user[length] = '\0';
00445 #if defined(ZTS) && defined(HAVE_GETPWNAM_R) && defined(_SC_GETPW_R_SIZE_MAX)
00446                      if (getpwnam_r(user, &pwstruc, pwbuf, pwbuflen, &pw)) {
00447                             efree(pwbuf);
00448                             return FAILURE;
00449                      }
00450 #else
00451                      pw = getpwnam(user);
00452 #endif
00453                      if (pw && pw->pw_dir) {
00454                             spprintf(&filename, 0, "%s%c%s%c%s", pw->pw_dir, PHP_DIR_SEPARATOR, PG(user_dir), PHP_DIR_SEPARATOR, s + 1); /* Safe */
00455                      } else {
00456                             filename = SG(request_info).path_translated;
00457                      } 
00458 #if defined(ZTS) && defined(HAVE_GETPWNAM_R) && defined(_SC_GETPW_R_SIZE_MAX)
00459                      efree(pwbuf);
00460 #endif
00461               }
00462        } else
00463 #endif
00464        if (PG(doc_root) && path_info && (length = strlen(PG(doc_root))) &&
00465               IS_ABSOLUTE_PATH(PG(doc_root), length)) {
00466               int path_len = strlen(path_info);
00467               filename = emalloc(length + path_len + 2);
00468               if (filename) {
00469                      memcpy(filename, PG(doc_root), length);
00470                      if (!IS_SLASH(filename[length - 1])) {    /* length is never 0 */
00471                             filename[length++] = PHP_DIR_SEPARATOR;
00472                      }
00473                      if (IS_SLASH(path_info[0])) {
00474                             length--;
00475                      }
00476                      strncpy(filename + length, path_info, path_len + 1);
00477               }
00478        } else {
00479               filename = SG(request_info).path_translated;
00480        }
00481 
00482 
00483        if (filename) {
00484               resolved_path = zend_resolve_path(filename, strlen(filename) TSRMLS_CC);
00485        }
00486 
00487        if (!resolved_path) {
00488               if (SG(request_info).path_translated != filename) {
00489                      STR_FREE(filename);
00490               }
00491               /* we have to free SG(request_info).path_translated here because
00492                * php_destroy_request_info assumes that it will get
00493                * freed when the include_names hash is emptied, but
00494                * we're not adding it in this case */
00495               STR_FREE(SG(request_info).path_translated);
00496               SG(request_info).path_translated = NULL;
00497               return FAILURE;
00498        }
00499        fp = VCWD_FOPEN(resolved_path, "rb");
00500 
00501 #ifndef PHP_WIN32
00502        /* refuse to open anything that is not a regular file */
00503        if (fp && (0 > fstat(fileno(fp), &st) || !S_ISREG(st.st_mode))) {
00504               fclose(fp);
00505               fp = NULL;
00506        }
00507 #endif
00508 
00509        if (!fp) {
00510               if (SG(request_info).path_translated != filename) {
00511                      STR_FREE(filename);
00512               }
00513               STR_FREE(SG(request_info).path_translated);      /* for same reason as above */
00514               SG(request_info).path_translated = NULL;
00515               return FAILURE;
00516        }
00517 
00518        file_handle->opened_path = resolved_path;
00519 
00520        if (SG(request_info).path_translated != filename) {
00521               STR_FREE(SG(request_info).path_translated);      /* for same reason as above */
00522               SG(request_info).path_translated = filename;
00523        }
00524 
00525        file_handle->filename = SG(request_info).path_translated;
00526        file_handle->free_filename = 0;
00527        file_handle->handle.fp = fp;
00528        file_handle->type = ZEND_HANDLE_FP;
00529 
00530        return SUCCESS;
00531 }
00532 /* }}} */
00533 
00534 /* {{{ php_resolve_path
00535  * Returns the realpath for given filename according to include path
00536  */
00537 PHPAPI char *php_resolve_path(const char *filename, int filename_length, const char *path TSRMLS_DC)
00538 {
00539        char resolved_path[MAXPATHLEN];
00540        char trypath[MAXPATHLEN];
00541        const char *ptr, *end, *p;
00542        char *actual_path;
00543        php_stream_wrapper *wrapper;
00544 
00545        if (!filename) {
00546               return NULL;
00547        }
00548 
00549        if (strlen(filename) != filename_length) {
00550               return NULL;
00551        }
00552 
00553        /* Don't resolve paths which contain protocol (except of file://) */
00554        for (p = filename; isalnum((int)*p) || *p == '+' || *p == '-' || *p == '.'; p++);
00555        if ((*p == ':') && (p - filename > 1) && (p[1] == '/') && (p[2] == '/')) {
00556               wrapper = php_stream_locate_url_wrapper(filename, &actual_path, STREAM_OPEN_FOR_INCLUDE TSRMLS_CC);
00557               if (wrapper == &php_plain_files_wrapper) {
00558                      if (tsrm_realpath(actual_path, resolved_path TSRMLS_CC)) {
00559                             return estrdup(resolved_path);
00560                      }
00561               }
00562               return NULL;
00563        }
00564 
00565        if ((*filename == '.' && 
00566             (IS_SLASH(filename[1]) || 
00567              ((filename[1] == '.') && IS_SLASH(filename[2])))) ||
00568            IS_ABSOLUTE_PATH(filename, filename_length) ||
00569            !path ||
00570            !*path) {
00571               if (tsrm_realpath(filename, resolved_path TSRMLS_CC)) {
00572                      return estrdup(resolved_path);
00573               } else {
00574                      return NULL;
00575               }
00576        }
00577 
00578        ptr = path;
00579        while (ptr && *ptr) {
00580               /* Check for stream wrapper */
00581               int is_stream_wrapper = 0;
00582 
00583               for (p = ptr; isalnum((int)*p) || *p == '+' || *p == '-' || *p == '.'; p++);
00584               if ((*p == ':') && (p - ptr > 1) && (p[1] == '/') && (p[2] == '/')) {
00585                      /* .:// or ..:// is not a stream wrapper */
00586                      if (p[-1] != '.' || p[-2] != '.' || p - 2 != ptr) {
00587                             p += 3;
00588                             is_stream_wrapper = 1;
00589                      }
00590               }
00591               end = strchr(p, DEFAULT_DIR_SEPARATOR);
00592               if (end) {
00593                      if ((end-ptr) + 1 + filename_length + 1 >= MAXPATHLEN) {
00594                             ptr = end + 1;
00595                             continue;
00596                      }
00597                      memcpy(trypath, ptr, end-ptr);
00598                      trypath[end-ptr] = '/';
00599                      memcpy(trypath+(end-ptr)+1, filename, filename_length+1);
00600                      ptr = end+1;
00601               } else {
00602                      int len = strlen(ptr);
00603 
00604                      if (len + 1 + filename_length + 1 >= MAXPATHLEN) {
00605                             break;
00606                      }
00607                      memcpy(trypath, ptr, len);
00608                      trypath[len] = '/';
00609                      memcpy(trypath+len+1, filename, filename_length+1);
00610                      ptr = NULL;
00611               }
00612               actual_path = trypath;
00613               if (is_stream_wrapper) {
00614                      wrapper = php_stream_locate_url_wrapper(trypath, &actual_path, STREAM_OPEN_FOR_INCLUDE TSRMLS_CC);
00615                      if (!wrapper) {
00616                             continue;
00617                      } else if (wrapper != &php_plain_files_wrapper) {
00618                             if (wrapper->wops->url_stat) {
00619                                    php_stream_statbuf ssb;
00620 
00621                                    if (SUCCESS == wrapper->wops->url_stat(wrapper, trypath, 0, &ssb, NULL TSRMLS_CC)) {
00622                                           return estrdup(trypath);
00623                                    }
00624                             }
00625                             continue;
00626                      }
00627               }
00628               if (tsrm_realpath(actual_path, resolved_path TSRMLS_CC)) {
00629                      return estrdup(resolved_path);
00630               }
00631        } /* end provided path */
00632 
00633        /* check in calling scripts' current working directory as a fall back case
00634         */
00635        if (zend_is_executing(TSRMLS_C)) {
00636               char *exec_fname = zend_get_executed_filename(TSRMLS_C);
00637               int exec_fname_length = strlen(exec_fname);
00638 
00639               while ((--exec_fname_length >= 0) && !IS_SLASH(exec_fname[exec_fname_length]));
00640               if (exec_fname && exec_fname[0] != '[' &&
00641                   exec_fname_length > 0 &&
00642                   exec_fname_length + 1 + filename_length + 1 < MAXPATHLEN) {
00643                      memcpy(trypath, exec_fname, exec_fname_length + 1);
00644                      memcpy(trypath+exec_fname_length + 1, filename, filename_length+1);
00645                      actual_path = trypath;
00646 
00647                      /* Check for stream wrapper */
00648                      for (p = trypath; isalnum((int)*p) || *p == '+' || *p == '-' || *p == '.'; p++);
00649                      if ((*p == ':') && (p - trypath > 1) && (p[1] == '/') && (p[2] == '/')) {
00650                             wrapper = php_stream_locate_url_wrapper(trypath, &actual_path, STREAM_OPEN_FOR_INCLUDE TSRMLS_CC);
00651                             if (!wrapper) {
00652                                    return NULL;
00653                             } else if (wrapper != &php_plain_files_wrapper) {
00654                                    if (wrapper->wops->url_stat) {
00655                                           php_stream_statbuf ssb;
00656 
00657                                           if (SUCCESS == wrapper->wops->url_stat(wrapper, trypath, 0, &ssb, NULL TSRMLS_CC)) {
00658                                                  return estrdup(trypath);
00659                                           }
00660                                    }
00661                                    return NULL;
00662                             }
00663                      }
00664 
00665                      if (tsrm_realpath(actual_path, resolved_path TSRMLS_CC)) {
00666                             return estrdup(resolved_path);
00667                      }
00668               }
00669        }
00670 
00671        return NULL;
00672 }
00673 /* }}} */
00674 
00675 /* {{{ php_fopen_with_path
00676  * Tries to open a file with a PATH-style list of directories.
00677  * If the filename starts with "." or "/", the path is ignored.
00678  */
00679 PHPAPI FILE *php_fopen_with_path(const char *filename, const char *mode, const char *path, char **opened_path TSRMLS_DC)
00680 {
00681        char *pathbuf, *ptr, *end;
00682        char *exec_fname;
00683        char trypath[MAXPATHLEN];
00684        struct stat sb;
00685        FILE *fp;
00686        int path_length;
00687        int filename_length;
00688        int exec_fname_length;
00689 
00690        if (opened_path) {
00691               *opened_path = NULL;
00692        }
00693 
00694        if (!filename) {
00695               return NULL;
00696        }
00697 
00698        filename_length = strlen(filename);
00699 
00700        /* Relative path open */
00701        if (*filename == '.') {
00702               if (PG(safe_mode) && (!php_checkuid(filename, mode, CHECKUID_CHECK_MODE_PARAM))) {
00703                      return NULL;
00704               }
00705               return php_fopen_and_set_opened_path(filename, mode, opened_path TSRMLS_CC);
00706        }
00707 
00708        /*
00709         * files in safe_mode_include_dir (or subdir) are excluded from
00710         * safe mode GID/UID checks
00711         */
00712 
00713        /* Absolute path open */
00714        if (IS_ABSOLUTE_PATH(filename, filename_length)) {
00715               if (php_check_safe_mode_include_dir(filename TSRMLS_CC) == 0) {
00716                      /* filename is in safe_mode_include_dir (or subdir) */
00717                      return php_fopen_and_set_opened_path(filename, mode, opened_path TSRMLS_CC);
00718               }
00719               if (PG(safe_mode) && (!php_checkuid(filename, mode, CHECKUID_CHECK_MODE_PARAM))) {
00720                      return NULL;
00721               }
00722               return php_fopen_and_set_opened_path(filename, mode, opened_path TSRMLS_CC);
00723        }
00724 
00725        if (!path || (path && !*path)) {
00726               if (PG(safe_mode) && (!php_checkuid(filename, mode, CHECKUID_CHECK_MODE_PARAM))) {
00727                      return NULL;
00728               }
00729               return php_fopen_and_set_opened_path(filename, mode, opened_path TSRMLS_CC);
00730        }
00731 
00732        /* check in provided path */
00733        /* append the calling scripts' current working directory
00734         * as a fall back case
00735         */
00736        if (zend_is_executing(TSRMLS_C)) {
00737               exec_fname = zend_get_executed_filename(TSRMLS_C);
00738               exec_fname_length = strlen(exec_fname);
00739               path_length = strlen(path);
00740 
00741               while ((--exec_fname_length >= 0) && !IS_SLASH(exec_fname[exec_fname_length]));
00742               if ((exec_fname && exec_fname[0] == '[') || exec_fname_length <= 0) {
00743                      /* [no active file] or no path */
00744                      pathbuf = estrdup(path);
00745               } else {
00746                      pathbuf = (char *) emalloc(exec_fname_length + path_length + 1 + 1);
00747                      memcpy(pathbuf, path, path_length);
00748                      pathbuf[path_length] = DEFAULT_DIR_SEPARATOR;
00749                      memcpy(pathbuf + path_length + 1, exec_fname, exec_fname_length);
00750                      pathbuf[path_length + exec_fname_length + 1] = '\0';
00751               }
00752        } else {
00753               pathbuf = estrdup(path);
00754        }
00755 
00756        ptr = pathbuf;
00757 
00758        while (ptr && *ptr) {
00759               end = strchr(ptr, DEFAULT_DIR_SEPARATOR);
00760               if (end != NULL) {
00761                      *end = '\0';
00762                      end++;
00763               }
00764               if (snprintf(trypath, MAXPATHLEN, "%s/%s", ptr, filename) >= MAXPATHLEN) {
00765                      php_error_docref(NULL TSRMLS_CC, E_NOTICE, "%s/%s path was truncated to %d", ptr, filename, MAXPATHLEN);
00766               }
00767               if (PG(safe_mode)) {
00768                      if (VCWD_STAT(trypath, &sb) == 0) {
00769                             /* file exists ... check permission */
00770                             if (php_check_safe_mode_include_dir(trypath TSRMLS_CC) == 0 ||
00771                                    php_checkuid(trypath, mode, CHECKUID_CHECK_MODE_PARAM)
00772                             ) {
00773                                    /* UID ok, or trypath is in safe_mode_include_dir */
00774                                    fp = php_fopen_and_set_opened_path(trypath, mode, opened_path TSRMLS_CC);
00775                             } else {
00776                                    fp = NULL;
00777                             }
00778                             efree(pathbuf);
00779                             return fp;
00780                      }
00781               }
00782               fp = php_fopen_and_set_opened_path(trypath, mode, opened_path TSRMLS_CC);
00783               if (fp) {
00784                      efree(pathbuf);
00785                      return fp;
00786               }
00787               ptr = end;
00788        } /* end provided path */
00789 
00790        efree(pathbuf);
00791        return NULL;
00792 }
00793 /* }}} */
00794 
00795 /* {{{ php_strip_url_passwd
00796  */
00797 PHPAPI char *php_strip_url_passwd(char *url)
00798 {
00799        register char *p, *url_start;
00800 
00801        if (url == NULL) {
00802               return "";
00803        }
00804 
00805        p = url;
00806 
00807        while (*p) {
00808               if (*p == ':' && *(p + 1) == '/' && *(p + 2) == '/') {
00809                      /* found protocol */
00810                      url_start = p = p + 3;
00811 
00812                      while (*p) {
00813                             if (*p == '@') {
00814                                    int i;
00815 
00816                                    for (i = 0; i < 3 && url_start < p; i++, url_start++) {
00817                                           *url_start = '.';
00818                                    }
00819                                    for (; *p; p++) {
00820                                           *url_start++ = *p;
00821                                    }
00822                                    *url_start=0;
00823                                    break;
00824                             }
00825                             p++;
00826                      }
00827                      return url;
00828               }
00829               p++;
00830        }
00831        return url;
00832 }
00833 /* }}} */
00834 
00835 /* {{{ expand_filepath
00836  */
00837 PHPAPI char *expand_filepath(const char *filepath, char *real_path TSRMLS_DC)
00838 {
00839        return expand_filepath_ex(filepath, real_path, NULL, 0 TSRMLS_CC);
00840 }
00841 /* }}} */
00842 
00843 /* {{{ expand_filepath_ex
00844  */
00845 PHPAPI char *expand_filepath_ex(const char *filepath, char *real_path, const char *relative_to, size_t relative_to_len TSRMLS_DC)
00846 {
00847        cwd_state new_state;
00848        char cwd[MAXPATHLEN];
00849        int copy_len;
00850 
00851        if (!filepath[0]) {
00852               return NULL;
00853        } else if (IS_ABSOLUTE_PATH(filepath, strlen(filepath))) {
00854               cwd[0] = '\0';
00855        } else {
00856               const char *iam = SG(request_info).path_translated;
00857               const char *result;
00858               if (relative_to) {
00859                      if (relative_to_len > MAXPATHLEN-1U) {
00860                             return NULL;
00861                      }
00862                      result = relative_to;
00863                      memcpy(cwd, relative_to, relative_to_len+1U);
00864               } else {
00865                      result = VCWD_GETCWD(cwd, MAXPATHLEN);
00866               }
00867 
00868               if (!result && (iam != filepath)) {
00869                      int fdtest = -1;
00870 
00871                      fdtest = VCWD_OPEN(filepath, O_RDONLY);
00872                      if (fdtest != -1) {
00873                             /* return a relative file path if for any reason
00874                              * we cannot cannot getcwd() and the requested,
00875                              * relatively referenced file is accessible */
00876                             copy_len = strlen(filepath) > MAXPATHLEN - 1 ? MAXPATHLEN - 1 : strlen(filepath);
00877                             real_path = estrndup(filepath, copy_len);
00878                             close(fdtest);
00879                             return real_path;
00880                      } else {
00881                             cwd[0] = '\0';
00882                      }
00883               } else if (!result) {
00884                      cwd[0] = '\0';
00885               }
00886        }
00887 
00888        new_state.cwd = strdup(cwd);
00889        new_state.cwd_length = strlen(cwd);
00890 
00891        if (virtual_file_ex(&new_state, filepath, NULL, CWD_FILEPATH)) {
00892               free(new_state.cwd);
00893               return NULL;
00894        }
00895 
00896        if (real_path) {
00897               copy_len = new_state.cwd_length > MAXPATHLEN - 1 ? MAXPATHLEN - 1 : new_state.cwd_length;
00898               memcpy(real_path, new_state.cwd, copy_len);
00899               real_path[copy_len] = '\0';
00900        } else {
00901               real_path = estrndup(new_state.cwd, new_state.cwd_length);
00902        }
00903        free(new_state.cwd);
00904 
00905        return real_path;
00906 }
00907 /* }}} */
00908 
00909 /*
00910  * Local variables:
00911  * tab-width: 4
00912  * c-basic-offset: 4
00913  * End:
00914  * vim600: sw=4 ts=4 fdm=marker
00915  * vim<600: sw=4 ts=4
00916  */