xref: /PHP-5.5/ext/standard/user_filters.c (revision 73c1be26)
1 /*
2    +----------------------------------------------------------------------+
3    | PHP Version 5                                                        |
4    +----------------------------------------------------------------------+
5    | Copyright (c) 1997-2015 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 	/* 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 
user_filter_factory_create(const char * filtername,zval * filterparams,int persistent TSRMLS_DC)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 
filter_item_dtor(struct php_user_filter_data * fdat)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 */
PHP_FUNCTION(stream_bucket_make_writeable)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 */
php_stream_bucket_attach(int append,INTERNAL_FUNCTION_PARAMETERS)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, resource bucket)
492    Prepend bucket to brigade */
PHP_FUNCTION(stream_bucket_prepend)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, resource bucket)
500    Append bucket to brigade */
PHP_FUNCTION(stream_bucket_append)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 */
PHP_FUNCTION(stream_bucket_new)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 */
PHP_FUNCTION(stream_get_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 */
PHP_FUNCTION(stream_filter_register)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  */
629