#include "php.h"
#include "fopen_wrappers.h"
-
+#include "file.h"
#include "php_dir.h"
#ifdef HAVE_DIRENT_H
php_dir_globals dir_globals;
#endif
+#if 0
typedef struct {
int id;
DIR *dir;
} php_dir;
static int le_dirp;
+#endif
static zend_class_entry *dir_class_entry_ptr;
php_error(E_WARNING, "unable to find my handle property"); \
RETURN_FALSE; \
} \
- ZEND_FETCH_RESOURCE(dirp, php_dir *, tmp, -1, "Directory", le_dirp); \
+ ZEND_FETCH_RESOURCE(dirp, php_stream *, tmp, -1, "Directory", php_file_le_stream()); \
} else { \
- ZEND_FETCH_RESOURCE(dirp, php_dir *, 0, DIRG(default_dir), "Directory", le_dirp); \
+ ZEND_FETCH_RESOURCE(dirp, php_stream *, 0, DIRG(default_dir), "Directory", php_file_le_stream()); \
} \
} else if ((ZEND_NUM_ARGS() != 1) || zend_get_parameters_ex(1, &id) == FAILURE) { \
WRONG_PARAM_COUNT; \
} else { \
- ZEND_FETCH_RESOURCE(dirp, php_dir *, id,-1, "Directory", le_dirp); \
+ ZEND_FETCH_RESOURCE(dirp, php_stream *, id,-1, "Directory", php_file_le_stream()); \
}
static zend_function_entry php_dir_class_functions[] = {
DIRG(default_dir) = id;
}
-
-static void _dir_dtor(zend_rsrc_list_entry *rsrc TSRMLS_DC)
-{
- php_dir *dirp = (php_dir *)rsrc->ptr;
-
- closedir(dirp->dir);
- efree(dirp);
-}
-
PHP_RINIT_FUNCTION(dir)
{
DIRG(default_dir) = -1;
static char tmpstr[2];
zend_class_entry dir_class_entry;
- le_dirp = zend_register_list_destructors_ex(_dir_dtor, NULL, "dir", module_number);
-
INIT_CLASS_ENTRY(dir_class_entry, "Directory", php_dir_class_functions);
dir_class_entry_ptr = zend_register_internal_class(&dir_class_entry TSRMLS_CC);
static void _php_do_opendir(INTERNAL_FUNCTION_PARAMETERS, int createobject)
{
pval **arg;
- php_dir *dirp;
+ php_stream *dirp;
if (ZEND_NUM_ARGS() != 1 || zend_get_parameters_ex(1, &arg) == FAILURE) {
WRONG_PARAM_COUNT;
}
convert_to_string_ex(arg);
-
- if (php_check_open_basedir(Z_STRVAL_PP(arg) TSRMLS_CC)) {
- RETURN_FALSE;
- }
- if (PG(safe_mode) &&(!php_checkuid(Z_STRVAL_PP(arg), NULL, CHECKUID_ALLOW_ONLY_FILE))) {
- RETURN_FALSE;
- }
-
- dirp = emalloc(sizeof(php_dir));
-
- dirp->dir = VCWD_OPENDIR(Z_STRVAL_PP(arg));
+ dirp = php_stream_opendir(Z_STRVAL_PP(arg), ENFORCE_SAFE_MODE|REPORT_ERRORS, NULL);
-#ifdef PHP_WIN32
- if (!dirp->dir || dirp->dir->finished) {
- if (dirp->dir) {
- closedir(dirp->dir);
- }
-#else
- if (!dirp->dir) {
-#endif
- efree(dirp);
- php_error(E_WARNING, "OpenDir: %s (errno %d)", strerror(errno), errno);
+ if (dirp == NULL) {
RETURN_FALSE;
}
-
- dirp->id = zend_list_insert(dirp, le_dirp);
-
- php_set_default_dir(dirp->id TSRMLS_CC);
+
+ php_set_default_dir(dirp->rsrc_id TSRMLS_CC);
if (createobject) {
object_init_ex(return_value, dir_class_entry_ptr);
add_property_stringl(return_value, "path", Z_STRVAL_PP(arg), Z_STRLEN_PP(arg), 1);
- add_property_resource(return_value, "handle", dirp->id);
- zend_list_addref(dirp->id);
+ add_property_resource(return_value, "handle", dirp->rsrc_id);
+ zend_list_addref(dirp->rsrc_id); /* might not be needed */
+ php_stream_auto_cleanup(dirp); /* so we don't get warnings under debug */
} else {
- RETURN_RESOURCE(dirp->id);
+ php_stream_to_zval(dirp, return_value);
}
}
PHP_FUNCTION(closedir)
{
pval **id, **tmp, *myself;
- php_dir *dirp;
+ php_stream *dirp;
FETCH_DIRP();
- zend_list_delete(dirp->id);
+ zend_list_delete(dirp->rsrc_id);
- if (dirp->id == DIRG(default_dir)) {
+ if (dirp->rsrc_id == DIRG(default_dir)) {
php_set_default_dir(-1 TSRMLS_CC);
}
}
PHP_FUNCTION(rewinddir)
{
pval **id, **tmp, *myself;
- php_dir *dirp;
+ php_stream *dirp;
FETCH_DIRP();
- rewinddir(dirp->dir);
+ php_stream_rewinddir(dirp);
}
/* }}} */
PHP_NAMED_FUNCTION(php_if_readdir)
{
pval **id, **tmp, *myself;
- php_dir *dirp;
- char entry[sizeof(struct dirent)+MAXPATHLEN];
- struct dirent *result = (struct dirent *)&entry; /* patch for libc5 readdir problems */
+ php_stream *dirp;
+ php_stream_dirent entry;
FETCH_DIRP();
- if (php_readdir_r(dirp->dir, (struct dirent *) entry, &result) == 0 && result) {
- RETURN_STRINGL(result->d_name, strlen(result->d_name), 1);
+ if (php_stream_readdir(dirp, &entry)) {
+ RETURN_STRINGL(entry.d_name, strlen(entry.d_name), 1);
}
RETURN_FALSE;
}
/* extended info to go here some day */
} php_stream_statbuf;
+typedef struct _php_stream_dirent {
+ char d_name[MAXPATHLEN];
+} php_stream_dirent;
+
#define PHP_STREAM_NOTIFIER_PROGRESS 1
typedef struct _php_stream_notifier {
* to go here some day */
} php_stream_wrapper_options;
+/* operations on streams that are file-handles */
typedef struct _php_stream_ops {
/* stdio like functions - these are mandatory! */
size_t (*write)(php_stream *stream, const char *buf, size_t count TSRMLS_DC);
typedef struct _php_stream_wrapper_ops {
/* open/create a wrapped stream */
- php_stream *(*opener)(php_stream_wrapper *wrapper, char *filename, char *mode,
+ php_stream *(*stream_opener)(php_stream_wrapper *wrapper, char *filename, char *mode,
int options, char **opened_path, php_stream_context *context STREAMS_DC TSRMLS_DC);
/* close/destroy a wrapped stream */
- int (*closer)(php_stream_wrapper *wrapper, php_stream *stream TSRMLS_DC);
+ int (*stream_closer)(php_stream_wrapper *wrapper, php_stream *stream TSRMLS_DC);
/* stat a wrapped stream */
int (*stream_stat)(php_stream_wrapper *wrapper, php_stream *stream, php_stream_statbuf *ssb TSRMLS_DC);
/* stat a URL */
- int (*url_stat)(php_stream_wrapper *wrapper, php_stream_statbuf *ssb TSRMLS_DC);
+ int (*url_stat)(php_stream_wrapper *wrapper, char *url, php_stream_statbuf *ssb TSRMLS_DC);
+ /* open a "directory" stream */
+ php_stream *(*dir_opener)(php_stream_wrapper *wrapper, char *filename, char *mode,
+ int options, char **opened_path, php_stream_context *context STREAMS_DC TSRMLS_DC);
+
} php_stream_wrapper_ops;
struct _php_stream_wrapper {
php_stream_wrapper_ops *wops; /* operations the wrapper can perform */
void *abstract; /* context for the wrapper */
+ int is_url; /* so that PG(allow_url_fopen) can be respected */
};
-
struct _php_stream {
php_stream_ops *ops;
void *abstract; /* convenience pointer for abstraction */
PHPAPI int _php_stream_stat(php_stream *stream, php_stream_statbuf *ssb TSRMLS_DC);
#define php_stream_stat(stream, ssb) _php_stream_stat((stream), (ssb) TSRMLS_CC)
+PHPAPI int _php_stream_stat_path(char *path, php_stream_statbuf *ssb TSRMLS_DC);
+#define php_stream_stat_path(path, ssb) _php_stream_stat((path), (ssb) TSRMLS_CC)
+
+PHPAPI php_stream *_php_stream_opendir(char *path, int options, php_stream_context *context STREAMS_DC TSRMLS_DC);
+#define php_stream_opendir(path, options, context) _php_stream_opendir((path), (options), (context) STREAMS_CC TSRMLS_CC)
+PHPAPI php_stream_dirent *_php_stream_readdir(php_stream *dirstream, php_stream_dirent *ent TSRMLS_DC);
+#define php_stream_readdir(dirstream, dirent) _php_stream_readdir((dirstream), (dirent) TSRMLS_CC)
+#define php_stream_closedir(dirstream) php_stream_close((dirstream))
+#define php_stream_rewinddir(dirstream) php_stream_rewind((dirstream))
+
+
/* copy up to maxlen bytes from src to dest. If maxlen is PHP_STREAM_COPY_ALL, copy until eof(src).
* Uses mmap if the src is a plain file and at offset 0 */
#define PHP_STREAM_COPY_ALL -1
PHPAPI size_t _php_stream_copy_to_stream(php_stream *src, php_stream *dest, size_t maxlen STREAMS_DC TSRMLS_DC);
#define php_stream_copy_to_stream(src, dest, maxlen) _php_stream_copy_to_stream((src), (dest), (maxlen) STREAMS_CC TSRMLS_CC)
+
/* read all data from stream and put into a buffer. Caller must free buffer when done.
* The copy will use mmap if available. */
PHPAPI size_t _php_stream_copy_to_mem(php_stream *src, char **buf, size_t maxlen,
#define php_stream_open_wrapper_as_file(path, mode, options, opened_path) _php_stream_open_wrapper_as_file((path), (mode), (options), (opened_path) STREAMS_CC TSRMLS_CC)
/* for user-space streams */
-extern php_stream_ops php_stream_userspace_ops;
+PHPAPI extern php_stream_ops php_stream_userspace_ops;
#define PHP_STREAM_IS_USERSPACE &php_stream_userspace_ops
PHPAPI void php_stream_context_free(php_stream_context *context);
#define STREAM_DEBUG 0
+#define STREAM_WRAPPER_PLAIN_FILES ((php_stream_wrapper*)-1)
+
/* {{{ some macros to help track leaks */
#if ZEND_DEBUG
#define emalloc_rel_orig(size) \
if (close_options & PHP_STREAM_FREE_RELEASE_STREAM) {
- if (stream->wrapper && stream->wrapper->wops && stream->wrapper->wops->closer) {
- stream->wrapper->wops->closer(stream->wrapper, stream TSRMLS_CC);
+ if (stream->wrapper && stream->wrapper->wops && stream->wrapper->wops->stream_closer) {
+ stream->wrapper->wops->stream_closer(stream->wrapper, stream TSRMLS_CC);
stream->wrapper = NULL;
}
/* {{{ generic stream operations */
PHPAPI size_t _php_stream_read(php_stream *stream, char *buf, size_t size TSRMLS_DC)
{
- return stream->ops->read(stream, buf, size TSRMLS_CC);
+ return stream->ops->read == NULL ? 0 : stream->ops->read(stream, buf, size TSRMLS_CC);
}
PHPAPI int _php_stream_eof(php_stream *stream TSRMLS_DC)
/* we define our stream reading function so that it
must return EOF when an EOF condition occurs, when
working in unbuffered mode and called with these args */
- return stream->ops->read(stream, NULL, 0 TSRMLS_CC) == EOF ? 1 : 0;
+ return stream->ops->read == NULL ? -1 : stream->ops->read(stream, NULL, 0 TSRMLS_CC) == EOF ? 1 : 0;
}
PHPAPI int _php_stream_putc(php_stream *stream, int c TSRMLS_DC)
if (stream->ops->gets) {
return stream->ops->gets(stream, buf, maxlen TSRMLS_CC);
+ } else if (stream->ops->read == NULL) {
+ return NULL;
} else {
/* unbuffered fgets - poor performance ! */
size_t n = 1;
PHPAPI size_t _php_stream_write(php_stream *stream, const char *buf, size_t count TSRMLS_DC)
{
assert(stream);
- if (buf == NULL || count == 0)
+ if (buf == NULL || count == 0 || stream->ops->write == NULL)
return 0;
return stream->ops->write(stream, buf, count TSRMLS_CC);
}
/* {{{ wrapper init and registration */
int php_init_stream_wrappers(TSRMLS_D)
{
- if (PG(allow_url_fopen)) {
- return zend_hash_init(&url_stream_wrappers_hash, 0, NULL, NULL, 1);
- }
- return SUCCESS;
+ return zend_hash_init(&url_stream_wrappers_hash, 0, NULL, NULL, 1);
}
int php_shutdown_stream_wrappers(TSRMLS_D)
{
- if (PG(allow_url_fopen)) {
- zend_hash_destroy(&url_stream_wrappers_hash);
- }
+ zend_hash_destroy(&url_stream_wrappers_hash);
return SUCCESS;
}
PHPAPI int php_register_url_stream_wrapper(char *protocol, php_stream_wrapper *wrapper TSRMLS_DC)
{
- if (PG(allow_url_fopen)) {
- return zend_hash_add(&url_stream_wrappers_hash, protocol, strlen(protocol), wrapper, sizeof(*wrapper), NULL);
- }
- return FAILURE;
+ if (!PG(allow_url_fopen) && wrapper->is_url)
+ return FAILURE;
+
+ return zend_hash_add(&url_stream_wrappers_hash, protocol, strlen(protocol), wrapper, sizeof(*wrapper), NULL);
}
PHPAPI int php_unregister_url_stream_wrapper(char *protocol TSRMLS_DC)
{
- if (PG(allow_url_fopen)) {
- return zend_hash_del(&url_stream_wrappers_hash, protocol, strlen(protocol));
- }
- return SUCCESS;
+ return zend_hash_del(&url_stream_wrappers_hash, protocol, strlen(protocol));
}
/* }}} */
-/* {{{ php_stream_open_url */
-static php_stream *php_stream_open_url(char *path, char *mode, int options,
- char **opened_path, php_stream_context *context STREAMS_DC TSRMLS_DC)
+
+static size_t php_plain_files_dirstream_read(php_stream *stream, char *buf, size_t count TSRMLS_DC)
{
- php_stream_wrapper *wrapper;
+ DIR *dir = (DIR*)stream->abstract;
+ /* avoid libc5 readdir problems */
+ char entry[sizeof(struct dirent)+MAXPATHLEN];
+ struct dirent *result = (struct dirent *)&entry;
+ php_stream_dirent *ent = (php_stream_dirent*)buf;
+
+ /* avoid problems if someone mis-uses the stream */
+ if (count != sizeof(php_stream_dirent))
+ return 0;
+
+ if (php_readdir_r(dir, (struct dirent *)entry, &result) == 0 && result) {
+ PHP_STRLCPY(ent->d_name, result->d_name, sizeof(ent->d_name), strlen(result->d_name));
+ return sizeof(php_stream_dirent);
+ }
+ return 0;
+}
+
+static int php_plain_files_dirstream_close(php_stream *stream, int close_handle TSRMLS_DC)
+{
+ return closedir((DIR *)stream->abstract);
+}
+
+static int php_plain_files_dirstream_rewind(php_stream *stream, off_t offset, int whence TSRMLS_DC)
+{
+ rewinddir((DIR *)stream->abstract);
+ return 0;
+}
+
+static php_stream_ops php_plain_files_dirstream_ops = {
+ NULL, php_plain_files_dirstream_read,
+ php_plain_files_dirstream_close, NULL,
+ "dir",
+ php_plain_files_dirstream_rewind,
+ NULL, NULL,
+ NULL
+};
+
+static php_stream *php_plain_files_dir_opener(php_stream_wrapper *wrapper, char *path, char *mode,
+ int options, char **opened_path, php_stream_context *context STREAMS_DC TSRMLS_DC)
+{
+ DIR *dir = NULL;
+ php_stream *stream = NULL;
+
+ if (php_check_open_basedir(path TSRMLS_CC)) {
+ return NULL;
+ }
+
+ if (PG(safe_mode) &&(!php_checkuid(path, NULL, CHECKUID_ALLOW_ONLY_FILE))) {
+ return NULL;
+ }
+
+ dir = VCWD_OPENDIR(path);
+
+#ifdef PHP_WIN32
+ if (dir && dir->finished) {
+ closedir(dir);
+ dir = NULL;
+ }
+#endif
+ if (dir) {
+ stream = php_stream_alloc(&php_plain_files_dirstream_ops, dir, 0, mode);
+ if (stream == NULL)
+ closedir(dir);
+ }
+
+ return stream;
+}
+
+
+
+static php_stream *php_plain_files_stream_opener(php_stream_wrapper *wrapper, char *path, char *mode,
+ int options, char **opened_path, php_stream_context *context STREAMS_DC TSRMLS_DC)
+{
+ if ((options & USE_PATH) && PG(include_path) != NULL) {
+ return php_stream_fopen_with_path_rel(path, mode, PG(include_path), opened_path);
+ }
+
+ if ((options & ENFORCE_SAFE_MODE) && PG(safe_mode) && (!php_checkuid(path, mode, CHECKUID_CHECK_MODE_PARAM)))
+ return NULL;
+
+ return php_stream_fopen_rel(path, mode, opened_path);
+}
+
+static int php_plain_files_url_stater(php_stream_wrapper *wrapper, char *url, php_stream_statbuf *ssb TSRMLS_DC)
+{
+ return VCWD_STAT(url, &ssb->sb);
+}
+
+static php_stream_wrapper_ops php_plain_files_wrapper_ops = {
+ php_plain_files_stream_opener,
+ NULL,
+ NULL,
+ php_plain_files_url_stater,
+ php_plain_files_dir_opener
+};
+
+static php_stream_wrapper php_plain_files_wrapper = {
+ &php_plain_files_wrapper_ops,
+ NULL,
+ 0
+};
+
+static php_stream_wrapper *locate_url_wrapper(char *path, char **path_for_open, int options TSRMLS_DC)
+{
+ php_stream_wrapper *wrapper = NULL;
const char *p, *protocol = NULL;
int n = 0;
+ *path_for_open = path;
+
+ if (options & IGNORE_URL)
+ return &php_plain_files_wrapper;
+
for (p = path; isalnum((int)*p); p++) {
n++;
}
wrapper = NULL;
protocol = NULL;
}
- if (wrapper) {
- php_stream *stream = wrapper->wops->opener(wrapper, path, mode, options, opened_path, context STREAMS_REL_CC TSRMLS_CC);
- if (stream)
- stream->wrapper = wrapper;
- return stream;
- }
}
-
if (!protocol || !strncasecmp(protocol, "file", n)) {
if (protocol && path[n+1] == '/' && path[n+2] == '/') {
zend_error(E_WARNING, "remote host file access not supported, %s", path);
return NULL;
}
if (protocol)
- path += n + 1;
-
+ *path_for_open = path + n + 1;
+
/* fall back on regular file access */
- return php_stream_open_wrapper_rel(path, mode, (options & ~REPORT_ERRORS) | IGNORE_URL,
- opened_path);
+ return &php_plain_files_wrapper;
}
- return NULL;
+
+ if (wrapper && wrapper->is_url && !PG(allow_url_fopen)) {
+ zend_error(E_WARNING, "URL file-access is disabled in the server configuration");
+ return NULL;
+ }
+
+ return wrapper;
+}
+
+PHPAPI int _php_stream_stat_path(char *path, php_stream_statbuf *ssb TSRMLS_DC)
+{
+ php_stream_wrapper *wrapper = NULL;
+ char *path_to_open = path;
+
+ wrapper = locate_url_wrapper(path, &path_to_open, ENFORCE_SAFE_MODE TSRMLS_CC);
+ if (wrapper && wrapper->wops->url_stat) {
+ return wrapper->wops->url_stat(wrapper, path_to_open, ssb TSRMLS_CC);
+ }
+ return -1;
+}
+
+/* {{{ php_stream_opendir */
+PHPAPI php_stream *_php_stream_opendir(char *path, int options,
+ php_stream_context *context STREAMS_DC TSRMLS_DC)
+{
+ php_stream *stream = NULL;
+ php_stream_wrapper *wrapper = NULL;
+ char *path_to_open;
+
+ if (!path || !*path)
+ return NULL;
+
+ path_to_open = path;
+
+ wrapper = locate_url_wrapper(path, &path_to_open, options TSRMLS_CC);
+
+ if (wrapper && wrapper->wops->dir_opener) {
+ stream = wrapper->wops->dir_opener(wrapper,
+ path_to_open, "r", options, NULL,
+ context STREAMS_REL_CC TSRMLS_CC);
+
+ if (stream)
+ stream->wrapper = wrapper;
+ }
+
+ if (stream == NULL && (options & REPORT_ERRORS)) {
+ char *tmp = estrdup(path);
+ char *msg;
+
+ if (wrapper)
+ msg = strerror(errno);
+ else
+ msg = "no suitable wrapper could be found";
+
+ php_strip_url_passwd(tmp);
+ zend_error(E_WARNING, "%s(\"%s\") - %s", get_active_function_name(TSRMLS_C), tmp, msg);
+ efree(tmp);
+ }
+ return stream;
}
/* }}} */
+PHPAPI php_stream_dirent *_php_stream_readdir(php_stream *dirstream, php_stream_dirent *ent TSRMLS_DC)
+{
+ if (sizeof(php_stream_dirent) == php_stream_read(dirstream, (char*)ent, sizeof(php_stream_dirent)))
+ return ent;
+
+ return NULL;
+}
+
/* {{{ php_stream_open_wrapper_ex */
PHPAPI php_stream *_php_stream_open_wrapper_ex(char *path, char *mode, int options,
char **opened_path, php_stream_context *context STREAMS_DC TSRMLS_DC)
{
php_stream *stream = NULL;
-
+ php_stream_wrapper *wrapper = NULL;
+ char *path_to_open;
+
if (opened_path)
*opened_path = NULL;
if (!path || !*path)
return NULL;
- if (PG(allow_url_fopen) && !(options & IGNORE_URL)) {
- stream = php_stream_open_url(path, mode, options, opened_path, context STREAMS_REL_CC TSRMLS_CC);
- goto out;
- }
+ path_to_open = path;
- if ((options & USE_PATH) && PG(include_path) != NULL) {
- stream = php_stream_fopen_with_path_rel(path, mode, PG(include_path), opened_path);
- goto out;
- }
+ wrapper = locate_url_wrapper(path, &path_to_open, options TSRMLS_CC);
- if ((options & ENFORCE_SAFE_MODE) && PG(safe_mode) && (!php_checkuid(path, mode, CHECKUID_CHECK_MODE_PARAM)))
- return NULL;
-
- stream = php_stream_fopen_rel(path, mode, opened_path);
-out:
+ if (wrapper) {
+ stream = wrapper->wops->stream_opener(wrapper,
+ path_to_open, mode, options,
+ opened_path, context STREAMS_REL_CC TSRMLS_CC);
+ if (stream)
+ stream->wrapper = wrapper;
+ }
if (stream != NULL && (options & STREAM_MUST_SEEK)) {
php_stream *newstream;
}
if (stream == NULL && (options & REPORT_ERRORS)) {
char *tmp = estrdup(path);
+ char *msg;
+
+ if (wrapper)
+ msg = strerror(errno);
+ else
+ msg = "no suitable wrapper could be found";
+
php_strip_url_passwd(tmp);
- zend_error(E_WARNING, "%s(\"%s\") - %s", get_active_function_name(TSRMLS_C), tmp, strerror(errno));
+ zend_error(E_WARNING, "%s(\"%s\") - %s", get_active_function_name(TSRMLS_C), tmp, msg);
efree(tmp);
}
return stream;