mirror of
https://github.com/php/php-src.git
synced 2024-11-24 10:24:11 +08:00
1323 lines
35 KiB
C
1323 lines
35 KiB
C
/*
|
|
+----------------------------------------------------------------------+
|
|
| PHP Version 5 |
|
|
+----------------------------------------------------------------------+
|
|
| Copyright (c) 1997-2005 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;
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto bool stream_wrapper_unregister(string protocol)
|
|
Unregister a wrapper for the life of the current request. */
|
|
PHP_FUNCTION(stream_wrapper_unregister)
|
|
{
|
|
char *protocol;
|
|
int protocol_len;
|
|
|
|
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &protocol, &protocol_len) == FAILURE) {
|
|
RETURN_FALSE;
|
|
}
|
|
|
|
if (php_unregister_url_stream_wrapper_volatile(protocol TSRMLS_CC) == FAILURE) {
|
|
/* We failed */
|
|
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to unregister protocol %s://", protocol);
|
|
RETURN_FALSE;
|
|
}
|
|
|
|
RETURN_TRUE;
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto bool stream_wrapper_restore(string protocol)
|
|
Restore the original protocol handler, overriding if necessary */
|
|
PHP_FUNCTION(stream_wrapper_restore)
|
|
{
|
|
char *protocol;
|
|
int protocol_len;
|
|
php_stream_wrapper *wrapper = NULL;
|
|
HashTable *global_wrapper_hash;
|
|
|
|
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &protocol, &protocol_len) == FAILURE) {
|
|
RETURN_FALSE;
|
|
}
|
|
|
|
global_wrapper_hash = php_stream_get_url_stream_wrappers_hash_global();
|
|
if (php_stream_get_url_stream_wrappers_hash() == global_wrapper_hash) {
|
|
php_error_docref(NULL TSRMLS_CC, E_NOTICE, "%s:// was never changed, nothing to restore", protocol);
|
|
RETURN_TRUE;
|
|
}
|
|
|
|
if ((zend_hash_find(global_wrapper_hash, protocol, protocol_len, (void**)&wrapper) == FAILURE) || !wrapper) {
|
|
php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s:// never existed, nothing to restore", protocol);
|
|
RETURN_FALSE;
|
|
}
|
|
|
|
/* A failure here could be okay given that the protocol might have been merely unregistered */
|
|
php_unregister_url_stream_wrapper_volatile(protocol TSRMLS_CC);
|
|
|
|
if (php_register_url_stream_wrapper_volatile(protocol, wrapper TSRMLS_CC) == FAILURE) {
|
|
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to restore original %s:// wrapper", protocol);
|
|
RETURN_FALSE;
|
|
}
|
|
|
|
RETURN_TRUE;
|
|
}
|
|
/* }}} */
|
|
|
|
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 *zbufptr;
|
|
size_t didwrite = 0;
|
|
|
|
assert(us != NULL);
|
|
|
|
ZVAL_STRINGL(&func_name, USERSTREAM_WRITE, sizeof(USERSTREAM_WRITE)-1, 0);
|
|
|
|
MAKE_STD_ZVAL(zbufptr);
|
|
ZVAL_STRINGL(zbufptr, (char*)buf, count, 1);;
|
|
args[0] = &zbufptr;
|
|
|
|
call_result = call_user_function_ex(NULL,
|
|
&us->object,
|
|
&func_name,
|
|
&retval,
|
|
1, args,
|
|
0, NULL TSRMLS_CC);
|
|
zval_ptr_dtor(&zbufptr);
|
|
|
|
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);
|
|
#ifdef NETWARE
|
|
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 = zval_is_true(retval) ? PHP_STREAM_OPTION_RETURN_ERR : PHP_STREAM_OPTION_RETURN_OK;
|
|
} 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 */
|
|
};
|
|
|
|
|