--- /dev/null
+/*
+ +----------------------------------------------------------------------+
+ | 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"
+};
+
+