1 : /*
2 : +----------------------------------------------------------------------+
3 : | PHP Version 5 |
4 : +----------------------------------------------------------------------+
5 : | Copyright (c) 1997-2009 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: user_filters.c 273098 2009-01-08 18:40:27Z lbarnaud $ */
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 :
54 : PHP_FUNCTION(user_filter_nop)
55 107 : {
56 107 : }
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 : { NULL, NULL, NULL }
75 : };
76 :
77 : static zend_class_entry user_filter_class_entry;
78 :
79 : static ZEND_RSRC_DTOR_FUNC(php_bucket_dtor)
80 23 : {
81 23 : php_stream_bucket *bucket = (php_stream_bucket *)rsrc->ptr;
82 23 : if (bucket) {
83 23 : php_stream_bucket_delref(bucket TSRMLS_CC);
84 23 : bucket = NULL;
85 : }
86 23 : }
87 :
88 : PHP_MINIT_FUNCTION(user_filters)
89 17633 : {
90 : zend_class_entry *php_user_filter;
91 : /* init the filter class ancestor */
92 17633 : INIT_CLASS_ENTRY(user_filter_class_entry, "php_user_filter", user_filter_class_funcs);
93 17633 : if ((php_user_filter = zend_register_internal_class(&user_filter_class_entry TSRMLS_CC)) == NULL) {
94 0 : return FAILURE;
95 : }
96 17633 : zend_declare_property_string(php_user_filter, "filtername", sizeof("filtername")-1, "", ZEND_ACC_PUBLIC TSRMLS_CC);
97 17633 : 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 17633 : le_userfilters = zend_register_list_destructors_ex(NULL, NULL, PHP_STREAM_FILTER_RES_NAME, 0);
102 :
103 17633 : if (le_userfilters == FAILURE) {
104 0 : return FAILURE;
105 : }
106 :
107 : /* Filters will dispose of their brigades */
108 17633 : 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 17633 : le_bucket = zend_register_list_destructors_ex(php_bucket_dtor, NULL, PHP_STREAM_BUCKET_RES_NAME, module_number);
111 :
112 17633 : if (le_bucket_brigade == FAILURE) {
113 0 : return FAILURE;
114 : }
115 :
116 17633 : REGISTER_LONG_CONSTANT("PSFS_PASS_ON", PSFS_PASS_ON, CONST_CS | CONST_PERSISTENT);
117 17633 : REGISTER_LONG_CONSTANT("PSFS_FEED_ME", PSFS_FEED_ME, CONST_CS | CONST_PERSISTENT);
118 17633 : REGISTER_LONG_CONSTANT("PSFS_ERR_FATAL", PSFS_ERR_FATAL, CONST_CS | CONST_PERSISTENT);
119 :
120 17633 : REGISTER_LONG_CONSTANT("PSFS_FLAG_NORMAL", PSFS_FLAG_NORMAL, CONST_CS | CONST_PERSISTENT);
121 17633 : REGISTER_LONG_CONSTANT("PSFS_FLAG_FLUSH_INC", PSFS_FLAG_FLUSH_INC, CONST_CS | CONST_PERSISTENT);
122 17633 : REGISTER_LONG_CONSTANT("PSFS_FLAG_FLUSH_CLOSE", PSFS_FLAG_FLUSH_CLOSE, CONST_CS | CONST_PERSISTENT);
123 :
124 17633 : return SUCCESS;
125 : }
126 :
127 : PHP_RSHUTDOWN_FUNCTION(user_filters)
128 17651 : {
129 17651 : if (BG(user_filter_map)) {
130 9 : zend_hash_destroy(BG(user_filter_map));
131 9 : efree(BG(user_filter_map));
132 9 : BG(user_filter_map) = NULL;
133 : }
134 :
135 17651 : return SUCCESS;
136 : }
137 :
138 : static void userfilter_dtor(php_stream_filter *thisfilter TSRMLS_DC)
139 57 : {
140 57 : zval *obj = (zval*)thisfilter->abstract;
141 : zval func_name;
142 57 : zval *retval = NULL;
143 :
144 57 : if (obj == NULL) {
145 : /* If there's no object associated then there's nothing to dispose of */
146 0 : return;
147 : }
148 :
149 57 : ZVAL_STRINGL(&func_name, "onclose", sizeof("onclose")-1, 0);
150 :
151 57 : call_user_function_ex(NULL,
152 : &obj,
153 : &func_name,
154 : &retval,
155 : 0, NULL,
156 : 0, NULL TSRMLS_CC);
157 :
158 57 : if (retval)
159 55 : zval_ptr_dtor(&retval);
160 :
161 : /* kill the object */
162 57 : zval_ptr_dtor(&obj);
163 : }
164 :
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 57 : {
174 57 : int ret = PSFS_ERR_FATAL;
175 57 : zval *obj = (zval*)thisfilter->abstract;
176 : zval func_name;
177 57 : zval *retval = NULL;
178 : zval **args[4];
179 : zval *zclosing, *zconsumed, *zin, *zout, *zstream;
180 : zval zpropname;
181 : int call_result;
182 :
183 57 : 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 57 : ALLOC_INIT_ZVAL(zstream);
186 57 : php_stream_to_zval(stream, zstream);
187 57 : zval_copy_ctor(zstream);
188 57 : add_property_zval(obj, "stream", zstream);
189 : /* add_property_zval increments the refcount which is unwanted here */
190 57 : zval_ptr_dtor(&zstream);
191 : }
192 :
193 57 : ZVAL_STRINGL(&func_name, "filter", sizeof("filter")-1, 0);
194 :
195 : /* Setup calling arguments */
196 57 : ALLOC_INIT_ZVAL(zin);
197 57 : ZEND_REGISTER_RESOURCE(zin, buckets_in, le_bucket_brigade);
198 57 : args[0] = &zin;
199 :
200 57 : ALLOC_INIT_ZVAL(zout);
201 57 : ZEND_REGISTER_RESOURCE(zout, buckets_out, le_bucket_brigade);
202 57 : args[1] = &zout;
203 :
204 57 : ALLOC_INIT_ZVAL(zconsumed);
205 57 : if (bytes_consumed) {
206 25 : ZVAL_LONG(zconsumed, *bytes_consumed);
207 : } else {
208 32 : ZVAL_NULL(zconsumed);
209 : }
210 57 : args[2] = &zconsumed;
211 :
212 57 : ALLOC_INIT_ZVAL(zclosing);
213 57 : ZVAL_BOOL(zclosing, flags & PSFS_FLAG_FLUSH_CLOSE);
214 57 : args[3] = &zclosing;
215 :
216 57 : call_result = call_user_function_ex(NULL,
217 : &obj,
218 : &func_name,
219 : &retval,
220 : 4, args,
221 : 0, NULL TSRMLS_CC);
222 :
223 114 : if (call_result == SUCCESS && retval != NULL) {
224 57 : convert_to_long(retval);
225 57 : ret = Z_LVAL_P(retval);
226 0 : } else if (call_result == FAILURE) {
227 0 : php_error_docref(NULL TSRMLS_CC, E_WARNING, "failed to call filter function");
228 : }
229 :
230 57 : if (bytes_consumed) {
231 25 : *bytes_consumed = Z_LVAL_P(zconsumed);
232 : }
233 :
234 57 : if (retval) {
235 57 : zval_ptr_dtor(&retval);
236 : }
237 :
238 57 : if (buckets_in->head) {
239 11 : php_stream_bucket *bucket = buckets_in->head;
240 :
241 11 : php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unprocessed filter buckets remaining on input brigade");
242 33 : while ((bucket = buckets_in->head)) {
243 : /* Remove unconsumed buckets from the brigade */
244 11 : php_stream_bucket_unlink(bucket TSRMLS_CC);
245 11 : php_stream_bucket_delref(bucket TSRMLS_CC);
246 : }
247 : }
248 57 : if (ret != PSFS_PASS_ON) {
249 39 : php_stream_bucket *bucket = buckets_out->head;
250 84 : while (bucket != NULL) {
251 6 : php_stream_bucket_unlink(bucket TSRMLS_CC);
252 6 : php_stream_bucket_delref(bucket TSRMLS_CC);
253 6 : 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 57 : INIT_ZVAL(zpropname);
261 57 : ZVAL_STRINGL(&zpropname, "stream", sizeof("stream")-1, 0);
262 57 : Z_OBJ_HANDLER_P(obj, unset_property)(obj, &zpropname TSRMLS_CC);
263 :
264 57 : zval_ptr_dtor(&zclosing);
265 57 : zval_ptr_dtor(&zconsumed);
266 57 : zval_ptr_dtor(&zout);
267 57 : zval_ptr_dtor(&zin);
268 :
269 57 : return ret;
270 : }
271 :
272 : static php_stream_filter_ops userfilter_ops = {
273 : userfilter_filter,
274 : userfilter_dtor,
275 : "user-filter"
276 : };
277 :
278 : static php_stream_filter *user_filter_factory_create(const char *filtername,
279 : zval *filterparams, int persistent TSRMLS_DC)
280 57 : {
281 57 : struct php_user_filter_data *fdat = NULL;
282 : php_stream_filter *filter;
283 : zval *obj, *zfilter;
284 : zval func_name;
285 57 : zval *retval = NULL;
286 : int len;
287 :
288 : /* some sanity checks */
289 57 : if (persistent) {
290 0 : php_error_docref(NULL TSRMLS_CC, E_WARNING,
291 : "cannot use a user-space filter with a persistent stream");
292 0 : return NULL;
293 : }
294 :
295 57 : len = strlen(filtername);
296 :
297 : /* determine the classname/class entry */
298 57 : 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 0 : if ((period = strrchr(filtername, '.'))) {
307 0 : char *wildcard = emalloc(len + 3);
308 :
309 : /* Search for wildcard matches instead */
310 0 : memcpy(wildcard, filtername, len + 1); /* copy \0 */
311 0 : period = wildcard + (period - filtername);
312 0 : while (period) {
313 0 : *period = '\0';
314 0 : strcat(wildcard, ".*");
315 0 : if (SUCCESS == zend_hash_find(BG(user_filter_map), wildcard, strlen(wildcard) + 1, (void**)&fdat)) {
316 0 : period = NULL;
317 : } else {
318 0 : *period = '\0';
319 0 : period = strrchr(wildcard, '.');
320 : }
321 : }
322 0 : efree(wildcard);
323 : }
324 0 : if (fdat == NULL) {
325 0 : 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 0 : return NULL;
328 : }
329 : }
330 :
331 : /* bind the classname to the actual class */
332 57 : if (fdat->ce == NULL) {
333 12 : if (FAILURE == zend_lookup_class(fdat->classname, strlen(fdat->classname),
334 : (zend_class_entry ***)&fdat->ce TSRMLS_CC)) {
335 0 : 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 0 : return NULL;
339 : }
340 12 : fdat->ce = *(zend_class_entry**)fdat->ce;
341 :
342 : }
343 :
344 57 : filter = php_stream_filter_alloc(&userfilter_ops, NULL, 0);
345 57 : if (filter == NULL) {
346 0 : return NULL;
347 : }
348 :
349 : /* create the object */
350 57 : ALLOC_ZVAL(obj);
351 57 : object_init_ex(obj, fdat->ce);
352 57 : Z_SET_REFCOUNT_P(obj, 1);
353 57 : Z_SET_ISREF_P(obj);
354 :
355 : /* filtername */
356 57 : add_property_string(obj, "filtername", (char*)filtername, 1);
357 :
358 : /* and the parameters, if any */
359 57 : if (filterparams) {
360 1 : add_property_zval(obj, "params", filterparams);
361 : } else {
362 56 : add_property_null(obj, "params");
363 : }
364 :
365 : /* invoke the constructor */
366 57 : ZVAL_STRINGL(&func_name, "oncreate", sizeof("oncreate")-1, 0);
367 :
368 57 : call_user_function_ex(NULL,
369 : &obj,
370 : &func_name,
371 : &retval,
372 : 0, NULL,
373 : 0, NULL TSRMLS_CC);
374 :
375 57 : if (retval) {
376 55 : if (Z_TYPE_P(retval) == IS_BOOL && Z_LVAL_P(retval) == 0) {
377 : /* User reported filter creation error "return false;" */
378 0 : zval_ptr_dtor(&retval);
379 :
380 : /* Kill the filter (safely) */
381 0 : filter->abstract = NULL;
382 0 : php_stream_filter_free(filter TSRMLS_CC);
383 :
384 : /* Kill the object */
385 0 : zval_ptr_dtor(&obj);
386 :
387 : /* Report failure to filter_alloc */
388 0 : return NULL;
389 : }
390 55 : zval_ptr_dtor(&retval);
391 : }
392 :
393 : /* set the filter property, this will be used during cleanup */
394 57 : ALLOC_INIT_ZVAL(zfilter);
395 57 : ZEND_REGISTER_RESOURCE(zfilter, filter, le_userfilters);
396 57 : filter->abstract = obj;
397 57 : add_property_zval(obj, "filter", zfilter);
398 : /* add_property_zval increments the refcount which is unwanted here */
399 57 : zval_ptr_dtor(&zfilter);
400 :
401 57 : return filter;
402 : }
403 :
404 : static php_stream_filter_factory user_filter_factory = {
405 : user_filter_factory_create
406 : };
407 :
408 : static void filter_item_dtor(struct php_user_filter_data *fdat)
409 13 : {
410 13 : }
411 :
412 : /* {{{ proto object stream_bucket_make_writeable(resource brigade)
413 : Return a bucket object from the brigade for operating on */
414 : PHP_FUNCTION(stream_bucket_make_writeable)
415 40 : {
416 : zval *zbrigade, *zbucket;
417 : php_stream_bucket_brigade *brigade;
418 : php_stream_bucket *bucket;
419 :
420 40 : if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &zbrigade) == FAILURE) {
421 0 : RETURN_FALSE;
422 : }
423 :
424 40 : ZEND_FETCH_RESOURCE(brigade, php_stream_bucket_brigade *, &zbrigade, -1, PHP_STREAM_BRIGADE_RES_NAME, le_bucket_brigade);
425 :
426 40 : ZVAL_NULL(return_value);
427 :
428 40 : if (brigade->head && (bucket = php_stream_bucket_make_writeable(brigade->head TSRMLS_CC))) {
429 13 : ALLOC_INIT_ZVAL(zbucket);
430 13 : ZEND_REGISTER_RESOURCE(zbucket, bucket, le_bucket);
431 13 : object_init(return_value);
432 13 : add_property_zval(return_value, "bucket", zbucket);
433 : /* add_property_zval increments the refcount which is unwanted here */
434 13 : zval_ptr_dtor(&zbucket);
435 13 : add_property_stringl(return_value, "data", bucket->buf, bucket->buflen, 1);
436 13 : add_property_long(return_value, "datalen", bucket->buflen);
437 : }
438 : }
439 : /* }}} */
440 :
441 : /* {{{ php_stream_bucket_attach */
442 : static void php_stream_bucket_attach(int append, INTERNAL_FUNCTION_PARAMETERS)
443 17 : {
444 : zval *zbrigade, *zobject;
445 : zval **pzbucket, **pzdata;
446 : php_stream_bucket_brigade *brigade;
447 : php_stream_bucket *bucket;
448 :
449 17 : if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zo", &zbrigade, &zobject) == FAILURE) {
450 0 : RETURN_FALSE;
451 : }
452 :
453 17 : if (FAILURE == zend_hash_find(Z_OBJPROP_P(zobject), "bucket", 7, (void**)&pzbucket)) {
454 0 : php_error_docref(NULL TSRMLS_CC, E_WARNING, "Object has no bucket property");
455 0 : RETURN_FALSE;
456 : }
457 :
458 17 : ZEND_FETCH_RESOURCE(brigade, php_stream_bucket_brigade *, &zbrigade, -1, PHP_STREAM_BRIGADE_RES_NAME, le_bucket_brigade);
459 17 : ZEND_FETCH_RESOURCE(bucket, php_stream_bucket *, pzbucket, -1, PHP_STREAM_BUCKET_RES_NAME, le_bucket);
460 :
461 17 : if (SUCCESS == zend_hash_find(Z_OBJPROP_P(zobject), "data", 5, (void**)&pzdata) && (*pzdata)->type == IS_STRING) {
462 17 : if (!bucket->own_buf) {
463 0 : bucket = php_stream_bucket_make_writeable(bucket TSRMLS_CC);
464 : }
465 17 : if ((int)bucket->buflen != Z_STRLEN_PP(pzdata)) {
466 0 : bucket->buf = perealloc(bucket->buf, Z_STRLEN_PP(pzdata), bucket->is_persistent);
467 0 : bucket->buflen = Z_STRLEN_PP(pzdata);
468 : }
469 17 : memcpy(bucket->buf, Z_STRVAL_PP(pzdata), bucket->buflen);
470 : }
471 :
472 17 : if (append) {
473 17 : php_stream_bucket_append(brigade, bucket TSRMLS_CC);
474 : } else {
475 0 : 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 17 : if (bucket->refcount == 1) {
481 16 : bucket->refcount++;
482 : }
483 : }
484 : /* }}} */
485 :
486 : /* {{{ proto void stream_bucket_prepend(resource brigade, resource bucket)
487 : Prepend bucket to brigade */
488 : PHP_FUNCTION(stream_bucket_prepend)
489 0 : {
490 0 : php_stream_bucket_attach(0, INTERNAL_FUNCTION_PARAM_PASSTHRU);
491 0 : }
492 : /* }}} */
493 :
494 : /* {{{ proto void stream_bucket_append(resource brigade, resource bucket)
495 : Append bucket to brigade */
496 : PHP_FUNCTION(stream_bucket_append)
497 17 : {
498 17 : php_stream_bucket_attach(1, INTERNAL_FUNCTION_PARAM_PASSTHRU);
499 17 : }
500 : /* }}} */
501 :
502 : /* {{{ proto resource stream_bucket_new(resource stream, string buffer)
503 : Create a new bucket for use on the current stream */
504 : PHP_FUNCTION(stream_bucket_new)
505 10 : {
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 10 : if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zs", &zstream, &buffer, &buffer_len) == FAILURE) {
514 0 : RETURN_FALSE;
515 : }
516 :
517 10 : php_stream_from_zval(stream, &zstream);
518 :
519 10 : if (!(pbuffer = pemalloc(buffer_len, php_stream_is_persistent(stream)))) {
520 0 : RETURN_FALSE;
521 : }
522 :
523 10 : memcpy(pbuffer, buffer, buffer_len);
524 :
525 10 : bucket = php_stream_bucket_new(stream, pbuffer, buffer_len, 1, php_stream_is_persistent(stream) TSRMLS_CC);
526 :
527 10 : if (bucket == NULL) {
528 0 : RETURN_FALSE;
529 : }
530 :
531 10 : ALLOC_INIT_ZVAL(zbucket);
532 10 : ZEND_REGISTER_RESOURCE(zbucket, bucket, le_bucket);
533 10 : object_init(return_value);
534 10 : add_property_zval(return_value, "bucket", zbucket);
535 : /* add_property_zval increments the refcount which is unwanted here */
536 10 : zval_ptr_dtor(&zbucket);
537 10 : add_property_stringl(return_value, "data", bucket->buf, bucket->buflen, 1);
538 10 : 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 */
544 : PHP_FUNCTION(stream_get_filters)
545 5 : {
546 : char *filter_name;
547 5 : int key_flags, filter_name_len = 0;
548 : HashTable *filters_hash;
549 : ulong num_key;
550 :
551 5 : if (zend_parse_parameters_none() == FAILURE) {
552 0 : return;
553 : }
554 :
555 5 : array_init(return_value);
556 :
557 5 : filters_hash = php_get_stream_filters_hash();
558 :
559 5 : if (filters_hash) {
560 5 : for(zend_hash_internal_pointer_reset(filters_hash);
561 60 : (key_flags = zend_hash_get_current_key_ex(filters_hash, &filter_name, &filter_name_len, &num_key, 0, NULL)) != HASH_KEY_NON_EXISTANT;
562 50 : zend_hash_move_forward(filters_hash))
563 50 : if (key_flags == HASH_KEY_IS_STRING) {
564 50 : 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 */
573 : PHP_FUNCTION(stream_filter_register)
574 18 : {
575 : char *filtername, *classname;
576 : int filtername_len, classname_len;
577 : struct php_user_filter_data *fdat;
578 :
579 18 : if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &filtername, &filtername_len,
580 : &classname, &classname_len) == FAILURE) {
581 2 : RETURN_FALSE;
582 : }
583 :
584 16 : RETVAL_FALSE;
585 :
586 16 : if (!filtername_len) {
587 2 : php_error_docref(NULL TSRMLS_CC, E_WARNING, "Filter name cannot be empty");
588 2 : return;
589 : }
590 :
591 14 : if (!classname_len) {
592 1 : php_error_docref(NULL TSRMLS_CC, E_WARNING, "Class name cannot be empty");
593 1 : return;
594 : }
595 :
596 13 : if (!BG(user_filter_map)) {
597 9 : BG(user_filter_map) = (HashTable*) emalloc(sizeof(HashTable));
598 9 : zend_hash_init(BG(user_filter_map), 5, NULL, (dtor_func_t) filter_item_dtor, 0);
599 : }
600 :
601 13 : fdat = ecalloc(1, sizeof(struct php_user_filter_data) + classname_len);
602 13 : memcpy(fdat->classname, classname, classname_len);
603 :
604 13 : 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 13 : RETVAL_TRUE;
608 : }
609 :
610 13 : 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 : */
|