diff options
| author | Wez Furlong <wez@php.net> | 2002-03-15 21:03:08 +0000 |
|---|---|---|
| committer | Wez Furlong <wez@php.net> | 2002-03-15 21:03:08 +0000 |
| commit | 0f65280cb5118d8c1a85db6626f7be365f3d1b26 (patch) | |
| tree | 931b09acc5041eb771017e3ebf9ecb9aa833d722 /main | |
| parent | 3a1ebd4f519facbd7ec769304857aad40e49cf1c (diff) | |
| download | php-git-0f65280cb5118d8c1a85db6626f7be365f3d1b26.tar.gz | |
New PHP streams...
Diffstat (limited to 'main')
| -rw-r--r-- | main/fopen_wrappers.c | 137 | ||||
| -rw-r--r-- | main/fopen_wrappers.h | 20 | ||||
| -rw-r--r-- | main/internal_functions.c.in | 6 | ||||
| -rw-r--r-- | main/main.c | 42 | ||||
| -rw-r--r-- | main/network.c | 403 | ||||
| -rw-r--r-- | main/php_network.h | 54 | ||||
| -rwxr-xr-x | main/php_streams.h | 55 | ||||
| -rwxr-xr-x | main/streams.c | 595 |
8 files changed, 1099 insertions, 213 deletions
diff --git a/main/fopen_wrappers.c b/main/fopen_wrappers.c index 7cf55651d4..9bbd126571 100644 --- a/main/fopen_wrappers.c +++ b/main/fopen_wrappers.c @@ -82,55 +82,6 @@ #endif /* }}} */ -static FILE *php_fopen_url_wrapper(const char *, char *, int, int *, int *, char ** TSRMLS_DC); -static HashTable fopen_url_wrappers_hash; - -/* {{{ php_register_url_wrapper - */ -PHPAPI int php_register_url_wrapper(const char *protocol, php_fopen_url_wrapper_t wrapper TSRMLS_DC) -{ - if(PG(allow_url_fopen)) { - return zend_hash_add(&fopen_url_wrappers_hash, (char *) protocol, strlen(protocol), &wrapper, sizeof(wrapper), NULL); - } else { - return FAILURE; - } -} -/* }}} */ - -/* {{{ php_unregister_url_wrapper - */ -PHPAPI int php_unregister_url_wrapper(char *protocol TSRMLS_DC) -{ - if(PG(allow_url_fopen)) { - return zend_hash_del(&fopen_url_wrappers_hash, protocol, strlen(protocol)); - } else { - return SUCCESS; - } -} -/* }}} */ - -/* {{{ php_init_fopen_wrappers - */ -int php_init_fopen_wrappers(TSRMLS_D) -{ - if(PG(allow_url_fopen)) { - return zend_hash_init(&fopen_url_wrappers_hash, 0, NULL, NULL, 1); - } - return SUCCESS; -} -/* }}} */ - -/* {{{ php_shutdown_fopen_wrappers - */ -int php_shutdown_fopen_wrappers(TSRMLS_D) -{ - if(PG(allow_url_fopen)) { - zend_hash_destroy(&fopen_url_wrappers_hash); - } - return SUCCESS; -} -/* }}} */ - /* {{{ php_check_specific_open_basedir When open_basedir is not NULL, check if the given filename is located in open_basedir. Returns -1 if error or not in the open_basedir, else 0 @@ -289,36 +240,6 @@ static FILE *php_fopen_and_set_opened_path(const char *path, char *mode, char ** } /* }}} */ -/* {{{ php_fopen_wrapper - */ -PHPAPI FILE *php_fopen_wrapper(char *path, char *mode, int options, int *issock, int *socketd, char **opened_path TSRMLS_DC) -{ - if (opened_path) { - *opened_path = NULL; - } - - if(!path || !*path) { - return NULL; - } - - - if(PG(allow_url_fopen)) { - if (!(options & IGNORE_URL)) { - return php_fopen_url_wrapper(path, mode, options, issock, socketd, opened_path TSRMLS_CC); - } - } - - if (options & USE_PATH && PG(include_path) != NULL) { - return php_fopen_with_path(path, mode, PG(include_path), opened_path TSRMLS_CC); - } else { - if (options & ENFORCE_SAFE_MODE && PG(safe_mode) && (!php_checkuid(path, mode, CHECKUID_CHECK_MODE_PARAM))) { - return NULL; - } - return php_fopen_and_set_opened_path(path, mode, opened_path TSRMLS_CC); - } -} -/* }}} */ - /* {{{ php_fopen_primary_script */ PHPAPI int php_fopen_primary_script(zend_file_handle *file_handle TSRMLS_DC) @@ -535,64 +456,6 @@ PHPAPI FILE *php_fopen_with_path(char *filename, char *mode, char *path, char ** } /* }}} */ -/* {{{ php_fopen_url_wrapper - */ -static FILE *php_fopen_url_wrapper(const char *path, char *mode, int options, int *issock, int *socketd, char **opened_path TSRMLS_DC) -{ - FILE *fp = NULL; - const char *p; - const char *protocol=NULL; - int n=0; - - for (p=path; isalnum((int)*p); p++) { - n++; - } - if ((*p==':')&&(n>1)) { - protocol=path; - } - - if (protocol) { - php_fopen_url_wrapper_t *wrapper=NULL; - - if (FAILURE==zend_hash_find(&fopen_url_wrappers_hash, (char *) protocol, n, (void **)&wrapper)) { - wrapper=NULL; - protocol=NULL; - } - if (wrapper) { - return (*wrapper)(path, mode, options, issock, socketd, opened_path TSRMLS_CC); - } - } - - if (!protocol || !strncasecmp(protocol, "file", n)){ - *issock = 0; - - if(protocol) { - if(path[n+1]=='/') { - if(path[n+2]=='/') { - php_error(E_WARNING, "remote host file access not supported, %s", path); - return NULL; - } - } - path+= n+1; - } - - if (options & USE_PATH) { - fp = php_fopen_with_path((char *) path, mode, PG(include_path), opened_path TSRMLS_CC); - } else { - if (options & ENFORCE_SAFE_MODE && PG(safe_mode) && (!php_checkuid(path, mode, CHECKUID_CHECK_MODE_PARAM))) { - fp = NULL; - } else { - fp = php_fopen_and_set_opened_path(path, mode, opened_path TSRMLS_CC); - } - } - return (fp); - } - - php_error(E_WARNING, "Invalid URL specified, %s", path); - return NULL; -} -/* }}} */ - /* {{{ php_strip_url_passwd */ PHPAPI char *php_strip_url_passwd(char *url) diff --git a/main/fopen_wrappers.h b/main/fopen_wrappers.h index dc28af9f47..24e60dcc00 100644 --- a/main/fopen_wrappers.h +++ b/main/fopen_wrappers.h @@ -27,12 +27,14 @@ #define IGNORE_URL 2 /* There's no USE_URL. */ #ifdef PHP_WIN32 -# define IGNORE_URL_WIN 2 +# define IGNORE_URL_WIN IGNORE_URL #else # define IGNORE_URL_WIN 0 #endif #define ENFORCE_SAFE_MODE 4 +#define REPORT_ERRORS 8 + #ifdef PHP_WIN32 # define SOCK_ERR INVALID_SOCKET # define SOCK_CONN_ERR SOCKET_ERROR @@ -42,22 +44,6 @@ # define SOCK_CONN_ERR -1 # define SOCK_RECV_ERR -1 #endif -#define SOCK_WRITE(d, s) send(s, d, strlen(d), 0) -#define SOCK_WRITEL(d, l, s) send(s, d, l, 0) -#define SOCK_FGETC(s) php_sock_fgetc((s)) -#define SOCK_FGETS(b, l, s) php_sock_fgets((b), (l), (s)) -#define SOCK_FEOF(sock) php_sock_feof((sock)) -#define SOCK_FREAD(ptr, size, sock) php_sock_fread((ptr), (size), (sock)) -#define SOCK_FCLOSE(s) php_sock_close(s) - -#define FP_FGETS(buf, len, sock, fp, issock) \ - ((issock)?SOCK_FGETS(buf, len, sock):fgets(buf, len, fp)) -#define FP_FREAD(buf, len, sock, fp, issock) \ - ((issock)?SOCK_FREAD(buf, len, sock):fread(buf, 1, len, fp)) -#define FP_FEOF(sock, fp, issock) \ - ((issock)?SOCK_FEOF(sock):feof(fp)) -#define FP_FGETC(sock, fp, issock) \ - ((issock)?SOCK_FGETC(sock):fgetc(fp)) /* values for issock */ #define IS_NOT_SOCKET 0 diff --git a/main/internal_functions.c.in b/main/internal_functions.c.in index f8d840d755..4903d6c4bb 100644 --- a/main/internal_functions.c.in +++ b/main/internal_functions.c.in @@ -29,6 +29,12 @@ #include <stdlib.h> #include <stdio.h> +#if HAVE_OPENSSL_EXT +/* zlib typedefs free_func which causes problems if the SSL includes happen + * after zlib.h is included */ +# include <openssl/ssl.h> +#endif + @EXT_INCLUDE_CODE@ zend_module_entry *php_builtin_extensions[] = { diff --git a/main/main.c b/main/main.c index 4d7d0b7cf3..e6d968dedb 100644 --- a/main/main.c +++ b/main/main.c @@ -73,6 +73,7 @@ #include "php_content_types.h" #include "php_ticks.h" #include "php_logos.h" +#include "php_streams.h" #include "SAPI.h" /* }}} */ @@ -562,17 +563,24 @@ PHP_FUNCTION(set_time_limit) */ static FILE *php_fopen_wrapper_for_zend(const char *filename, char **opened_path) { - int issock=0, socketd=0; - int old_chunk_size; - FILE *retval; + FILE *retval = NULL; + php_stream * stream; TSRMLS_FETCH(); - - old_chunk_size = php_sock_set_def_chunk_size(1); - retval=php_fopen_wrapper((char *) filename, "rb", USE_PATH|IGNORE_URL_WIN, &issock, &socketd, opened_path TSRMLS_CC); - php_sock_set_def_chunk_size(old_chunk_size); - - if (issock) { - retval = fdopen(socketd, "rb"); + + stream = php_stream_open_wrapper((char *)filename, "rb", USE_PATH|IGNORE_URL_WIN|REPORT_ERRORS, opened_path TSRMLS_CC); + if (stream) { + /* no need for us to check the stream type here */ + php_stream_sock_set_chunk_size(stream, 1); + + if (php_stream_cast(stream, PHP_STREAM_AS_STDIO | PHP_STREAM_CAST_TRY_HARD, (void**)&retval, 1) == SUCCESS) { + ZEND_REGISTER_RESOURCE(NULL, stream, php_file_le_stream()); + } + else { + php_stream_close(stream); + if (opened_path && *opened_path) + efree(*opened_path); + retval = NULL; + } } return retval; } @@ -941,14 +949,14 @@ int php_module_startup(sapi_module_struct *sf) REGISTER_INI_ENTRIES(); - /* initialize fopen wrappers registry - (this uses configuration parameters from php.ini) + /* initialize stream wrappers registry + * (this uses configuration parameters from php.ini) */ - if (php_init_fopen_wrappers(TSRMLS_C) == FAILURE) { - php_printf("PHP: Unable to initialize fopen url wrappers.\n"); + if (php_init_stream_wrappers(TSRMLS_C) == FAILURE) { + php_printf("PHP: Unable to initialize stream url wrappers.\n"); return FAILURE; } - + /* initialize registry for images to be used in phpinfo() (this uses configuration parameters from php.ini) */ @@ -1048,7 +1056,9 @@ void php_module_shutdown(TSRMLS_D) sapi_flush(TSRMLS_C); zend_shutdown(TSRMLS_C); - php_shutdown_fopen_wrappers(TSRMLS_C); + + php_shutdown_stream_wrappers(TSRMLS_C); + php_shutdown_info_logos(); UNREGISTER_INI_ENTRIES(); diff --git a/main/network.c b/main/network.c index 6be650875a..1aedbe416a 100644 --- a/main/network.c +++ b/main/network.c @@ -17,6 +17,9 @@ */ /* $Id$ */ +#define PHP_SOCK_CHUNK_SIZE 8192 +#define MAX_CHUNKS_PER_READ 10 + #include "php.h" #ifdef PHP_WIN32 @@ -408,6 +411,406 @@ int php_sockaddr_size(php_sockaddr_storage *addr) } /* }}} */ +PHPAPI php_stream * php_stream_sock_open_from_socket(int socket, int persistent) +{ + php_stream * stream; + php_netstream_data_t * sock; + + sock = pemalloc(sizeof(php_netstream_data_t), persistent); + memset(sock, 0, sizeof(php_netstream_data_t)); + + sock->is_blocked = 1; + sock->chunk_size = PHP_SOCK_CHUNK_SIZE; + sock->timeout.tv_sec = -1; + sock->socket = socket; + + stream = php_stream_alloc(&php_stream_socket_ops, sock, persistent, "r+"); + + if (stream == NULL) + pefree(sock, persistent); + + return stream; +} + +PHPAPI php_stream * php_stream_sock_open_host(const char * host, unsigned short port, + int socktype, int timeout, int persistent) +{ + int socket; + + socket = php_hostconnect(host, port, socktype, timeout); + + if (socket == -1) + return NULL; + + return php_stream_sock_open_from_socket(socket, persistent); +} + +PHPAPI php_stream * php_stream_sock_open_unix(const char * path, int persistent, struct timeval * timeout) +{ +#if defined(AF_UNIX) + int socketd; + struct sockaddr_un unix_addr; + + socketd = socket(PF_UNIX, SOCK_STREAM, 0); + if (socketd == SOCK_ERR) + return NULL; + + memset(&unix_addr, 0, sizeof(unix_addr)); + unix_addr.sun_family = AF_UNIX; + strlcpy(unix_addr.sun_path, path, sizeof(unix_addr.sun_path)); + + if (php_connect_nonb(socketd, (struct sockaddr *) &unix_addr, sizeof(unix_addr), timeout) == SOCK_CONN_ERR) + return NULL; + + return php_stream_sock_open_from_socket(socketd, persistent); +#else + return NULL; +#endif +} + +#if HAVE_OPENSSL_EXT +PHPAPI int php_stream_sock_ssl_activate_with_method(php_stream * stream, int activate, SSL_METHOD * method) +{ + php_netstream_data_t * sock = (php_netstream_data_t*)stream->abstract; + SSL_CTX * ctx = NULL; + + if (!php_stream_is(stream, PHP_STREAM_IS_SOCKET)) + return FAILURE; + + if (activate == sock->ssl_active) + return SUCCESS; /* already in desired mode */ + + if (activate && sock->ssl_handle == NULL) { + ctx = SSL_CTX_new(method); + if (ctx == NULL) + return FAILURE; + + sock->ssl_handle = SSL_new(ctx); + if (sock->ssl_handle == NULL) { + SSL_CTX_free(ctx); + return FAILURE; + } + + SSL_set_fd(sock->ssl_handle, sock->socket); + } + + if (activate) { + if (SSL_connect(sock->ssl_handle) <= 0) { + SSL_shutdown(sock->ssl_handle); + return FAILURE; + } + sock->ssl_active = activate; + } + else { + SSL_shutdown(sock->ssl_handle); + sock->ssl_active = 0; + } + return SUCCESS; +} +#endif + +PHPAPI void php_stream_sock_set_timeout(php_stream * stream, struct timeval *timeout) +{ + php_netstream_data_t * sock = (php_netstream_data_t*)stream->abstract; + + if (!php_stream_is(stream, PHP_STREAM_IS_SOCKET)) + return; + + sock->timeout = *timeout; + sock->timeout_event = 0; +} + +PHPAPI int php_stream_sock_set_blocking(php_stream * stream, int mode) +{ + int oldmode; + php_netstream_data_t * sock = (php_netstream_data_t*)stream->abstract; + + if (!php_stream_is(stream, PHP_STREAM_IS_SOCKET)) + return 0; + + oldmode = sock->is_blocked; + sock->is_blocked = mode; + + return oldmode; +} + +PHPAPI size_t php_stream_sock_set_chunk_size(php_stream * stream, size_t size) +{ + size_t oldsize; + php_netstream_data_t * sock = (php_netstream_data_t*)stream->abstract; + + if (!php_stream_is(stream, PHP_STREAM_IS_SOCKET)) + return 0; + + oldsize = sock->chunk_size; + sock->chunk_size = size; + + return oldsize; +} + +#define TOREAD(sock) ((sock)->writepos - (sock)->readpos) +#define READPTR(sock) ((sock)->readbuf + (sock)->readpos) +#define WRITEPTR(sock) ((sock)->readbuf + (sock)->writepos) + +static size_t php_sockop_write(php_stream * stream, const char * buf, size_t count) +{ + php_netstream_data_t * sock = (php_netstream_data_t*)stream->abstract; +#if HAVE_OPENSSL_EXT + if (sock->ssl_active) + return SSL_write(sock->ssl_handle, buf, count); +#endif + return send(sock->socket, buf, count, 0); +} +static void php_sock_stream_wait_for_data(php_stream * stream, php_netstream_data_t * sock) +{ + fd_set fdr, tfdr; + int retval; + struct timeval timeout, *ptimeout; + + FD_ZERO(&fdr); + FD_SET(sock->socket, &fdr); + sock->timeout_event = 0; + + if (sock->timeout.tv_sec == -1) + ptimeout = NULL; + else + ptimeout = &timeout; + + while(1) { + tfdr = fdr; + timeout = sock->timeout; + + retval = select(sock->socket + 1, &tfdr, NULL, NULL, ptimeout); + + if (retval == 0) + sock->timeout_event = 1; + + if (retval >= 0) + break; + } +} + +static size_t php_sock_stream_read_internal(php_stream * stream, php_netstream_data_t * sock) +{ + char buf[PHP_SOCK_CHUNK_SIZE]; + int nr_bytes; + size_t nr_read = 0; + + /* For blocking sockets, we wait until there is some + data to read (real data or EOF) + + Otherwise, recv() may time out and return 0 and + therefore sock->eof would be set errornously. + */ + + + if(sock->is_blocked) { + php_sock_stream_wait_for_data(stream, sock); + if (sock->timeout_event) + return 0; + } + + /* read at a maximum sock->chunk_size */ +#if HAVE_OPENSSL_EXT + if (sock->ssl_active) + nr_bytes = SSL_read(sock->ssl_handle, buf, sock->chunk_size); + else +#endif + nr_bytes = recv(sock->socket, buf, sock->chunk_size, 0); + if(nr_bytes > 0) { + if(sock->writepos + nr_bytes > sock->readbuflen) { + sock->readbuflen += sock->chunk_size; + sock->readbuf = perealloc(sock->readbuf, sock->readbuflen, + php_stream_is_persistent(stream)); + } + memcpy(WRITEPTR(sock), buf, nr_bytes); + sock->writepos += nr_bytes; + nr_read = nr_bytes; + } else if(nr_bytes == 0 || (nr_bytes < 0 && errno != EWOULDBLOCK)) { + sock->eof = 1; + } + + return nr_read; + +} + +static size_t php_sock_stream_read(php_stream * stream, php_netstream_data_t * sock) +{ + size_t nr_bytes; + size_t nr_read = 0; + int i; + + for(i = 0; !sock->eof && i < MAX_CHUNKS_PER_READ; i++) { + nr_bytes = php_sock_stream_read_internal(stream, sock); + if(nr_bytes == 0) break; + nr_read += nr_bytes; + } + + return nr_read; +} + +static size_t php_sockop_read(php_stream * stream, char * buf, size_t count) +{ + php_netstream_data_t * sock = (php_netstream_data_t*)stream->abstract; + size_t ret = 0; + + if (sock->is_blocked) { + while(!sock->eof && TOREAD(sock) < count && !sock->timeout_event) + php_sock_stream_read_internal(stream, sock); + } + else + php_sock_stream_read(stream, sock); + + if(count < 0) + return ret; + + ret = MIN(TOREAD(sock), count); + if (ret) { + memcpy(buf, READPTR(sock), ret); + sock->readpos += ret; + } + + return ret; +} + +static int php_sockop_close(php_stream * stream) +{ + php_netstream_data_t * sock = (php_netstream_data_t*)stream->abstract; + +#if HAVE_OPENSSL_EXT + if (sock->ssl_active) { + SSL_shutdown(sock->ssl_handle); + sock->ssl_active = 0; + SSL_free(sock->ssl_handle); + sock->ssl_handle = NULL; + } +#endif + + shutdown(sock->socket, 0); + closesocket(sock->socket); + + if (sock->readbuf) + pefree(sock->readbuf, php_stream_is_persistent(stream)); + + pefree(sock, php_stream_is_persistent(stream)); + + return 0; +} + +static int php_sockop_flush(php_stream * stream) +{ + php_netstream_data_t * sock = (php_netstream_data_t*)stream->abstract; + return fsync(sock->socket); +} + +static int php_sockop_cast(php_stream * stream, int castas, void ** ret) +{ + php_netstream_data_t * sock = (php_netstream_data_t*)stream->abstract; + TSRMLS_FETCH(); + + switch(castas) { + case PHP_STREAM_AS_STDIO: +#if HAVE_OPENSSL_EXT + if (sock->ssl_active) + return FAILURE; +#endif + if (ret) { + /* DANGER!: data buffered in stream->readbuf will be forgotten! */ + if (TOREAD(sock) > 0) + zend_error(E_WARNING, "%s(): buffered data lost during conversion to FILE*!", get_active_function_name(TSRMLS_C)); + *ret = fdopen(sock->socket, stream->mode); + if (*ret) + return SUCCESS; + return FAILURE; + } + return SUCCESS; + case PHP_STREAM_AS_FD: + case PHP_STREAM_AS_SOCKETD: +#if HAVE_OPENSSL_EXT + if (sock->ssl_active) + return FAILURE; +#endif + if (ret) + *ret = (void*)sock->socket; + return SUCCESS; + default: + return FAILURE; + } +} + +#define SEARCHCR() do { \ + if (TOREAD(sock)) { \ + for (p = READPTR(sock), pe = p + MIN(TOREAD(sock), maxlen); \ + *p != '\n'; ) \ + if (++p >= pe) { \ + p = NULL; \ + break; \ + } \ + } else \ + p = NULL; \ +} while (0) + + +static char * php_sockop_gets(php_stream * stream, char *buf, size_t maxlen) +{ + php_netstream_data_t * sock = (php_netstream_data_t*)stream->abstract; + char *p = NULL, *pe; + char *ret = NULL; + size_t amount = 0; + + if (maxlen==0) { + buf[0] = 0; + return buf; + } + + SEARCHCR(); + + if(!p) { + if(sock->is_blocked) { + while(!p && !sock->eof && !sock->timeout_event && TOREAD(sock) < maxlen) { + php_sock_stream_read_internal(stream, sock); + SEARCHCR(); + } + } else { + php_sock_stream_read(stream, sock); + SEARCHCR(); + } + } + + if(p) { + amount = (ptrdiff_t) p - (ptrdiff_t) READPTR(sock) + 1; + } else { + amount = TOREAD(sock); + } + + amount = MIN(amount, maxlen); + + if(amount > 0) { + memcpy(buf, READPTR(sock), amount); + sock->readpos += amount; + } + buf[amount] = '\0'; + + /* signal error only, if we don't return data from this call and + if there is no data to read and if the eof flag is set */ + if(amount || TOREAD(sock) || !sock->eof) { + ret = buf; + } + + return ret; +} + +php_stream_ops php_stream_socket_ops = { + php_sockop_write, php_sockop_read, + php_sockop_close, php_sockop_flush, + NULL, php_sockop_gets, + php_sockop_cast, + "socket" +}; + + + + /* * Local variables: * tab-width: 8 diff --git a/main/php_network.h b/main/php_network.h index b71737265f..b87e709fff 100644 --- a/main/php_network.h +++ b/main/php_network.h @@ -27,8 +27,17 @@ # undef FD_SETSIZE # include "arpa/inet.h" # define socklen_t unsigned int +#else +# undef closesocket +# define closesocket close +#endif + +#ifndef HAVE_SHUTDOWN +#undef shutdown +#define shutdown(s,n) /* nothing */ #endif + #ifdef HAVE_NETINET_IN_H # include <netinet/in.h> #endif @@ -41,6 +50,10 @@ #include <sys/time.h> #endif +#if HAVE_OPENSSL_EXT +#include <openssl/ssl.h> +#endif + #ifdef HAVE_SOCKADDR_STORAGE typedef struct sockaddr_storage php_sockaddr_storage; #else @@ -56,6 +69,47 @@ PHPAPI int php_connect_nonb(int sockfd, const struct sockaddr *addr, socklen_t a void php_any_addr(int family, php_sockaddr_storage *addr, unsigned short port); int php_sockaddr_size(php_sockaddr_storage *addr); +struct _php_netstream_data_t { + int socket; + unsigned char *readbuf; + size_t readbuflen; + size_t readpos; + size_t writepos; + char eof; + char is_blocked; + size_t chunk_size; + struct timeval timeout; + char timeout_event; +#if HAVE_OPENSSL_EXT + /* openssl specific bits here */ + SSL * ssl_handle; + int ssl_active; +#endif +}; +typedef struct _php_netstream_data_t php_netstream_data_t; + +#define PHP_NETSTREAM_DATA_FROM_STREAM(stream) (php_netstream_data_t*)(stream)->abstract + +extern php_stream_ops php_stream_socket_ops; +#define PHP_STREAM_IS_SOCKET (&php_stream_socket_ops) + +PHPAPI php_stream * php_stream_sock_open_from_socket(int socket, int persistent); +/* open a connection to a host using php_hostconnect and return a stream */ +PHPAPI php_stream * php_stream_sock_open_host(const char * host, unsigned short port, + int socktype, int timeout, int persistent); +PHPAPI php_stream * php_stream_sock_open_unix(const char * path, int persistent, struct timeval * timeout); + +PHPAPI void php_stream_sock_set_timeout(php_stream * stream, struct timeval *timeout); +PHPAPI int php_stream_sock_set_blocking(php_stream * stream, int mode); +/* set the chunk size for the stream; return the old chunk size */ +PHPAPI size_t php_stream_sock_set_chunk_size(php_stream * stream, size_t size); + +#if HAVE_OPENSSL_EXT +PHPAPI int php_stream_sock_ssl_activate_with_method(php_stream * stream, int activate, SSL_METHOD * method); +#define php_stream_sock_ssl_activate(stream, activate) php_stream_sock_ssl_activate_with_method((stream), (activate), SSLv23_client_method()) + +#endif + #endif /* _PHP_NETWORK_H */ /* diff --git a/main/php_streams.h b/main/php_streams.h index 6acaafb3fc..39b8e70a46 100755 --- a/main/php_streams.h +++ b/main/php_streams.h @@ -19,8 +19,6 @@ #ifndef PHP_STREAMS_H #define PHP_STREAMS_H -#if HAVE_PHP_STREAM - #ifdef HAVE_SYS_TIME_H #include <sys/time.h> #endif @@ -42,9 +40,23 @@ typedef struct _php_stream_ops { const char * label; /* label for this ops structure */ } php_stream_ops; +/* options uses the IGNORE_URL family of defines from fopen_wrappers.h */ +typedef php_stream * (*php_stream_factory_func_t)(char * filename, char * mode, int options, char ** opened_path TSRMLS_DC); +typedef void (*php_stream_wrapper_dtor_func_t)(php_stream * stream); + +typedef struct _php_stream_wrapper { + php_stream_factory_func_t create; + php_stream_wrapper_dtor_func_t destroy; +} php_stream_wrapper; + struct _php_stream { php_stream_ops * ops; void * abstract; /* convenience pointer for abstraction */ + + php_stream_wrapper * wrapper; /* which wrapper was used to open the stream */ + void * wrapperthis; /* convenience pointer for a instance of a wrapper */ + zval * wrapperdata; /* fgetwrapperdata retrieves this */ + int is_persistent; char mode[16]; /* "rwb" etc. ala stdio */ /* so we know how to clean it up correctly. This should be set to @@ -68,15 +80,34 @@ PHPAPI int php_stream_seek(php_stream * stream, off_t offset, int whence); PHPAPI off_t php_stream_tell(php_stream * stream); PHPAPI size_t php_stream_read(php_stream * stream, char * buf, size_t count); PHPAPI size_t php_stream_write(php_stream * stream, const char * buf, size_t count); +#define php_stream_write_string(stream, str) php_stream_write(stream, str, strlen(str)) PHPAPI int php_stream_eof(php_stream * stream); PHPAPI int php_stream_getc(php_stream * stream); +PHPAPI int php_stream_putc(php_stream * stream, int c); PHPAPI int php_stream_flush(php_stream * stream); PHPAPI char *php_stream_gets(php_stream * stream, char *buf, size_t maxlen); +PHPAPI int php_stream_puts(php_stream * stream, char * buf); -/* operations for a stdio FILE; the FILE * must be placed in stream->abstract */ +/* copy up to maxlen bytes from src to dest. If maxlen is 0, copy until eof(src). + * Uses mmap if the src is a plain file and at offset 0 */ +PHPAPI size_t php_stream_copy_to_stream(php_stream * src, php_stream * dest, size_t maxlen); +/* read all data from stream and put into a buffer. Caller must free buffer when done, + * according to allocopts. + * The copy will use mmap if available. */ +PHPAPI size_t php_stream_read_all(php_stream * src, char ** buf, int persistent); + +/* maybe implement someday */ +#define php_stream_error(stream) (0) + +/* operations for a stdio FILE; use the php_stream_fopen_XXX funcs below */ extern php_stream_ops php_stream_stdio_ops; /* like fopen, but returns a stream */ -PHPAPI php_stream * php_stream_fopen(const char * filename, const char * mode); +PHPAPI php_stream * php_stream_fopen(const char * filename, const char * mode, char **opened_path TSRMLS_DC); +PHPAPI php_stream * php_stream_fopen_with_path(char * filename, char * mode, char * path, char **opened_path TSRMLS_DC); +PHPAPI php_stream * php_stream_fopen_from_file(FILE * file, const char * mode); +PHPAPI php_stream * php_stream_fopen_from_pipe(FILE * file, const char * mode); +PHPAPI php_stream * php_stream_fopen_tmpfile(void); +PHPAPI php_stream * php_stream_fopen_temporary_file(const char * dir, const char * pfx, char **opened_path TSRMLS_DC); /* coerce the stream into some other form */ /* cast as a stdio FILE * */ @@ -86,15 +117,27 @@ PHPAPI php_stream * php_stream_fopen(const char * filename, const char * mode); /* cast as a socketd */ #define PHP_STREAM_AS_SOCKETD 2 +/* try really, really hard to make sure the cast happens (socketpair) */ +#define PHP_STREAM_CAST_TRY_HARD 0x80000000 + PHPAPI int php_stream_cast(php_stream * stream, int castas, void ** ret, int show_err); /* use this to check if a stream can be cast into another form */ #define php_stream_can_cast(stream, as) php_stream_cast(stream, as, NULL, 0) /* use this to check if a stream is of a particular type: * PHPAPI int php_stream_is(php_stream * stream, php_stream_ops * ops); */ -#define php_stream_is(stream, anops) (stream->ops == anops) +#define php_stream_is(stream, anops) ((stream)->ops == anops) +#define PHP_STREAM_IS_STDIO &php_stream_stdio_ops + +#define php_stream_is_persistent(stream) (stream)->is_persistent + +/* Wrappers support */ +int php_init_stream_wrappers(TSRMLS_D); +int php_shutdown_stream_wrappers(TSRMLS_D); +PHPAPI int php_register_url_stream_wrapper(char * protocol, php_stream_wrapper * wrapper TSRMLS_DC); +PHPAPI int php_unregister_url_stream_wrapper(char * protocol TSRMLS_DC); -#endif /* HAVE_PHP_STREAM */ +PHPAPI php_stream * php_stream_open_wrapper(char * path, char * mode, int options, char ** opened_path TSRMLS_DC); #endif diff --git a/main/streams.c b/main/streams.c index b79d04a6d4..58e6ba44bb 100755 --- a/main/streams.c +++ b/main/streams.c @@ -12,14 +12,28 @@ | obtain it through the world-wide-web, please send a note to | | license@php.net so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Wez Furlong (wez@thebrainroom.com) | + | Authors: | + | Wez Furlong (wez@thebrainroom.com) | + | Borrowed code from: | + | Rasmus Lerdorf <rasmus@lerdorf.on.ca> | + | Jim Winstead <jimw@php.net> | +----------------------------------------------------------------------+ */ #define _GNU_SOURCE #include "php.h" +#include "php_globals.h" +#include "php_network.h" +#include "php_open_temporary_file.h" + +#ifdef HAVE_SYS_MMAN_H +#include <sys/mman.h> +#endif + +#ifndef MAP_FAILED +#define MAP_FAILED ((void *) -1) +#endif -#if HAVE_PHP_STREAM #ifdef PHP_WIN32 #define EWOULDBLOCK WSAEWOULDBLOCK @@ -27,18 +41,10 @@ #include "build-defs.h" #endif -#define MAX_CHUNK_SIZE 8192 - -#define TOREAD(stream) ((stream)->readbuf.writepos - (stream)->readbuf.readpos) -#define TOWRITE(stream) ((stream)->readbuf.writepos - (stream)->readbuf.readpos) - -#define READPTR(stream) ((stream)->readbuf.buffer + (stream)->readbuf.readpos) -#define WRITEPTR(stream) ((stream)->readbuf.buffer + (stream)->readbuf.writepos) - -#define READ_MAX(stream, max) if (stream->is_blocked) stream_read_total(sock, max); else stream_readahead(sock) +static HashTable url_stream_wrappers_hash; /* allocate a new stream for a particular ops */ -PHPAPI php_stream * php_stream_alloc(php_stream_ops * ops, void * abstract, int persistent, const char * mode) +PHPAPI php_stream * php_stream_alloc(php_stream_ops * ops, void * abstract, int persistent, const char * mode) /* {{{ */ { php_stream * ret; @@ -54,11 +60,18 @@ PHPAPI php_stream * php_stream_alloc(php_stream_ops * ops, void * abstract, int return ret; } +/* }}} */ -PHPAPI int php_stream_free(php_stream * stream, int call_dtor) +PHPAPI int php_stream_free(php_stream * stream, int call_dtor) /* {{{ */ { int ret = 1; + php_stream_flush(stream); + + if (stream->wrapper && stream->wrapper->destroy) { + stream->wrapper->destroy(stream); + } + if (call_dtor) { if (stream->fclose_stdiocast == PHP_STREAM_FCLOSE_FOPENCOOKIE) { @@ -82,11 +95,17 @@ PHPAPI int php_stream_free(php_stream * stream, int call_dtor) stream->stdiocast = NULL; } } + + if (stream->wrapperdata) { + FREE_ZVAL(stream->wrapperdata); + } pefree(stream, stream->is_persistent); return ret; } +/* }}} */ +/* {{{ generic stream operations */ PHPAPI size_t php_stream_read(php_stream * stream, char * buf, size_t size) { return stream->ops->read(stream, buf, size); @@ -100,6 +119,14 @@ PHPAPI int php_stream_eof(php_stream * stream) return stream->ops->read(stream, NULL, 0) == EOF ? 1 : 0; } +PHPAPI int php_stream_putc(php_stream * stream, int c) +{ + unsigned char buf = c; + if (php_stream_write(stream, &buf, 1) > 0) + return 1; + return EOF; +} + PHPAPI int php_stream_getc(php_stream * stream) { char buf; @@ -109,6 +136,17 @@ PHPAPI int php_stream_getc(php_stream * stream) return EOF; } +PHPAPI int php_stream_puts(php_stream * stream, char * buf) +{ + int len; + char newline[2] = "\n"; /* is this OK for Win? */ + len = strlen(buf); + + if (len > 0 && php_stream_write(stream, buf, len) && php_stream_write(stream, newline, 1)) + return 1; + return 0; +} + PHPAPI char *php_stream_gets(php_stream * stream, char *buf, size_t maxlen) { @@ -142,18 +180,13 @@ PHPAPI char *php_stream_gets(php_stream * stream, char *buf, size_t maxlen) PHPAPI int php_stream_flush(php_stream * stream) { - return stream->ops->flush(stream); + if (stream->ops->flush) + return stream->ops->flush(stream); + return 0; } PHPAPI size_t php_stream_write(php_stream * stream, const char * buf, size_t count) { - if (strchr(stream->mode, 'w') == NULL) { - TSRMLS_FETCH(); - - zend_error(E_WARNING, "%s(): stream was not opened for writing", get_active_function_name(TSRMLS_C)); - return 0; - } - return stream->ops->write(stream, buf, count); } @@ -171,60 +204,310 @@ PHPAPI int php_stream_seek(php_stream * stream, off_t offset, int whence) if (stream->ops->seek) return stream->ops->seek(stream, offset, whence); + /* emulate forward moving seeks with reads */ + if (whence == SEEK_CUR && offset > 0) { + while(offset-- > 0) + if (php_stream_getc(stream) == EOF) + return -1; + return 0; + } + zend_error(E_WARNING, "streams of type %s do not support seeking", stream->ops->label); return -1; } -/*------- STDIO stream implementation -------*/ +#define CHUNK_SIZE 8192 + +PHPAPI size_t php_stream_read_all(php_stream * src, char ** buf, int persistent) +{ + size_t ret = 0; + char * ptr; + size_t len = 0, max_len; + int step = CHUNK_SIZE; + int min_room = CHUNK_SIZE / 4; +#if HAVE_MMAP + int srcfd; +#endif + +#if HAVE_MMAP + /* try and optimize the case where we are copying from the start of a plain file. + * We could probably make this work in more situations, but I don't trust the stdio + * buffering layer. + * */ + if ( php_stream_is(src, PHP_STREAM_IS_STDIO) && + php_stream_tell(src) == 0 && + SUCCESS == php_stream_cast(src, PHP_STREAM_AS_FD, (void**)&srcfd, 0)) + { + struct stat sbuf; + + if (fstat(srcfd, &sbuf) == 0) { + void * srcfile; + + srcfile = mmap(NULL, sbuf.st_size, PROT_READ, MAP_SHARED, srcfd, 0); + if (srcfile != (void*)MAP_FAILED) { + + *buf = pemalloc(persistent, sbuf.st_size); + + if (*buf) { + memcpy(*buf, srcfile, sbuf.st_size); + ret = sbuf.st_size; + } + + munmap(srcfile, sbuf.st_size); + return ret; + } + } + /* fall through - we might be able to copy in smaller chunks */ + } +#endif + + ptr = *buf = pemalloc(persistent, step); + max_len = step; + + while((ret = php_stream_read(src, ptr, max_len - len))) { + len += ret; + if (len + min_room >= max_len) { + *buf = perealloc(*buf, max_len + step, persistent); + max_len += step; + ptr = *buf + len; + } + } + if (len) { + *buf = perealloc(*buf, len, persistent); + } + else { + pefree(*buf, persistent); + *buf = NULL; + } + return len; +} + +PHPAPI size_t php_stream_copy_to_stream(php_stream * src, php_stream * dest, size_t maxlen) +{ + char buf[CHUNK_SIZE]; + size_t readchunk; + size_t haveread = 0; + size_t didread; +#if HAVE_MMAP + int srcfd; +#endif + +#if HAVE_MMAP + /* try and optimize the case where we are copying from the start of a plain file. + * We could probably make this work in more situations, but I don't trust the stdio + * buffering layer. + * */ + if ( php_stream_is(src, PHP_STREAM_IS_STDIO) && + php_stream_tell(src) == 0 && + SUCCESS == php_stream_cast(src, PHP_STREAM_AS_FD, (void**)&srcfd, 0)) + { + struct stat sbuf; + + if (fstat(srcfd, &sbuf) == 0) { + void * srcfile; + + srcfile = mmap(NULL, sbuf.st_size, PROT_READ, MAP_SHARED, srcfd, 0); + if (srcfile != (void*)MAP_FAILED) { + haveread = php_stream_write(dest, srcfile, sbuf.st_size); + munmap(srcfile, sbuf.st_size); + return haveread; + } + } + /* fall through - we might be able to copy in smaller chunks */ + } +#endif + + while(1) { + readchunk = sizeof(buf); + + if (maxlen && (maxlen - haveread) < readchunk) + readchunk = maxlen - haveread; + + didread = php_stream_read(src, buf, readchunk); + if (didread) { + /* extra paranoid */ + size_t didwrite, towrite; + char * writeptr; + + towrite = didread; + writeptr = buf; + haveread += didread; + + while(towrite) { + didwrite = php_stream_write(dest, writeptr, towrite); + + if (didwrite == 0) + return 0; /* error */ + + towrite -= didwrite; + writeptr += didwrite; + } + } + else + return 0; /* error */ + + if (maxlen - haveread == 0) + break; + } + + return haveread; + +} +/* }}} */ + + + +/* {{{ ------- STDIO stream implementation -------*/ + +typedef struct { + FILE * file; + int is_pipe; /* use pclose */ +#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 TSRMLS_DC) +{ + FILE * fp = php_open_temporary_file(dir, pfx, opened_path); + + if (fp) { + php_stream * stream = php_stream_fopen_from_file(fp, "wb"); + if (stream) + return stream; + fclose(fp); + + zend_error(E_WARNING, "%s(): unable to allocate stream", get_active_function_name(TSRMLS_C)); + + return NULL; + } + return NULL; +} + +PHPAPI php_stream * php_stream_fopen_tmpfile(void) +{ + FILE * fp; + php_stream * stream; + + fp = tmpfile(); + if (fp == NULL) { + zend_error(E_WARNING, "tmpfile(): %s", strerror(errno)); + return NULL; + } + stream = php_stream_fopen_from_file(fp, "r+"); + if (stream == NULL) { + zend_error(E_WARNING, "tmpfile(): %s", strerror(errno)); + fclose(fp); + return NULL; + } + return stream; +} + + +PHPAPI php_stream * php_stream_fopen_from_file(FILE * file, const char * mode) +{ + php_stdio_stream_data * self; + + self = emalloc(sizeof(*self)); + self->file = file; + self->is_pipe = 0; + return php_stream_alloc(&php_stream_stdio_ops, self, 0, mode); +} + +PHPAPI php_stream * php_stream_fopen_from_pipe(FILE * file, const char * mode) +{ + php_stdio_stream_data * self; + + self = emalloc(sizeof(*self)); + self->file = file; + self->is_pipe = 1; + return php_stream_alloc(&php_stream_stdio_ops, self, 0, mode); +} static size_t php_stdiop_write(php_stream * stream, const char * buf, size_t count) { - return fwrite(buf, 1, count, (FILE*)stream->abstract); + php_stdio_stream_data * data = (php_stdio_stream_data*)stream->abstract; + +#if HAVE_FLUSHIO + if (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) { + php_stdio_stream_data * data = (php_stdio_stream_data*)stream->abstract; + if (buf == NULL && count == 0) { /* check for EOF condition */ - if (feof((FILE*)stream->abstract)) { + if (feof(data->file)) { return EOF; } return 0; } - return fread(buf, 1, count, (FILE*)stream->abstract); + +#if HAVE_FLUSHIO + if (data->last_op == 'w') + fseek(data->file, 0, SEEK_CUR); + data->last_op = 'r'; +#endif + + return fread(buf, 1, count, data->file); } static int php_stdiop_close(php_stream * stream) { - return fclose((FILE*)stream->abstract); + int ret; + php_stdio_stream_data * data = (php_stdio_stream_data*)stream->abstract; + + if (data->is_pipe) + ret = pclose(data->file); + else + ret = fclose(data->file); + + efree(data); + + return ret; } static int php_stdiop_flush(php_stream * stream) { - return fflush((FILE*)stream->abstract); + return fflush(((php_stdio_stream_data*)stream->abstract)->file); } static int php_stdiop_seek(php_stream * stream, off_t offset, int whence) { - return fseek((FILE*)stream->abstract, offset, whence); + return fseek(((php_stdio_stream_data*)stream->abstract)->file, offset, whence); } static char * php_stdiop_gets(php_stream * stream, char * buf, size_t size) { - return fgets(buf, size, (FILE*)stream->abstract); + php_stdio_stream_data * data = (php_stdio_stream_data*)stream->abstract; + +#if HAVE_FLUSHIO + if (data->last_op == 'w') + fseek(data->file, 0, SEEK_CUR); + data->last_op = 'r'; +#endif + + return fgets(buf, size, data->file); } static int php_stdiop_cast(php_stream * stream, int castas, void ** ret) { int fd; + php_stdio_stream_data * data = (php_stdio_stream_data*)stream->abstract; switch (castas) { case PHP_STREAM_AS_STDIO: if (ret) - *ret = stream->abstract; + *ret = data->file; return SUCCESS; case PHP_STREAM_AS_FD: - fd = fileno((FILE*)stream->abstract); + fd = fileno(data->file); if (fd < 0) return FAILURE; if (ret) @@ -242,21 +525,153 @@ php_stream_ops php_stream_stdio_ops = { "STDIO" }; -PHPAPI php_stream * php_stream_fopen(const char * filename, const char * mode) +PHPAPI php_stream * php_stream_fopen_with_path(char * filename, char * mode, char * path, char **opened_path 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 == '.') { + if (PG(safe_mode) && (!php_checkuid(filename, mode, CHECKUID_CHECK_MODE_PARAM))) { + return NULL; + } + return php_stream_fopen(filename, mode, opened_path TSRMLS_CC); + } + + /* + * files in safe_mode_include_dir (or subdir) are excluded from + * safe mode GID/UID checks + */ + + /* Absolute path open */ + if (IS_ABSOLUTE_PATH(filename, filename_length)) { + if ((php_check_safe_mode_include_dir(filename TSRMLS_CC)) == 0) + /* filename is in safe_mode_include_dir (or subdir) */ + return php_stream_fopen(filename, mode, opened_path TSRMLS_CC); + + if (PG(safe_mode) && (!php_checkuid(filename, mode, CHECKUID_CHECK_MODE_PARAM))) + return NULL; + + return php_stream_fopen(filename, mode, opened_path TSRMLS_CC); + } + + if (!path || (path && !*path)) { + if (PG(safe_mode) && (!php_checkuid(filename, mode, CHECKUID_CHECK_MODE_PARAM))) { + return NULL; + } + return php_stream_fopen(filename, mode, opened_path TSRMLS_CC); + } + + /* 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 (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(trypath, mode, opened_path TSRMLS_CC); + else + stream = NULL; + + efree(pathbuf); + return stream; + } + } + stream = php_stream_fopen(trypath, mode, opened_path TSRMLS_CC); + if (stream) { + efree(pathbuf); + return stream; + } + ptr = end; + } /* end provided path */ + + efree(pathbuf); + return NULL; + +} +/* }}} */ + +PHPAPI php_stream * php_stream_fopen(const char * filename, const char * mode, char **opened_path TSRMLS_DC) { - FILE * fp = fopen(filename, mode); + FILE * fp; + char * realpath; + + realpath = expand_filepath(filename, NULL TSRMLS_C); + + fp = fopen(realpath, mode); if (fp) { - php_stream * ret = php_stream_alloc(&php_stream_stdio_ops, fp, 0, mode); + php_stream * ret = php_stream_fopen_from_file(fp, mode); + + if (ret) { + if (opened_path) { + *opened_path = realpath; + realpath = NULL; + } + if (realpath) + efree(realpath); - if (ret) return ret; + } fclose(fp); } + efree(realpath); return NULL; } +/* }}} */ +/* {{{ STDIO with fopencookie */ #if HAVE_FOPENCOOKIE static ssize_t stream_cookie_reader(void *cookie, char *buffer, size_t size) { @@ -286,9 +701,14 @@ static COOKIE_IO_FUNCTIONS_T stream_cookie_functions = #else /* TODO: use socketpair() to emulate fopencookie, as suggested by Hartmut ? */ #endif +/* }}} */ -PHPAPI int php_stream_cast(php_stream * stream, int castas, void ** ret, int show_err) +PHPAPI int php_stream_cast(php_stream * stream, int castas, void ** ret, int show_err) /* {{{ */ { + + /* trying hard is not yet implemented */ + castas &= ~PHP_STREAM_CAST_TRY_HARD; + if (castas == PHP_STREAM_AS_STDIO) { if (stream->stdiocast) { if (ret) @@ -354,9 +774,110 @@ exit_success: return SUCCESS; +} /* }}} */ + +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; +} + +int php_shutdown_stream_wrappers(TSRMLS_D) +{ + if (PG(allow_url_fopen)) + zend_hash_destroy(&url_stream_wrappers_hash); + return SUCCESS; } -#endif +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; +} +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; +} + +static php_stream * php_stream_open_url(char * path, char * mode, int options, char ** opened_path TSRMLS_DC) +{ + php_stream_wrapper * wrapper; + const char * p, *protocol = NULL; + int n = 0; + + for (p = path; isalnum((int)*p); p++) + n++; + + if ((*p == ':') && (n > 1)) + protocol = path; + + if (protocol) { + if (FAILURE == zend_hash_find(&url_stream_wrappers_hash, (char*)protocol, n, (void**)&wrapper)) { + wrapper = NULL; + protocol = NULL; + } + if (wrapper) { + php_stream * stream = wrapper->create(path, mode, options, opened_path 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; + + /* fall back on regular file access */ + return php_stream_open_wrapper(path, mode, (options & ~REPORT_ERRORS) | IGNORE_URL, + opened_path TSRMLS_CC); + } + return NULL; +} + +PHPAPI php_stream * php_stream_open_wrapper(char * path, char * mode, int options, char ** opened_path TSRMLS_DC) +{ + php_stream * stream = NULL; + + 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 TSRMLS_CC); + goto out; + } + + if ((options & USE_PATH) && PG(include_path) != NULL) { + stream = php_stream_fopen_with_path(path, mode, PG(include_path), opened_path TSRMLS_CC); + goto out; + } + + if ((options & ENFORCE_SAFE_MODE) && PG(safe_mode) && (!php_checkuid(path, mode, CHECKUID_CHECK_MODE_PARAM))) + return NULL; + + stream = php_stream_fopen(path, mode, opened_path TSRMLS_CC); +out: + if (stream == NULL && (options & REPORT_ERRORS)) { + char * tmp = estrdup(path); + php_strip_url_passwd(tmp); + zend_error(E_WARNING, "%s(\"%s\") - %s", get_active_function_name(TSRMLS_CC), tmp, strerror(errno)); + efree(tmp); + } + return stream; +} + + /* * Local variables: * tab-width: 4 |
