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
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
PHP_FUNCTION(user_filter_nop)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
ZEND_RSRC_DTOR_FUNC(php_bucket_dtor)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
PHP_MINIT_FUNCTION(user_filters)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
PHP_RSHUTDOWN_FUNCTION(user_filters)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
userfilter_dtor(php_stream_filter * thisfilter TSRMLS_DC)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
userfilter_filter(php_stream * stream,php_stream_filter * thisfilter,php_stream_bucket_brigade * buckets_in,php_stream_bucket_brigade * buckets_out,size_t * bytes_consumed,int flags TSRMLS_DC)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 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
272 static php_stream_filter_ops userfilter_ops = {
273 userfilter_filter,
274 userfilter_dtor,
275 "user-filter"
276 };
277
user_filter_factory_create(const char * filtername,zval * filterparams,int persistent TSRMLS_DC)278 static 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
404 static php_stream_filter_factory user_filter_factory = {
405 user_filter_factory_create
406 };
407
filter_item_dtor(struct php_user_filter_data * fdat)408 static 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 */
PHP_FUNCTION(stream_bucket_make_writeable)414 PHP_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 */
php_stream_bucket_attach(int append,INTERNAL_FUNCTION_PARAMETERS)442 static 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 */
PHP_FUNCTION(stream_bucket_prepend)488 PHP_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 */
PHP_FUNCTION(stream_bucket_append)496 PHP_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 */
PHP_FUNCTION(stream_bucket_new)504 PHP_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 */
PHP_FUNCTION(stream_get_filters)544 PHP_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 */
PHP_FUNCTION(stream_filter_register)573 PHP_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