diff options
Diffstat (limited to 'main/user_streams.c')
-rw-r--r-- | main/user_streams.c | 484 |
1 files changed, 484 insertions, 0 deletions
diff --git a/main/user_streams.c b/main/user_streams.c new file mode 100644 index 0000000000..164639e1fd --- /dev/null +++ b/main/user_streams.c @@ -0,0 +1,484 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 4 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2002 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) | + +----------------------------------------------------------------------+ + */ + +#include "php.h" +#include "php_globals.h" + +static int le_protocols; + +struct php_user_stream_wrapper { + char * protoname; + char * classname; + zend_class_entry *ce; + php_stream_wrapper wrapper; +}; + +static php_stream *user_wrapper_factory(char *filename, char *mode, int options, char **opened_path, void * wrappercontext STREAMS_DC TSRMLS_DC); + +static void stream_wrapper_dtor(zend_rsrc_list_entry *rsrc TSRMLS_DC) +{ + struct php_user_stream_wrapper * uwrap = (struct php_user_stream_wrapper*)rsrc->ptr; + + php_unregister_url_stream_wrapper(uwrap->protoname TSRMLS_CC); + efree(uwrap->protoname); + efree(uwrap->classname); + efree(uwrap); +} + +int php_init_user_streams(TSRMLS_D) +{ + le_protocols = zend_register_list_destructors_ex(stream_wrapper_dtor, NULL, "stream factory", 0); + return le_protocols == FAILURE ? FAILURE : SUCCESS; +} + +struct _php_userstream_data { + struct php_user_stream_wrapper * wrapper; + zval * object; +}; +typedef struct _php_userstream_data php_userstream_data_t; + +/* names of methods */ +#define USERSTREAM_OPEN "stream_open" +#define USERSTREAM_CLOSE "stream_close" +#define USERSTREAM_READ "stream_read" +#define USERSTREAM_WRITE "stream_write" +#define USERSTREAM_FLUSH "stream_flush" +#define USERSTREAM_SEEK "stream_seek" +#define USERSTREAM_GETS "stream_gets" +#define USERSTREAM_TELL "stream_tell" +#define USERSTREAM_EOF "stream_eof" + +/* class should have methods like these: + +function stream_open($path, $mode, $options, &$opened_path) + { + return true/false; + } + function stream_read($count) + { + return false on error; + else return string; + } + function stream_write($data) + { + return false on error; + else return count written; + } + function stream_close() + { + } + function stream_flush() + { + } + function stream_seek($offset, $whence) + { + } + function stream_gets($size) + { + return false on error; + else return string; + } + + * */ + +static php_stream *user_wrapper_factory(char *filename, char *mode, int options, char **opened_path, void *wrappercontext STREAMS_DC TSRMLS_DC) +{ + struct php_user_stream_wrapper *uwrap = (struct php_user_stream_wrapper*)wrappercontext; + php_userstream_data_t *us; + zval *zfilename, *zmode, *zopened, *zoptions, *zretval = NULL, *zfuncname; + zval **args[4]; + int call_result; + php_stream *stream = NULL; + + us = emalloc(sizeof(*us)); + us->wrapper = uwrap; + + /* create an instance of our class */ + ALLOC_ZVAL(us->object); + object_init_ex(us->object, uwrap->ce); + ZVAL_REFCOUNT(us->object) = 1; + PZVAL_IS_REF(us->object) = 1; + + /* call it's stream_open method - set up params first */ + MAKE_STD_ZVAL(zfilename); + ZVAL_STRING(zfilename, filename, 1); + args[0] = &zfilename; + + MAKE_STD_ZVAL(zmode); + ZVAL_STRING(zmode, mode, 1); + args[1] = &zmode; + + MAKE_STD_ZVAL(zoptions); + ZVAL_LONG(zoptions, options); + args[2] = &zoptions; + + MAKE_STD_ZVAL(zopened); + ZVAL_REFCOUNT(zopened) = 1; + PZVAL_IS_REF(zopened) = 1; + ZVAL_NULL(zopened); + args[3] = &zopened; + + MAKE_STD_ZVAL(zfuncname); + ZVAL_STRING(zfuncname, USERSTREAM_OPEN, 1); + + call_result = call_user_function_ex(NULL, + &us->object, + zfuncname, + &zretval, + 4, args, + 0, NULL TSRMLS_CC); + + if (call_result == SUCCESS && zretval != NULL) { + /* the stream is now open! */ + stream = php_stream_alloc_rel(&php_stream_userspace_ops, us, 0, mode); + + /* if the opened path is set, copy it out */ + if (Z_TYPE_P(zopened) == IS_STRING && opened_path) { + *opened_path = estrndup(Z_STRVAL_P(zopened), Z_STRLEN_P(zopened)); + } + } else { + /* destroy the object */ + zval_ptr_dtor(&us->object); + efree(us); + } + + /* destroy everything else */ + if (zretval) + zval_ptr_dtor(&zretval); + + zval_ptr_dtor(&zfuncname); + zval_ptr_dtor(&zopened); + zval_ptr_dtor(&zoptions); + zval_ptr_dtor(&zmode); + zval_ptr_dtor(&zfilename); + + return stream; +} + +/* {{{ proto bool file_register_wrapper(string protocol, string classname) + Registers a custom URL protocol handler class */ +PHP_FUNCTION(file_register_wrapper) +{ + char *protocol, *classname; + int protocol_len, classname_len; + struct php_user_stream_wrapper * uwrap; + int rsrc_id; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &protocol, &protocol_len, &classname, &classname_len) == FAILURE) { + RETURN_FALSE; + } + + if (!PG(allow_url_fopen)) { + zend_error(E_WARNING, "%s(): fopen wrappers have been disabled", get_active_function_name(TSRMLS_C)); + RETURN_FALSE; + } + + uwrap = (struct php_user_stream_wrapper *)ecalloc(1, sizeof(*uwrap)); + uwrap->protoname = estrndup(protocol, protocol_len); + uwrap->classname = estrndup(classname, classname_len); + uwrap->wrapper.create = user_wrapper_factory; + uwrap->wrapper.wrappercontext = uwrap; + + zend_str_tolower(uwrap->classname, classname_len); + rsrc_id = ZEND_REGISTER_RESOURCE(NULL, uwrap, le_protocols); + + if (zend_hash_find(EG(class_table), uwrap->classname, classname_len + 1, (void**)&uwrap->ce) == SUCCESS) { +#ifdef ZEND_ENGINE_2 + uwrap->ce = *(zend_class_entry**)uwrap->ce; +#endif + if (php_register_url_stream_wrapper(protocol, &uwrap->wrapper TSRMLS_CC) == SUCCESS) { + RETURN_TRUE; + } + } else { + zend_error(E_WARNING, "%s(): class '%s' is undefined", get_active_function_name(TSRMLS_C), + classname); + } + + zend_list_delete(rsrc_id); + RETURN_FALSE; +} +/* }}} */ + + +static size_t php_userstreamop_write(php_stream *stream, const char *buf, size_t count TSRMLS_DC) +{ + zval func_name; + zval *retval = NULL; + int call_result; + php_userstream_data_t *us = (php_userstream_data_t *)stream->abstract; + zval **args[1]; + zval zbuff, *zbufptr; + size_t didwrite = 0; + + assert(us != NULL); + + ZVAL_STRINGL(&func_name, USERSTREAM_WRITE, sizeof(USERSTREAM_WRITE)-1, 0); + + ZVAL_STRINGL(&zbuff, (char*)buf, count, 0); + zbufptr = &zbuff; + args[0] = &zbufptr; + + call_result = call_user_function_ex(NULL, + &us->object, + &func_name, + &retval, + 1, args, + 0, NULL TSRMLS_CC); + + if (call_result == SUCCESS && retval != NULL && Z_TYPE_P(retval) == IS_LONG) + didwrite = Z_LVAL_P(retval); + else + didwrite = 0; + + if (retval) + zval_ptr_dtor(&retval); + + return didwrite; +} + +static size_t php_userstreamop_read(php_stream *stream, char *buf, size_t count TSRMLS_DC) +{ + zval func_name; + zval *retval = NULL; + zval **args[1]; + int call_result; + size_t didread = 0; + php_userstream_data_t *us = (php_userstream_data_t *)stream->abstract; + + assert(us != NULL); + + if (buf == NULL && count == 0) { + ZVAL_STRINGL(&func_name, USERSTREAM_EOF, sizeof(USERSTREAM_EOF)-1, 0); + + call_result = call_user_function_ex(NULL, + &us->object, + &func_name, + &retval, + 0, NULL, 0, NULL TSRMLS_CC); + + if (call_result == SUCCESS && retval != NULL && zval_is_true(retval)) + didread = 0; + else + didread = EOF; + + } else { + zval *zcount; + + ZVAL_STRINGL(&func_name, USERSTREAM_READ, sizeof(USERSTREAM_READ)-1, 0); + + MAKE_STD_ZVAL(zcount); + ZVAL_LONG(zcount, count); + args[0] = &zcount; + + call_result = call_user_function_ex(NULL, + &us->object, + &func_name, + &retval, + 1, args, + 0, NULL TSRMLS_CC); + + if (retval && Z_TYPE_P(retval) == IS_STRING) { + didread = Z_STRLEN_P(retval); + if (didread > count) { + zend_error(E_WARNING, "%s::" USERSTREAM_READ " - read more data than requested; some data will be lost", + us->wrapper->classname); + didread = count; + } + if (didread > 0) + memcpy(buf, Z_STRVAL_P(retval), didread); + } + + zval_ptr_dtor(&zcount); + } + + if (retval) + zval_ptr_dtor(&retval); + + return didread; +} + +static int php_userstreamop_close(php_stream *stream, int close_handle TSRMLS_DC) +{ + zval func_name; + zval *retval = NULL; + int call_result; + php_userstream_data_t *us = (php_userstream_data_t *)stream->abstract; + + assert(us != NULL); + + ZVAL_STRINGL(&func_name, USERSTREAM_CLOSE, sizeof(USERSTREAM_CLOSE)-1, 0); + + call_result = call_user_function_ex(NULL, + &us->object, + &func_name, + &retval, + 0, NULL, 0, NULL TSRMLS_CC); + + if (retval) + zval_ptr_dtor(&retval); + + zval_ptr_dtor(&us->object); + + efree(us); + + return 0; +} + +static int php_userstreamop_flush(php_stream *stream TSRMLS_DC) +{ + zval func_name; + zval *retval = NULL; + int call_result; + php_userstream_data_t *us = (php_userstream_data_t *)stream->abstract; + + assert(us != NULL); + + ZVAL_STRINGL(&func_name, USERSTREAM_FLUSH, sizeof(USERSTREAM_FLUSH)-1, 0); + + call_result = call_user_function_ex(NULL, + &us->object, + &func_name, + &retval, + 0, NULL, 0, NULL TSRMLS_CC); + + if (call_result == SUCCESS && retval != NULL && zval_is_true(retval)) + call_result = 0; + else + call_result = -1; + + if (retval) + zval_ptr_dtor(&retval); + + return call_result; +} + +static int php_userstreamop_seek(php_stream *stream, off_t offset, int whence TSRMLS_DC) +{ + zval func_name; + zval *retval = NULL; + int call_result; + php_userstream_data_t *us = (php_userstream_data_t *)stream->abstract; + + assert(us != NULL); + + if (offset == 0 && whence == SEEK_CUR) { + ZVAL_STRINGL(&func_name, USERSTREAM_TELL, sizeof(USERSTREAM_TELL)-1, 0); + + call_result = call_user_function_ex(NULL, + &us->object, + &func_name, + &retval, + 0, NULL, 0, NULL TSRMLS_CC); + + + } else { + zval **args[2]; + zval *zoffs, *zwhence; + + ZVAL_STRINGL(&func_name, USERSTREAM_SEEK, sizeof(USERSTREAM_SEEK)-1, 0); + + MAKE_STD_ZVAL(zoffs); + ZVAL_LONG(zoffs, offset); + args[0] = &zoffs; + + MAKE_STD_ZVAL(zwhence); + ZVAL_LONG(zwhence, whence); + args[1] = &zwhence; + + call_result = call_user_function_ex(NULL, + &us->object, + &func_name, + &retval, + 2, args, + 0, NULL TSRMLS_CC); + + zval_ptr_dtor(&zoffs); + zval_ptr_dtor(&zwhence); + + } + + if (call_result == SUCCESS && retval != NULL && Z_TYPE_P(retval) == IS_LONG) + call_result = Z_LVAL_P(retval); + else + call_result = -1; + + if (retval) + zval_ptr_dtor(&retval); + + return 0; +} + +static char *php_userstreamop_gets(php_stream *stream, char *buf, size_t size TSRMLS_DC) +{ + zval func_name; + zval *retval = NULL; + zval *zcount; + zval **args[2]; + int call_result; + size_t didread = 0; + php_userstream_data_t *us = (php_userstream_data_t *)stream->abstract; + + assert(us != NULL); + + /* TODO: when the gets method is not available, fall back on emulated version using read */ + + ZVAL_STRINGL(&func_name, USERSTREAM_GETS, sizeof(USERSTREAM_GETS)-1, 0); + + MAKE_STD_ZVAL(zcount); + ZVAL_LONG(zcount, size); + args[0] = &zcount; + + call_result = call_user_function_ex(NULL, + &us->object, + &func_name, + &retval, + 1, args, + 0, NULL TSRMLS_CC); + + if (retval && Z_TYPE_P(retval) == IS_STRING) { + didread = Z_STRLEN_P(retval); + if (didread > size) { + zend_error(E_WARNING, "%s::" USERSTREAM_GETS " - read more data than requested; some data will be lost", + us->wrapper->classname); + didread = size; + } + if (didread > 0) + memcpy(buf, Z_STRVAL_P(retval), didread); + + zval_ptr_dtor(&retval); + } + + if (retval) + zval_ptr_dtor(&zcount); + + if (didread) + return buf; + + return 0; +} + +php_stream_ops php_stream_userspace_ops = { + php_userstreamop_write, php_userstreamop_read, + php_userstreamop_close, php_userstreamop_flush, + php_userstreamop_seek, php_userstreamop_gets, + NULL, /* cast */ + "user-space" +}; + + |