mirror of
https://github.com/php/php-src.git
synced 2024-10-08 18:26:27 +00:00
7fcfa8865e
Userdefined wrappers were being registered into a global wrapper hash which can cross threads. Termination of once instance then has the potential to leave an active stream in another instance with no wrapper leading to segfault.
1266 lines
33 KiB
C
1266 lines
33 KiB
C
/*
|
|
+----------------------------------------------------------------------+
|
|
| PHP Version 5 |
|
|
+----------------------------------------------------------------------+
|
|
| Copyright (c) 1997-2004 The PHP Group |
|
|
+----------------------------------------------------------------------+
|
|
| This source file is subject to version 3.0 of the PHP license, |
|
|
| that is bundled with this package in the file LICENSE, and is |
|
|
| available through the world-wide-web at the following url: |
|
|
| http://www.php.net/license/3_0.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> |
|
|
| Sara Golemon <pollita@php.net> |
|
|
+----------------------------------------------------------------------+
|
|
*/
|
|
|
|
/* $Id$ */
|
|
|
|
#include "php.h"
|
|
#include "php_globals.h"
|
|
#include "ext/standard/file.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_opener(php_stream_wrapper *wrapper, char *filename, char *mode, int options, char **opened_path, php_stream_context *context STREAMS_DC TSRMLS_DC);
|
|
static int user_wrapper_stat_url(php_stream_wrapper *wrapper, char *url, int flags, php_stream_statbuf *ssb, php_stream_context *context TSRMLS_DC);
|
|
static int user_wrapper_unlink(php_stream_wrapper *wrapper, char *url, int options, php_stream_context *context TSRMLS_DC);
|
|
static int user_wrapper_rename(php_stream_wrapper *wrapper, char *url_from, char *url_to, int options, php_stream_context *context TSRMLS_DC);
|
|
static int user_wrapper_mkdir(php_stream_wrapper *wrapper, char *url, int mode, int options, php_stream_context *context TSRMLS_DC);
|
|
static int user_wrapper_rmdir(php_stream_wrapper *wrapper, char *url, int options, php_stream_context *context TSRMLS_DC);
|
|
static php_stream *user_wrapper_opendir(php_stream_wrapper *wrapper, char *filename, char *mode,
|
|
int options, char **opened_path, php_stream_context *context STREAMS_DC TSRMLS_DC);
|
|
|
|
static php_stream_wrapper_ops user_stream_wops = {
|
|
user_wrapper_opener,
|
|
NULL, /* close - the streams themselves know how */
|
|
NULL, /* stat - the streams themselves know how */
|
|
user_wrapper_stat_url,
|
|
user_wrapper_opendir,
|
|
"user-space",
|
|
user_wrapper_unlink,
|
|
user_wrapper_rename,
|
|
user_wrapper_mkdir,
|
|
user_wrapper_rmdir
|
|
};
|
|
|
|
|
|
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;
|
|
|
|
efree(uwrap->protoname);
|
|
efree(uwrap->classname);
|
|
efree(uwrap);
|
|
}
|
|
|
|
|
|
PHP_MINIT_FUNCTION(user_streams)
|
|
{
|
|
le_protocols = zend_register_list_destructors_ex(stream_wrapper_dtor, NULL, "stream factory", 0);
|
|
if (le_protocols == FAILURE)
|
|
return FAILURE;
|
|
|
|
REGISTER_LONG_CONSTANT("STREAM_USE_PATH", USE_PATH, CONST_CS|CONST_PERSISTENT);
|
|
REGISTER_LONG_CONSTANT("STREAM_IGNORE_URL", IGNORE_URL, CONST_CS|CONST_PERSISTENT);
|
|
REGISTER_LONG_CONSTANT("STREAM_ENFORCE_SAFE_MODE", ENFORCE_SAFE_MODE, CONST_CS|CONST_PERSISTENT);
|
|
REGISTER_LONG_CONSTANT("STREAM_REPORT_ERRORS", REPORT_ERRORS, CONST_CS|CONST_PERSISTENT);
|
|
REGISTER_LONG_CONSTANT("STREAM_MUST_SEEK", STREAM_MUST_SEEK, CONST_CS|CONST_PERSISTENT);
|
|
|
|
REGISTER_LONG_CONSTANT("STREAM_URL_STAT_LINK", PHP_STREAM_URL_STAT_LINK, CONST_CS|CONST_PERSISTENT);
|
|
REGISTER_LONG_CONSTANT("STREAM_URL_STAT_QUIET", PHP_STREAM_URL_STAT_QUIET, CONST_CS|CONST_PERSISTENT);
|
|
REGISTER_LONG_CONSTANT("STREAM_MKDIR_RECURSIVE", PHP_STREAM_MKDIR_RECURSIVE, CONST_CS|CONST_PERSISTENT);
|
|
|
|
return 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_TELL "stream_tell"
|
|
#define USERSTREAM_EOF "stream_eof"
|
|
#define USERSTREAM_STAT "stream_stat"
|
|
#define USERSTREAM_STATURL "url_stat"
|
|
#define USERSTREAM_UNLINK "unlink"
|
|
#define USERSTREAM_RENAME "rename"
|
|
#define USERSTREAM_MKDIR "mkdir"
|
|
#define USERSTREAM_RMDIR "rmdir"
|
|
#define USERSTREAM_DIR_OPEN "dir_opendir"
|
|
#define USERSTREAM_DIR_READ "dir_readdir"
|
|
#define USERSTREAM_DIR_REWIND "dir_rewinddir"
|
|
#define USERSTREAM_DIR_CLOSE "dir_closedir"
|
|
#define USERSTREAM_LOCK "stream_lock"
|
|
|
|
/* {{{ 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()
|
|
{
|
|
return true/false;
|
|
}
|
|
|
|
function stream_seek($offset, $whence)
|
|
{
|
|
return true/false;
|
|
}
|
|
|
|
function stream_tell()
|
|
{
|
|
return (int)$position;
|
|
}
|
|
|
|
function stream_eof()
|
|
{
|
|
return true/false;
|
|
}
|
|
|
|
function stream_stat()
|
|
{
|
|
return array( just like that returned by fstat() );
|
|
}
|
|
|
|
function url_stat(string $url, int $flags)
|
|
{
|
|
return array( just like that returned by stat() );
|
|
}
|
|
|
|
function unlink(string $url)
|
|
{
|
|
return true / false;
|
|
}
|
|
|
|
function rename(string $from, string $to)
|
|
{
|
|
return true / false;
|
|
}
|
|
|
|
function mkdir($dir, $mode, $options)
|
|
{
|
|
return true / false;
|
|
}
|
|
|
|
function rmdir($dir, $options)
|
|
{
|
|
return true / false;
|
|
}
|
|
|
|
function dir_opendir(string $url, int $options)
|
|
{
|
|
return true / false;
|
|
}
|
|
|
|
function dir_readdir()
|
|
{
|
|
return string next filename in dir ;
|
|
}
|
|
|
|
function dir_closedir()
|
|
{
|
|
release dir related resources;
|
|
}
|
|
|
|
function dir_rewinddir()
|
|
{
|
|
reset to start of dir list;
|
|
}
|
|
|
|
}}} **/
|
|
|
|
static php_stream *user_wrapper_opener(php_stream_wrapper *wrapper, char *filename, char *mode, int options, char **opened_path, php_stream_context *context STREAMS_DC TSRMLS_DC)
|
|
{
|
|
struct php_user_stream_wrapper *uwrap = (struct php_user_stream_wrapper*)wrapper->abstract;
|
|
php_userstream_data_t *us;
|
|
zval *zfilename, *zmode, *zopened, *zoptions, *zretval = NULL, *zfuncname;
|
|
zval **args[4];
|
|
int call_result;
|
|
php_stream *stream = NULL;
|
|
zval *zcontext = NULL;
|
|
|
|
/* Try to catch bad usage without preventing flexibility */
|
|
if (FG(user_stream_current_filename) != NULL && strcmp(filename, FG(user_stream_current_filename)) == 0) {
|
|
php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "infinite recursion prevented");
|
|
return NULL;
|
|
}
|
|
FG(user_stream_current_filename) = filename;
|
|
|
|
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;
|
|
|
|
if (context) {
|
|
MAKE_STD_ZVAL(zcontext);
|
|
php_stream_context_to_zval(context, zcontext);
|
|
add_property_zval(us->object, "context", zcontext);
|
|
/* The object property should be the only reference,
|
|
'get rid' of our local reference. */
|
|
zval_ptr_dtor(&zcontext);
|
|
} else {
|
|
add_property_null(us->object, "context");
|
|
}
|
|
|
|
/* 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 && zval_is_true(zretval)) {
|
|
/* 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));
|
|
}
|
|
|
|
/* set wrapper data to be a reference to our object */
|
|
stream->wrapperdata = us->object;
|
|
zval_add_ref(&stream->wrapperdata);
|
|
} else {
|
|
php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "\"%s::" USERSTREAM_OPEN "\" call failed",
|
|
us->wrapper->classname);
|
|
}
|
|
|
|
/* destroy everything else */
|
|
if (stream == NULL) {
|
|
zval_ptr_dtor(&us->object);
|
|
efree(us);
|
|
}
|
|
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);
|
|
|
|
FG(user_stream_current_filename) = NULL;
|
|
|
|
return stream;
|
|
}
|
|
|
|
static php_stream *user_wrapper_opendir(php_stream_wrapper *wrapper, char *filename, char *mode,
|
|
int options, char **opened_path, php_stream_context *context STREAMS_DC TSRMLS_DC)
|
|
{
|
|
struct php_user_stream_wrapper *uwrap = (struct php_user_stream_wrapper*)wrapper->abstract;
|
|
php_userstream_data_t *us;
|
|
zval *zfilename, *zoptions, *zretval = NULL, *zfuncname, *zcontext;
|
|
zval **args[2];
|
|
int call_result;
|
|
php_stream *stream = NULL;
|
|
|
|
/* Try to catch bad usage without preventing flexibility */
|
|
if (FG(user_stream_current_filename) != NULL && strcmp(filename, FG(user_stream_current_filename)) == 0) {
|
|
php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "infinite recursion prevented");
|
|
return NULL;
|
|
}
|
|
FG(user_stream_current_filename) = filename;
|
|
|
|
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;
|
|
|
|
if (context) {
|
|
MAKE_STD_ZVAL(zcontext);
|
|
php_stream_context_to_zval(context, zcontext);
|
|
add_property_zval(us->object, "context", zcontext);
|
|
/* The object property should be the only reference,
|
|
'get rid' of our local reference. */
|
|
zval_ptr_dtor(&zcontext);
|
|
} else {
|
|
add_property_null(us->object, "context");
|
|
}
|
|
|
|
/* call it's dir_open method - set up params first */
|
|
MAKE_STD_ZVAL(zfilename);
|
|
ZVAL_STRING(zfilename, filename, 1);
|
|
args[0] = &zfilename;
|
|
|
|
MAKE_STD_ZVAL(zoptions);
|
|
ZVAL_LONG(zoptions, options);
|
|
args[1] = &zoptions;
|
|
|
|
MAKE_STD_ZVAL(zfuncname);
|
|
ZVAL_STRING(zfuncname, USERSTREAM_DIR_OPEN, 1);
|
|
|
|
call_result = call_user_function_ex(NULL,
|
|
&us->object,
|
|
zfuncname,
|
|
&zretval,
|
|
2, args,
|
|
0, NULL TSRMLS_CC);
|
|
|
|
if (call_result == SUCCESS && zretval != NULL && zval_is_true(zretval)) {
|
|
/* the stream is now open! */
|
|
stream = php_stream_alloc_rel(&php_stream_userspace_dir_ops, us, 0, mode);
|
|
|
|
/* set wrapper data to be a reference to our object */
|
|
stream->wrapperdata = us->object;
|
|
zval_add_ref(&stream->wrapperdata);
|
|
} else {
|
|
php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "\"%s::" USERSTREAM_DIR_OPEN "\" call failed",
|
|
us->wrapper->classname);
|
|
}
|
|
|
|
/* destroy everything else */
|
|
if (stream == NULL) {
|
|
zval_ptr_dtor(&us->object);
|
|
efree(us);
|
|
}
|
|
if (zretval)
|
|
zval_ptr_dtor(&zretval);
|
|
|
|
zval_ptr_dtor(&zfuncname);
|
|
zval_ptr_dtor(&zoptions);
|
|
zval_ptr_dtor(&zfilename);
|
|
|
|
FG(user_stream_current_filename) = NULL;
|
|
|
|
return stream;
|
|
}
|
|
|
|
|
|
/* {{{ proto bool stream_wrapper_register(string protocol, string classname)
|
|
Registers a custom URL protocol handler class */
|
|
PHP_FUNCTION(stream_wrapper_register)
|
|
{
|
|
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;
|
|
}
|
|
|
|
uwrap = (struct php_user_stream_wrapper *)ecalloc(1, sizeof(*uwrap));
|
|
uwrap->protoname = estrndup(protocol, protocol_len);
|
|
uwrap->classname = estrndup(classname, classname_len);
|
|
uwrap->wrapper.wops = &user_stream_wops;
|
|
uwrap->wrapper.abstract = uwrap;
|
|
|
|
rsrc_id = ZEND_REGISTER_RESOURCE(NULL, uwrap, le_protocols);
|
|
|
|
if (zend_lookup_class(uwrap->classname, classname_len, (zend_class_entry***)&uwrap->ce TSRMLS_CC) == SUCCESS) {
|
|
uwrap->ce = *(zend_class_entry**)uwrap->ce;
|
|
if (php_register_url_stream_wrapper_volatile(protocol, &uwrap->wrapper TSRMLS_CC) == SUCCESS) {
|
|
RETURN_TRUE;
|
|
} else {
|
|
/* We failed. But why? */
|
|
if (zend_hash_exists(php_stream_get_url_stream_wrappers_hash(), protocol, protocol_len)) {
|
|
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Protocol %s:// is already defined.", protocol);
|
|
} else {
|
|
/* Should never happen */
|
|
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to register wrapper class %s to %s://", classname, protocol);
|
|
}
|
|
}
|
|
} else {
|
|
php_error_docref(NULL TSRMLS_CC, E_WARNING, "class '%s' is undefined", 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);
|
|
|
|
didwrite = 0;
|
|
if (call_result == SUCCESS && retval != NULL) {
|
|
convert_to_long(retval);
|
|
didwrite = Z_LVAL_P(retval);
|
|
} else if (call_result == FAILURE) {
|
|
php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s::" USERSTREAM_WRITE " is not implemented!",
|
|
us->wrapper->classname);
|
|
}
|
|
|
|
/* don't allow strange buffer overruns due to bogus return */
|
|
if (didwrite > count) {
|
|
php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s::" USERSTREAM_WRITE " wrote %ld bytes more data than requested (%ld written, %ld max)",
|
|
us->wrapper->classname,
|
|
(long)(didwrite - count), (long)didwrite, (long)count);
|
|
didwrite = count;
|
|
}
|
|
|
|
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;
|
|
zval *zcount;
|
|
|
|
assert(us != NULL);
|
|
|
|
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 (call_result == SUCCESS && retval != NULL) {
|
|
convert_to_string(retval);
|
|
didread = Z_STRLEN_P(retval);
|
|
if (didread > count) {
|
|
php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s::" USERSTREAM_READ " - read %ld bytes more data than requested (%ld read, %ld max) - excess data will be lost",
|
|
us->wrapper->classname, (long)(didread - count), (long)didread, (long)count);
|
|
didread = count;
|
|
}
|
|
if (didread > 0)
|
|
memcpy(buf, Z_STRVAL_P(retval), didread);
|
|
} else if (call_result == FAILURE) {
|
|
php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s::" USERSTREAM_READ " is not implemented!",
|
|
us->wrapper->classname);
|
|
}
|
|
zval_ptr_dtor(&zcount);
|
|
|
|
if (retval) {
|
|
zval_ptr_dtor(&retval);
|
|
retval = NULL;
|
|
}
|
|
|
|
/* since the user stream has no way of setting the eof flag directly, we need to ask it if we hit eof */
|
|
|
|
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)) {
|
|
stream->eof = 1;
|
|
} else if (call_result == FAILURE) {
|
|
php_error_docref(NULL TSRMLS_CC, E_WARNING,
|
|
"%s::" USERSTREAM_EOF " is not implemented! Assuming EOF",
|
|
us->wrapper->classname);
|
|
|
|
stream->eof = 1;
|
|
}
|
|
|
|
if (retval) {
|
|
zval_ptr_dtor(&retval);
|
|
retval = NULL;
|
|
}
|
|
|
|
return didread;
|
|
}
|
|
|
|
static int php_userstreamop_close(php_stream *stream, int close_handle TSRMLS_DC)
|
|
{
|
|
zval func_name;
|
|
zval *retval = NULL;
|
|
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_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, off_t *newoffs TSRMLS_DC)
|
|
{
|
|
zval func_name;
|
|
zval *retval = NULL;
|
|
int call_result, ret;
|
|
php_userstream_data_t *us = (php_userstream_data_t *)stream->abstract;
|
|
zval **args[2];
|
|
zval *zoffs, *zwhence;
|
|
|
|
assert(us != NULL);
|
|
|
|
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 == FAILURE) {
|
|
/* stream_seek is not implemented, so disable seeks for this stream */
|
|
stream->flags |= PHP_STREAM_FLAG_NO_SEEK;
|
|
/* there should be no retval to clean up */
|
|
|
|
if (retval)
|
|
zval_ptr_dtor(&retval);
|
|
|
|
return -1;
|
|
} else if (call_result == SUCCESS && retval != NULL && zval_is_true(retval)) {
|
|
ret = 0;
|
|
} else {
|
|
ret = -1;
|
|
}
|
|
|
|
if (retval) {
|
|
zval_ptr_dtor(&retval);
|
|
retval = NULL;
|
|
}
|
|
|
|
/* now determine where we are */
|
|
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);
|
|
|
|
if (call_result == SUCCESS && retval != NULL && Z_TYPE_P(retval) == IS_LONG)
|
|
*newoffs = Z_LVAL_P(retval);
|
|
else
|
|
php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s::" USERSTREAM_TELL " is not implemented!",
|
|
us->wrapper->classname);
|
|
|
|
if (retval)
|
|
zval_ptr_dtor(&retval);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* parse the return value from one of the stat functions and store the
|
|
* relevant fields into the statbuf provided */
|
|
static int statbuf_from_array(zval *array, php_stream_statbuf *ssb TSRMLS_DC)
|
|
{
|
|
zval **elem;
|
|
|
|
#define STAT_PROP_ENTRY_EX(name, name2) \
|
|
if (SUCCESS == zend_hash_find(Z_ARRVAL_P(array), #name, sizeof(#name), (void**)&elem)) { \
|
|
convert_to_long(*elem); \
|
|
ssb->sb.st_##name2 = Z_LVAL_PP(elem); \
|
|
}
|
|
|
|
#define STAT_PROP_ENTRY(name) STAT_PROP_ENTRY_EX(name,name)
|
|
|
|
STAT_PROP_ENTRY(dev);
|
|
STAT_PROP_ENTRY(ino);
|
|
STAT_PROP_ENTRY(mode);
|
|
STAT_PROP_ENTRY(nlink);
|
|
STAT_PROP_ENTRY(uid);
|
|
STAT_PROP_ENTRY(gid);
|
|
#if HAVE_ST_RDEV
|
|
STAT_PROP_ENTRY(rdev);
|
|
#endif
|
|
STAT_PROP_ENTRY(size);
|
|
#if defined(NETWARE) && defined(CLIB_STAT_PATCH)
|
|
STAT_PROP_ENTRY_EX(atime, atime.tv_sec);
|
|
STAT_PROP_ENTRY_EX(mtime, mtime.tv_sec);
|
|
STAT_PROP_ENTRY_EX(ctime, ctime.tv_sec);
|
|
#else
|
|
STAT_PROP_ENTRY(atime);
|
|
STAT_PROP_ENTRY(mtime);
|
|
STAT_PROP_ENTRY(ctime);
|
|
#endif
|
|
#ifdef HAVE_ST_BLKSIZE
|
|
STAT_PROP_ENTRY(blksize);
|
|
#endif
|
|
#ifdef HAVE_ST_BLOCKS
|
|
STAT_PROP_ENTRY(blocks);
|
|
#endif
|
|
|
|
#undef STAT_PROP_ENTRY
|
|
#undef STAT_PROP_ENTRY_EX
|
|
return SUCCESS;
|
|
}
|
|
|
|
static int php_userstreamop_stat(php_stream *stream, php_stream_statbuf *ssb TSRMLS_DC)
|
|
{
|
|
zval func_name;
|
|
zval *retval = NULL;
|
|
int call_result;
|
|
php_userstream_data_t *us = (php_userstream_data_t *)stream->abstract;
|
|
int ret = -1;
|
|
|
|
ZVAL_STRINGL(&func_name, USERSTREAM_STAT, sizeof(USERSTREAM_STAT)-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 && Z_TYPE_P(retval) == IS_ARRAY) {
|
|
if (SUCCESS == statbuf_from_array(retval, ssb TSRMLS_CC))
|
|
ret = 0;
|
|
} else {
|
|
if (call_result == FAILURE) {
|
|
php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s::" USERSTREAM_STAT " is not implemented!",
|
|
us->wrapper->classname);
|
|
}
|
|
}
|
|
|
|
if (retval)
|
|
zval_ptr_dtor(&retval);
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
static int php_userstreamop_set_option(php_stream *stream, int option, int value, void *ptrparam TSRMLS_DC) {
|
|
zval func_name;
|
|
zval *retval = NULL;
|
|
int call_result;
|
|
php_userstream_data_t *us = (php_userstream_data_t *)stream->abstract;
|
|
int ret = -1;
|
|
zval *zvalue = NULL;
|
|
zval **args[1];
|
|
|
|
switch (option) {
|
|
case PHP_STREAM_OPTION_CHECK_LIVENESS:
|
|
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 && Z_TYPE_P(retval) == IS_BOOL) {
|
|
ret = Z_LVAL_P(retval) ? PHP_STREAM_OPTION_RETURN_OK : PHP_STREAM_OPTION_RETURN_ERR;
|
|
} else {
|
|
ret = PHP_STREAM_OPTION_RETURN_ERR;
|
|
php_error_docref(NULL TSRMLS_CC, E_WARNING,
|
|
"%s::" USERSTREAM_EOF " is not implemented! Assuming EOF",
|
|
us->wrapper->classname);
|
|
}
|
|
break;
|
|
|
|
case PHP_STREAM_OPTION_LOCKING:
|
|
MAKE_STD_ZVAL(zvalue);
|
|
ZVAL_LONG(zvalue, value);
|
|
args[0] = &zvalue;
|
|
|
|
/* TODO wouldblock */
|
|
ZVAL_STRINGL(&func_name, USERSTREAM_LOCK, sizeof(USERSTREAM_LOCK)-1, 0);
|
|
|
|
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_BOOL) {
|
|
ret = !Z_LVAL_P(retval);
|
|
} else if (call_result == FAILURE) {
|
|
if (value == 0) {
|
|
/* lock support test (TODO: more check) */
|
|
ret = 0;
|
|
} else {
|
|
php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s::" USERSTREAM_LOCK " is not implemented!",
|
|
us->wrapper->classname);
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
/* clean up */
|
|
if (retval) {
|
|
zval_ptr_dtor(&retval);
|
|
}
|
|
|
|
|
|
if (zvalue) {
|
|
zval_ptr_dtor(&zvalue);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
static int user_wrapper_unlink(php_stream_wrapper *wrapper, char *url, int options, php_stream_context *context TSRMLS_DC)
|
|
{
|
|
struct php_user_stream_wrapper *uwrap = (struct php_user_stream_wrapper*)wrapper->abstract;
|
|
zval *zfilename, *zfuncname, *zretval, *zcontext;
|
|
zval **args[1];
|
|
int call_result;
|
|
zval *object;
|
|
int ret = 0;
|
|
|
|
/* create an instance of our class */
|
|
ALLOC_ZVAL(object);
|
|
object_init_ex(object, uwrap->ce);
|
|
ZVAL_REFCOUNT(object) = 1;
|
|
PZVAL_IS_REF(object) = 1;
|
|
|
|
if (context) {
|
|
MAKE_STD_ZVAL(zcontext);
|
|
php_stream_context_to_zval(context, zcontext);
|
|
add_property_zval(object, "context", zcontext);
|
|
/* The object property should be the only reference,
|
|
'get rid' of our local reference. */
|
|
zval_ptr_dtor(&zcontext);
|
|
} else {
|
|
add_property_null(object, "context");
|
|
}
|
|
|
|
/* call the unlink method */
|
|
MAKE_STD_ZVAL(zfilename);
|
|
ZVAL_STRING(zfilename, url, 1);
|
|
args[0] = &zfilename;
|
|
|
|
MAKE_STD_ZVAL(zfuncname);
|
|
ZVAL_STRING(zfuncname, USERSTREAM_UNLINK, 1);
|
|
|
|
call_result = call_user_function_ex(NULL,
|
|
&object,
|
|
zfuncname,
|
|
&zretval,
|
|
1, args,
|
|
0, NULL TSRMLS_CC);
|
|
|
|
if (call_result == SUCCESS && zretval && Z_TYPE_P(zretval) == IS_BOOL) {
|
|
ret = Z_LVAL_P(zretval);
|
|
} else if (call_result == FAILURE) {
|
|
php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s::" USERSTREAM_UNLINK " is not implemented!", uwrap->classname);
|
|
}
|
|
|
|
/* clean up */
|
|
zval_ptr_dtor(&object);
|
|
if (zretval)
|
|
zval_ptr_dtor(&zretval);
|
|
|
|
zval_ptr_dtor(&zfuncname);
|
|
zval_ptr_dtor(&zfilename);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int user_wrapper_rename(php_stream_wrapper *wrapper, char *url_from, char *url_to, int options, php_stream_context *context TSRMLS_DC)
|
|
{
|
|
struct php_user_stream_wrapper *uwrap = (struct php_user_stream_wrapper*)wrapper->abstract;
|
|
zval *zold_name, *znew_name, *zfuncname, *zretval, *zcontext;
|
|
zval **args[2];
|
|
int call_result;
|
|
zval *object;
|
|
int ret = 0;
|
|
|
|
/* create an instance of our class */
|
|
ALLOC_ZVAL(object);
|
|
object_init_ex(object, uwrap->ce);
|
|
ZVAL_REFCOUNT(object) = 1;
|
|
PZVAL_IS_REF(object) = 1;
|
|
|
|
if (context) {
|
|
MAKE_STD_ZVAL(zcontext);
|
|
php_stream_context_to_zval(context, zcontext);
|
|
add_property_zval(object, "context", zcontext);
|
|
/* The object property should be the only reference,
|
|
'get rid' of our local reference. */
|
|
zval_ptr_dtor(&zcontext);
|
|
} else {
|
|
add_property_null(object, "context");
|
|
}
|
|
|
|
/* call the rename method */
|
|
MAKE_STD_ZVAL(zold_name);
|
|
ZVAL_STRING(zold_name, url_from, 1);
|
|
args[0] = &zold_name;
|
|
|
|
MAKE_STD_ZVAL(znew_name);
|
|
ZVAL_STRING(znew_name, url_to, 1);
|
|
args[1] = &znew_name;
|
|
|
|
MAKE_STD_ZVAL(zfuncname);
|
|
ZVAL_STRING(zfuncname, USERSTREAM_RENAME, 1);
|
|
|
|
call_result = call_user_function_ex(NULL,
|
|
&object,
|
|
zfuncname,
|
|
&zretval,
|
|
2, args,
|
|
0, NULL TSRMLS_CC);
|
|
|
|
if (call_result == SUCCESS && zretval && Z_TYPE_P(zretval) == IS_BOOL) {
|
|
ret = Z_LVAL_P(zretval);
|
|
} else if (call_result == FAILURE) {
|
|
php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s::" USERSTREAM_RENAME " is not implemented!", uwrap->classname);
|
|
}
|
|
|
|
/* clean up */
|
|
zval_ptr_dtor(&object);
|
|
if (zretval)
|
|
zval_ptr_dtor(&zretval);
|
|
|
|
zval_ptr_dtor(&zfuncname);
|
|
zval_ptr_dtor(&zold_name);
|
|
zval_ptr_dtor(&znew_name);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int user_wrapper_mkdir(php_stream_wrapper *wrapper, char *url, int mode, int options, php_stream_context *context TSRMLS_DC)
|
|
{
|
|
struct php_user_stream_wrapper *uwrap = (struct php_user_stream_wrapper*)wrapper->abstract;
|
|
zval *zfilename, *zmode, *zoptions, *zfuncname, *zretval, *zcontext;
|
|
zval **args[3];
|
|
int call_result;
|
|
zval *object;
|
|
int ret = 0;
|
|
|
|
/* create an instance of our class */
|
|
ALLOC_ZVAL(object);
|
|
object_init_ex(object, uwrap->ce);
|
|
ZVAL_REFCOUNT(object) = 1;
|
|
PZVAL_IS_REF(object) = 1;
|
|
|
|
if (context) {
|
|
MAKE_STD_ZVAL(zcontext);
|
|
php_stream_context_to_zval(context, zcontext);
|
|
add_property_zval(object, "context", zcontext);
|
|
/* The object property should be the only reference,
|
|
'get rid' of our local reference. */
|
|
zval_ptr_dtor(&zcontext);
|
|
} else {
|
|
add_property_null(object, "context");
|
|
}
|
|
|
|
/* call the unlink method */
|
|
MAKE_STD_ZVAL(zfilename);
|
|
ZVAL_STRING(zfilename, url, 1);
|
|
args[0] = &zfilename;
|
|
|
|
MAKE_STD_ZVAL(zmode);
|
|
ZVAL_LONG(zmode, mode);
|
|
args[1] = &zmode;
|
|
|
|
MAKE_STD_ZVAL(zoptions);
|
|
ZVAL_LONG(zoptions, options);
|
|
args[2] = &zoptions;
|
|
|
|
MAKE_STD_ZVAL(zfuncname);
|
|
ZVAL_STRING(zfuncname, USERSTREAM_MKDIR, 1);
|
|
|
|
call_result = call_user_function_ex(NULL,
|
|
&object,
|
|
zfuncname,
|
|
&zretval,
|
|
3, args,
|
|
0, NULL TSRMLS_CC);
|
|
|
|
if (call_result == SUCCESS && zretval && Z_TYPE_P(zretval) == IS_BOOL) {
|
|
ret = Z_LVAL_P(zretval);
|
|
} else if (call_result == FAILURE) {
|
|
php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s::" USERSTREAM_MKDIR " is not implemented!", uwrap->classname);
|
|
}
|
|
|
|
/* clean up */
|
|
zval_ptr_dtor(&object);
|
|
if (zretval) {
|
|
zval_ptr_dtor(&zretval);
|
|
}
|
|
|
|
zval_ptr_dtor(&zfuncname);
|
|
zval_ptr_dtor(&zfilename);
|
|
zval_ptr_dtor(&zmode);
|
|
zval_ptr_dtor(&zoptions);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int user_wrapper_rmdir(php_stream_wrapper *wrapper, char *url, int options, php_stream_context *context TSRMLS_DC)
|
|
{
|
|
struct php_user_stream_wrapper *uwrap = (struct php_user_stream_wrapper*)wrapper->abstract;
|
|
zval *zfilename, *zoptions, *zfuncname, *zretval, *zcontext;
|
|
zval **args[3];
|
|
int call_result;
|
|
zval *object;
|
|
int ret = 0;
|
|
|
|
/* create an instance of our class */
|
|
ALLOC_ZVAL(object);
|
|
object_init_ex(object, uwrap->ce);
|
|
ZVAL_REFCOUNT(object) = 1;
|
|
PZVAL_IS_REF(object) = 1;
|
|
|
|
if (context) {
|
|
MAKE_STD_ZVAL(zcontext);
|
|
php_stream_context_to_zval(context, zcontext);
|
|
add_property_zval(object, "context", zcontext);
|
|
/* The object property should be the only reference,
|
|
'get rid' of our local reference. */
|
|
zval_ptr_dtor(&zcontext);
|
|
} else {
|
|
add_property_null(object, "context");
|
|
}
|
|
|
|
/* call the unlink method */
|
|
MAKE_STD_ZVAL(zfilename);
|
|
ZVAL_STRING(zfilename, url, 1);
|
|
args[0] = &zfilename;
|
|
|
|
MAKE_STD_ZVAL(zoptions);
|
|
ZVAL_LONG(zoptions, options);
|
|
args[1] = &zoptions;
|
|
|
|
MAKE_STD_ZVAL(zfuncname);
|
|
ZVAL_STRING(zfuncname, USERSTREAM_RMDIR, 1);
|
|
|
|
call_result = call_user_function_ex(NULL,
|
|
&object,
|
|
zfuncname,
|
|
&zretval,
|
|
2, args,
|
|
0, NULL TSRMLS_CC);
|
|
|
|
if (call_result == SUCCESS && zretval && Z_TYPE_P(zretval) == IS_BOOL) {
|
|
ret = Z_LVAL_P(zretval);
|
|
} else if (call_result == FAILURE) {
|
|
php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s::" USERSTREAM_RMDIR " is not implemented!", uwrap->classname);
|
|
}
|
|
|
|
/* clean up */
|
|
zval_ptr_dtor(&object);
|
|
if (zretval) {
|
|
zval_ptr_dtor(&zretval);
|
|
}
|
|
|
|
zval_ptr_dtor(&zfuncname);
|
|
zval_ptr_dtor(&zfilename);
|
|
zval_ptr_dtor(&zoptions);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int user_wrapper_stat_url(php_stream_wrapper *wrapper, char *url, int flags, php_stream_statbuf *ssb, php_stream_context *context TSRMLS_DC)
|
|
{
|
|
struct php_user_stream_wrapper *uwrap = (struct php_user_stream_wrapper*)wrapper->abstract;
|
|
zval *zfilename, *zfuncname, *zretval, *zflags, *zcontext;
|
|
zval **args[2];
|
|
int call_result;
|
|
zval *object;
|
|
int ret = -1;
|
|
|
|
/* create an instance of our class */
|
|
ALLOC_ZVAL(object);
|
|
object_init_ex(object, uwrap->ce);
|
|
ZVAL_REFCOUNT(object) = 1;
|
|
PZVAL_IS_REF(object) = 1;
|
|
|
|
if (context) {
|
|
MAKE_STD_ZVAL(zcontext);
|
|
php_stream_context_to_zval(context, zcontext);
|
|
add_property_zval(object, "context", zcontext);
|
|
/* The object property should be the only reference,
|
|
'get rid' of our local reference. */
|
|
zval_ptr_dtor(&zcontext);
|
|
} else {
|
|
add_property_null(object, "context");
|
|
}
|
|
|
|
/* call the stat_url method */
|
|
|
|
/* call it's stream_open method - set up params first */
|
|
MAKE_STD_ZVAL(zfilename);
|
|
ZVAL_STRING(zfilename, url, 1);
|
|
args[0] = &zfilename;
|
|
|
|
MAKE_STD_ZVAL(zflags);
|
|
ZVAL_LONG(zflags, flags);
|
|
args[1] = &zflags;
|
|
|
|
MAKE_STD_ZVAL(zfuncname);
|
|
ZVAL_STRING(zfuncname, USERSTREAM_STATURL, 1);
|
|
|
|
call_result = call_user_function_ex(NULL,
|
|
&object,
|
|
zfuncname,
|
|
&zretval,
|
|
2, args,
|
|
0, NULL TSRMLS_CC);
|
|
|
|
if (call_result == SUCCESS && zretval != NULL && Z_TYPE_P(zretval) == IS_ARRAY) {
|
|
/* We got the info we needed */
|
|
if (SUCCESS == statbuf_from_array(zretval, ssb TSRMLS_CC))
|
|
ret = 0;
|
|
} else {
|
|
if (call_result == FAILURE) {
|
|
php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s::" USERSTREAM_STATURL " is not implemented!",
|
|
uwrap->classname);
|
|
}
|
|
}
|
|
|
|
/* clean up */
|
|
zval_ptr_dtor(&object);
|
|
if (zretval)
|
|
zval_ptr_dtor(&zretval);
|
|
|
|
zval_ptr_dtor(&zfuncname);
|
|
zval_ptr_dtor(&zfilename);
|
|
zval_ptr_dtor(&zflags);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
static size_t php_userstreamop_readdir(php_stream *stream, char *buf, size_t count TSRMLS_DC)
|
|
{
|
|
zval func_name;
|
|
zval *retval = NULL;
|
|
int call_result;
|
|
size_t didread = 0;
|
|
php_userstream_data_t *us = (php_userstream_data_t *)stream->abstract;
|
|
php_stream_dirent *ent = (php_stream_dirent*)buf;
|
|
|
|
/* avoid problems if someone mis-uses the stream */
|
|
if (count != sizeof(php_stream_dirent))
|
|
return 0;
|
|
|
|
ZVAL_STRINGL(&func_name, USERSTREAM_DIR_READ, sizeof(USERSTREAM_DIR_READ)-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 && Z_TYPE_P(retval) != IS_BOOL) {
|
|
convert_to_string(retval);
|
|
PHP_STRLCPY(ent->d_name, Z_STRVAL_P(retval), sizeof(ent->d_name), Z_STRLEN_P(retval));
|
|
|
|
didread = sizeof(php_stream_dirent);
|
|
} else if (call_result == FAILURE) {
|
|
php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s::" USERSTREAM_DIR_READ " is not implemented!",
|
|
us->wrapper->classname);
|
|
}
|
|
|
|
if (retval)
|
|
zval_ptr_dtor(&retval);
|
|
|
|
return didread;
|
|
}
|
|
|
|
static int php_userstreamop_closedir(php_stream *stream, int close_handle TSRMLS_DC)
|
|
{
|
|
zval func_name;
|
|
zval *retval = NULL;
|
|
php_userstream_data_t *us = (php_userstream_data_t *)stream->abstract;
|
|
|
|
assert(us != NULL);
|
|
|
|
ZVAL_STRINGL(&func_name, USERSTREAM_DIR_CLOSE, sizeof(USERSTREAM_DIR_CLOSE)-1, 0);
|
|
|
|
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_rewinddir(php_stream *stream, off_t offset, int whence, off_t *newoffs TSRMLS_DC)
|
|
{
|
|
zval func_name;
|
|
zval *retval = NULL;
|
|
php_userstream_data_t *us = (php_userstream_data_t *)stream->abstract;
|
|
|
|
ZVAL_STRINGL(&func_name, USERSTREAM_DIR_REWIND, sizeof(USERSTREAM_DIR_REWIND)-1, 0);
|
|
|
|
call_user_function_ex(NULL,
|
|
&us->object,
|
|
&func_name,
|
|
&retval,
|
|
0, NULL, 0, NULL TSRMLS_CC);
|
|
|
|
if (retval)
|
|
zval_ptr_dtor(&retval);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
php_stream_ops php_stream_userspace_ops = {
|
|
php_userstreamop_write, php_userstreamop_read,
|
|
php_userstreamop_close, php_userstreamop_flush,
|
|
"user-space",
|
|
php_userstreamop_seek,
|
|
NULL, /* cast */
|
|
php_userstreamop_stat,
|
|
php_userstreamop_set_option,
|
|
};
|
|
|
|
php_stream_ops php_stream_userspace_dir_ops = {
|
|
NULL, /* write */
|
|
php_userstreamop_readdir,
|
|
php_userstreamop_closedir,
|
|
NULL, /* flush */
|
|
"user-space-dir",
|
|
php_userstreamop_rewinddir,
|
|
NULL, /* cast */
|
|
NULL, /* stat */
|
|
NULL /* set_option */
|
|
};
|
|
|
|
|