1/* 2 +----------------------------------------------------------------------+ 3 | PHP Version 5 | 4 +----------------------------------------------------------------------+ 5 | Copyright (c) 1997-2013 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 32struct 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 */ 39static int le_userfilters; 40static int le_bucket_brigade; 41static 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 54PHP_FUNCTION(user_filter_nop) 55{ 56} 57ZEND_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) 62ZEND_END_ARG_INFO() 63 64ZEND_BEGIN_ARG_INFO(arginfo_php_user_filter_onCreate, 0) 65ZEND_END_ARG_INFO() 66 67ZEND_BEGIN_ARG_INFO(arginfo_php_user_filter_onClose, 0) 68ZEND_END_ARG_INFO() 69 70static 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 77static zend_class_entry user_filter_class_entry; 78 79static 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 88PHP_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 127PHP_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 138static 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 165php_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 if (FAILURE == zend_hash_find(Z_OBJPROP_P(obj), "stream", sizeof("stream"), (void**)&zstream)) { 184 /* Give the userfilter class a hook back to the stream */ 185 ALLOC_INIT_ZVAL(zstream); 186 php_stream_to_zval(stream, zstream); 187 zval_copy_ctor(zstream); 188 add_property_zval(obj, "stream", zstream); 189 /* add_property_zval increments the refcount which is unwanted here */ 190 zval_ptr_dtor(&zstream); 191 } 192 193 ZVAL_STRINGL(&func_name, "filter", sizeof("filter")-1, 0); 194 195 /* Setup calling arguments */ 196 ALLOC_INIT_ZVAL(zin); 197 ZEND_REGISTER_RESOURCE(zin, buckets_in, le_bucket_brigade); 198 args[0] = &zin; 199 200 ALLOC_INIT_ZVAL(zout); 201 ZEND_REGISTER_RESOURCE(zout, buckets_out, le_bucket_brigade); 202 args[1] = &zout; 203 204 ALLOC_INIT_ZVAL(zconsumed); 205 if (bytes_consumed) { 206 ZVAL_LONG(zconsumed, *bytes_consumed); 207 } else { 208 ZVAL_NULL(zconsumed); 209 } 210 args[2] = &zconsumed; 211 212 ALLOC_INIT_ZVAL(zclosing); 213 ZVAL_BOOL(zclosing, flags & PSFS_FLAG_FLUSH_CLOSE); 214 args[3] = &zclosing; 215 216 call_result = call_user_function_ex(NULL, 217 &obj, 218 &func_name, 219 &retval, 220 4, args, 221 0, NULL TSRMLS_CC); 222 223 if (call_result == SUCCESS && retval != NULL) { 224 convert_to_long(retval); 225 ret = Z_LVAL_P(retval); 226 } else if (call_result == FAILURE) { 227 php_error_docref(NULL TSRMLS_CC, E_WARNING, "failed to call filter function"); 228 } 229 230 if (bytes_consumed) { 231 *bytes_consumed = Z_LVAL_P(zconsumed); 232 } 233 234 if (retval) { 235 zval_ptr_dtor(&retval); 236 } 237 238 if (buckets_in->head) { 239 php_stream_bucket *bucket = buckets_in->head; 240 241 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unprocessed filter buckets remaining on input brigade"); 242 while ((bucket = buckets_in->head)) { 243 /* Remove unconsumed buckets from the brigade */ 244 php_stream_bucket_unlink(bucket TSRMLS_CC); 245 php_stream_bucket_delref(bucket TSRMLS_CC); 246 } 247 } 248 if (ret != PSFS_PASS_ON) { 249 php_stream_bucket *bucket = buckets_out->head; 250 while (bucket != NULL) { 251 php_stream_bucket_unlink(bucket TSRMLS_CC); 252 php_stream_bucket_delref(bucket TSRMLS_CC); 253 bucket = buckets_out->head; 254 } 255 } 256 257 /* filter resources are cleaned up by the stream destructor, 258 * keeping a reference to the stream resource here would prevent it 259 * from being destroyed properly */ 260 INIT_ZVAL(zpropname); 261 ZVAL_STRINGL(&zpropname, "stream", sizeof("stream")-1, 0); 262 Z_OBJ_HANDLER_P(obj, unset_property)(obj, &zpropname TSRMLS_CC); 263 264 zval_ptr_dtor(&zclosing); 265 zval_ptr_dtor(&zconsumed); 266 zval_ptr_dtor(&zout); 267 zval_ptr_dtor(&zin); 268 269 return ret; 270} 271 272static php_stream_filter_ops userfilter_ops = { 273 userfilter_filter, 274 userfilter_dtor, 275 "user-filter" 276}; 277 278static php_stream_filter *user_filter_factory_create(const char *filtername, 279 zval *filterparams, int persistent TSRMLS_DC) 280{ 281 struct php_user_filter_data *fdat = NULL; 282 php_stream_filter *filter; 283 zval *obj, *zfilter; 284 zval func_name; 285 zval *retval = NULL; 286 int len; 287 288 /* some sanity checks */ 289 if (persistent) { 290 php_error_docref(NULL TSRMLS_CC, E_WARNING, 291 "cannot use a user-space filter with a persistent stream"); 292 return NULL; 293 } 294 295 len = strlen(filtername); 296 297 /* determine the classname/class entry */ 298 if (FAILURE == zend_hash_find(BG(user_filter_map), (char*)filtername, len + 1, (void**)&fdat)) { 299 char *period; 300 301 /* Userspace Filters using ambiguous wildcards could cause problems. 302 i.e.: myfilter.foo.bar will always call into myfilter.foo.* 303 never seeing myfilter.* 304 TODO: Allow failed userfilter creations to continue 305 scanning through the list */ 306 if ((period = strrchr(filtername, '.'))) { 307 char *wildcard = emalloc(len + 3); 308 309 /* Search for wildcard matches instead */ 310 memcpy(wildcard, filtername, len + 1); /* copy \0 */ 311 period = wildcard + (period - filtername); 312 while (period) { 313 *period = '\0'; 314 strncat(wildcard, ".*", 2); 315 if (SUCCESS == zend_hash_find(BG(user_filter_map), wildcard, strlen(wildcard) + 1, (void**)&fdat)) { 316 period = NULL; 317 } else { 318 *period = '\0'; 319 period = strrchr(wildcard, '.'); 320 } 321 } 322 efree(wildcard); 323 } 324 if (fdat == NULL) { 325 php_error_docref(NULL TSRMLS_CC, E_WARNING, 326 "Err, filter \"%s\" is not in the user-filter map, but somehow the user-filter-factory was invoked for it!?", filtername); 327 return NULL; 328 } 329 } 330 331 /* bind the classname to the actual class */ 332 if (fdat->ce == NULL) { 333 if (FAILURE == zend_lookup_class(fdat->classname, strlen(fdat->classname), 334 (zend_class_entry ***)&fdat->ce TSRMLS_CC)) { 335 php_error_docref(NULL TSRMLS_CC, E_WARNING, 336 "user-filter \"%s\" requires class \"%s\", but that class is not defined", 337 filtername, fdat->classname); 338 return NULL; 339 } 340 fdat->ce = *(zend_class_entry**)fdat->ce; 341 342 } 343 344 filter = php_stream_filter_alloc(&userfilter_ops, NULL, 0); 345 if (filter == NULL) { 346 return NULL; 347 } 348 349 /* create the object */ 350 ALLOC_ZVAL(obj); 351 object_init_ex(obj, fdat->ce); 352 Z_SET_REFCOUNT_P(obj, 1); 353 Z_SET_ISREF_P(obj); 354 355 /* filtername */ 356 add_property_string(obj, "filtername", (char*)filtername, 1); 357 358 /* and the parameters, if any */ 359 if (filterparams) { 360 add_property_zval(obj, "params", filterparams); 361 } else { 362 add_property_null(obj, "params"); 363 } 364 365 /* invoke the constructor */ 366 ZVAL_STRINGL(&func_name, "oncreate", sizeof("oncreate")-1, 0); 367 368 call_user_function_ex(NULL, 369 &obj, 370 &func_name, 371 &retval, 372 0, NULL, 373 0, NULL TSRMLS_CC); 374 375 if (retval) { 376 if (Z_TYPE_P(retval) == IS_BOOL && Z_LVAL_P(retval) == 0) { 377 /* User reported filter creation error "return false;" */ 378 zval_ptr_dtor(&retval); 379 380 /* Kill the filter (safely) */ 381 filter->abstract = NULL; 382 php_stream_filter_free(filter TSRMLS_CC); 383 384 /* Kill the object */ 385 zval_ptr_dtor(&obj); 386 387 /* Report failure to filter_alloc */ 388 return NULL; 389 } 390 zval_ptr_dtor(&retval); 391 } 392 393 /* set the filter property, this will be used during cleanup */ 394 ALLOC_INIT_ZVAL(zfilter); 395 ZEND_REGISTER_RESOURCE(zfilter, filter, le_userfilters); 396 filter->abstract = obj; 397 add_property_zval(obj, "filter", zfilter); 398 /* add_property_zval increments the refcount which is unwanted here */ 399 zval_ptr_dtor(&zfilter); 400 401 return filter; 402} 403 404static php_stream_filter_factory user_filter_factory = { 405 user_filter_factory_create 406}; 407 408static void filter_item_dtor(struct php_user_filter_data *fdat) 409{ 410} 411 412/* {{{ proto object stream_bucket_make_writeable(resource brigade) 413 Return a bucket object from the brigade for operating on */ 414PHP_FUNCTION(stream_bucket_make_writeable) 415{ 416 zval *zbrigade, *zbucket; 417 php_stream_bucket_brigade *brigade; 418 php_stream_bucket *bucket; 419 420 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &zbrigade) == FAILURE) { 421 RETURN_FALSE; 422 } 423 424 ZEND_FETCH_RESOURCE(brigade, php_stream_bucket_brigade *, &zbrigade, -1, PHP_STREAM_BRIGADE_RES_NAME, le_bucket_brigade); 425 426 ZVAL_NULL(return_value); 427 428 if (brigade->head && (bucket = php_stream_bucket_make_writeable(brigade->head TSRMLS_CC))) { 429 ALLOC_INIT_ZVAL(zbucket); 430 ZEND_REGISTER_RESOURCE(zbucket, bucket, le_bucket); 431 object_init(return_value); 432 add_property_zval(return_value, "bucket", zbucket); 433 /* add_property_zval increments the refcount which is unwanted here */ 434 zval_ptr_dtor(&zbucket); 435 add_property_stringl(return_value, "data", bucket->buf, bucket->buflen, 1); 436 add_property_long(return_value, "datalen", bucket->buflen); 437 } 438} 439/* }}} */ 440 441/* {{{ php_stream_bucket_attach */ 442static void php_stream_bucket_attach(int append, INTERNAL_FUNCTION_PARAMETERS) 443{ 444 zval *zbrigade, *zobject; 445 zval **pzbucket, **pzdata; 446 php_stream_bucket_brigade *brigade; 447 php_stream_bucket *bucket; 448 449 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zo", &zbrigade, &zobject) == FAILURE) { 450 RETURN_FALSE; 451 } 452 453 if (FAILURE == zend_hash_find(Z_OBJPROP_P(zobject), "bucket", 7, (void**)&pzbucket)) { 454 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Object has no bucket property"); 455 RETURN_FALSE; 456 } 457 458 ZEND_FETCH_RESOURCE(brigade, php_stream_bucket_brigade *, &zbrigade, -1, PHP_STREAM_BRIGADE_RES_NAME, le_bucket_brigade); 459 ZEND_FETCH_RESOURCE(bucket, php_stream_bucket *, pzbucket, -1, PHP_STREAM_BUCKET_RES_NAME, le_bucket); 460 461 if (SUCCESS == zend_hash_find(Z_OBJPROP_P(zobject), "data", 5, (void**)&pzdata) && (*pzdata)->type == IS_STRING) { 462 if (!bucket->own_buf) { 463 bucket = php_stream_bucket_make_writeable(bucket TSRMLS_CC); 464 } 465 if ((int)bucket->buflen != Z_STRLEN_PP(pzdata)) { 466 bucket->buf = perealloc(bucket->buf, Z_STRLEN_PP(pzdata), bucket->is_persistent); 467 bucket->buflen = Z_STRLEN_PP(pzdata); 468 } 469 memcpy(bucket->buf, Z_STRVAL_PP(pzdata), bucket->buflen); 470 } 471 472 if (append) { 473 php_stream_bucket_append(brigade, bucket TSRMLS_CC); 474 } else { 475 php_stream_bucket_prepend(brigade, bucket TSRMLS_CC); 476 } 477 /* This is a hack necessary to accomodate situations where bucket is appended to the stream 478 * multiple times. See bug35916.phpt for reference. 479 */ 480 if (bucket->refcount == 1) { 481 bucket->refcount++; 482 } 483} 484/* }}} */ 485 486/* {{{ proto void stream_bucket_prepend(resource brigade, resource bucket) 487 Prepend bucket to brigade */ 488PHP_FUNCTION(stream_bucket_prepend) 489{ 490 php_stream_bucket_attach(0, INTERNAL_FUNCTION_PARAM_PASSTHRU); 491} 492/* }}} */ 493 494/* {{{ proto void stream_bucket_append(resource brigade, resource bucket) 495 Append bucket to brigade */ 496PHP_FUNCTION(stream_bucket_append) 497{ 498 php_stream_bucket_attach(1, INTERNAL_FUNCTION_PARAM_PASSTHRU); 499} 500/* }}} */ 501 502/* {{{ proto resource stream_bucket_new(resource stream, string buffer) 503 Create a new bucket for use on the current stream */ 504PHP_FUNCTION(stream_bucket_new) 505{ 506 zval *zstream, *zbucket; 507 php_stream *stream; 508 char *buffer; 509 char *pbuffer; 510 int buffer_len; 511 php_stream_bucket *bucket; 512 513 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zs", &zstream, &buffer, &buffer_len) == FAILURE) { 514 RETURN_FALSE; 515 } 516 517 php_stream_from_zval(stream, &zstream); 518 519 if (!(pbuffer = pemalloc(buffer_len, php_stream_is_persistent(stream)))) { 520 RETURN_FALSE; 521 } 522 523 memcpy(pbuffer, buffer, buffer_len); 524 525 bucket = php_stream_bucket_new(stream, pbuffer, buffer_len, 1, php_stream_is_persistent(stream) TSRMLS_CC); 526 527 if (bucket == NULL) { 528 RETURN_FALSE; 529 } 530 531 ALLOC_INIT_ZVAL(zbucket); 532 ZEND_REGISTER_RESOURCE(zbucket, bucket, le_bucket); 533 object_init(return_value); 534 add_property_zval(return_value, "bucket", zbucket); 535 /* add_property_zval increments the refcount which is unwanted here */ 536 zval_ptr_dtor(&zbucket); 537 add_property_stringl(return_value, "data", bucket->buf, bucket->buflen, 1); 538 add_property_long(return_value, "datalen", bucket->buflen); 539} 540/* }}} */ 541 542/* {{{ proto array stream_get_filters(void) 543 Returns a list of registered filters */ 544PHP_FUNCTION(stream_get_filters) 545{ 546 char *filter_name; 547 int key_flags, filter_name_len = 0; 548 HashTable *filters_hash; 549 ulong num_key; 550 551 if (zend_parse_parameters_none() == FAILURE) { 552 return; 553 } 554 555 array_init(return_value); 556 557 filters_hash = php_get_stream_filters_hash(); 558 559 if (filters_hash) { 560 for(zend_hash_internal_pointer_reset(filters_hash); 561 (key_flags = zend_hash_get_current_key_ex(filters_hash, &filter_name, &filter_name_len, &num_key, 0, NULL)) != HASH_KEY_NON_EXISTANT; 562 zend_hash_move_forward(filters_hash)) 563 if (key_flags == HASH_KEY_IS_STRING) { 564 add_next_index_stringl(return_value, filter_name, filter_name_len - 1, 1); 565 } 566 } 567 /* It's okay to return an empty array if no filters are registered */ 568} 569/* }}} */ 570 571/* {{{ proto bool stream_filter_register(string filtername, string classname) 572 Registers a custom filter handler class */ 573PHP_FUNCTION(stream_filter_register) 574{ 575 char *filtername, *classname; 576 int filtername_len, classname_len; 577 struct php_user_filter_data *fdat; 578 579 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &filtername, &filtername_len, 580 &classname, &classname_len) == FAILURE) { 581 RETURN_FALSE; 582 } 583 584 RETVAL_FALSE; 585 586 if (!filtername_len) { 587 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Filter name cannot be empty"); 588 return; 589 } 590 591 if (!classname_len) { 592 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Class name cannot be empty"); 593 return; 594 } 595 596 if (!BG(user_filter_map)) { 597 BG(user_filter_map) = (HashTable*) emalloc(sizeof(HashTable)); 598 zend_hash_init(BG(user_filter_map), 5, NULL, (dtor_func_t) filter_item_dtor, 0); 599 } 600 601 fdat = ecalloc(1, sizeof(struct php_user_filter_data) + classname_len); 602 memcpy(fdat->classname, classname, classname_len); 603 604 if (zend_hash_add(BG(user_filter_map), filtername, filtername_len + 1, (void*)fdat, 605 sizeof(*fdat) + classname_len, NULL) == SUCCESS && 606 php_stream_filter_register_factory_volatile(filtername, &user_filter_factory TSRMLS_CC) == SUCCESS) { 607 RETVAL_TRUE; 608 } 609 610 efree(fdat); 611} 612/* }}} */ 613 614 615/* 616 * Local variables: 617 * tab-width: 4 618 * c-basic-offset: 4 619 * End: 620 * vim600: sw=4 ts=4 fdm=marker 621 * vim<600: sw=4 ts=4 622 */ 623