diff options
author | Wez Furlong <wez@php.net> | 2003-02-16 22:19:28 +0000 |
---|---|---|
committer | Wez Furlong <wez@php.net> | 2003-02-16 22:19:28 +0000 |
commit | 2cc68ad7ade79683bc61b0c2ffcdf6bfee943bd0 (patch) | |
tree | 6bdc49dde2ed86f178d7110d6cc821adc6fafd4b | |
parent | fe9a33a002079c18514e7581be648f709975a2a3 (diff) | |
download | php-git-2cc68ad7ade79683bc61b0c2ffcdf6bfee943bd0.tar.gz |
Move streams files around a bit, to ease maintenance.
I will update the win32 .dsp in a moment.
-rw-r--r-- | configure.in | 8 | ||||
-rw-r--r-- | main/streams/cast.c | 372 | ||||
-rw-r--r-- | main/streams/filter.c | 164 | ||||
-rw-r--r-- | main/streams/memory.c (renamed from main/memory_streams.c) | 0 | ||||
-rw-r--r-- | main/streams/php_streams_int.h | 39 | ||||
-rw-r--r-- | main/streams/plain_wrapper.c | 856 | ||||
-rwxr-xr-x | main/streams/streams.c (renamed from main/streams.c) | 1452 | ||||
-rw-r--r-- | main/streams/userspace.c (renamed from main/user_streams.c) | 0 |
8 files changed, 1512 insertions, 1379 deletions
diff --git a/configure.in b/configure.in index 78e0507166..00aa4ac5de 100644 --- a/configure.in +++ b/configure.in @@ -1098,8 +1098,12 @@ PHP_ADD_SOURCES(main, main.c snprintf.c spprintf.c php_sprintf.c \ safe_mode.c fopen_wrappers.c alloca.c php_scandir.c \ php_ini.c SAPI.c rfc1867.c php_content_types.c strlcpy.c \ strlcat.c mergesort.c reentrancy.c php_variables.c php_ticks.c \ - streams.c network.c php_open_temporary_file.c php_logos.c \ - output.c memory_streams.c user_streams.c) + network.c php_open_temporary_file.c php_logos.c \ + output.c ) + +PHP_ADD_SOURCES(main/streams, streams.c cast.c memory.c filter.c \ + plain_wrapper.c userspace.c) + PHP_ADD_SOURCES(/main, internal_functions.c,, sapi) PHP_ADD_SOURCES(/main, internal_functions_cli.c,, cli) diff --git a/main/streams/cast.c b/main/streams/cast.c new file mode 100644 index 0000000000..d940a1d0ad --- /dev/null +++ b/main/streams/cast.c @@ -0,0 +1,372 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 4 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2003 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.02 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available at through the world-wide-web at | + | http://www.php.net/license/2_02.txt. | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: | + | Wez Furlong (wez@thebrainroom.com) | + +----------------------------------------------------------------------+ + */ + +/* $Id$ */ + +#define _GNU_SOURCE +#include "php.h" +#include "php_globals.h" +#include "php_network.h" +#include "php_open_temporary_file.h" +#include "ext/standard/file.h" +#include <stddef.h> +#include <fcntl.h> + +#include "php_streams_int.h" + +/* Under BSD, emulate fopencookie using funopen */ +#if HAVE_FUNOPEN +typedef struct { + int (*reader)(void *, char *, int); + int (*writer)(void *, const char *, int); + fpos_t (*seeker)(void *, fpos_t, int); + int (*closer)(void *); +} COOKIE_IO_FUNCTIONS_T; + +FILE *fopencookie(void *cookie, const char *mode, COOKIE_IO_FUNCTIONS_T *funcs) +{ + return funopen(cookie, funcs->reader, funcs->writer, funcs->seeker, funcs->closer); +} +# define HAVE_FOPENCOOKIE 1 +# define PHP_STREAM_COOKIE_FUNCTIONS &stream_cookie_functions +#elif HAVE_FOPENCOOKIE +# define PHP_STREAM_COOKIE_FUNCTIONS stream_cookie_functions +#endif + +/* {{{ STDIO with fopencookie */ +#if HAVE_FUNOPEN +/* use our fopencookie emulation */ +static int stream_cookie_reader(void *cookie, char *buffer, int size) +{ + int ret; + TSRMLS_FETCH(); + ret = php_stream_read((php_stream*)cookie, buffer, size); + return ret; +} + +static int stream_cookie_writer(void *cookie, const char *buffer, int size) +{ + TSRMLS_FETCH(); + return php_stream_write((php_stream *)cookie, (char *)buffer, size); +} + +static fpos_t stream_cookie_seeker(void *cookie, off_t position, int whence) +{ + TSRMLS_FETCH(); + return (fpos_t)php_stream_seek((php_stream *)cookie, position, whence); +} + +static int stream_cookie_closer(void *cookie) +{ + php_stream *stream = (php_stream*)cookie; + TSRMLS_FETCH(); + + /* prevent recursion */ + stream->fclose_stdiocast = PHP_STREAM_FCLOSE_NONE; + return php_stream_close(stream); +} + +#elif HAVE_FOPENCOOKIE +static ssize_t stream_cookie_reader(void *cookie, char *buffer, size_t size) +{ + ssize_t ret; + TSRMLS_FETCH(); + ret = php_stream_read(((php_stream *)cookie), buffer, size); + return ret; +} + +static ssize_t stream_cookie_writer(void *cookie, const char *buffer, size_t size) +{ + TSRMLS_FETCH(); + return php_stream_write(((php_stream *)cookie), (char *)buffer, size); +} + +#ifdef COOKIE_SEEKER_USES_OFF64_T +static int stream_cookie_seeker(void *cookie, __off64_t *position, int whence) +{ + TSRMLS_FETCH(); + + *position = php_stream_seek((php_stream *)cookie, (off_t)*position, whence); + + if (*position == -1) + return -1; + return 0; +} +#else +static int stream_cookie_seeker(void *cookie, off_t position, int whence) +{ + TSRMLS_FETCH(); + return php_stream_seek((php_stream *)cookie, position, whence); +} +#endif + +static int stream_cookie_closer(void *cookie) +{ + php_stream *stream = (php_stream*)cookie; + TSRMLS_FETCH(); + + /* prevent recursion */ + stream->fclose_stdiocast = PHP_STREAM_FCLOSE_NONE; + return php_stream_close(stream); +} +#endif /* elif HAVE_FOPENCOOKIE */ + +#if HAVE_FOPENCOOKIE +static COOKIE_IO_FUNCTIONS_T stream_cookie_functions = +{ + stream_cookie_reader, stream_cookie_writer, + stream_cookie_seeker, stream_cookie_closer +}; +#else +/* TODO: use socketpair() to emulate fopencookie, as suggested by Hartmut ? */ +#endif +/* }}} */ + + + + + +/* {{{ php_stream_cast */ +PHPAPI int _php_stream_cast(php_stream *stream, int castas, void **ret, int show_err TSRMLS_DC) +{ + int flags = castas & PHP_STREAM_CAST_MASK; + castas &= ~PHP_STREAM_CAST_MASK; + + /* synchronize our buffer (if possible) */ + if (ret) { + php_stream_flush(stream); + if (stream->ops->seek && (stream->flags & PHP_STREAM_FLAG_NO_SEEK) == 0) { + off_t dummy; + + stream->ops->seek(stream, stream->position, SEEK_SET, &dummy TSRMLS_CC); + stream->readpos = stream->writepos = 0; + } + } + + /* filtered streams can only be cast as stdio, and only when fopencookie is present */ + + if (castas == PHP_STREAM_AS_STDIO) { + if (stream->stdiocast) { + if (ret) { + *ret = stream->stdiocast; + } + goto exit_success; + } + + /* if the stream is a stdio stream let's give it a chance to respond + * first, to avoid doubling up the layers of stdio with an fopencookie */ + if (php_stream_is(stream, PHP_STREAM_IS_STDIO) && + stream->ops->cast && + stream->filterhead == NULL && + stream->ops->cast(stream, castas, ret TSRMLS_CC) == SUCCESS) + { + goto exit_success; + } + +#if HAVE_FOPENCOOKIE + /* if just checking, say yes we can be a FILE*, but don't actually create it yet */ + if (ret == NULL) + goto exit_success; + + *ret = fopencookie(stream, stream->mode, PHP_STREAM_COOKIE_FUNCTIONS); + + if (*ret != NULL) { + off_t pos; + + stream->fclose_stdiocast = PHP_STREAM_FCLOSE_FOPENCOOKIE; + + /* If the stream position is not at the start, we need to force + * the stdio layer to believe it's real location. */ + pos = php_stream_tell(stream); + if (pos > 0) + fseek(*ret, pos, SEEK_SET); + + goto exit_success; + } + + /* must be either: + a) programmer error + b) no memory + -> lets bail + */ + php_error_docref(NULL TSRMLS_CC, E_ERROR, "fopencookie failed"); + return FAILURE; +#endif + + if (flags & PHP_STREAM_CAST_TRY_HARD) { + php_stream *newstream; + + newstream = php_stream_fopen_tmpfile(); + if (newstream) { + size_t copied = php_stream_copy_to_stream(stream, newstream, PHP_STREAM_COPY_ALL); + + if (copied == 0) { + php_stream_close(newstream); + } else { + int retcode = php_stream_cast(newstream, castas | flags, ret, show_err); + + if (retcode == SUCCESS) + rewind((FILE*)*ret); + + /* do some specialized cleanup */ + if ((flags & PHP_STREAM_CAST_RELEASE)) { + php_stream_free(stream, PHP_STREAM_FREE_CLOSE_CASTED); + } + + return retcode; + } + } + } + } + + if (stream->filterhead) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "cannot cast a filtered stream on this system"); + return FAILURE; + } else if (stream->ops->cast && stream->ops->cast(stream, castas, ret TSRMLS_CC) == SUCCESS) { + goto exit_success; + } + + if (show_err) { + /* these names depend on the values of the PHP_STREAM_AS_XXX defines in php_streams.h */ + static const char *cast_names[3] = { + "STDIO FILE*", "File Descriptor", "Socket Descriptor" + }; + + php_error_docref(NULL TSRMLS_CC, E_WARNING, "cannot represent a stream of type %s as a %s", + stream->ops->label, + cast_names[castas] + ); + } + + return FAILURE; + +exit_success: + + if ((stream->writepos - stream->readpos) > 0 && + stream->fclose_stdiocast != PHP_STREAM_FCLOSE_FOPENCOOKIE && + (flags & PHP_STREAM_CAST_INTERNAL) == 0) { + /* the data we have buffered will be lost to the third party library that + * will be accessing the stream. Emit a warning so that the end-user will + * know that they should try something else */ + + php_error_docref(NULL TSRMLS_CC, E_WARNING, + "%d bytes of buffered data lost during stream conversion!", + stream->writepos - stream->readpos); + } + + if (castas == PHP_STREAM_AS_STDIO && ret) + stream->stdiocast = *ret; + + if (flags & PHP_STREAM_CAST_RELEASE) { + php_stream_free(stream, PHP_STREAM_FREE_CLOSE_CASTED); + } + + return SUCCESS; + +} +/* }}} */ + +/* {{{ php_stream_open_wrapper_as_file */ +PHPAPI FILE * _php_stream_open_wrapper_as_file(char *path, char *mode, int options, char **opened_path STREAMS_DC TSRMLS_DC) +{ + FILE *fp = NULL; + php_stream *stream = NULL; + + stream = php_stream_open_wrapper_rel(path, mode, options|STREAM_WILL_CAST, opened_path); + + if (stream == NULL) + return NULL; + +#ifdef PHP_WIN32 + /* Avoid possible strange problems when working with socket based streams */ + if ((options & STREAM_OPEN_FOR_INCLUDE) && php_stream_is(stream, PHP_STREAM_IS_SOCKET)) { + char buf[CHUNK_SIZE]; + + fp = php_open_temporary_file(NULL, "php", NULL TSRMLS_CC); + if (fp) { + while (!php_stream_eof(stream)) { + size_t didread = php_stream_read(stream, buf, sizeof(buf)); + if (didread > 0) { + fwrite(buf, 1, didread, fp); + } else { + break; + } + } + php_stream_close(stream); + rewind(fp); + return fp; + } + } +#endif + + if (php_stream_cast(stream, PHP_STREAM_AS_STDIO|PHP_STREAM_CAST_TRY_HARD|PHP_STREAM_CAST_RELEASE, + (void**)&fp, REPORT_ERRORS) == FAILURE) + { + php_stream_close(stream); + if (opened_path && *opened_path) + efree(*opened_path); + return NULL; + } + return fp; +} +/* }}} */ + +/* {{{ php_stream_make_seekable */ +PHPAPI int _php_stream_make_seekable(php_stream *origstream, php_stream **newstream, int flags STREAMS_DC TSRMLS_DC) +{ + assert(newstream != NULL); + + *newstream = NULL; + + if (((flags & PHP_STREAM_FORCE_CONVERSION) == 0) && origstream->ops->seek != NULL) { + *newstream = origstream; + return PHP_STREAM_UNCHANGED; + } + + /* Use a tmpfile and copy the old streams contents into it */ + + if (flags & PHP_STREAM_PREFER_STDIO) + *newstream = php_stream_fopen_tmpfile(); + else + *newstream = php_stream_temp_new(); + + if (*newstream == NULL) + return PHP_STREAM_FAILED; + + if (php_stream_copy_to_stream(origstream, *newstream, PHP_STREAM_COPY_ALL) == 0) { + php_stream_close(*newstream); + *newstream = NULL; + return PHP_STREAM_CRITICAL; + } + + php_stream_close(origstream); + php_stream_seek(*newstream, 0, SEEK_SET); + + return PHP_STREAM_RELEASED; +} +/* }}} */ + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ diff --git a/main/streams/filter.c b/main/streams/filter.c new file mode 100644 index 0000000000..5739f0f314 --- /dev/null +++ b/main/streams/filter.c @@ -0,0 +1,164 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 4 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2003 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.02 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available at through the world-wide-web at | + | http://www.php.net/license/2_02.txt. | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: | + | Wez Furlong (wez@thebrainroom.com) | + +----------------------------------------------------------------------+ + */ + +/* $Id$ */ + +#include "php.h" +#include "php_globals.h" +#include "php_network.h" +#include "php_open_temporary_file.h" +#include "ext/standard/file.h" +#include <stddef.h> +#include <fcntl.h> + +#include "php_streams_int.h" + +static HashTable stream_filters_hash; + +PHPAPI HashTable *php_get_stream_filters_hash() +{ + return &stream_filters_hash; +} + +PHPAPI int php_stream_filter_register_factory(const char *filterpattern, php_stream_filter_factory *factory TSRMLS_DC) +{ + return zend_hash_add(&stream_filters_hash, (char*)filterpattern, strlen(filterpattern), factory, sizeof(*factory), NULL); +} + +PHPAPI int php_stream_filter_unregister_factory(const char *filterpattern TSRMLS_DC) +{ + return zend_hash_del(&stream_filters_hash, (char*)filterpattern, strlen(filterpattern)); +} + +/* We allow very simple pattern matching for filter factories: + * if "charset.utf-8/sjis" is requested, we search first for an exact + * match. If that fails, we try "charset.*". + * This means that we don't need to clog up the hashtable with a zillion + * charsets (for example) but still be able to provide them all as filters */ +PHPAPI php_stream_filter *php_stream_filter_create(const char *filtername, const char *filterparams, int filterparamslen, int persistent TSRMLS_DC) +{ + php_stream_filter_factory *factory; + php_stream_filter *filter = NULL; + int n; + char *period; + + n = strlen(filtername); + + if (SUCCESS == zend_hash_find(&stream_filters_hash, (char*)filtername, n, (void**)&factory)) { + filter = factory->create_filter(filtername, filterparams, filterparamslen, persistent TSRMLS_CC); + } else if ((period = strchr(filtername, '.'))) { + /* try a wildcard */ + char wildname[128]; + + PHP_STRLCPY(wildname, filtername, sizeof(wildname) - 1, period-filtername + 1); + strcat(wildname, "*"); + + if (SUCCESS == zend_hash_find(&stream_filters_hash, wildname, strlen(wildname), (void**)&factory)) { + filter = factory->create_filter(filtername, filterparams, filterparamslen, persistent TSRMLS_CC); + } + } + + if (filter == NULL) { + /* TODO: these need correct docrefs */ + if (factory == NULL) + php_error_docref(NULL TSRMLS_CC, E_WARNING, "unable to locate filter \"%s\"", filtername); + else + php_error_docref(NULL TSRMLS_CC, E_WARNING, "unable to create or locate filter \"%s\"", filtername); + } + + return filter; +} + +PHPAPI php_stream_filter *_php_stream_filter_alloc(php_stream_filter_ops *fops, void *abstract, int persistent STREAMS_DC TSRMLS_DC) +{ + php_stream_filter *filter; + + filter = (php_stream_filter*) pemalloc_rel_orig(sizeof(php_stream_filter), persistent); + memset(filter, 0, sizeof(php_stream_filter)); + + filter->fops = fops; + filter->abstract = abstract; + filter->is_persistent = persistent; + + return filter; +} + +PHPAPI void php_stream_filter_free(php_stream_filter *filter TSRMLS_DC) +{ + if (filter->fops->dtor) + filter->fops->dtor(filter TSRMLS_CC); + pefree(filter, filter->is_persistent); +} + +PHPAPI void php_stream_filter_prepend(php_stream *stream, php_stream_filter *filter) +{ + filter->next = stream->filterhead; + filter->prev = NULL; + + if (stream->filterhead) { + stream->filterhead->prev = filter; + } else { + stream->filtertail = filter; + } + stream->filterhead = filter; + filter->stream = stream; +} + +PHPAPI void php_stream_filter_append(php_stream *stream, php_stream_filter *filter) +{ + filter->prev = stream->filtertail; + filter->next = NULL; + if (stream->filtertail) { + stream->filtertail->next = filter; + } else { + stream->filterhead = filter; + } + stream->filtertail = filter; + filter->stream = stream; +} + +PHPAPI php_stream_filter *php_stream_filter_remove(php_stream *stream, php_stream_filter *filter, int call_dtor TSRMLS_DC) +{ + assert(stream == filter->stream); + + if (filter->prev) { + filter->prev->next = filter->next; + } else { + stream->filterhead = filter->next; + } + if (filter->next) { + filter->next->prev = filter->prev; + } else { + stream->filtertail = filter->prev; + } + if (call_dtor) { + php_stream_filter_free(filter TSRMLS_CC); + return NULL; + } + return filter; +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ diff --git a/main/memory_streams.c b/main/streams/memory.c index 02f64b9fa2..02f64b9fa2 100644 --- a/main/memory_streams.c +++ b/main/streams/memory.c diff --git a/main/streams/php_streams_int.h b/main/streams/php_streams_int.h new file mode 100644 index 0000000000..667ed2a203 --- /dev/null +++ b/main/streams/php_streams_int.h @@ -0,0 +1,39 @@ +#if ZEND_DEBUG +#define emalloc_rel_orig(size) \ + ( __php_stream_call_depth == 0 \ + ? _emalloc((size) ZEND_FILE_LINE_CC ZEND_FILE_LINE_RELAY_CC) \ + : _emalloc((size) ZEND_FILE_LINE_CC ZEND_FILE_LINE_ORIG_RELAY_CC) ) + +#define erealloc_rel_orig(ptr, size) \ + ( __php_stream_call_depth == 0 \ + ? _erealloc((ptr), (size), 0 ZEND_FILE_LINE_CC ZEND_FILE_LINE_RELAY_CC) \ + : _erealloc((ptr), (size), 0 ZEND_FILE_LINE_CC ZEND_FILE_LINE_ORIG_RELAY_CC) ) + + +#define pemalloc_rel_orig(size, persistent) ((persistent) ? malloc((size)) : emalloc_rel_orig((size))) +#define perealloc_rel_orig(ptr, size, persistent) ((persistent) ? realloc((ptr), (size)) : erealloc_rel_orig((ptr), (size))) +#else +# define pemalloc_rel_orig(size, persistent) pemalloc((size), (persistent)) +# define perealloc_rel_orig(ptr, size, persistent) perealloc((ptr), (size), (persistent)) +# define emalloc_rel_orig(size) emalloc((size)) +#endif + +#define STREAM_DEBUG 0 +#define STREAM_WRAPPER_PLAIN_FILES ((php_stream_wrapper*)-1) +extern php_stream_wrapper php_plain_files_wrapper; + +#ifndef MAP_FAILED +#define MAP_FAILED ((void *) -1) +#endif + +#define CHUNK_SIZE 8192 + +#ifdef PHP_WIN32 +#define EWOULDBLOCK WSAEWOULDBLOCK +#endif + +#ifndef S_ISREG +#define S_ISREG(mode) (((mode)&S_IFMT) == S_IFREG) +#endif + + diff --git a/main/streams/plain_wrapper.c b/main/streams/plain_wrapper.c new file mode 100644 index 0000000000..b1e7a489bb --- /dev/null +++ b/main/streams/plain_wrapper.c @@ -0,0 +1,856 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 4 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2003 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.02 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available at through the world-wide-web at | + | http://www.php.net/license/2_02.txt. | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: | + | Wez Furlong (wez@thebrainroom.com) | + +----------------------------------------------------------------------+ + */ + +/* $Id$ */ + +#include "php.h" +#include "php_globals.h" +#include "php_network.h" +#include "php_open_temporary_file.h" +#include "ext/standard/file.h" +#include <stddef.h> +#include <fcntl.h> +#if HAVE_SYS_WAIT_H +#include <sys/wait.h> +#endif + +#include "php_streams_int.h" + +/* parse standard "fopen" modes into open() flags */ +PHPAPI int php_stream_parse_fopen_modes(const char *mode, int *open_flags) +{ + int flags; + + switch (mode[0]) { + case 'r': + flags = 0; + break; + case 'w': + flags = O_TRUNC|O_CREAT; + break; + case 'a': + flags = O_CREAT|O_APPEND; + break; + case 'x': + flags = O_CREAT|O_EXCL; + break; + default: + /* unknown mode */ + return FAILURE; + } + + if (strchr(mode, '+')) { + flags |= O_RDWR; + } else if (flags) { + flags |= O_WRONLY; + } else { + flags |= O_RDONLY; + } + +#ifdef O_BINARY + if (strchr(mode, 'b')) { + flags |= O_BINARY; + } +#endif + + *open_flags = flags; + return SUCCESS; +} + + +/* {{{ php_stream_fopen */ +PHPAPI php_stream *_php_stream_fopen(const char *filename, const char *mode, char **opened_path, int options STREAMS_DC TSRMLS_DC) +{ + char *realpath = NULL; + struct stat st; + int open_flags; + int fd; + php_stream *ret; + + if (FAILURE == php_stream_parse_fopen_modes(mode, &open_flags)) { + if (options & REPORT_ERRORS) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "`%s' is not a valid mode for fopen", mode); + } + return NULL; + } + + realpath = expand_filepath(filename, NULL TSRMLS_CC); + + fd = open(realpath, open_flags, 0666); + + if (fd != -1) { + /* sanity checks for include/require */ + if (options & STREAM_OPEN_FOR_INCLUDE && (fstat(fd, &st) == -1 || !S_ISREG(st.st_mode))) { +#ifdef PHP_WIN32 + /* skip the sanity check; fstat doesn't appear to work on + * UNC paths */ + if (!IS_UNC_PATH(filename, strlen(filename))) +#endif + goto err; + } + + ret = php_stream_fopen_from_fd_rel(fd, mode); + + if (ret) { + if (opened_path) { + *opened_path = realpath; + realpath = NULL; + } + if (realpath) + efree(realpath); + + return ret; + } +err: + close(fd); + } + efree(realpath); + return NULL; +} +/* }}} */ + +/* {{{ ------- STDIO stream implementation -------*/ + +typedef struct { + FILE *file; + int fd; /* underlying file descriptor */ + int is_process_pipe; /* use pclose instead of fclose */ + int is_pipe; /* don't try and seek */ + char *temp_file_name; /* if non-null, this is the path to a temporary file that + * is to be deleted when the stream is closed */ +#if HAVE_FLUSHIO + char last_op; +#endif +} php_stdio_stream_data; + +PHPAPI php_stream *_php_stream_fopen_temporary_file(const char *dir, const char *pfx, char **opened_path STREAMS_DC TSRMLS_DC) +{ + FILE *fp = php_open_temporary_file(dir, pfx, opened_path TSRMLS_CC); + + if (fp) { + php_stream *stream = php_stream_fopen_from_file_rel(fp, "r+b"); + if (stream) { + return stream; + } + fclose(fp); + + php_error_docref(NULL TSRMLS_CC, E_WARNING, "unable to allocate stream"); + + return NULL; + } + return NULL; +} + +PHPAPI php_stream *_php_stream_fopen_tmpfile(int dummy STREAMS_DC TSRMLS_DC) +{ + char *opened_path = NULL; + FILE *fp = php_open_temporary_file(NULL, "php", &opened_path TSRMLS_CC); + + if (fp) { + php_stream *stream = php_stream_fopen_from_file_rel(fp, "r+b"); + if (stream) { + php_stdio_stream_data *self = (php_stdio_stream_data*)stream->abstract; + + self->temp_file_name = opened_path; + return stream; + } + fclose(fp); + + php_error_docref(NULL TSRMLS_CC, E_WARNING, "unable to allocate stream"); + + return NULL; + } + return NULL; +} + +PHPAPI php_stream *_php_stream_fopen_from_fd(int fd, const char *mode STREAMS_DC TSRMLS_DC) +{ + php_stdio_stream_data *self; + php_stream *stream; + + self = emalloc_rel_orig(sizeof(*self)); + self->file = NULL; + self->is_pipe = 0; + self->is_process_pipe = 0; + self->temp_file_name = NULL; + self->fd = fd; + +#ifdef S_ISFIFO + /* detect if this is a pipe */ + if (self->fd >= 0) { + struct stat sb; + self->is_pipe = (fstat(self->fd, &sb) == 0 && S_ISFIFO(sb.st_mode)) ? 1 : 0; + } +#elif defined(PHP_WIN32) + { + long handle = _get_osfhandle(self->fd); + DWORD in_buf_size, out_buf_size; + + if (handle != 0xFFFFFFFF) { + self->is_pipe = GetNamedPipeInfo((HANDLE)handle, NULL, &out_buf_size, &in_buf_size, NULL); + } + } +#endif + + stream = php_stream_alloc_rel(&php_stream_stdio_ops, self, 0, mode); + + if (stream) { + if (self->is_pipe) { + stream->flags |= PHP_STREAM_FLAG_NO_SEEK; + } else { + stream->position = lseek(self->fd, 0, SEEK_CUR); + } + } + + return stream; +} + +PHPAPI php_stream *_php_stream_fopen_from_file(FILE *file, const char *mode STREAMS_DC TSRMLS_DC) +{ + php_stdio_stream_data *self; + php_stream *stream; + + self = emalloc_rel_orig(sizeof(*self)); + self->file = file; + self->is_pipe = 0; + self->is_process_pipe = 0; + self->temp_file_name = NULL; + self->fd = fileno(file); + +#ifdef S_ISFIFO + /* detect if this is a pipe */ + if (self->fd >= 0) { + struct stat sb; + self->is_pipe = (fstat(self->fd, &sb) == 0 && S_ISFIFO(sb.st_mode)) ? 1 : 0; + } +#elif defined(PHP_WIN32) + { + long handle = _get_osfhandle(self->fd); + DWORD in_buf_size, out_buf_size; + + if (handle != 0xFFFFFFFF) { + self->is_pipe = GetNamedPipeInfo((HANDLE)handle, NULL, &out_buf_size, &in_buf_size, NULL); + } + } +#endif + + stream = php_stream_alloc_rel(&php_stream_stdio_ops, self, 0, mode); + + if (stream) { + if (self->is_pipe) { + stream->flags |= PHP_STREAM_FLAG_NO_SEEK; + } else { + stream->position = ftell(file); + } + } + + return stream; +} + +PHPAPI php_stream *_php_stream_fopen_from_pipe(FILE *file, const char *mode STREAMS_DC TSRMLS_DC) +{ + php_stdio_stream_data *self; + php_stream *stream; + + self = emalloc_rel_orig(sizeof(*self)); + self->file = file; + self->is_pipe = 1; + self->is_process_pipe = 1; + self->fd = fileno(file); + self->temp_file_name = NULL; + + stream = php_stream_alloc_rel(&php_stream_stdio_ops, self, 0, mode); + stream->flags |= PHP_STREAM_FLAG_NO_SEEK; + return stream; +} + +#define PHP_STDIOP_GET_FD(anfd, data) anfd = (data)->file ? fileno((data)->file) : (data)->fd + +static size_t php_stdiop_write(php_stream *stream, const char *buf, size_t count TSRMLS_DC) +{ + php_stdio_stream_data *data = (php_stdio_stream_data*)stream->abstract; + + assert(data != NULL); + + if (data->fd >= 0) { + int bytes_written = write(data->fd, buf, count); + if (bytes_written < 0) return 0; + return (size_t) bytes_written; + } else { + +#if HAVE_FLUSHIO + if (!data->is_pipe && data->last_op == 'r') { + fseek(data->file, 0, SEEK_CUR); + } + data->last_op = 'w'; +#endif + + return fwrite(buf, 1, count, data->file); + } +} + +static size_t php_stdiop_read(php_stream *stream, char *buf, size_t count TSRMLS_DC) +{ + php_stdio_stream_data *data = (php_stdio_stream_data*)stream->abstract; + size_t ret; + + assert(data != NULL); + + if (data->fd >= 0) { + ret = read(data->fd, buf, count); + + if (ret == 0 || (ret == -1 && errno != EWOULDBLOCK)) + stream->eof = 1; + + } else { +#if HAVE_FLUSHIO + if (!data->is_pipe && data->last_op == 'w') + fseek(data->file, 0, SEEK_CUR); + data->last_op = 'r'; +#endif + + ret = fread(buf, 1, count, data->file); + + if (feof(data->file)) + stream->eof = 1; + } + return ret; +} + +static int php_stdiop_close(php_stream *stream, int close_handle TSRMLS_DC) +{ + int ret; + php_stdio_stream_data *data = (php_stdio_stream_data*)stream->abstract; + + assert(data != NULL); + + if (close_handle) { + if (data->file) { + if (data->is_process_pipe) { + errno = 0; + ret = pclose(data->file); + +#if HAVE_SYS_WAIT_H + if (WIFEXITED(ret)) { + ret = WEXITSTATUS(ret); + } +#endif + } else { + ret = fclose(data->file); + data->file = NULL; + } + } else if (data->fd != -1) { + ret = close(data->fd); + data->fd = -1; + } else { + return 0; /* everything should be closed already -> success */ + } + if (data->temp_file_name) { + unlink(data->temp_file_name); + efree(data->temp_file_name); + data->temp_file_name = NULL; + } + } else { + ret = 0; + data->file = NULL; + data->fd = -1; + } + + /* STDIO streams are never persistent! */ + efree(data); + + return ret; +} + +static int php_stdiop_flush(php_stream *stream TSRMLS_DC) +{ + php_stdio_stream_data *data = (php_stdio_stream_data*)stream->abstract; + + assert(data != NULL); + + /* + * stdio buffers data in user land. By calling fflush(3), this + * data is send to the kernel using write(2). fsync'ing is + * something completely different. + */ + if (data->file) { + return fflush(data->file); + } + return 0; +} + +static int php_stdiop_seek(php_stream *stream, off_t offset, int whence, off_t *newoffset TSRMLS_DC) +{ + php_stdio_stream_data *data = (php_stdio_stream_data*)stream->abstract; + int ret; + + assert(data != NULL); + + if (data->is_pipe) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "cannot seek on a pipe"); + return -1; + } + + if (data->fd >= 0) { + off_t result; + + result = lseek(data->fd, offset, whence); + if (result == (off_t)-1) + return -1; + + *newoffset = result; + return 0; + + } else { + ret = fseek(data->file, offset, whence); + *newoffset = ftell(data->file); + return ret; + } +} + +static int php_stdiop_cast(php_stream *stream, int castas, void **ret TSRMLS_DC) +{ + int fd; + php_stdio_stream_data *data = (php_stdio_stream_data*) stream->abstract; + + assert(data != NULL); + + /* as soon as someone touches the stdio layer, buffering may ensue, + * so we need to stop using the fd directly in that case */ + + switch (castas) { + case PHP_STREAM_AS_STDIO: + if (ret) { + + if (data->file == NULL) { + /* we were opened as a plain file descriptor, so we + * need fdopen now */ + data->file = fdopen(data->fd, stream->mode); + } + + *ret = data->file; + data->fd = -1; + } + return SUCCESS; + + case PHP_STREAM_AS_FD: + /* fetch the fileno rather than using data->fd, since we may + * have zeroed that member if someone requested the FILE* + * first (see above case) */ + PHP_STDIOP_GET_FD(fd, data); + + if (fd < 0) { + return FAILURE; + } + if (data->file) { + fflush(data->file); + } + if (ret) { + *ret = (void*)fd; + } + return SUCCESS; + default: + return FAILURE; + } +} + +static int php_stdiop_stat(php_stream *stream, php_stream_statbuf *ssb TSRMLS_DC) +{ + int fd; + php_stdio_stream_data *data = (php_stdio_stream_data*) stream->abstract; + + assert(data != NULL); + + PHP_STDIOP_GET_FD(fd, data); + + return fstat(fd, &ssb->sb); +} + +static int php_stdiop_set_option(php_stream *stream, int option, int value, void *ptrparam TSRMLS_DC) +{ + php_stdio_stream_data *data = (php_stdio_stream_data*) stream->abstract; + size_t size; + int fd; +#ifdef O_NONBLOCK + /* FIXME: make this work for win32 */ + int flags; + int oldval; +#endif + + PHP_STDIOP_GET_FD(fd, data); + + switch(option) { + case PHP_STREAM_OPTION_BLOCKING: + if (fd == -1) + return -1; +#ifdef O_NONBLOCK + flags = fcntl(fd, F_GETFL, 0); + oldval = (flags & O_NONBLOCK) ? 0 : 1; + if (value) + flags ^= O_NONBLOCK; + else + flags |= O_NONBLOCK; + + if (-1 == fcntl(fd, F_SETFL, flags)) + return -1; + return oldval; +#else + return -1; /* not yet implemented */ +#endif + + case PHP_STREAM_OPTION_WRITE_BUFFER: + + if (data->file == NULL) { + return -1; + } + + if (ptrparam) + size = *(size_t *)ptrparam; + else + size = BUFSIZ; + + switch(value) { + case PHP_STREAM_BUFFER_NONE: + stream->flags |= PHP_STREAM_FLAG_NO_BUFFER; + return setvbuf(data->file, NULL, _IONBF, 0); + + case PHP_STREAM_BUFFER_LINE: + stream->flags ^= PHP_STREAM_FLAG_NO_BUFFER; + return setvbuf(data->file, NULL, _IOLBF, size); + + case PHP_STREAM_BUFFER_FULL: + stream->flags ^= PHP_STREAM_FLAG_NO_BUFFER; + return setvbuf(data->file, NULL, _IOFBF, size); + + default: + return -1; + } + break; + default: + return -1; + } +} + +PHPAPI php_stream_ops php_stream_stdio_ops = { + php_stdiop_write, php_stdiop_read, + php_stdiop_close, php_stdiop_flush, + "STDIO", + php_stdiop_seek, + php_stdiop_cast, + php_stdiop_stat, + php_stdiop_set_option +}; +/* }}} */ + +static size_t php_plain_files_dirstream_read(php_stream *stream, char *buf, size_t count TSRMLS_DC) +{ + 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, off_t *newoffs 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, /* cast */ + NULL, /* stat */ + NULL /* set_option */ +}; + +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, options); + } + + if (php_check_open_basedir(path TSRMLS_CC)) { + return NULL; + } + + 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, options); +} + +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, + "plainfile" +}; + +php_stream_wrapper php_plain_files_wrapper = { + &php_plain_files_wrapper_ops, + NULL, + 0 +}; + +/* {{{ php_stream_fopen_with_path */ +PHPAPI php_stream *_php_stream_fopen_with_path(char *filename, char *mode, char *path, char **opened_path, int options STREAMS_DC TSRMLS_DC) +{ + /* code ripped off from fopen_wrappers.c */ + char *pathbuf, *ptr, *end; + char *exec_fname; + char trypath[MAXPATHLEN]; + struct stat sb; + php_stream *stream; + int path_length; + int filename_length; + int exec_fname_length; + + if (opened_path) { + *opened_path = NULL; + } + + if(!filename) { + return NULL; + } + + filename_length = strlen(filename); + + /* Relative path open */ + if (*filename == '.' && (IS_SLASH(filename[1]) || filename[1] == '.')) { + /* further checks, we could have ....... filenames */ + ptr = filename + 1; + if (*ptr == '.') { + while (*(++ptr) == '.'); + if (!IS_SLASH(*ptr)) { /* not a relative path after all */ + goto not_relative_path; + } + } + + + if (php_check_open_basedir(filename TSRMLS_CC)) { + return NULL; + } + + if (PG(safe_mode) && (!php_checkuid(filename, mode, CHECKUID_CHECK_MODE_PARAM))) { + return NULL; + } + return php_stream_fopen_rel(filename, mode, opened_path, options); + } + + /* + * files in safe_mode_include_dir (or subdir) are excluded from + * safe mode GID/UID checks + */ + +not_relative_path: + + /* Absolute path open */ + if (IS_ABSOLUTE_PATH(filename, filename_length)) { + + if (php_check_open_basedir(filename TSRMLS_CC)) { + return NULL; + } + + if ((php_check_safe_mode_include_dir(filename TSRMLS_CC)) == 0) + /* filename is in safe_mode_include_dir (or subdir) */ + return php_stream_fopen_rel(filename, mode, opened_path, options); + + if (PG(safe_mode) && (!php_checkuid(filename, mode, CHECKUID_CHECK_MODE_PARAM))) + return NULL; + + return php_stream_fopen_rel(filename, mode, opened_path, options); + } + +#ifdef PHP_WIN32 + if (IS_SLASH(filename[0])) { + int cwd_len; + char *cwd; + cwd = virtual_getcwd_ex(&cwd_len TSRMLS_CC); + /* getcwd() will return always return [DRIVE_LETTER]:/) on windows. */ + *(cwd+3) = '\0'; + + snprintf(trypath, MAXPATHLEN, "%s%s", cwd, filename); + + free(cwd); + + if (php_check_open_basedir(trypath TSRMLS_CC)) { + return NULL; + } + if ((php_check_safe_mode_include_dir(trypath TSRMLS_CC)) == 0) { + return php_stream_fopen_rel(trypath, mode, opened_path, options); + } + if (PG(safe_mode) && (!php_checkuid(trypath, mode, CHECKUID_CHECK_MODE_PARAM))) { + return NULL; + } + + return php_stream_fopen_rel(trypath, mode, opened_path, options); + } +#endif + + if (!path || (path && !*path)) { + + if (php_check_open_basedir(path TSRMLS_CC)) { + return NULL; + } + + if (PG(safe_mode) && (!php_checkuid(filename, mode, CHECKUID_CHECK_MODE_PARAM))) { + return NULL; + } + return php_stream_fopen_rel(filename, mode, opened_path, options); + } + + /* check in provided path */ + /* append the calling scripts' current working directory + * as a fall back case + */ + if (zend_is_executing(TSRMLS_C)) { + exec_fname = zend_get_executed_filename(TSRMLS_C); + exec_fname_length = strlen(exec_fname); + path_length = strlen(path); + + while ((--exec_fname_length >= 0) && !IS_SLASH(exec_fname[exec_fname_length])); + if ((exec_fname && exec_fname[0] == '[') + || exec_fname_length<=0) { + /* [no active file] or no path */ + pathbuf = estrdup(path); + } else { + pathbuf = (char *) emalloc(exec_fname_length + path_length +1 +1); + memcpy(pathbuf, path, path_length); + pathbuf[path_length] = DEFAULT_DIR_SEPARATOR; + memcpy(pathbuf+path_length+1, exec_fname, exec_fname_length); + pathbuf[path_length + exec_fname_length +1] = '\0'; + } + } else { + pathbuf = estrdup(path); + } + + ptr = pathbuf; + + while (ptr && *ptr) { + end = strchr(ptr, DEFAULT_DIR_SEPARATOR); + if (end != NULL) { + *end = '\0'; + end++; + } + snprintf(trypath, MAXPATHLEN, "%s/%s", ptr, filename); + + if (php_check_open_basedir(trypath TSRMLS_CC)) { + stream = NULL; + goto stream_done; + } + + if (PG(safe_mode)) { + if (VCWD_STAT(trypath, &sb) == 0) { + /* file exists ... check permission */ + if ((php_check_safe_mode_include_dir(trypath TSRMLS_CC) == 0) || + php_checkuid(trypath, mode, CHECKUID_CHECK_MODE_PARAM)) { + /* UID ok, or trypath is in safe_mode_include_dir */ + stream = php_stream_fopen_rel(trypath, mode, opened_path, options); + } else { + stream = NULL; + } + goto stream_done; + } + } + stream = php_stream_fopen_rel(trypath, mode, opened_path, options); + if (stream) { +stream_done: + efree(pathbuf); + return stream; + } + ptr = end; + } /* end provided path */ + + efree(pathbuf); + return NULL; + +} +/* }}} */ + + + + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ diff --git a/main/streams.c b/main/streams/streams.c index d746e35996..6184d03829 100755 --- a/main/streams.c +++ b/main/streams/streams.c @@ -33,68 +33,11 @@ #ifdef HAVE_SYS_MMAN_H #include <sys/mman.h> #endif -#if HAVE_SYS_WAIT_H -#include <sys/wait.h> -#endif - #include <stddef.h> - #include <fcntl.h> +#include "php_streams_int.h" -#ifndef MAP_FAILED -#define MAP_FAILED ((void *) -1) -#endif - -#define CHUNK_SIZE 8192 - -#ifdef PHP_WIN32 -#define EWOULDBLOCK WSAEWOULDBLOCK -#endif - -#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) \ - ( __php_stream_call_depth == 0 \ - ? _emalloc((size) ZEND_FILE_LINE_CC ZEND_FILE_LINE_RELAY_CC) \ - : _emalloc((size) ZEND_FILE_LINE_CC ZEND_FILE_LINE_ORIG_RELAY_CC) ) - -#define erealloc_rel_orig(ptr, size) \ - ( __php_stream_call_depth == 0 \ - ? _erealloc((ptr), (size), 0 ZEND_FILE_LINE_CC ZEND_FILE_LINE_RELAY_CC) \ - : _erealloc((ptr), (size), 0 ZEND_FILE_LINE_CC ZEND_FILE_LINE_ORIG_RELAY_CC) ) - - -#define pemalloc_rel_orig(size, persistent) ((persistent) ? malloc((size)) : emalloc_rel_orig((size))) -#define perealloc_rel_orig(ptr, size, persistent) ((persistent) ? realloc((ptr), (size)) : erealloc_rel_orig((ptr), (size))) -#else -# define pemalloc_rel_orig(size, persistent) pemalloc((size), (persistent)) -# define perealloc_rel_orig(ptr, size, persistent) perealloc((ptr), (size), (persistent)) -# define emalloc_rel_orig(size) emalloc((size)) -#endif -/* }}} */ - -/* Under BSD, emulate fopencookie using funopen */ -#if HAVE_FUNOPEN -typedef struct { - int (*reader)(void *, char *, int); - int (*writer)(void *, const char *, int); - fpos_t (*seeker)(void *, fpos_t, int); - int (*closer)(void *); -} COOKIE_IO_FUNCTIONS_T; - -FILE *fopencookie(void *cookie, const char *mode, COOKIE_IO_FUNCTIONS_T *funcs) -{ - return funopen(cookie, funcs->reader, funcs->writer, funcs->seeker, funcs->closer); -} -# define HAVE_FOPENCOOKIE 1 -# define PHP_STREAM_COOKIE_FUNCTIONS &stream_cookie_functions -#elif HAVE_FOPENCOOKIE -# define PHP_STREAM_COOKIE_FUNCTIONS stream_cookie_functions -#endif - +/* {{{ resource and registration code */ static HashTable url_stream_wrappers_hash; static int le_stream = FAILURE; /* true global */ static int le_pstream = FAILURE; /* true global */ @@ -109,6 +52,11 @@ PHPAPI int php_file_le_pstream(void) return le_pstream; } +PHPAPI HashTable *php_stream_get_url_stream_wrappers_hash() +{ + return &url_stream_wrappers_hash; +} + static int forget_persistent_resource_id_numbers(zend_rsrc_list_entry *rsrc TSRMLS_DC) { php_stream *stream; @@ -153,6 +101,9 @@ PHPAPI int php_stream_from_persistent_id(const char *persistent_id, php_stream * return PHP_STREAM_PERSISTENT_NOT_EXIST; } +/* }}} */ + +/* {{{ wrapper error reporting */ static void display_wrapper_errors(php_stream_wrapper *wrapper, const char *path, const char *caption TSRMLS_DC) { char *tmp = estrdup(path); @@ -217,7 +168,28 @@ static void tidy_wrapper_error_log(php_stream_wrapper *wrapper TSRMLS_DC) } } +PHPAPI void php_stream_wrapper_log_error(php_stream_wrapper *wrapper, int options TSRMLS_DC, const char *fmt, ...) +{ + va_list args; + char *buffer = NULL; + va_start(args, fmt); + vspprintf(&buffer, 0, fmt, args); + va_end(args); + + if (options & REPORT_ERRORS || wrapper == NULL) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, buffer); + efree(buffer); + } else { + /* append to stack */ + wrapper->err_stack = erealloc(wrapper->err_stack, (wrapper->err_count + 1) * sizeof(char *)); + if (wrapper->err_stack) + wrapper->err_stack[wrapper->err_count++] = buffer; + } +} + + +/* }}} */ /* allocate a new stream for a particular ops */ PHPAPI php_stream *_php_stream_alloc(php_stream_ops *ops, void *abstract, const char *persistent_id, const char *mode STREAMS_DC TSRMLS_DC) /* {{{ */ @@ -385,133 +357,6 @@ fprintf(stderr, "stream_free: %s:%p[%s] preserve_handle=%d release_cast=%d remov } /* }}} */ -/* {{{ filter API */ -static HashTable stream_filters_hash; - -PHPAPI HashTable *php_get_stream_filters_hash() -{ - return &stream_filters_hash; -} - -PHPAPI int php_stream_filter_register_factory(const char *filterpattern, php_stream_filter_factory *factory TSRMLS_DC) -{ - return zend_hash_add(&stream_filters_hash, (char*)filterpattern, strlen(filterpattern), factory, sizeof(*factory), NULL); -} - -PHPAPI int php_stream_filter_unregister_factory(const char *filterpattern TSRMLS_DC) -{ - return zend_hash_del(&stream_filters_hash, (char*)filterpattern, strlen(filterpattern)); -} - -/* We allow very simple pattern matching for filter factories: - * if "charset.utf-8/sjis" is requested, we search first for an exact - * match. If that fails, we try "charset.*". - * This means that we don't need to clog up the hashtable with a zillion - * charsets (for example) but still be able to provide them all as filters */ -PHPAPI php_stream_filter *php_stream_filter_create(const char *filtername, const char *filterparams, int filterparamslen, int persistent TSRMLS_DC) -{ - php_stream_filter_factory *factory; - php_stream_filter *filter = NULL; - int n; - char *period; - - n = strlen(filtername); - - if (SUCCESS == zend_hash_find(&stream_filters_hash, (char*)filtername, n, (void**)&factory)) { - filter = factory->create_filter(filtername, filterparams, filterparamslen, persistent TSRMLS_CC); - } else if ((period = strchr(filtername, '.'))) { - /* try a wildcard */ - char wildname[128]; - - PHP_STRLCPY(wildname, filtername, sizeof(wildname) - 1, period-filtername + 1); - strcat(wildname, "*"); - - if (SUCCESS == zend_hash_find(&stream_filters_hash, wildname, strlen(wildname), (void**)&factory)) { - filter = factory->create_filter(filtername, filterparams, filterparamslen, persistent TSRMLS_CC); - } - } - - if (filter == NULL) { - /* TODO: these need correct docrefs */ - if (factory == NULL) - php_error_docref(NULL TSRMLS_CC, E_WARNING, "unable to locate filter \"%s\"", filtername); - else - php_error_docref(NULL TSRMLS_CC, E_WARNING, "unable to create or locate filter \"%s\"", filtername); - } - - return filter; -} - -PHPAPI php_stream_filter *_php_stream_filter_alloc(php_stream_filter_ops *fops, void *abstract, int persistent STREAMS_DC TSRMLS_DC) -{ - php_stream_filter *filter; - - filter = (php_stream_filter*) pemalloc_rel_orig(sizeof(php_stream_filter), persistent); - memset(filter, 0, sizeof(php_stream_filter)); - - filter->fops = fops; - filter->abstract = abstract; - filter->is_persistent = persistent; - - return filter; -} - -PHPAPI void php_stream_filter_free(php_stream_filter *filter TSRMLS_DC) -{ - if (filter->fops->dtor) - filter->fops->dtor(filter TSRMLS_CC); - pefree(filter, filter->is_persistent); -} - -PHPAPI void php_stream_filter_prepend(php_stream *stream, php_stream_filter *filter) -{ - filter->next = stream->filterhead; - filter->prev = NULL; - - if (stream->filterhead) { - stream->filterhead->prev = filter; - } else { - stream->filtertail = filter; - } - stream->filterhead = filter; - filter->stream = stream; -} - -PHPAPI void php_stream_filter_append(php_stream *stream, php_stream_filter *filter) -{ - filter->prev = stream->filtertail; - filter->next = NULL; - if (stream->filtertail) { - stream->filtertail->next = filter; - } else { - stream->filterhead = filter; - } - stream->filtertail = filter; - filter->stream = stream; -} - -PHPAPI php_stream_filter *php_stream_filter_remove(php_stream *stream, php_stream_filter *filter, int call_dtor TSRMLS_DC) -{ - assert(stream == filter->stream); - - if (filter->prev) { - filter->prev->next = filter->next; - } else { - stream->filterhead = filter->next; - } - if (filter->next) { - filter->next->prev = filter->prev; - } else { - stream->filtertail = filter->prev; - } - if (call_dtor) { - php_stream_filter_free(filter TSRMLS_CC); - return NULL; - } - return filter; -} -/* }}} */ - /* {{{ generic stream operations */ static void php_stream_fill_read_buffer(php_stream *stream, size_t size TSRMLS_DC) @@ -836,6 +681,40 @@ PHPAPI char *_php_stream_get_line(php_stream *stream, char *buf, size_t maxlen, return bufstart; } +PHPAPI char *php_stream_get_record(php_stream *stream, size_t maxlen, size_t *returned_len, char *delim, size_t delim_len TSRMLS_DC) +{ + char *e, *buf; + size_t toread; + + php_stream_fill_read_buffer(stream, maxlen TSRMLS_CC); + + if (delim_len == 0) { + toread = maxlen; + } else { + if (delim_len == 1) { + e = memchr(stream->readbuf, *delim, stream->readbuflen); + } else { + e = php_memnstr(stream->readbuf, delim, delim_len, (stream->readbuf + stream->readbuflen)); + } + + if (!e) { + toread = maxlen; + } else { + toread = e - (char *) stream->readbuf; + } + } + + buf = emalloc(toread + 1); + *returned_len = php_stream_read(stream, buf, toread); + + if (*returned_len >= 0) { + return buf; + } else { + efree(buf); + return NULL; + } +} + PHPAPI int _php_stream_flush(php_stream *stream, int closing TSRMLS_DC) { int ret = 0; @@ -1260,945 +1139,6 @@ PHPAPI size_t _php_stream_copy_to_stream(php_stream *src, php_stream *dest, size } /* }}} */ -/* {{{ ------- STDIO stream implementation -------*/ - -typedef struct { - FILE *file; - int fd; /* underlying file descriptor */ - int is_process_pipe; /* use pclose instead of fclose */ - int is_pipe; /* don't try and seek */ - char *temp_file_name; /* if non-null, this is the path to a temporary file that - * is to be deleted when the stream is closed */ -#if HAVE_FLUSHIO - char last_op; -#endif -} php_stdio_stream_data; - -PHPAPI php_stream *_php_stream_fopen_temporary_file(const char *dir, const char *pfx, char **opened_path STREAMS_DC TSRMLS_DC) -{ - FILE *fp = php_open_temporary_file(dir, pfx, opened_path TSRMLS_CC); - - if (fp) { - php_stream *stream = php_stream_fopen_from_file_rel(fp, "r+b"); - if (stream) { - return stream; - } - fclose(fp); - - php_error_docref(NULL TSRMLS_CC, E_WARNING, "unable to allocate stream"); - - return NULL; - } - return NULL; -} - -PHPAPI php_stream *_php_stream_fopen_tmpfile(int dummy STREAMS_DC TSRMLS_DC) -{ - char *opened_path = NULL; - FILE *fp = php_open_temporary_file(NULL, "php", &opened_path TSRMLS_CC); - - if (fp) { - php_stream *stream = php_stream_fopen_from_file_rel(fp, "r+b"); - if (stream) { - php_stdio_stream_data *self = (php_stdio_stream_data*)stream->abstract; - - self->temp_file_name = opened_path; - return stream; - } - fclose(fp); - - php_error_docref(NULL TSRMLS_CC, E_WARNING, "unable to allocate stream"); - - return NULL; - } - return NULL; -} - -PHPAPI php_stream *_php_stream_fopen_from_fd(int fd, const char *mode STREAMS_DC TSRMLS_DC) -{ - php_stdio_stream_data *self; - php_stream *stream; - - self = emalloc_rel_orig(sizeof(*self)); - self->file = NULL; - self->is_pipe = 0; - self->is_process_pipe = 0; - self->temp_file_name = NULL; - self->fd = fd; - -#ifdef S_ISFIFO - /* detect if this is a pipe */ - if (self->fd >= 0) { - struct stat sb; - self->is_pipe = (fstat(self->fd, &sb) == 0 && S_ISFIFO(sb.st_mode)) ? 1 : 0; - } -#elif defined(PHP_WIN32) - { - long handle = _get_osfhandle(self->fd); - DWORD in_buf_size, out_buf_size; - - if (handle != 0xFFFFFFFF) { - self->is_pipe = GetNamedPipeInfo((HANDLE)handle, NULL, &out_buf_size, &in_buf_size, NULL); - } - } -#endif - - stream = php_stream_alloc_rel(&php_stream_stdio_ops, self, 0, mode); - - if (stream) { - if (self->is_pipe) { - stream->flags |= PHP_STREAM_FLAG_NO_SEEK; - } else { - stream->position = lseek(self->fd, 0, SEEK_CUR); - } - } - - return stream; -} - -PHPAPI php_stream *_php_stream_fopen_from_file(FILE *file, const char *mode STREAMS_DC TSRMLS_DC) -{ - php_stdio_stream_data *self; - php_stream *stream; - - self = emalloc_rel_orig(sizeof(*self)); - self->file = file; - self->is_pipe = 0; - self->is_process_pipe = 0; - self->temp_file_name = NULL; - self->fd = fileno(file); - -#ifdef S_ISFIFO - /* detect if this is a pipe */ - if (self->fd >= 0) { - struct stat sb; - self->is_pipe = (fstat(self->fd, &sb) == 0 && S_ISFIFO(sb.st_mode)) ? 1 : 0; - } -#elif defined(PHP_WIN32) - { - long handle = _get_osfhandle(self->fd); - DWORD in_buf_size, out_buf_size; - - if (handle != 0xFFFFFFFF) { - self->is_pipe = GetNamedPipeInfo((HANDLE)handle, NULL, &out_buf_size, &in_buf_size, NULL); - } - } -#endif - - stream = php_stream_alloc_rel(&php_stream_stdio_ops, self, 0, mode); - -#ifdef HAVE_BROKEN_GLIBC_FOPEN_APPEND - if (strchr(mode, 'a')) { - fseek(file, 0, SEEK_END); - } -#endif - - if (stream) { - if (self->is_pipe) { - stream->flags |= PHP_STREAM_FLAG_NO_SEEK; - } else { - stream->position = ftell(file); - } - } - - return stream; -} - -PHPAPI php_stream *_php_stream_fopen_from_pipe(FILE *file, const char *mode STREAMS_DC TSRMLS_DC) -{ - php_stdio_stream_data *self; - php_stream *stream; - - self = emalloc_rel_orig(sizeof(*self)); - self->file = file; - self->is_pipe = 1; - self->is_process_pipe = 1; - self->fd = fileno(file); - self->temp_file_name = NULL; - - stream = php_stream_alloc_rel(&php_stream_stdio_ops, self, 0, mode); - stream->flags |= PHP_STREAM_FLAG_NO_SEEK; - return stream; -} - -#define PHP_STDIOP_GET_FD(anfd, data) anfd = (data)->file ? fileno((data)->file) : (data)->fd - -static size_t php_stdiop_write(php_stream *stream, const char *buf, size_t count TSRMLS_DC) -{ - php_stdio_stream_data *data = (php_stdio_stream_data*)stream->abstract; - - assert(data != NULL); - - if (data->fd >= 0) { - int bytes_written = write(data->fd, buf, count); - if (bytes_written < 0) return 0; - return (size_t) bytes_written; - } else { - -#if HAVE_FLUSHIO - if (!data->is_pipe && data->last_op == 'r') { - fseek(data->file, 0, SEEK_CUR); - } - data->last_op = 'w'; -#endif - - return fwrite(buf, 1, count, data->file); - } -} - -static size_t php_stdiop_read(php_stream *stream, char *buf, size_t count TSRMLS_DC) -{ - php_stdio_stream_data *data = (php_stdio_stream_data*)stream->abstract; - size_t ret; - - assert(data != NULL); - - if (data->fd >= 0) { - ret = read(data->fd, buf, count); - - if (ret == 0 || (ret == -1 && errno != EWOULDBLOCK)) - stream->eof = 1; - - } else { -#if HAVE_FLUSHIO - if (!data->is_pipe && data->last_op == 'w') - fseek(data->file, 0, SEEK_CUR); - data->last_op = 'r'; -#endif - - ret = fread(buf, 1, count, data->file); - - if (feof(data->file)) - stream->eof = 1; - } - return ret; -} - -static int php_stdiop_close(php_stream *stream, int close_handle TSRMLS_DC) -{ - int ret; - php_stdio_stream_data *data = (php_stdio_stream_data*)stream->abstract; - - assert(data != NULL); - - if (close_handle) { - if (data->file) { - if (data->is_process_pipe) { - errno = 0; - ret = pclose(data->file); - -#if HAVE_SYS_WAIT_H - if (WIFEXITED(ret)) { - ret = WEXITSTATUS(ret); - } -#endif - } else { - ret = fclose(data->file); - data->file = NULL; - } - } else if (data->fd != -1) { - ret = close(data->fd); - data->fd = -1; - } else { - return 0; /* everything should be closed already -> success */ - } - if (data->temp_file_name) { - unlink(data->temp_file_name); - efree(data->temp_file_name); - data->temp_file_name = NULL; - } - } else { - ret = 0; - data->file = NULL; - data->fd = -1; - } - - /* STDIO streams are never persistent! */ - efree(data); - - return ret; -} - -static int php_stdiop_flush(php_stream *stream TSRMLS_DC) -{ - php_stdio_stream_data *data = (php_stdio_stream_data*)stream->abstract; - - assert(data != NULL); - - /* - * stdio buffers data in user land. By calling fflush(3), this - * data is send to the kernel using write(2). fsync'ing is - * something completely different. - */ - if (data->file) { - return fflush(data->file); - } - return 0; -} - -static int php_stdiop_seek(php_stream *stream, off_t offset, int whence, off_t *newoffset TSRMLS_DC) -{ - php_stdio_stream_data *data = (php_stdio_stream_data*)stream->abstract; - int ret; - - assert(data != NULL); - - if (data->is_pipe) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "cannot seek on a pipe"); - return -1; - } - - if (data->fd >= 0) { - off_t result; - - result = lseek(data->fd, offset, whence); - if (result == (off_t)-1) - return -1; - - *newoffset = result; - return 0; - - } else { - ret = fseek(data->file, offset, whence); - *newoffset = ftell(data->file); - return ret; - } -} - -static int php_stdiop_cast(php_stream *stream, int castas, void **ret TSRMLS_DC) -{ - int fd; - php_stdio_stream_data *data = (php_stdio_stream_data*) stream->abstract; - - assert(data != NULL); - - /* as soon as someone touches the stdio layer, buffering may ensue, - * so we need to stop using the fd directly in that case */ - - switch (castas) { - case PHP_STREAM_AS_STDIO: - if (ret) { - - if (data->file == NULL) { - /* we were opened as a plain file descriptor, so we - * need fdopen now */ - data->file = fdopen(data->fd, stream->mode); - } - - *ret = data->file; - data->fd = -1; - } - return SUCCESS; - - case PHP_STREAM_AS_FD: - /* fetch the fileno rather than using data->fd, since we may - * have zeroed that member if someone requested the FILE* - * first (see above case) */ - PHP_STDIOP_GET_FD(fd, data); - - if (fd < 0) { - return FAILURE; - } - if (data->file) { - fflush(data->file); - } - if (ret) { - *ret = (void*)fd; - } - return SUCCESS; - default: - return FAILURE; - } -} - -static int php_stdiop_stat(php_stream *stream, php_stream_statbuf *ssb TSRMLS_DC) -{ - int fd; - php_stdio_stream_data *data = (php_stdio_stream_data*) stream->abstract; - - assert(data != NULL); - - PHP_STDIOP_GET_FD(fd, data); - - return fstat(fd, &ssb->sb); -} - -static int php_stdiop_set_option(php_stream *stream, int option, int value, void *ptrparam TSRMLS_DC) -{ - php_stdio_stream_data *data = (php_stdio_stream_data*) stream->abstract; - size_t size; - int fd; -#ifdef O_NONBLOCK - /* FIXME: make this work for win32 */ - int flags; - int oldval; -#endif - - PHP_STDIOP_GET_FD(fd, data); - - switch(option) { - case PHP_STREAM_OPTION_BLOCKING: - if (fd == -1) - return -1; -#ifdef O_NONBLOCK - flags = fcntl(fd, F_GETFL, 0); - oldval = (flags & O_NONBLOCK) ? 0 : 1; - if (value) - flags ^= O_NONBLOCK; - else - flags |= O_NONBLOCK; - - if (-1 == fcntl(fd, F_SETFL, flags)) - return -1; - return oldval; -#else - return -1; /* not yet implemented */ -#endif - - case PHP_STREAM_OPTION_WRITE_BUFFER: - - if (data->file == NULL) { - return -1; - } - - if (ptrparam) - size = *(size_t *)ptrparam; - else - size = BUFSIZ; - - switch(value) { - case PHP_STREAM_BUFFER_NONE: - stream->flags |= PHP_STREAM_FLAG_NO_BUFFER; - return setvbuf(data->file, NULL, _IONBF, 0); - - case PHP_STREAM_BUFFER_LINE: - stream->flags ^= PHP_STREAM_FLAG_NO_BUFFER; - return setvbuf(data->file, NULL, _IOLBF, size); - - case PHP_STREAM_BUFFER_FULL: - stream->flags ^= PHP_STREAM_FLAG_NO_BUFFER; - return setvbuf(data->file, NULL, _IOFBF, size); - - default: - return -1; - } - break; - default: - return -1; - } -} - -PHPAPI php_stream_ops php_stream_stdio_ops = { - php_stdiop_write, php_stdiop_read, - php_stdiop_close, php_stdiop_flush, - "STDIO", - php_stdiop_seek, - php_stdiop_cast, - php_stdiop_stat, - php_stdiop_set_option -}; -/* }}} */ - -/* {{{ php_stream_fopen_with_path */ -PHPAPI php_stream *_php_stream_fopen_with_path(char *filename, char *mode, char *path, char **opened_path, int options STREAMS_DC TSRMLS_DC) -{ - /* code ripped off from fopen_wrappers.c */ - char *pathbuf, *ptr, *end; - char *exec_fname; - char trypath[MAXPATHLEN]; - struct stat sb; - php_stream *stream; - int path_length; - int filename_length; - int exec_fname_length; - - if (opened_path) { - *opened_path = NULL; - } - - if(!filename) { - return NULL; - } - - filename_length = strlen(filename); - - /* Relative path open */ - if (*filename == '.' && (IS_SLASH(filename[1]) || filename[1] == '.')) { - /* further checks, we could have ....... filenames */ - ptr = filename + 1; - if (*ptr == '.') { - while (*(++ptr) == '.'); - if (!IS_SLASH(*ptr)) { /* not a relative path after all */ - goto not_relative_path; - } - } - - - if (php_check_open_basedir(filename TSRMLS_CC)) { - return NULL; - } - - if (PG(safe_mode) && (!php_checkuid(filename, mode, CHECKUID_CHECK_MODE_PARAM))) { - return NULL; - } - return php_stream_fopen_rel(filename, mode, opened_path, options); - } - - /* - * files in safe_mode_include_dir (or subdir) are excluded from - * safe mode GID/UID checks - */ - -not_relative_path: - - /* Absolute path open */ - if (IS_ABSOLUTE_PATH(filename, filename_length)) { - - if (php_check_open_basedir(filename TSRMLS_CC)) { - return NULL; - } - - if ((php_check_safe_mode_include_dir(filename TSRMLS_CC)) == 0) - /* filename is in safe_mode_include_dir (or subdir) */ - return php_stream_fopen_rel(filename, mode, opened_path, options); - - if (PG(safe_mode) && (!php_checkuid(filename, mode, CHECKUID_CHECK_MODE_PARAM))) - return NULL; - - return php_stream_fopen_rel(filename, mode, opened_path, options); - } - -#ifdef PHP_WIN32 - if (IS_SLASH(filename[0])) { - int cwd_len; - char *cwd; - cwd = virtual_getcwd_ex(&cwd_len TSRMLS_CC); - /* getcwd() will return always return [DRIVE_LETTER]:/) on windows. */ - *(cwd+3) = '\0'; - - snprintf(trypath, MAXPATHLEN, "%s%s", cwd, filename); - - free(cwd); - - if (php_check_open_basedir(trypath TSRMLS_CC)) { - return NULL; - } - if ((php_check_safe_mode_include_dir(trypath TSRMLS_CC)) == 0) { - return php_stream_fopen_rel(trypath, mode, opened_path, options); - } - if (PG(safe_mode) && (!php_checkuid(trypath, mode, CHECKUID_CHECK_MODE_PARAM))) { - return NULL; - } - - return php_stream_fopen_rel(trypath, mode, opened_path, options); - } -#endif - - if (!path || (path && !*path)) { - - if (php_check_open_basedir(path TSRMLS_CC)) { - return NULL; - } - - if (PG(safe_mode) && (!php_checkuid(filename, mode, CHECKUID_CHECK_MODE_PARAM))) { - return NULL; - } - return php_stream_fopen_rel(filename, mode, opened_path, options); - } - - /* check in provided path */ - /* append the calling scripts' current working directory - * as a fall back case - */ - if (zend_is_executing(TSRMLS_C)) { - exec_fname = zend_get_executed_filename(TSRMLS_C); - exec_fname_length = strlen(exec_fname); - path_length = strlen(path); - - while ((--exec_fname_length >= 0) && !IS_SLASH(exec_fname[exec_fname_length])); - if ((exec_fname && exec_fname[0] == '[') - || exec_fname_length<=0) { - /* [no active file] or no path */ - pathbuf = estrdup(path); - } else { - pathbuf = (char *) emalloc(exec_fname_length + path_length +1 +1); - memcpy(pathbuf, path, path_length); - pathbuf[path_length] = DEFAULT_DIR_SEPARATOR; - memcpy(pathbuf+path_length+1, exec_fname, exec_fname_length); - pathbuf[path_length + exec_fname_length +1] = '\0'; - } - } else { - pathbuf = estrdup(path); - } - - ptr = pathbuf; - - while (ptr && *ptr) { - end = strchr(ptr, DEFAULT_DIR_SEPARATOR); - if (end != NULL) { - *end = '\0'; - end++; - } - snprintf(trypath, MAXPATHLEN, "%s/%s", ptr, filename); - - if (php_check_open_basedir(trypath TSRMLS_CC)) { - stream = NULL; - goto stream_done; - } - - if (PG(safe_mode)) { - if (VCWD_STAT(trypath, &sb) == 0) { - /* file exists ... check permission */ - if ((php_check_safe_mode_include_dir(trypath TSRMLS_CC) == 0) || - php_checkuid(trypath, mode, CHECKUID_CHECK_MODE_PARAM)) { - /* UID ok, or trypath is in safe_mode_include_dir */ - stream = php_stream_fopen_rel(trypath, mode, opened_path, options); - } else { - stream = NULL; - } - goto stream_done; - } - } - stream = php_stream_fopen_rel(trypath, mode, opened_path, options); - if (stream) { -stream_done: - efree(pathbuf); - return stream; - } - ptr = end; - } /* end provided path */ - - efree(pathbuf); - return NULL; - -} -/* }}} */ - -#ifndef S_ISREG -#define S_ISREG(mode) (((mode)&S_IFMT) == S_IFREG) -#endif - -/* parse standard "fopen" modes into open() flags */ -PHPAPI int php_stream_parse_fopen_modes(const char *mode, int *open_flags) -{ - int flags; - - switch (mode[0]) { - case 'r': - flags = 0; - break; - case 'w': - flags = O_TRUNC|O_CREAT; - break; - case 'a': - flags = O_CREAT|O_APPEND; - break; - case 'x': - flags = O_CREAT|O_EXCL; - break; - default: - /* unknown mode */ - return FAILURE; - } - - if (strchr(mode, '+')) { - flags |= O_RDWR; - } else if (flags) { - flags |= O_WRONLY; - } else { - flags |= O_RDONLY; - } - -#ifdef O_BINARY - if (strchr(mode, 'b')) { - flags |= O_BINARY; - } -#endif - - *open_flags = flags; - return SUCCESS; -} - -/* {{{ php_stream_fopen */ -PHPAPI php_stream *_php_stream_fopen(const char *filename, const char *mode, char **opened_path, int options STREAMS_DC TSRMLS_DC) -{ - char *realpath = NULL; - struct stat st; - int open_flags; - int fd; - php_stream *ret; - - if (FAILURE == php_stream_parse_fopen_modes(mode, &open_flags)) { - if (options & REPORT_ERRORS) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "`%s' is not a valid mode for fopen", mode); - } - return NULL; - } - - realpath = expand_filepath(filename, NULL TSRMLS_CC); - - fd = open(realpath, open_flags, 0666); - - if (fd != -1) { - /* sanity checks for include/require */ - if (options & STREAM_OPEN_FOR_INCLUDE && (fstat(fd, &st) == -1 || !S_ISREG(st.st_mode))) { -#ifdef PHP_WIN32 - /* skip the sanity check; fstat doesn't appear to work on - * UNC paths */ - if (!IS_UNC_PATH(filename, strlen(filename))) -#endif - goto err; - } - - ret = php_stream_fopen_from_fd_rel(fd, mode); - - if (ret) { - if (opened_path) { - *opened_path = realpath; - realpath = NULL; - } - if (realpath) - efree(realpath); - - return ret; - } -err: - close(fd); - } - efree(realpath); - return NULL; -} -/* }}} */ - -/* {{{ STDIO with fopencookie */ -#if HAVE_FUNOPEN -/* use our fopencookie emulation */ -static int stream_cookie_reader(void *cookie, char *buffer, int size) -{ - int ret; - TSRMLS_FETCH(); - ret = php_stream_read((php_stream*)cookie, buffer, size); - return ret; -} - -static int stream_cookie_writer(void *cookie, const char *buffer, int size) -{ - TSRMLS_FETCH(); - return php_stream_write((php_stream *)cookie, (char *)buffer, size); -} - -static fpos_t stream_cookie_seeker(void *cookie, off_t position, int whence) -{ - TSRMLS_FETCH(); - return (fpos_t)php_stream_seek((php_stream *)cookie, position, whence); -} - -static int stream_cookie_closer(void *cookie) -{ - php_stream *stream = (php_stream*)cookie; - TSRMLS_FETCH(); - - /* prevent recursion */ - stream->fclose_stdiocast = PHP_STREAM_FCLOSE_NONE; - return php_stream_close(stream); -} - -#elif HAVE_FOPENCOOKIE -static ssize_t stream_cookie_reader(void *cookie, char *buffer, size_t size) -{ - ssize_t ret; - TSRMLS_FETCH(); - ret = php_stream_read(((php_stream *)cookie), buffer, size); - return ret; -} - -static ssize_t stream_cookie_writer(void *cookie, const char *buffer, size_t size) -{ - TSRMLS_FETCH(); - return php_stream_write(((php_stream *)cookie), (char *)buffer, size); -} - -#ifdef COOKIE_SEEKER_USES_OFF64_T -static int stream_cookie_seeker(void *cookie, __off64_t *position, int whence) -{ - TSRMLS_FETCH(); - - *position = php_stream_seek((php_stream *)cookie, (off_t)*position, whence); - - if (*position == -1) - return -1; - return 0; -} -#else -static int stream_cookie_seeker(void *cookie, off_t position, int whence) -{ - TSRMLS_FETCH(); - return php_stream_seek((php_stream *)cookie, position, whence); -} -#endif - -static int stream_cookie_closer(void *cookie) -{ - php_stream *stream = (php_stream*)cookie; - TSRMLS_FETCH(); - - /* prevent recursion */ - stream->fclose_stdiocast = PHP_STREAM_FCLOSE_NONE; - return php_stream_close(stream); -} -#endif /* elif HAVE_FOPENCOOKIE */ - -#if HAVE_FOPENCOOKIE -static COOKIE_IO_FUNCTIONS_T stream_cookie_functions = -{ - stream_cookie_reader, stream_cookie_writer, - stream_cookie_seeker, stream_cookie_closer -}; -#else -/* TODO: use socketpair() to emulate fopencookie, as suggested by Hartmut ? */ -#endif -/* }}} */ - -/* {{{ php_stream_cast */ -PHPAPI int _php_stream_cast(php_stream *stream, int castas, void **ret, int show_err TSRMLS_DC) -{ - int flags = castas & PHP_STREAM_CAST_MASK; - castas &= ~PHP_STREAM_CAST_MASK; - - /* synchronize our buffer (if possible) */ - if (ret) { - php_stream_flush(stream); - if (stream->ops->seek && (stream->flags & PHP_STREAM_FLAG_NO_SEEK) == 0) { - off_t dummy; - - stream->ops->seek(stream, stream->position, SEEK_SET, &dummy TSRMLS_CC); - stream->readpos = stream->writepos = 0; - } - } - - /* filtered streams can only be cast as stdio, and only when fopencookie is present */ - - if (castas == PHP_STREAM_AS_STDIO) { - if (stream->stdiocast) { - if (ret) { - *ret = stream->stdiocast; - } - goto exit_success; - } - - /* if the stream is a stdio stream let's give it a chance to respond - * first, to avoid doubling up the layers of stdio with an fopencookie */ - if (php_stream_is(stream, PHP_STREAM_IS_STDIO) && - stream->ops->cast && - stream->filterhead == NULL && - stream->ops->cast(stream, castas, ret TSRMLS_CC) == SUCCESS) - { - goto exit_success; - } - -#if HAVE_FOPENCOOKIE - /* if just checking, say yes we can be a FILE*, but don't actually create it yet */ - if (ret == NULL) - goto exit_success; - - *ret = fopencookie(stream, stream->mode, PHP_STREAM_COOKIE_FUNCTIONS); - - if (*ret != NULL) { - off_t pos; - - stream->fclose_stdiocast = PHP_STREAM_FCLOSE_FOPENCOOKIE; - - /* If the stream position is not at the start, we need to force - * the stdio layer to believe it's real location. */ - pos = php_stream_tell(stream); - if (pos > 0) - fseek(*ret, pos, SEEK_SET); - - goto exit_success; - } - - /* must be either: - a) programmer error - b) no memory - -> lets bail - */ - php_error_docref(NULL TSRMLS_CC, E_ERROR, "fopencookie failed"); - return FAILURE; -#endif - - if (flags & PHP_STREAM_CAST_TRY_HARD) { - php_stream *newstream; - - newstream = php_stream_fopen_tmpfile(); - if (newstream) { - size_t copied = php_stream_copy_to_stream(stream, newstream, PHP_STREAM_COPY_ALL); - - if (copied == 0) { - php_stream_close(newstream); - } else { - int retcode = php_stream_cast(newstream, castas | flags, ret, show_err); - - if (retcode == SUCCESS) - rewind((FILE*)*ret); - - /* do some specialized cleanup */ - if ((flags & PHP_STREAM_CAST_RELEASE)) { - php_stream_free(stream, PHP_STREAM_FREE_CLOSE_CASTED); - } - - return retcode; - } - } - } - } - - if (stream->filterhead) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "cannot cast a filtered stream on this system"); - return FAILURE; - } else if (stream->ops->cast && stream->ops->cast(stream, castas, ret TSRMLS_CC) == SUCCESS) { - goto exit_success; - } - - if (show_err) { - /* these names depend on the values of the PHP_STREAM_AS_XXX defines in php_streams.h */ - static const char *cast_names[3] = { - "STDIO FILE*", "File Descriptor", "Socket Descriptor" - }; - - php_error_docref(NULL TSRMLS_CC, E_WARNING, "cannot represent a stream of type %s as a %s", - stream->ops->label, - cast_names[castas] - ); - } - - return FAILURE; - -exit_success: - - if ((stream->writepos - stream->readpos) > 0 && - stream->fclose_stdiocast != PHP_STREAM_FCLOSE_FOPENCOOKIE && - (flags & PHP_STREAM_CAST_INTERNAL) == 0) { - /* the data we have buffered will be lost to the third party library that - * will be accessing the stream. Emit a warning so that the end-user will - * know that they should try something else */ - - php_error_docref(NULL TSRMLS_CC, E_WARNING, - "%d bytes of buffered data lost during conversion!", - stream->writepos - stream->readpos); - } - - if (castas == PHP_STREAM_AS_STDIO && ret) - stream->stdiocast = *ret; - - if (flags & PHP_STREAM_CAST_RELEASE) { - php_stream_free(stream, PHP_STREAM_FREE_CLOSE_CASTED); - } - - return SUCCESS; - -} -/* }}} */ - /* {{{ wrapper init and registration */ static void stream_resource_regular_dtor(zend_rsrc_list_entry *rsrc TSRMLS_DC) @@ -2222,14 +1162,14 @@ int php_init_stream_wrappers(int module_number TSRMLS_DC) return ( zend_hash_init(&url_stream_wrappers_hash, 0, NULL, NULL, 1) == SUCCESS && - zend_hash_init(&stream_filters_hash, 0, NULL, NULL, 1) == SUCCESS + zend_hash_init(php_get_stream_filters_hash(), 0, NULL, NULL, 1) == SUCCESS ) ? SUCCESS : FAILURE; } int php_shutdown_stream_wrappers(int module_number TSRMLS_DC) { zend_hash_destroy(&url_stream_wrappers_hash); - zend_hash_destroy(&stream_filters_hash); + zend_hash_destroy(php_get_stream_filters_hash()); return SUCCESS; } @@ -2244,117 +1184,7 @@ PHPAPI int php_unregister_url_stream_wrapper(char *protocol TSRMLS_DC) } /* }}} */ - -static size_t php_plain_files_dirstream_read(php_stream *stream, char *buf, size_t count TSRMLS_DC) -{ - 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, off_t *newoffs 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, /* cast */ - NULL, /* stat */ - NULL /* set_option */ -}; - -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, options); - } - - if (php_check_open_basedir(path TSRMLS_CC)) { - return NULL; - } - - 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, options); -} - -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, - "plainfile" -}; - -static php_stream_wrapper php_plain_files_wrapper = { - &php_plain_files_wrapper_ops, - NULL, - 0 -}; - +/* {{{ php_stream_locate_url_wrapper */ PHPAPI php_stream_wrapper *php_stream_locate_url_wrapper(const char *path, char **path_for_open, int options TSRMLS_DC) { php_stream_wrapper *wrapper = NULL; @@ -2417,7 +1247,9 @@ PHPAPI php_stream_wrapper *php_stream_locate_url_wrapper(const char *path, char return wrapper; } +/* }}} */ +/* {{{ _php_stream_stat_path */ PHPAPI int _php_stream_stat_path(char *path, php_stream_statbuf *ssb TSRMLS_DC) { php_stream_wrapper *wrapper = NULL; @@ -2429,6 +1261,7 @@ PHPAPI int _php_stream_stat_path(char *path, php_stream_statbuf *ssb TSRMLS_DC) } return -1; } +/* }}} */ /* {{{ php_stream_opendir */ PHPAPI php_stream *_php_stream_opendir(char *path, int options, @@ -2466,6 +1299,7 @@ PHPAPI php_stream *_php_stream_opendir(char *path, int options, } /* }}} */ +/* {{{ _php_stream_readdir */ PHPAPI php_stream_dirent *_php_stream_readdir(php_stream *dirstream, php_stream_dirent *ent TSRMLS_DC) { @@ -2474,26 +1308,7 @@ PHPAPI php_stream_dirent *_php_stream_readdir(php_stream *dirstream, php_stream_ return NULL; } - -PHPAPI void php_stream_wrapper_log_error(php_stream_wrapper *wrapper, int options TSRMLS_DC, const char *fmt, ...) -{ - va_list args; - char *buffer = NULL; - - va_start(args, fmt); - vspprintf(&buffer, 0, fmt, args); - va_end(args); - - if (options & REPORT_ERRORS || wrapper == NULL) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, buffer); - efree(buffer); - } else { - /* append to stack */ - wrapper->err_stack = erealloc(wrapper->err_stack, (wrapper->err_count + 1) * sizeof(char *)); - if (wrapper->err_stack) - wrapper->err_stack[wrapper->err_count++] = buffer; - } -} +/* }}} */ /* {{{ php_stream_open_wrapper_ex */ PHPAPI php_stream *_php_stream_open_wrapper_ex(char *path, char *mode, int options, @@ -2591,86 +1406,7 @@ PHPAPI php_stream *_php_stream_open_wrapper_ex(char *path, char *mode, int optio } /* }}} */ -/* {{{ php_stream_open_wrapper_as_file */ -PHPAPI FILE * _php_stream_open_wrapper_as_file(char *path, char *mode, int options, char **opened_path STREAMS_DC TSRMLS_DC) -{ - FILE *fp = NULL; - php_stream *stream = NULL; - - stream = php_stream_open_wrapper_rel(path, mode, options|STREAM_WILL_CAST, opened_path); - - if (stream == NULL) - return NULL; - -#ifdef PHP_WIN32 - /* Avoid possible strange problems when working with socket based streams */ - if ((options & STREAM_OPEN_FOR_INCLUDE) && php_stream_is(stream, PHP_STREAM_IS_SOCKET)) { - char buf[CHUNK_SIZE]; - - fp = php_open_temporary_file(NULL, "php", NULL TSRMLS_CC); - if (fp) { - while (!php_stream_eof(stream)) { - size_t didread = php_stream_read(stream, buf, sizeof(buf)); - if (didread > 0) { - fwrite(buf, 1, didread, fp); - } else { - break; - } - } - php_stream_close(stream); - rewind(fp); - return fp; - } - } -#endif - - if (php_stream_cast(stream, PHP_STREAM_AS_STDIO|PHP_STREAM_CAST_TRY_HARD|PHP_STREAM_CAST_RELEASE, - (void**)&fp, REPORT_ERRORS) == FAILURE) - { - php_stream_close(stream); - if (opened_path && *opened_path) - efree(*opened_path); - return NULL; - } - return fp; -} -/* }}} */ - -/* {{{ php_stream_make_seekable */ -PHPAPI int _php_stream_make_seekable(php_stream *origstream, php_stream **newstream, int flags STREAMS_DC TSRMLS_DC) -{ - assert(newstream != NULL); - - *newstream = NULL; - - if (((flags & PHP_STREAM_FORCE_CONVERSION) == 0) && origstream->ops->seek != NULL) { - *newstream = origstream; - return PHP_STREAM_UNCHANGED; - } - - /* Use a tmpfile and copy the old streams contents into it */ - - if (flags & PHP_STREAM_PREFER_STDIO) - *newstream = php_stream_fopen_tmpfile(); - else - *newstream = php_stream_temp_new(); - - if (*newstream == NULL) - return PHP_STREAM_FAILED; - - if (php_stream_copy_to_stream(origstream, *newstream, PHP_STREAM_COPY_ALL) == 0) { - php_stream_close(*newstream); - *newstream = NULL; - return PHP_STREAM_CRITICAL; - } - - php_stream_close(origstream); - php_stream_seek(*newstream, 0, SEEK_SET); - - return PHP_STREAM_RELEASED; -} -/* }}} */ - +/* {{{ context API */ PHPAPI php_stream_context *php_stream_context_set(php_stream *stream, php_stream_context *context) { php_stream_context *oldcontext = stream->context; @@ -2740,45 +1476,7 @@ PHPAPI int php_stream_context_set_option(php_stream_context *context, } return zend_hash_update(Z_ARRVAL_PP(wrapperhash), (char*)optionname, strlen(optionname)+1, (void**)&optionvalue, sizeof(zval *), NULL); } - -PHPAPI HashTable *php_stream_get_url_stream_wrappers_hash() -{ - return &url_stream_wrappers_hash; -} - -PHPAPI char *php_stream_get_record(php_stream *stream, size_t maxlen, size_t *returned_len, char *delim, size_t delim_len TSRMLS_DC) -{ - char *e, *buf; - size_t toread; - - php_stream_fill_read_buffer(stream, maxlen TSRMLS_CC); - - if (delim_len == 0) { - toread = maxlen; - } else { - if (delim_len == 1) { - e = memchr(stream->readbuf, *delim, stream->readbuflen); - } else { - e = php_memnstr(stream->readbuf, delim, delim_len, (stream->readbuf + stream->readbuflen)); - } - - if (!e) { - toread = maxlen; - } else { - toread = e - (char *) stream->readbuf; - } - } - - buf = emalloc(toread + 1); - *returned_len = php_stream_read(stream, buf, toread); - - if (*returned_len >= 0) { - return buf; - } else { - efree(buf); - return NULL; - } -} +/* }}} */ /* * Local variables: diff --git a/main/user_streams.c b/main/streams/userspace.c index 849d6dd561..849d6dd561 100644 --- a/main/user_streams.c +++ b/main/streams/userspace.c |