mirror of
https://github.com/php/php-src.git
synced 2024-10-18 23:12:35 +00:00
448 lines
12 KiB
C
448 lines
12 KiB
C
|
/*
|
||
|
+----------------------------------------------------------------------+
|
||
|
| PHP Version 4 |
|
||
|
+----------------------------------------------------------------------+
|
||
|
| Copyright (c) 1997-2003 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) |
|
||
|
+----------------------------------------------------------------------+
|
||
|
*/
|
||
|
|
||
|
/* $Id$ */
|
||
|
|
||
|
#include "php.h"
|
||
|
#include "php_globals.h"
|
||
|
#include "ext/standard/basic_functions.h"
|
||
|
#include "ext/standard/file.h"
|
||
|
|
||
|
struct php_user_filter_data {
|
||
|
zend_class_entry *ce;
|
||
|
/* variable length; this *must* be last in the structure */
|
||
|
char classname[1];
|
||
|
};
|
||
|
|
||
|
/* to provide context for calling into the next filter from user-space */
|
||
|
static int le_userfilters;
|
||
|
|
||
|
#define GET_FILTER_FROM_OBJ() { \
|
||
|
zval **tmp; \
|
||
|
if (FAILURE == zend_hash_index_find(Z_OBJPROP_P(this_ptr), 0, (void**)&tmp)) { \
|
||
|
php_error_docref(NULL TSRMLS_CC, E_WARNING, "filter property vanished"); \
|
||
|
RETURN_FALSE; \
|
||
|
} \
|
||
|
ZEND_FETCH_RESOURCE(filter, php_stream_filter*, tmp, -1, "filter", le_userfilters); \
|
||
|
}
|
||
|
|
||
|
/* define the base filter class */
|
||
|
|
||
|
/* Descendants call this function to actually send the data on to the next
|
||
|
* filter (or the stream itself).
|
||
|
* The intention is to invoke it as parent::write($data)
|
||
|
* */
|
||
|
PHP_FUNCTION(user_filter_write)
|
||
|
{
|
||
|
char *data;
|
||
|
int data_len;
|
||
|
size_t wrote = 0;
|
||
|
php_stream_filter *filter;
|
||
|
|
||
|
GET_FILTER_FROM_OBJ();
|
||
|
|
||
|
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &data, &data_len) == FAILURE) {
|
||
|
RETURN_FALSE;
|
||
|
}
|
||
|
|
||
|
wrote = php_stream_filter_write_next(filter->stream, filter, data, data_len);
|
||
|
|
||
|
RETURN_LONG(wrote);
|
||
|
}
|
||
|
|
||
|
PHP_FUNCTION(user_filter_read)
|
||
|
{
|
||
|
long data_to_read;
|
||
|
char *data;
|
||
|
size_t didread = 0;
|
||
|
php_stream_filter *filter;
|
||
|
|
||
|
RETVAL_FALSE;
|
||
|
|
||
|
GET_FILTER_FROM_OBJ();
|
||
|
|
||
|
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &data_to_read) == FAILURE) {
|
||
|
RETURN_FALSE;
|
||
|
}
|
||
|
|
||
|
data = emalloc(data_to_read + 1);
|
||
|
didread = php_stream_filter_read_next(filter->stream, filter, data, data_to_read);
|
||
|
|
||
|
if (didread > 0) {
|
||
|
data = erealloc(data, didread + 1);
|
||
|
RETURN_STRINGL(data, didread, 0);
|
||
|
} else {
|
||
|
efree(data);
|
||
|
RETURN_FALSE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
PHP_FUNCTION(user_filter_flush)
|
||
|
{
|
||
|
zend_bool closing;
|
||
|
php_stream_filter *filter;
|
||
|
|
||
|
GET_FILTER_FROM_OBJ();
|
||
|
|
||
|
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "b", &closing) == FAILURE) {
|
||
|
RETURN_FALSE;
|
||
|
}
|
||
|
|
||
|
RETURN_LONG(php_stream_filter_flush_next(filter->stream, filter, closing));
|
||
|
}
|
||
|
|
||
|
PHP_FUNCTION(user_filter_nop)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
static zend_function_entry user_filter_class_funcs[] = {
|
||
|
PHP_NAMED_FE(write, PHP_FN(user_filter_write), NULL)
|
||
|
PHP_NAMED_FE(read, PHP_FN(user_filter_read), NULL)
|
||
|
PHP_NAMED_FE(flush, PHP_FN(user_filter_flush), NULL)
|
||
|
PHP_NAMED_FE(oncreate, PHP_FN(user_filter_nop), NULL)
|
||
|
PHP_NAMED_FE(onclose, PHP_FN(user_filter_nop), NULL)
|
||
|
{ NULL, NULL, NULL }
|
||
|
};
|
||
|
|
||
|
static zend_class_entry user_filter_class_entry;
|
||
|
|
||
|
PHP_MINIT_FUNCTION(user_filters)
|
||
|
{
|
||
|
/* init the filter class ancestor */
|
||
|
INIT_CLASS_ENTRY(user_filter_class_entry, "php_user_filter", user_filter_class_funcs);
|
||
|
if (NULL == zend_register_internal_class(&user_filter_class_entry TSRMLS_CC)) {
|
||
|
return FAILURE;
|
||
|
}
|
||
|
|
||
|
/* init the filter resource; it has no dtor, as streams will always clean it up
|
||
|
* at the correct time */
|
||
|
le_userfilters = zend_register_list_destructors_ex(NULL, NULL, "stream filter", 0);
|
||
|
|
||
|
if (le_userfilters == FAILURE)
|
||
|
return FAILURE;
|
||
|
|
||
|
return SUCCESS;
|
||
|
}
|
||
|
|
||
|
static size_t userfilter_write(php_stream *stream, php_stream_filter *thisfilter,
|
||
|
const char *buf, size_t count TSRMLS_DC)
|
||
|
{
|
||
|
size_t wrote = 0;
|
||
|
zval *obj = (zval*)thisfilter->abstract;
|
||
|
zval func_name;
|
||
|
zval *retval = NULL;
|
||
|
zval **args[1];
|
||
|
zval *zbuf;
|
||
|
int call_result;
|
||
|
|
||
|
ZVAL_STRINGL(&func_name, "write", sizeof("write")-1, 0);
|
||
|
|
||
|
ALLOC_INIT_ZVAL(zbuf);
|
||
|
ZVAL_STRINGL(zbuf, (char*)buf, count, 1);
|
||
|
|
||
|
args[0] = &zbuf;
|
||
|
|
||
|
call_result = call_user_function_ex(NULL,
|
||
|
&obj,
|
||
|
&func_name,
|
||
|
&retval,
|
||
|
1, args,
|
||
|
0, NULL TSRMLS_CC);
|
||
|
|
||
|
if (call_result == SUCCESS && retval != NULL) {
|
||
|
convert_to_long(retval);
|
||
|
wrote = Z_LVAL_P(retval);
|
||
|
} else if (call_result == FAILURE) {
|
||
|
php_error_docref(NULL TSRMLS_CC, E_WARNING, "failed to call user-filter write function!?");
|
||
|
}
|
||
|
|
||
|
/* beware of buffer overruns */
|
||
|
if (wrote > count) {
|
||
|
php_error_docref(NULL TSRMLS_CC, E_WARNING,
|
||
|
"wrote %d bytes more data than requested (%d written, %d max)",
|
||
|
wrote - count,
|
||
|
wrote,
|
||
|
count);
|
||
|
wrote = count;
|
||
|
}
|
||
|
|
||
|
if (retval)
|
||
|
zval_ptr_dtor(&retval);
|
||
|
|
||
|
return wrote;
|
||
|
}
|
||
|
|
||
|
static size_t userfilter_read(php_stream *stream, php_stream_filter *thisfilter,
|
||
|
char *buf, size_t count TSRMLS_DC)
|
||
|
{
|
||
|
size_t didread = 0;
|
||
|
zval *obj = (zval*)thisfilter->abstract;
|
||
|
zval func_name;
|
||
|
zval *retval = NULL;
|
||
|
zval **args[1];
|
||
|
zval *zcount;
|
||
|
int call_result;
|
||
|
|
||
|
ZVAL_STRINGL(&func_name, "read", sizeof("read")-1, 0);
|
||
|
|
||
|
ALLOC_INIT_ZVAL(zcount);
|
||
|
ZVAL_LONG(zcount, count);
|
||
|
args[0] = &zcount;
|
||
|
|
||
|
call_result = call_user_function_ex(NULL,
|
||
|
&obj,
|
||
|
&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,
|
||
|
"read %d bytes more data than requested (%d read, %d max) - excess data will be lost",
|
||
|
didread - count, didread, 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, "failed to call read function!");
|
||
|
}
|
||
|
|
||
|
if (retval)
|
||
|
zval_ptr_dtor(&retval);
|
||
|
|
||
|
zval_ptr_dtor(&zcount);
|
||
|
|
||
|
return didread;
|
||
|
}
|
||
|
|
||
|
static int userfilter_flush(php_stream *stream, php_stream_filter *thisfilter, int closing TSRMLS_DC)
|
||
|
{
|
||
|
int ret = EOF;
|
||
|
zval *obj = (zval*)thisfilter->abstract;
|
||
|
zval func_name;
|
||
|
zval *retval = NULL;
|
||
|
zval **args[1];
|
||
|
zval *zcount;
|
||
|
int call_result;
|
||
|
|
||
|
ZVAL_STRINGL(&func_name, "flush", sizeof("flush")-1, 0);
|
||
|
|
||
|
ALLOC_INIT_ZVAL(zcount);
|
||
|
ZVAL_BOOL(zcount, closing);
|
||
|
args[0] = &zcount;
|
||
|
|
||
|
call_result = call_user_function_ex(NULL,
|
||
|
&obj,
|
||
|
&func_name,
|
||
|
&retval,
|
||
|
1, args,
|
||
|
0, NULL TSRMLS_CC);
|
||
|
|
||
|
if (call_result == SUCCESS && retval != NULL) {
|
||
|
convert_to_long(retval);
|
||
|
ret = Z_LVAL_P(retval);
|
||
|
} else if (call_result == FAILURE) {
|
||
|
php_error_docref(NULL TSRMLS_CC, E_WARNING, "failed to call flush function");
|
||
|
}
|
||
|
|
||
|
if (retval)
|
||
|
zval_ptr_dtor(&retval);
|
||
|
zval_ptr_dtor(&zcount);
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
static int userfilter_eof(php_stream *stream, php_stream_filter *thisfilter TSRMLS_DC)
|
||
|
{
|
||
|
/* TODO: does this actually ever get called!? */
|
||
|
return php_stream_filter_eof_next(stream, thisfilter);
|
||
|
}
|
||
|
|
||
|
static void userfilter_dtor(php_stream_filter *thisfilter TSRMLS_DC)
|
||
|
{
|
||
|
zval *obj = (zval*)thisfilter->abstract;
|
||
|
zval func_name;
|
||
|
zval *retval = NULL;
|
||
|
zval **tmp;
|
||
|
|
||
|
ZVAL_STRINGL(&func_name, "onclose", sizeof("onclose")-1, 0);
|
||
|
|
||
|
call_user_function_ex(NULL,
|
||
|
&obj,
|
||
|
&func_name,
|
||
|
&retval,
|
||
|
0, NULL,
|
||
|
0, NULL TSRMLS_CC);
|
||
|
|
||
|
if (retval)
|
||
|
zval_ptr_dtor(&retval);
|
||
|
|
||
|
if (SUCCESS == zend_hash_index_find(Z_OBJPROP_P(obj), 0, (void**)&tmp)) {
|
||
|
zend_list_delete(Z_LVAL_PP(tmp));
|
||
|
FREE_ZVAL(*tmp);
|
||
|
}
|
||
|
|
||
|
/* kill the object */
|
||
|
zval_ptr_dtor(&obj);
|
||
|
}
|
||
|
|
||
|
static php_stream_filter_ops userfilter_ops = {
|
||
|
userfilter_write,
|
||
|
userfilter_read,
|
||
|
userfilter_flush,
|
||
|
userfilter_eof,
|
||
|
userfilter_dtor,
|
||
|
"user-filter"
|
||
|
};
|
||
|
|
||
|
static php_stream_filter *user_filter_factory_create(const char *filtername,
|
||
|
const char *filterparams, int filterparamslen, int persistent TSRMLS_DC)
|
||
|
{
|
||
|
struct php_user_filter_data *fdat = NULL;
|
||
|
php_stream_filter *filter;
|
||
|
zval *obj, *zfilter;
|
||
|
zval func_name;
|
||
|
zval *retval = NULL;
|
||
|
zval **tmp;
|
||
|
|
||
|
/* some sanity checks */
|
||
|
if (persistent) {
|
||
|
php_error_docref(NULL TSRMLS_CC, E_WARNING,
|
||
|
"cannot use a user-space filter with a persistent stream");
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
/* determine the classname/class entry */
|
||
|
if (FAILURE == zend_hash_find(BG(user_filter_map), (char*)filtername,
|
||
|
strlen(filtername), (void**)&fdat)) {
|
||
|
php_error_docref(NULL TSRMLS_CC, E_WARNING,
|
||
|
"Err, filter \"%s\" is not in the user-filter map, but somehow the user-filter-factory was invoked for it!?", filtername);
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
/* bind the classname to the actual class */
|
||
|
if (fdat->ce == NULL) {
|
||
|
if (FAILURE == zend_hash_find(EG(class_table), fdat->classname, strlen(fdat->classname)+1,
|
||
|
(void **)&fdat->ce)) {
|
||
|
php_error_docref(NULL TSRMLS_CC, E_WARNING,
|
||
|
"user-filter \"%s\" requires class \"%s\", but that class is not defined",
|
||
|
filtername, fdat->classname);
|
||
|
return NULL;
|
||
|
}
|
||
|
#ifdef ZEND_ENGINE_2
|
||
|
fdat->ce = *(zend_class_entry**)fdat->ce;
|
||
|
#endif
|
||
|
|
||
|
/* the class *must* be a descendant of the user-space filter
|
||
|
* base class, otherwise it will never work */
|
||
|
/* TODO: make this sanity check */
|
||
|
}
|
||
|
|
||
|
filter = php_stream_filter_alloc(&userfilter_ops, NULL, 0);
|
||
|
if (filter == NULL) {
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
ALLOC_INIT_ZVAL(zfilter);
|
||
|
ZEND_REGISTER_RESOURCE(zfilter, filter, le_userfilters);
|
||
|
|
||
|
/* create the object */
|
||
|
ALLOC_ZVAL(obj);
|
||
|
object_init_ex(obj, fdat->ce);
|
||
|
ZVAL_REFCOUNT(obj) = 1;
|
||
|
PZVAL_IS_REF(obj) = 1;
|
||
|
|
||
|
/* set the filter property */
|
||
|
filter->abstract = obj;
|
||
|
|
||
|
zend_hash_index_update(Z_OBJPROP_P(obj), 0, &zfilter, sizeof(zfilter), NULL);
|
||
|
|
||
|
/* filtername */
|
||
|
add_property_string(obj, "filtername", (char*)filtername, 1);
|
||
|
|
||
|
/* and the parameters, if any */
|
||
|
if (filterparams) {
|
||
|
add_property_stringl(obj, "params", (char*)filterparams, filterparamslen, 1);
|
||
|
} else {
|
||
|
add_property_null(obj, "params");
|
||
|
}
|
||
|
|
||
|
/* invoke the constructor */
|
||
|
ZVAL_STRINGL(&func_name, "oncreate", sizeof("oncreate")-1, 0);
|
||
|
|
||
|
call_user_function_ex(NULL,
|
||
|
&obj,
|
||
|
&func_name,
|
||
|
&retval,
|
||
|
0, NULL,
|
||
|
0, NULL TSRMLS_CC);
|
||
|
|
||
|
if (retval)
|
||
|
zval_ptr_dtor(&retval);
|
||
|
|
||
|
return filter;
|
||
|
}
|
||
|
|
||
|
static php_stream_filter_factory user_filter_factory = {
|
||
|
user_filter_factory_create
|
||
|
};
|
||
|
|
||
|
static void filter_item_dtor(struct php_user_filter_data *fdat)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
/* {{{ proto bool stream_register_filter(string filtername, string classname)
|
||
|
Registers a custom filter handler class */
|
||
|
PHP_FUNCTION(stream_register_filter)
|
||
|
{
|
||
|
char *filtername, *classname;
|
||
|
int filtername_len, classname_len;
|
||
|
struct php_user_filter_data *fdat;
|
||
|
|
||
|
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &filtername, &filtername_len,
|
||
|
&classname, &classname_len) == FAILURE) {
|
||
|
RETURN_FALSE;
|
||
|
}
|
||
|
|
||
|
RETVAL_FALSE;
|
||
|
|
||
|
if (!BG(user_filter_map)) {
|
||
|
BG(user_filter_map) = (HashTable*) emalloc(sizeof(HashTable));
|
||
|
zend_hash_init(BG(user_filter_map), 5, NULL, (dtor_func_t) filter_item_dtor, 0);
|
||
|
}
|
||
|
|
||
|
fdat = ecalloc(1, sizeof(*fdat) + classname_len);
|
||
|
memcpy(fdat->classname, classname, classname_len);
|
||
|
zend_str_tolower(fdat->classname, classname_len);
|
||
|
|
||
|
if (zend_hash_add(BG(user_filter_map), filtername, filtername_len, (void*)fdat,
|
||
|
sizeof(*fdat) + classname_len, NULL) == SUCCESS &&
|
||
|
php_stream_filter_register_factory(filtername, &user_filter_factory TSRMLS_CC) == SUCCESS) {
|
||
|
RETVAL_TRUE;
|
||
|
}
|
||
|
|
||
|
efree(fdat);
|
||
|
}
|
||
|
/* }}} */
|