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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 */ |
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 */ |
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 */ |
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 */ |
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 */ |
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 */ |
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 */ |
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 | |