root/ext/standard/user_filters.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. PHP_FUNCTION
  2. ZEND_RSRC_DTOR_FUNC
  3. PHP_MINIT_FUNCTION
  4. PHP_RSHUTDOWN_FUNCTION
  5. userfilter_dtor
  6. userfilter_filter
  7. user_filter_factory_create
  8. filter_item_dtor
  9. PHP_FUNCTION
  10. php_stream_bucket_attach
  11. PHP_FUNCTION
  12. PHP_FUNCTION
  13. PHP_FUNCTION
  14. PHP_FUNCTION
  15. PHP_FUNCTION

   1 /*
   2    +----------------------------------------------------------------------+
   3    | PHP Version 5                                                        |
   4    +----------------------------------------------------------------------+
   5    | Copyright (c) 1997-2016 The PHP Group                                |
   6    +----------------------------------------------------------------------+
   7    | This source file is subject to version 3.01 of the PHP license,      |
   8    | that is bundled with this package in the file LICENSE, and is        |
   9    | available through the world-wide-web at the following url:           |
  10    | http://www.php.net/license/3_01.txt                                  |
  11    | If you did not receive a copy of the PHP license and are unable to   |
  12    | obtain it through the world-wide-web, please send a note to          |
  13    | license@php.net so we can mail you a copy immediately.               |
  14    +----------------------------------------------------------------------+
  15    | Authors:                                                             |
  16    | Wez Furlong (wez@thebrainroom.com)                                   |
  17    | Sara Golemon (pollita@php.net)                                       |
  18    +----------------------------------------------------------------------+
  19 */
  20 
  21 /* $Id$ */
  22 
  23 #include "php.h"
  24 #include "php_globals.h"
  25 #include "ext/standard/basic_functions.h"
  26 #include "ext/standard/file.h"
  27 
  28 #define PHP_STREAM_BRIGADE_RES_NAME     "userfilter.bucket brigade"
  29 #define PHP_STREAM_BUCKET_RES_NAME "userfilter.bucket"
  30 #define PHP_STREAM_FILTER_RES_NAME "userfilter.filter"
  31 
  32 struct php_user_filter_data {
  33         zend_class_entry *ce;
  34         /* variable length; this *must* be last in the structure */
  35         char classname[1];
  36 };
  37 
  38 /* to provide context for calling into the next filter from user-space */
  39 static int le_userfilters;
  40 static int le_bucket_brigade;
  41 static int le_bucket;
  42 
  43 #define GET_FILTER_FROM_OBJ()   { \
  44         zval **tmp; \
  45         if (FAILURE == zend_hash_index_find(Z_OBJPROP_P(this_ptr), 0, (void**)&tmp)) { \
  46                 php_error_docref(NULL TSRMLS_CC, E_WARNING, "filter property vanished"); \
  47                 RETURN_FALSE; \
  48         } \
  49         ZEND_FETCH_RESOURCE(filter, php_stream_filter*, tmp, -1, "filter", le_userfilters); \
  50 }
  51 
  52 /* define the base filter class */
  53 
  54 PHP_FUNCTION(user_filter_nop)
  55 {
  56 }
  57 ZEND_BEGIN_ARG_INFO(arginfo_php_user_filter_filter, 0)
  58         ZEND_ARG_INFO(0, in)
  59         ZEND_ARG_INFO(0, out)
  60         ZEND_ARG_INFO(1, consumed)
  61         ZEND_ARG_INFO(0, closing)
  62 ZEND_END_ARG_INFO()
  63 
  64 ZEND_BEGIN_ARG_INFO(arginfo_php_user_filter_onCreate, 0)
  65 ZEND_END_ARG_INFO()
  66 
  67 ZEND_BEGIN_ARG_INFO(arginfo_php_user_filter_onClose, 0)
  68 ZEND_END_ARG_INFO()
  69 
  70 static const zend_function_entry user_filter_class_funcs[] = {
  71         PHP_NAMED_FE(filter,    PHP_FN(user_filter_nop),                arginfo_php_user_filter_filter)
  72         PHP_NAMED_FE(onCreate,  PHP_FN(user_filter_nop),                arginfo_php_user_filter_onCreate)
  73         PHP_NAMED_FE(onClose,   PHP_FN(user_filter_nop),                arginfo_php_user_filter_onClose)
  74         PHP_FE_END
  75 };
  76 
  77 static zend_class_entry user_filter_class_entry;
  78 
  79 static ZEND_RSRC_DTOR_FUNC(php_bucket_dtor)
  80 {
  81         php_stream_bucket *bucket = (php_stream_bucket *)rsrc->ptr;
  82         if (bucket) {
  83                 php_stream_bucket_delref(bucket TSRMLS_CC);
  84                 bucket = NULL;
  85         }
  86 }
  87 
  88 PHP_MINIT_FUNCTION(user_filters)
  89 {
  90         zend_class_entry *php_user_filter;
  91         /* init the filter class ancestor */
  92         INIT_CLASS_ENTRY(user_filter_class_entry, "php_user_filter", user_filter_class_funcs);
  93         if ((php_user_filter = zend_register_internal_class(&user_filter_class_entry TSRMLS_CC)) == NULL) {
  94                 return FAILURE;
  95         }
  96         zend_declare_property_string(php_user_filter, "filtername", sizeof("filtername")-1, "", ZEND_ACC_PUBLIC TSRMLS_CC);
  97         zend_declare_property_string(php_user_filter, "params", sizeof("params")-1, "", ZEND_ACC_PUBLIC TSRMLS_CC);
  98 
  99         /* init the filter resource; it has no dtor, as streams will always clean it up
 100          * at the correct time */
 101         le_userfilters = zend_register_list_destructors_ex(NULL, NULL, PHP_STREAM_FILTER_RES_NAME, 0);
 102 
 103         if (le_userfilters == FAILURE) {
 104                 return FAILURE;
 105         }
 106 
 107         /* Filters will dispose of their brigades */
 108         le_bucket_brigade = zend_register_list_destructors_ex(NULL, NULL, PHP_STREAM_BRIGADE_RES_NAME, module_number);
 109         /* Brigades will dispose of their buckets */
 110         le_bucket = zend_register_list_destructors_ex(php_bucket_dtor, NULL, PHP_STREAM_BUCKET_RES_NAME, module_number);
 111         
 112         if (le_bucket_brigade == FAILURE) {
 113                 return FAILURE;
 114         }
 115 
 116         REGISTER_LONG_CONSTANT("PSFS_PASS_ON",                  PSFS_PASS_ON,                   CONST_CS | CONST_PERSISTENT);
 117         REGISTER_LONG_CONSTANT("PSFS_FEED_ME",                  PSFS_FEED_ME,                   CONST_CS | CONST_PERSISTENT);
 118         REGISTER_LONG_CONSTANT("PSFS_ERR_FATAL",                PSFS_ERR_FATAL,                 CONST_CS | CONST_PERSISTENT);
 119 
 120         REGISTER_LONG_CONSTANT("PSFS_FLAG_NORMAL",              PSFS_FLAG_NORMAL,               CONST_CS | CONST_PERSISTENT);
 121         REGISTER_LONG_CONSTANT("PSFS_FLAG_FLUSH_INC",   PSFS_FLAG_FLUSH_INC,    CONST_CS | CONST_PERSISTENT);
 122         REGISTER_LONG_CONSTANT("PSFS_FLAG_FLUSH_CLOSE", PSFS_FLAG_FLUSH_CLOSE,  CONST_CS | CONST_PERSISTENT);
 123         
 124         return SUCCESS;
 125 }
 126 
 127 PHP_RSHUTDOWN_FUNCTION(user_filters)
 128 {
 129         if (BG(user_filter_map)) {
 130                 zend_hash_destroy(BG(user_filter_map));
 131                 efree(BG(user_filter_map));
 132                 BG(user_filter_map) = NULL;
 133         }
 134 
 135         return SUCCESS;
 136 }
 137 
 138 static void userfilter_dtor(php_stream_filter *thisfilter TSRMLS_DC)
 139 {
 140         zval *obj = (zval*)thisfilter->abstract;
 141         zval func_name;
 142         zval *retval = NULL;
 143 
 144         if (obj == NULL) {
 145                 /* If there's no object associated then there's nothing to dispose of */
 146                 return;
 147         }
 148 
 149         ZVAL_STRINGL(&func_name, "onclose", sizeof("onclose")-1, 0);
 150 
 151         call_user_function_ex(NULL,
 152                         &obj,
 153                         &func_name,
 154                         &retval,
 155                         0, NULL,
 156                         0, NULL TSRMLS_CC);
 157 
 158         if (retval)
 159                 zval_ptr_dtor(&retval);
 160 
 161         /* kill the object */
 162         zval_ptr_dtor(&obj);
 163 }
 164 
 165 php_stream_filter_status_t userfilter_filter(
 166                         php_stream *stream,
 167                         php_stream_filter *thisfilter,
 168                         php_stream_bucket_brigade *buckets_in,
 169                         php_stream_bucket_brigade *buckets_out,
 170                         size_t *bytes_consumed,
 171                         int flags
 172                         TSRMLS_DC)
 173 {
 174         int ret = PSFS_ERR_FATAL;
 175         zval *obj = (zval*)thisfilter->abstract;
 176         zval func_name;
 177         zval *retval = NULL;
 178         zval **args[4];
 179         zval *zclosing, *zconsumed, *zin, *zout, *zstream;
 180         zval zpropname;
 181         int call_result;
 182 
 183         /* the userfilter object probably doesn't exist anymore */
 184         if (CG(unclean_shutdown)) {
 185                 return ret;
 186         }
 187 
 188         if (FAILURE == zend_hash_find(Z_OBJPROP_P(obj), "stream", sizeof("stream"), (void**)&zstream)) {
 189                 /* Give the userfilter class a hook back to the stream */
 190                 ALLOC_INIT_ZVAL(zstream);
 191                 php_stream_to_zval(stream, zstream);
 192                 zval_copy_ctor(zstream);
 193                 add_property_zval(obj, "stream", zstream);
 194                 /* add_property_zval increments the refcount which is unwanted here */
 195                 zval_ptr_dtor(&zstream);
 196         }
 197 
 198         ZVAL_STRINGL(&func_name, "filter", sizeof("filter")-1, 0);
 199 
 200         /* Setup calling arguments */
 201         ALLOC_INIT_ZVAL(zin);
 202         ZEND_REGISTER_RESOURCE(zin, buckets_in, le_bucket_brigade);
 203         args[0] = &zin;
 204 
 205         ALLOC_INIT_ZVAL(zout);
 206         ZEND_REGISTER_RESOURCE(zout, buckets_out, le_bucket_brigade);
 207         args[1] = &zout;
 208 
 209         ALLOC_INIT_ZVAL(zconsumed);
 210         if (bytes_consumed) {
 211                 ZVAL_LONG(zconsumed, *bytes_consumed);
 212         } else {
 213                 ZVAL_NULL(zconsumed);
 214         }
 215         args[2] = &zconsumed;
 216 
 217         ALLOC_INIT_ZVAL(zclosing);
 218         ZVAL_BOOL(zclosing, flags & PSFS_FLAG_FLUSH_CLOSE);
 219         args[3] = &zclosing;
 220 
 221         call_result = call_user_function_ex(NULL,
 222                         &obj,
 223                         &func_name,
 224                         &retval,
 225                         4, args,
 226                         0, NULL TSRMLS_CC);
 227 
 228         if (call_result == SUCCESS && retval != NULL) {
 229                 convert_to_long(retval);
 230                 ret = Z_LVAL_P(retval);
 231         } else if (call_result == FAILURE) {
 232                 php_error_docref(NULL TSRMLS_CC, E_WARNING, "failed to call filter function");
 233         }
 234 
 235         if (bytes_consumed) {
 236                 *bytes_consumed = Z_LVAL_P(zconsumed);
 237         }
 238 
 239         if (retval) {
 240                 zval_ptr_dtor(&retval);
 241         }
 242 
 243         if (buckets_in->head) {
 244                 php_stream_bucket *bucket = buckets_in->head;
 245 
 246                 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unprocessed filter buckets remaining on input brigade");
 247                 while ((bucket = buckets_in->head)) {
 248                         /* Remove unconsumed buckets from the brigade */
 249                         php_stream_bucket_unlink(bucket TSRMLS_CC);
 250                         php_stream_bucket_delref(bucket TSRMLS_CC);
 251                 }
 252         }
 253         if (ret != PSFS_PASS_ON) {
 254                 php_stream_bucket *bucket = buckets_out->head;
 255                 while (bucket != NULL) {
 256                         php_stream_bucket_unlink(bucket TSRMLS_CC);
 257                         php_stream_bucket_delref(bucket TSRMLS_CC);
 258                         bucket = buckets_out->head;
 259                 }
 260         }
 261 
 262         /* filter resources are cleaned up by the stream destructor,
 263          * keeping a reference to the stream resource here would prevent it
 264          * from being destroyed properly */
 265         INIT_ZVAL(zpropname);
 266         ZVAL_STRINGL(&zpropname, "stream", sizeof("stream")-1, 0);
 267         Z_OBJ_HANDLER_P(obj, unset_property)(obj, &zpropname, 0 TSRMLS_CC);
 268 
 269         zval_ptr_dtor(&zclosing);
 270         zval_ptr_dtor(&zconsumed);
 271         zval_ptr_dtor(&zout);
 272         zval_ptr_dtor(&zin);
 273 
 274         return ret;
 275 }
 276 
 277 static php_stream_filter_ops userfilter_ops = {
 278         userfilter_filter,
 279         userfilter_dtor,
 280         "user-filter"
 281 };
 282 
 283 static php_stream_filter *user_filter_factory_create(const char *filtername,
 284                 zval *filterparams, int persistent TSRMLS_DC)
 285 {
 286         struct php_user_filter_data *fdat = NULL;
 287         php_stream_filter *filter;
 288         zval *obj, *zfilter;
 289         zval func_name;
 290         zval *retval = NULL;
 291         int len;
 292         
 293         /* some sanity checks */
 294         if (persistent) {
 295                 php_error_docref(NULL TSRMLS_CC, E_WARNING,
 296                                 "cannot use a user-space filter with a persistent stream");
 297                 return NULL;
 298         }
 299 
 300         len = strlen(filtername);
 301 
 302         /* determine the classname/class entry */
 303         if (FAILURE == zend_hash_find(BG(user_filter_map), (char*)filtername, len + 1, (void**)&fdat)) {
 304                 char *period;
 305 
 306                 /* Userspace Filters using ambiguous wildcards could cause problems.
 307            i.e.: myfilter.foo.bar will always call into myfilter.foo.*
 308                  never seeing myfilter.* 
 309            TODO: Allow failed userfilter creations to continue
 310                  scanning through the list */
 311                 if ((period = strrchr(filtername, '.'))) {
 312                         char *wildcard = emalloc(len + 3);
 313 
 314                         /* Search for wildcard matches instead */
 315                         memcpy(wildcard, filtername, len + 1); /* copy \0 */
 316                         period = wildcard + (period - filtername);
 317                         while (period) {
 318                                 *period = '\0';
 319                                 strncat(wildcard, ".*", 2);
 320                                 if (SUCCESS == zend_hash_find(BG(user_filter_map), wildcard, strlen(wildcard) + 1, (void**)&fdat)) {
 321                                         period = NULL;
 322                                 } else {
 323                                         *period = '\0';
 324                                         period = strrchr(wildcard, '.');
 325                                 }
 326                         }
 327                         efree(wildcard);
 328                 }
 329                 if (fdat == NULL) {
 330                         php_error_docref(NULL TSRMLS_CC, E_WARNING,
 331                                         "Err, filter \"%s\" is not in the user-filter map, but somehow the user-filter-factory was invoked for it!?", filtername);
 332                         return NULL;
 333                 }
 334         }
 335 
 336         /* bind the classname to the actual class */
 337         if (fdat->ce == NULL) {
 338                 if (FAILURE == zend_lookup_class(fdat->classname, strlen(fdat->classname),
 339                                         (zend_class_entry ***)&fdat->ce TSRMLS_CC)) {
 340                         php_error_docref(NULL TSRMLS_CC, E_WARNING,
 341                                         "user-filter \"%s\" requires class \"%s\", but that class is not defined",
 342                                         filtername, fdat->classname);
 343                         return NULL;
 344                 }
 345                 fdat->ce = *(zend_class_entry**)fdat->ce;
 346 
 347         }
 348 
 349         filter = php_stream_filter_alloc(&userfilter_ops, NULL, 0);
 350         if (filter == NULL) {
 351                 return NULL;
 352         }
 353 
 354         /* create the object */
 355         ALLOC_ZVAL(obj);
 356         object_init_ex(obj, fdat->ce);
 357         Z_SET_REFCOUNT_P(obj, 1);
 358         Z_SET_ISREF_P(obj);
 359 
 360         /* filtername */
 361         add_property_string(obj, "filtername", (char*)filtername, 1);
 362         
 363         /* and the parameters, if any */
 364         if (filterparams) {
 365                 add_property_zval(obj, "params", filterparams);
 366         } else {
 367                 add_property_null(obj, "params");
 368         }
 369 
 370         /* invoke the constructor */
 371         ZVAL_STRINGL(&func_name, "oncreate", sizeof("oncreate")-1, 0);
 372 
 373         call_user_function_ex(NULL,
 374                         &obj,
 375                         &func_name,
 376                         &retval,
 377                         0, NULL,
 378                         0, NULL TSRMLS_CC);
 379 
 380         if (retval) {
 381                 if (Z_TYPE_P(retval) == IS_BOOL && Z_LVAL_P(retval) == 0) {
 382                         /* User reported filter creation error "return false;" */
 383                         zval_ptr_dtor(&retval);
 384 
 385                         /* Kill the filter (safely) */
 386                         filter->abstract = NULL;
 387                         php_stream_filter_free(filter TSRMLS_CC);
 388 
 389                         /* Kill the object */
 390                         zval_ptr_dtor(&obj);
 391 
 392                         /* Report failure to filter_alloc */
 393                         return NULL;
 394                 }                       
 395                 zval_ptr_dtor(&retval);
 396         }
 397 
 398         /* set the filter property, this will be used during cleanup */
 399         ALLOC_INIT_ZVAL(zfilter);
 400         ZEND_REGISTER_RESOURCE(zfilter, filter, le_userfilters);
 401         filter->abstract = obj;
 402         add_property_zval(obj, "filter", zfilter);
 403         /* add_property_zval increments the refcount which is unwanted here */
 404         zval_ptr_dtor(&zfilter);
 405 
 406         return filter;
 407 }
 408 
 409 static php_stream_filter_factory user_filter_factory = {
 410         user_filter_factory_create
 411 };
 412 
 413 static void filter_item_dtor(struct php_user_filter_data *fdat)
 414 {
 415 }
 416 
 417 /* {{{ proto object stream_bucket_make_writeable(resource brigade)
 418    Return a bucket object from the brigade for operating on */
 419 PHP_FUNCTION(stream_bucket_make_writeable)
 420 {
 421         zval *zbrigade, *zbucket;
 422         php_stream_bucket_brigade *brigade;
 423         php_stream_bucket *bucket;
 424 
 425         if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &zbrigade) == FAILURE) {
 426                 RETURN_FALSE;
 427         }
 428 
 429         ZEND_FETCH_RESOURCE(brigade, php_stream_bucket_brigade *, &zbrigade, -1, PHP_STREAM_BRIGADE_RES_NAME, le_bucket_brigade); 
 430 
 431         ZVAL_NULL(return_value);
 432 
 433         if (brigade->head && (bucket = php_stream_bucket_make_writeable(brigade->head TSRMLS_CC))) {
 434                 ALLOC_INIT_ZVAL(zbucket);
 435                 ZEND_REGISTER_RESOURCE(zbucket, bucket, le_bucket);
 436                 object_init(return_value);
 437                 add_property_zval(return_value, "bucket", zbucket);
 438                 /* add_property_zval increments the refcount which is unwanted here */
 439                 zval_ptr_dtor(&zbucket);
 440                 add_property_stringl(return_value, "data", bucket->buf, bucket->buflen, 1);
 441                 add_property_long(return_value, "datalen", bucket->buflen);
 442         }
 443 }
 444 /* }}} */
 445 
 446 /* {{{ php_stream_bucket_attach */
 447 static void php_stream_bucket_attach(int append, INTERNAL_FUNCTION_PARAMETERS)
 448 {
 449         zval *zbrigade, *zobject;
 450         zval **pzbucket, **pzdata;
 451         php_stream_bucket_brigade *brigade;
 452         php_stream_bucket *bucket;
 453 
 454         if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zo", &zbrigade, &zobject) == FAILURE) {
 455                 RETURN_FALSE;
 456         }
 457 
 458         if (FAILURE == zend_hash_find(Z_OBJPROP_P(zobject), "bucket", 7, (void**)&pzbucket)) {
 459                 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Object has no bucket property");
 460                 RETURN_FALSE;
 461         }
 462 
 463         ZEND_FETCH_RESOURCE(brigade, php_stream_bucket_brigade *, &zbrigade, -1, PHP_STREAM_BRIGADE_RES_NAME, le_bucket_brigade);
 464         ZEND_FETCH_RESOURCE(bucket, php_stream_bucket *, pzbucket, -1, PHP_STREAM_BUCKET_RES_NAME, le_bucket);
 465 
 466         if (SUCCESS == zend_hash_find(Z_OBJPROP_P(zobject), "data", 5, (void**)&pzdata) && (*pzdata)->type == IS_STRING) {
 467                 if (!bucket->own_buf) {
 468                         bucket = php_stream_bucket_make_writeable(bucket TSRMLS_CC);
 469                 }
 470                 if ((int)bucket->buflen != Z_STRLEN_PP(pzdata)) {
 471                         bucket->buf = perealloc(bucket->buf, Z_STRLEN_PP(pzdata), bucket->is_persistent);
 472                         bucket->buflen = Z_STRLEN_PP(pzdata);
 473                 }
 474                 memcpy(bucket->buf, Z_STRVAL_PP(pzdata), bucket->buflen);
 475         }
 476 
 477         if (append) {
 478                 php_stream_bucket_append(brigade, bucket TSRMLS_CC);
 479         } else {
 480                 php_stream_bucket_prepend(brigade, bucket TSRMLS_CC);
 481         }
 482         /* This is a hack necessary to accommodate situations where bucket is appended to the stream
 483          * multiple times. See bug35916.phpt for reference.
 484          */
 485         if (bucket->refcount == 1) {
 486                 bucket->refcount++;
 487         }
 488 }
 489 /* }}} */
 490 
 491 /* {{{ proto void stream_bucket_prepend(resource brigade, object bucket)
 492    Prepend bucket to brigade */
 493 PHP_FUNCTION(stream_bucket_prepend)
 494 {
 495         php_stream_bucket_attach(0, INTERNAL_FUNCTION_PARAM_PASSTHRU);
 496 }
 497 /* }}} */
 498 
 499 /* {{{ proto void stream_bucket_append(resource brigade, object bucket)
 500    Append bucket to brigade */
 501 PHP_FUNCTION(stream_bucket_append)
 502 {
 503         php_stream_bucket_attach(1, INTERNAL_FUNCTION_PARAM_PASSTHRU);
 504 }
 505 /* }}} */
 506 
 507 /* {{{ proto resource stream_bucket_new(resource stream, string buffer)
 508    Create a new bucket for use on the current stream */
 509 PHP_FUNCTION(stream_bucket_new)
 510 {
 511         zval *zstream, *zbucket;
 512         php_stream *stream;
 513         char *buffer;
 514         char *pbuffer;
 515         int buffer_len;
 516         php_stream_bucket *bucket;
 517 
 518         if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zs", &zstream, &buffer, &buffer_len) == FAILURE) {
 519                 RETURN_FALSE;
 520         }
 521 
 522         php_stream_from_zval(stream, &zstream);
 523 
 524         if (!(pbuffer = pemalloc(buffer_len, php_stream_is_persistent(stream)))) {
 525                 RETURN_FALSE;
 526         }
 527 
 528         memcpy(pbuffer, buffer, buffer_len);
 529 
 530         bucket = php_stream_bucket_new(stream, pbuffer, buffer_len, 1, php_stream_is_persistent(stream) TSRMLS_CC);
 531         
 532         if (bucket == NULL) {
 533                 RETURN_FALSE;
 534         }
 535 
 536         ALLOC_INIT_ZVAL(zbucket);
 537         ZEND_REGISTER_RESOURCE(zbucket, bucket, le_bucket);
 538         object_init(return_value);
 539         add_property_zval(return_value, "bucket", zbucket);
 540         /* add_property_zval increments the refcount which is unwanted here */
 541         zval_ptr_dtor(&zbucket);
 542         add_property_stringl(return_value, "data", bucket->buf, bucket->buflen, 1);
 543         add_property_long(return_value, "datalen", bucket->buflen);
 544 }
 545 /* }}} */
 546 
 547 /* {{{ proto array stream_get_filters(void)
 548    Returns a list of registered filters */
 549 PHP_FUNCTION(stream_get_filters)
 550 {
 551         char *filter_name;
 552         int key_flags;
 553         uint filter_name_len = 0;
 554         HashTable *filters_hash;
 555         ulong num_key;
 556 
 557         if (zend_parse_parameters_none() == FAILURE) {
 558                 return;
 559         }
 560 
 561         array_init(return_value);
 562 
 563         filters_hash = php_get_stream_filters_hash();
 564 
 565         if (filters_hash) {
 566                 for(zend_hash_internal_pointer_reset(filters_hash);
 567                         (key_flags = zend_hash_get_current_key_ex(filters_hash, &filter_name, &filter_name_len, &num_key, 0, NULL)) != HASH_KEY_NON_EXISTENT;
 568                         zend_hash_move_forward(filters_hash))
 569                                 if (key_flags == HASH_KEY_IS_STRING) {
 570                                         add_next_index_stringl(return_value, filter_name, filter_name_len - 1, 1);
 571                                 }
 572         }
 573         /* It's okay to return an empty array if no filters are registered */
 574 }
 575 /* }}} */       
 576 
 577 /* {{{ proto bool stream_filter_register(string filtername, string classname)
 578    Registers a custom filter handler class */
 579 PHP_FUNCTION(stream_filter_register)
 580 {
 581         char *filtername, *classname;
 582         int filtername_len, classname_len;
 583         struct php_user_filter_data *fdat;
 584         
 585         if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &filtername, &filtername_len,
 586                                 &classname, &classname_len) == FAILURE) {
 587                 RETURN_FALSE;
 588         }
 589 
 590         RETVAL_FALSE;
 591 
 592         if (!filtername_len) {
 593                 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Filter name cannot be empty");
 594                 return;
 595         }
 596 
 597         if (!classname_len) {
 598                 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Class name cannot be empty");
 599                 return;
 600         }
 601 
 602         if (!BG(user_filter_map)) {
 603                 BG(user_filter_map) = (HashTable*) emalloc(sizeof(HashTable));
 604                 zend_hash_init(BG(user_filter_map), 5, NULL, (dtor_func_t) filter_item_dtor, 0);
 605         }
 606 
 607         fdat = ecalloc(1, sizeof(struct php_user_filter_data) + classname_len);
 608         memcpy(fdat->classname, classname, classname_len);
 609 
 610         if (zend_hash_add(BG(user_filter_map), filtername, filtername_len + 1, (void*)fdat,
 611                                 sizeof(*fdat) + classname_len, NULL) == SUCCESS &&
 612                         php_stream_filter_register_factory_volatile(filtername, &user_filter_factory TSRMLS_CC) == SUCCESS) {
 613                 RETVAL_TRUE;
 614         }
 615 
 616         efree(fdat);
 617 }
 618 /* }}} */
 619 
 620 
 621 /*
 622  * Local variables:
 623  * tab-width: 4
 624  * c-basic-offset: 4
 625  * End:
 626  * vim600: sw=4 ts=4 fdm=marker
 627  * vim<600: sw=4 ts=4
 628  */

/* [<][>][^][v][top][bottom][index][help] */