Annotation of embedaddon/php/main/streams/filter.c, revision 1.1.1.4
1.1 misho 1: /*
2: +----------------------------------------------------------------------+
3: | PHP Version 5 |
4: +----------------------------------------------------------------------+
1.1.1.4 ! misho 5: | Copyright (c) 1997-2014 The PHP Group |
1.1 misho 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: Wez Furlong <wez@thebrainroom.com> |
16: +----------------------------------------------------------------------+
17: */
18:
1.1.1.2 misho 19: /* $Id$ */
1.1 misho 20:
21: #include "php.h"
22: #include "php_globals.h"
23: #include "php_network.h"
24: #include "php_open_temporary_file.h"
25: #include "ext/standard/file.h"
26: #include <stddef.h>
27: #include <fcntl.h>
28:
29: #include "php_streams_int.h"
30:
31: /* Global filter hash, copied to FG(stream_filters) on registration of volatile filter */
32: static HashTable stream_filters_hash;
33:
34: /* Should only be used during core initialization */
35: PHPAPI HashTable *php_get_stream_filters_hash_global(void)
36: {
37: return &stream_filters_hash;
38: }
39:
40: /* Normal hash selection/retrieval call */
41: PHPAPI HashTable *_php_get_stream_filters_hash(TSRMLS_D)
42: {
43: return (FG(stream_filters) ? FG(stream_filters) : &stream_filters_hash);
44: }
45:
46: /* API for registering GLOBAL filters */
47: PHPAPI int php_stream_filter_register_factory(const char *filterpattern, php_stream_filter_factory *factory TSRMLS_DC)
48: {
49: return zend_hash_add(&stream_filters_hash, (char*)filterpattern, strlen(filterpattern) + 1, factory, sizeof(*factory), NULL);
50: }
51:
52: PHPAPI int php_stream_filter_unregister_factory(const char *filterpattern TSRMLS_DC)
53: {
54: return zend_hash_del(&stream_filters_hash, (char*)filterpattern, strlen(filterpattern) + 1);
55: }
56:
57: /* API for registering VOLATILE wrappers */
58: PHPAPI int php_stream_filter_register_factory_volatile(const char *filterpattern, php_stream_filter_factory *factory TSRMLS_DC)
59: {
60: if (!FG(stream_filters)) {
61: php_stream_filter_factory tmpfactory;
62:
63: ALLOC_HASHTABLE(FG(stream_filters));
64: zend_hash_init(FG(stream_filters), zend_hash_num_elements(&stream_filters_hash), NULL, NULL, 1);
65: zend_hash_copy(FG(stream_filters), &stream_filters_hash, NULL, &tmpfactory, sizeof(php_stream_filter_factory));
66: }
67:
68: return zend_hash_add(FG(stream_filters), (char*)filterpattern, strlen(filterpattern) + 1, factory, sizeof(*factory), NULL);
69: }
70:
71: /* Buckets */
72:
73: PHPAPI php_stream_bucket *php_stream_bucket_new(php_stream *stream, char *buf, size_t buflen, int own_buf, int buf_persistent TSRMLS_DC)
74: {
75: int is_persistent = php_stream_is_persistent(stream);
76: php_stream_bucket *bucket;
77:
78: bucket = (php_stream_bucket*)pemalloc(sizeof(php_stream_bucket), is_persistent);
79:
80: if (bucket == NULL) {
81: return NULL;
82: }
83:
84: bucket->next = bucket->prev = NULL;
85:
86: if (is_persistent && !buf_persistent) {
87: /* all data in a persistent bucket must also be persistent */
88: bucket->buf = pemalloc(buflen, 1);
89:
90: if (bucket->buf == NULL) {
91: pefree(bucket, 1);
92: return NULL;
93: }
94:
95: memcpy(bucket->buf, buf, buflen);
96: bucket->buflen = buflen;
97: bucket->own_buf = 1;
98: } else {
99: bucket->buf = buf;
100: bucket->buflen = buflen;
101: bucket->own_buf = own_buf;
102: }
103: bucket->is_persistent = is_persistent;
104: bucket->refcount = 1;
105: bucket->brigade = NULL;
106:
107: return bucket;
108: }
109:
110: /* Given a bucket, returns a version of that bucket with a writeable buffer.
111: * If the original bucket has a refcount of 1 and owns its buffer, then it
112: * is returned unchanged.
113: * Otherwise, a copy of the buffer is made.
114: * In both cases, the original bucket is unlinked from its brigade.
115: * If a copy is made, the original bucket is delref'd.
116: * */
117: PHPAPI php_stream_bucket *php_stream_bucket_make_writeable(php_stream_bucket *bucket TSRMLS_DC)
118: {
119: php_stream_bucket *retval;
120:
121: php_stream_bucket_unlink(bucket TSRMLS_CC);
122:
123: if (bucket->refcount == 1 && bucket->own_buf) {
124: return bucket;
125: }
126:
127: retval = (php_stream_bucket*)pemalloc(sizeof(php_stream_bucket), bucket->is_persistent);
128: memcpy(retval, bucket, sizeof(*retval));
129:
130: retval->buf = pemalloc(retval->buflen, retval->is_persistent);
131: memcpy(retval->buf, bucket->buf, retval->buflen);
132:
133: retval->refcount = 1;
134: retval->own_buf = 1;
135:
136: php_stream_bucket_delref(bucket TSRMLS_CC);
137:
138: return retval;
139: }
140:
141: PHPAPI int php_stream_bucket_split(php_stream_bucket *in, php_stream_bucket **left, php_stream_bucket **right, size_t length TSRMLS_DC)
142: {
143: *left = (php_stream_bucket*)pecalloc(1, sizeof(php_stream_bucket), in->is_persistent);
144: *right = (php_stream_bucket*)pecalloc(1, sizeof(php_stream_bucket), in->is_persistent);
145:
146: if (*left == NULL || *right == NULL) {
147: goto exit_fail;
148: }
149:
150: (*left)->buf = pemalloc(length, in->is_persistent);
151: (*left)->buflen = length;
152: memcpy((*left)->buf, in->buf, length);
153: (*left)->refcount = 1;
154: (*left)->own_buf = 1;
155: (*left)->is_persistent = in->is_persistent;
156:
157: (*right)->buflen = in->buflen - length;
158: (*right)->buf = pemalloc((*right)->buflen, in->is_persistent);
159: memcpy((*right)->buf, in->buf + length, (*right)->buflen);
160: (*right)->refcount = 1;
161: (*right)->own_buf = 1;
162: (*right)->is_persistent = in->is_persistent;
163:
164: return SUCCESS;
165:
166: exit_fail:
167: if (*right) {
168: if ((*right)->buf) {
169: pefree((*right)->buf, in->is_persistent);
170: }
171: pefree(*right, in->is_persistent);
172: }
173: if (*left) {
174: if ((*left)->buf) {
175: pefree((*left)->buf, in->is_persistent);
176: }
177: pefree(*left, in->is_persistent);
178: }
179: return FAILURE;
180: }
181:
182: PHPAPI void php_stream_bucket_delref(php_stream_bucket *bucket TSRMLS_DC)
183: {
184: if (--bucket->refcount == 0) {
185: if (bucket->own_buf) {
186: pefree(bucket->buf, bucket->is_persistent);
187: }
188: pefree(bucket, bucket->is_persistent);
189: }
190: }
191:
192: PHPAPI void php_stream_bucket_prepend(php_stream_bucket_brigade *brigade, php_stream_bucket *bucket TSRMLS_DC)
193: {
194: bucket->next = brigade->head;
195: bucket->prev = NULL;
196:
197: if (brigade->head) {
198: brigade->head->prev = bucket;
199: } else {
200: brigade->tail = bucket;
201: }
202: brigade->head = bucket;
203: bucket->brigade = brigade;
204: }
205:
206: PHPAPI void php_stream_bucket_append(php_stream_bucket_brigade *brigade, php_stream_bucket *bucket TSRMLS_DC)
207: {
208: if (brigade->tail == bucket) {
209: return;
210: }
211:
212: bucket->prev = brigade->tail;
213: bucket->next = NULL;
214:
215: if (brigade->tail) {
216: brigade->tail->next = bucket;
217: } else {
218: brigade->head = bucket;
219: }
220: brigade->tail = bucket;
221: bucket->brigade = brigade;
222: }
223:
224: PHPAPI void php_stream_bucket_unlink(php_stream_bucket *bucket TSRMLS_DC)
225: {
226: if (bucket->prev) {
227: bucket->prev->next = bucket->next;
228: } else if (bucket->brigade) {
229: bucket->brigade->head = bucket->next;
230: }
231: if (bucket->next) {
232: bucket->next->prev = bucket->prev;
233: } else if (bucket->brigade) {
234: bucket->brigade->tail = bucket->prev;
235: }
236: bucket->brigade = NULL;
237: bucket->next = bucket->prev = NULL;
238: }
239:
240:
241:
242:
243:
244:
245:
246:
247: /* We allow very simple pattern matching for filter factories:
248: * if "convert.charset.utf-8/sjis" is requested, we search first for an exact
249: * match. If that fails, we try "convert.charset.*", then "convert.*"
250: * This means that we don't need to clog up the hashtable with a zillion
251: * charsets (for example) but still be able to provide them all as filters */
252: PHPAPI php_stream_filter *php_stream_filter_create(const char *filtername, zval *filterparams, int persistent TSRMLS_DC)
253: {
254: HashTable *filter_hash = (FG(stream_filters) ? FG(stream_filters) : &stream_filters_hash);
255: php_stream_filter_factory *factory = NULL;
256: php_stream_filter *filter = NULL;
257: int n;
258: char *period;
259:
260: n = strlen(filtername);
261:
262: if (SUCCESS == zend_hash_find(filter_hash, (char*)filtername, n + 1, (void**)&factory)) {
263: filter = factory->create_filter(filtername, filterparams, persistent TSRMLS_CC);
264: } else if ((period = strrchr(filtername, '.'))) {
265: /* try a wildcard */
266: char *wildname;
267:
268: wildname = emalloc(n+3);
269: memcpy(wildname, filtername, n+1);
270: period = wildname + (period - filtername);
271: while (period && !filter) {
272: *period = '\0';
273: strncat(wildname, ".*", 2);
274: if (SUCCESS == zend_hash_find(filter_hash, wildname, strlen(wildname) + 1, (void**)&factory)) {
275: filter = factory->create_filter(filtername, filterparams, persistent TSRMLS_CC);
276: }
277:
278: *period = '\0';
279: period = strrchr(wildname, '.');
280: }
281: efree(wildname);
282: }
283:
284: if (filter == NULL) {
285: /* TODO: these need correct docrefs */
286: if (factory == NULL)
287: php_error_docref(NULL TSRMLS_CC, E_WARNING, "unable to locate filter \"%s\"", filtername);
288: else
289: php_error_docref(NULL TSRMLS_CC, E_WARNING, "unable to create or locate filter \"%s\"", filtername);
290: }
291:
292: return filter;
293: }
294:
295: PHPAPI php_stream_filter *_php_stream_filter_alloc(php_stream_filter_ops *fops, void *abstract, int persistent STREAMS_DC TSRMLS_DC)
296: {
297: php_stream_filter *filter;
298:
299: filter = (php_stream_filter*) pemalloc_rel_orig(sizeof(php_stream_filter), persistent);
300: memset(filter, 0, sizeof(php_stream_filter));
301:
302: filter->fops = fops;
303: filter->abstract = abstract;
304: filter->is_persistent = persistent;
305:
306: return filter;
307: }
308:
309: PHPAPI void php_stream_filter_free(php_stream_filter *filter TSRMLS_DC)
310: {
311: if (filter->fops->dtor)
312: filter->fops->dtor(filter TSRMLS_CC);
313: pefree(filter, filter->is_persistent);
314: }
315:
316: PHPAPI int php_stream_filter_prepend_ex(php_stream_filter_chain *chain, php_stream_filter *filter TSRMLS_DC)
317: {
318: filter->next = chain->head;
319: filter->prev = NULL;
320:
321: if (chain->head) {
322: chain->head->prev = filter;
323: } else {
324: chain->tail = filter;
325: }
326: chain->head = filter;
327: filter->chain = chain;
328:
329: return SUCCESS;
330: }
331:
332: PHPAPI void _php_stream_filter_prepend(php_stream_filter_chain *chain, php_stream_filter *filter TSRMLS_DC)
333: {
334: php_stream_filter_prepend_ex(chain, filter TSRMLS_CC);
335: }
336:
337: PHPAPI int php_stream_filter_append_ex(php_stream_filter_chain *chain, php_stream_filter *filter TSRMLS_DC)
338: {
339: php_stream *stream = chain->stream;
340:
341: filter->prev = chain->tail;
342: filter->next = NULL;
343: if (chain->tail) {
344: chain->tail->next = filter;
345: } else {
346: chain->head = filter;
347: }
348: chain->tail = filter;
349: filter->chain = chain;
350:
351: if (&(stream->readfilters) == chain && (stream->writepos - stream->readpos) > 0) {
352: /* Let's going ahead and wind anything in the buffer through this filter */
353: php_stream_bucket_brigade brig_in = { NULL, NULL }, brig_out = { NULL, NULL };
354: php_stream_bucket_brigade *brig_inp = &brig_in, *brig_outp = &brig_out;
355: php_stream_filter_status_t status;
356: php_stream_bucket *bucket;
357: size_t consumed = 0;
358:
359: bucket = php_stream_bucket_new(stream, (char*) stream->readbuf + stream->readpos, stream->writepos - stream->readpos, 0, 0 TSRMLS_CC);
360: php_stream_bucket_append(brig_inp, bucket TSRMLS_CC);
361: status = filter->fops->filter(stream, filter, brig_inp, brig_outp, &consumed, PSFS_FLAG_NORMAL TSRMLS_CC);
362:
363: if (stream->readpos + consumed > (uint)stream->writepos) {
364: /* No behaving filter should cause this. */
365: status = PSFS_ERR_FATAL;
366: }
367:
368: switch (status) {
369: case PSFS_ERR_FATAL:
370: while (brig_in.head) {
371: bucket = brig_in.head;
372: php_stream_bucket_unlink(bucket TSRMLS_CC);
373: php_stream_bucket_delref(bucket TSRMLS_CC);
374: }
375: while (brig_out.head) {
376: bucket = brig_out.head;
377: php_stream_bucket_unlink(bucket TSRMLS_CC);
378: php_stream_bucket_delref(bucket TSRMLS_CC);
379: }
380: php_error_docref(NULL TSRMLS_CC, E_WARNING, "Filter failed to process pre-buffered data");
381: return FAILURE;
382: case PSFS_FEED_ME:
383: /* We don't actually need data yet,
384: leave this filter in a feed me state until data is needed.
385: Reset stream's internal read buffer since the filter is "holding" it. */
386: stream->readpos = 0;
387: stream->writepos = 0;
388: break;
389: case PSFS_PASS_ON:
390: /* If any data is consumed, we cannot rely upon the existing read buffer,
391: as the filtered data must replace the existing data, so invalidate the cache */
392: /* note that changes here should be reflected in
393: main/streams/streams.c::php_stream_fill_read_buffer */
394: stream->writepos = 0;
395: stream->readpos = 0;
396:
397: while (brig_outp->head) {
398: bucket = brig_outp->head;
399: /* Grow buffer to hold this bucket if need be.
400: TODO: See warning in main/stream/streams.c::php_stream_fill_read_buffer */
401: if (stream->readbuflen - stream->writepos < bucket->buflen) {
402: stream->readbuflen += bucket->buflen;
403: stream->readbuf = perealloc(stream->readbuf, stream->readbuflen, stream->is_persistent);
404: }
405: memcpy(stream->readbuf + stream->writepos, bucket->buf, bucket->buflen);
406: stream->writepos += bucket->buflen;
407:
408: php_stream_bucket_unlink(bucket TSRMLS_CC);
409: php_stream_bucket_delref(bucket TSRMLS_CC);
410: }
411: break;
412: }
413: }
414:
415: return SUCCESS;
416: }
417:
418: PHPAPI void _php_stream_filter_append(php_stream_filter_chain *chain, php_stream_filter *filter TSRMLS_DC)
419: {
420: if (php_stream_filter_append_ex(chain, filter TSRMLS_CC) != SUCCESS) {
421: if (chain->head == filter) {
422: chain->head = NULL;
423: chain->tail = NULL;
424: } else {
425: filter->prev->next = NULL;
426: chain->tail = filter->prev;
427: }
428: }
429: }
430:
431: PHPAPI int _php_stream_filter_flush(php_stream_filter *filter, int finish TSRMLS_DC)
432: {
433: php_stream_bucket_brigade brig_a = { NULL, NULL }, brig_b = { NULL, NULL }, *inp = &brig_a, *outp = &brig_b, *brig_temp;
434: php_stream_bucket *bucket;
435: php_stream_filter_chain *chain;
436: php_stream_filter *current;
437: php_stream *stream;
438: size_t flushed_size = 0;
439: long flags = (finish ? PSFS_FLAG_FLUSH_CLOSE : PSFS_FLAG_FLUSH_INC);
440:
441: if (!filter->chain || !filter->chain->stream) {
442: /* Filter is not attached to a chain, or chain is somehow not part of a stream */
443: return FAILURE;
444: }
445:
446: chain = filter->chain;
447: stream = chain->stream;
448:
449: for(current = filter; current; current = current->next) {
450: php_stream_filter_status_t status;
451:
452: status = filter->fops->filter(stream, filter, inp, outp, NULL, flags TSRMLS_CC);
453: if (status == PSFS_FEED_ME) {
454: /* We've flushed the data far enough */
455: return SUCCESS;
456: }
457: if (status == PSFS_ERR_FATAL) {
458: return FAILURE;
459: }
460: /* Otherwise we have data available to PASS_ON
461: Swap the brigades and continue */
462: brig_temp = inp;
463: inp = outp;
464: outp = brig_temp;
465: outp->head = NULL;
466: outp->tail = NULL;
467:
468: flags = PSFS_FLAG_NORMAL;
469: }
470:
471: /* Last filter returned data via PSFS_PASS_ON
472: Do something with it */
473:
474: for(bucket = inp->head; bucket; bucket = bucket->next) {
475: flushed_size += bucket->buflen;
476: }
477:
478: if (flushed_size == 0) {
479: /* Unlikely, but possible */
480: return SUCCESS;
481: }
482:
483: if (chain == &(stream->readfilters)) {
484: /* Dump any newly flushed data to the read buffer */
485: if (stream->readpos > 0) {
486: /* Back the buffer up */
487: memcpy(stream->readbuf, stream->readbuf + stream->readpos, stream->writepos - stream->readpos);
488: stream->readpos = 0;
489: stream->writepos -= stream->readpos;
490: }
491: if (flushed_size > (stream->readbuflen - stream->writepos)) {
492: /* Grow the buffer */
493: stream->readbuf = perealloc(stream->readbuf, stream->writepos + flushed_size + stream->chunk_size, stream->is_persistent);
494: }
495: while ((bucket = inp->head)) {
496: memcpy(stream->readbuf + stream->writepos, bucket->buf, bucket->buflen);
497: stream->writepos += bucket->buflen;
498: php_stream_bucket_unlink(bucket TSRMLS_CC);
499: php_stream_bucket_delref(bucket TSRMLS_CC);
500: }
501: } else if (chain == &(stream->writefilters)) {
502: /* Send flushed data to the stream */
503: while ((bucket = inp->head)) {
504: stream->ops->write(stream, bucket->buf, bucket->buflen TSRMLS_CC);
505: php_stream_bucket_unlink(bucket TSRMLS_CC);
506: php_stream_bucket_delref(bucket TSRMLS_CC);
507: }
508: }
509:
510: return SUCCESS;
511: }
512:
513: PHPAPI php_stream_filter *php_stream_filter_remove(php_stream_filter *filter, int call_dtor TSRMLS_DC)
514: {
515: if (filter->prev) {
516: filter->prev->next = filter->next;
517: } else {
518: filter->chain->head = filter->next;
519: }
520: if (filter->next) {
521: filter->next->prev = filter->prev;
522: } else {
523: filter->chain->tail = filter->prev;
524: }
525:
526: if (filter->rsrc_id > 0) {
527: zend_list_delete(filter->rsrc_id);
528: }
529:
530: if (call_dtor) {
531: php_stream_filter_free(filter TSRMLS_CC);
532: return NULL;
533: }
534: return filter;
535: }
536:
537: /*
538: * Local variables:
539: * tab-width: 4
540: * c-basic-offset: 4
541: * End:
542: * vim600: noet sw=4 ts=4 fdm=marker
543: * vim<600: noet sw=4 ts=4
544: */
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>