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

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