xref: /PHP-5.3/ext/standard/user_filters.c (revision a2045ff3)
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