1 : /*
2 : +----------------------------------------------------------------------+
3 : | PHP Version 5 |
4 : +----------------------------------------------------------------------+
5 : | Copyright (c) 1997-2007 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: Wez Furlong <wez@thebrainroom.com> |
16 : +----------------------------------------------------------------------+
17 : */
18 :
19 : /* $Id: filter.c,v 1.17.2.3.2.9 2007/01/15 17:07:07 tony2001 Exp $ */
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()
36 439 : {
37 439 : return &stream_filters_hash;
38 : }
39 :
40 : /* Normal hash selection/retrieval call */
41 : PHPAPI HashTable *_php_get_stream_filters_hash(TSRMLS_D)
42 0 : {
43 0 : 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 1760 : {
49 1760 : 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 1533 : {
54 1533 : 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 0 : {
60 0 : if (!FG(stream_filters)) {
61 : php_stream_filter_factory tmpfactory;
62 :
63 0 : ALLOC_HASHTABLE(FG(stream_filters));
64 0 : zend_hash_init(FG(stream_filters), zend_hash_num_elements(&stream_filters_hash), NULL, NULL, 1);
65 0 : zend_hash_copy(FG(stream_filters), &stream_filters_hash, NULL, &tmpfactory, sizeof(php_stream_filter_factory));
66 : }
67 :
68 0 : 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 133 : {
75 133 : int is_persistent = php_stream_is_persistent(stream);
76 : php_stream_bucket *bucket;
77 :
78 133 : bucket = (php_stream_bucket*)pemalloc(sizeof(php_stream_bucket), is_persistent);
79 :
80 133 : if (bucket == NULL) {
81 0 : return NULL;
82 : }
83 :
84 133 : bucket->next = bucket->prev = NULL;
85 :
86 133 : if (is_persistent && !buf_persistent) {
87 : /* all data in a persistent bucket must also be persistent */
88 0 : bucket->buf = pemalloc(buflen, 1);
89 :
90 0 : if (bucket->buf == NULL) {
91 0 : pefree(bucket, 1);
92 0 : return NULL;
93 : }
94 :
95 0 : memcpy(bucket->buf, buf, buflen);
96 0 : bucket->buflen = buflen;
97 0 : bucket->own_buf = 1;
98 : } else {
99 133 : bucket->buf = buf;
100 133 : bucket->buflen = buflen;
101 133 : bucket->own_buf = own_buf;
102 : }
103 133 : bucket->is_persistent = is_persistent;
104 133 : bucket->refcount = 1;
105 133 : bucket->brigade = NULL;
106 :
107 133 : 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 0 : {
119 : php_stream_bucket *retval;
120 :
121 0 : php_stream_bucket_unlink(bucket TSRMLS_CC);
122 :
123 0 : if (bucket->refcount == 1 && bucket->own_buf) {
124 0 : return bucket;
125 : }
126 :
127 0 : retval = (php_stream_bucket*)pemalloc(sizeof(php_stream_bucket), bucket->is_persistent);
128 0 : memcpy(retval, bucket, sizeof(*retval));
129 :
130 0 : retval->buf = pemalloc(retval->buflen, retval->is_persistent);
131 0 : memcpy(retval->buf, bucket->buf, retval->buflen);
132 :
133 0 : retval->refcount = 1;
134 0 : retval->own_buf = 1;
135 :
136 0 : php_stream_bucket_delref(bucket TSRMLS_CC);
137 :
138 0 : 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 0 : {
143 0 : *left = (php_stream_bucket*)pecalloc(1, sizeof(php_stream_bucket), in->is_persistent);
144 0 : *right = (php_stream_bucket*)pecalloc(1, sizeof(php_stream_bucket), in->is_persistent);
145 :
146 0 : if (*left == NULL || *right == NULL) {
147 : goto exit_fail;
148 : }
149 :
150 0 : (*left)->buf = pemalloc(length, in->is_persistent);
151 0 : (*left)->buflen = length;
152 0 : memcpy((*left)->buf, in->buf, length);
153 0 : (*left)->refcount = 1;
154 0 : (*left)->own_buf = 1;
155 0 : (*left)->is_persistent = in->is_persistent;
156 :
157 0 : (*right)->buflen = in->buflen - length;
158 0 : (*right)->buf = pemalloc((*right)->buflen, in->is_persistent);
159 0 : memcpy((*right)->buf, in->buf + length, (*right)->buflen);
160 0 : (*right)->refcount = 1;
161 0 : (*right)->own_buf = 1;
162 0 : (*right)->is_persistent = in->is_persistent;
163 :
164 0 : return SUCCESS;
165 :
166 0 : exit_fail:
167 0 : if (*right) {
168 0 : if ((*right)->buf) {
169 0 : pefree((*right)->buf, in->is_persistent);
170 : }
171 0 : pefree(*right, in->is_persistent);
172 : }
173 0 : if (*left) {
174 0 : if ((*left)->buf) {
175 0 : pefree((*left)->buf, in->is_persistent);
176 : }
177 0 : pefree(*left, in->is_persistent);
178 : }
179 0 : return FAILURE;
180 : }
181 :
182 : PHPAPI void php_stream_bucket_delref(php_stream_bucket *bucket TSRMLS_DC)
183 133 : {
184 133 : if (--bucket->refcount == 0) {
185 133 : if (bucket->own_buf) {
186 91 : pefree(bucket->buf, bucket->is_persistent);
187 : }
188 133 : pefree(bucket, bucket->is_persistent);
189 : }
190 133 : }
191 :
192 : PHPAPI void php_stream_bucket_prepend(php_stream_bucket_brigade *brigade, php_stream_bucket *bucket TSRMLS_DC)
193 0 : {
194 0 : bucket->next = brigade->head;
195 0 : bucket->prev = NULL;
196 :
197 0 : if (brigade->head) {
198 0 : brigade->head->prev = bucket;
199 : } else {
200 0 : brigade->tail = bucket;
201 : }
202 0 : brigade->head = bucket;
203 0 : bucket->brigade = brigade;
204 0 : }
205 :
206 : PHPAPI void php_stream_bucket_append(php_stream_bucket_brigade *brigade, php_stream_bucket *bucket TSRMLS_DC)
207 133 : {
208 133 : if (brigade->tail == bucket) {
209 0 : return;
210 : }
211 :
212 133 : bucket->prev = brigade->tail;
213 133 : bucket->next = NULL;
214 :
215 133 : if (brigade->tail) {
216 35 : brigade->tail->next = bucket;
217 : } else {
218 98 : brigade->head = bucket;
219 : }
220 133 : brigade->tail = bucket;
221 133 : bucket->brigade = brigade;
222 : }
223 :
224 : PHPAPI void php_stream_bucket_unlink(php_stream_bucket *bucket TSRMLS_DC)
225 133 : {
226 133 : if (bucket->prev) {
227 0 : bucket->prev->next = bucket->next;
228 133 : } else if (bucket->brigade) {
229 133 : bucket->brigade->head = bucket->next;
230 : }
231 133 : if (bucket->next) {
232 35 : bucket->next->prev = bucket->prev;
233 98 : } else if (bucket->brigade) {
234 98 : bucket->brigade->tail = bucket->prev;
235 : }
236 133 : bucket->brigade = NULL;
237 133 : bucket->next = bucket->prev = NULL;
238 133 : }
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 13 : {
254 13 : HashTable *filter_hash = (FG(stream_filters) ? FG(stream_filters) : &stream_filters_hash);
255 13 : php_stream_filter_factory *factory = NULL;
256 13 : php_stream_filter *filter = NULL;
257 : int n;
258 : char *period;
259 :
260 13 : n = strlen(filtername);
261 :
262 13 : if (SUCCESS == zend_hash_find(filter_hash, (char*)filtername, n + 1, (void**)&factory)) {
263 0 : filter = factory->create_filter(filtername, filterparams, persistent TSRMLS_CC);
264 13 : } else if ((period = strrchr(filtername, '.'))) {
265 : /* try a wildcard */
266 : char *wildname;
267 :
268 13 : wildname = emalloc(n+3);
269 13 : memcpy(wildname, filtername, n+1);
270 13 : period = wildname + (period - filtername);
271 39 : while (period && !filter) {
272 13 : *period = '\0';
273 13 : strcat(wildname, ".*");
274 13 : if (SUCCESS == zend_hash_find(filter_hash, wildname, strlen(wildname) + 1, (void**)&factory)) {
275 13 : filter = factory->create_filter(filtername, filterparams, persistent TSRMLS_CC);
276 : }
277 :
278 13 : *period = '\0';
279 13 : period = strrchr(wildname, '.');
280 : }
281 13 : efree(wildname);
282 : }
283 :
284 13 : if (filter == NULL) {
285 : /* TODO: these need correct docrefs */
286 0 : if (factory == NULL)
287 0 : php_error_docref(NULL TSRMLS_CC, E_WARNING, "unable to locate filter \"%s\"", filtername);
288 : else
289 0 : php_error_docref(NULL TSRMLS_CC, E_WARNING, "unable to create or locate filter \"%s\"", filtername);
290 : }
291 :
292 13 : 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 13 : {
297 : php_stream_filter *filter;
298 :
299 13 : filter = (php_stream_filter*) pemalloc_rel_orig(sizeof(php_stream_filter), persistent);
300 13 : memset(filter, 0, sizeof(php_stream_filter));
301 :
302 13 : filter->fops = fops;
303 13 : filter->abstract = abstract;
304 13 : filter->is_persistent = persistent;
305 :
306 13 : return filter;
307 : }
308 :
309 : PHPAPI void php_stream_filter_free(php_stream_filter *filter TSRMLS_DC)
310 13 : {
311 13 : if (filter->fops->dtor)
312 10 : filter->fops->dtor(filter TSRMLS_CC);
313 13 : pefree(filter, filter->is_persistent);
314 13 : }
315 :
316 : PHPAPI void _php_stream_filter_prepend(php_stream_filter_chain *chain, php_stream_filter *filter TSRMLS_DC)
317 0 : {
318 0 : filter->next = chain->head;
319 0 : filter->prev = NULL;
320 :
321 0 : if (chain->head) {
322 0 : chain->head->prev = filter;
323 : } else {
324 0 : chain->tail = filter;
325 : }
326 0 : chain->head = filter;
327 0 : filter->chain = chain;
328 0 : }
329 :
330 : PHPAPI void _php_stream_filter_append(php_stream_filter_chain *chain, php_stream_filter *filter TSRMLS_DC)
331 13 : {
332 13 : php_stream *stream = chain->stream;
333 :
334 13 : filter->prev = chain->tail;
335 13 : filter->next = NULL;
336 13 : if (chain->tail) {
337 7 : chain->tail->next = filter;
338 : } else {
339 6 : chain->head = filter;
340 : }
341 13 : chain->tail = filter;
342 13 : filter->chain = chain;
343 :
344 13 : if (&(stream->readfilters) == chain && (stream->writepos - stream->readpos) > 0) {
345 : /* Let's going ahead and wind anything in the buffer through this filter */
346 0 : php_stream_bucket_brigade brig_in = { NULL, NULL }, brig_out = { NULL, NULL };
347 0 : php_stream_bucket_brigade *brig_inp = &brig_in, *brig_outp = &brig_out;
348 : php_stream_filter_status_t status;
349 : php_stream_bucket *bucket;
350 0 : size_t consumed = 0;
351 :
352 0 : bucket = php_stream_bucket_new(stream, stream->readbuf + stream->readpos, stream->writepos - stream->readpos, 0, 0 TSRMLS_CC);
353 0 : php_stream_bucket_append(brig_inp, bucket TSRMLS_CC);
354 0 : status = filter->fops->filter(stream, filter, brig_inp, brig_outp, &consumed, PSFS_FLAG_NORMAL TSRMLS_CC);
355 :
356 0 : if (stream->readpos + consumed > (uint)stream->writepos || consumed < 0) {
357 : /* No behaving filter should cause this. */
358 0 : status = PSFS_ERR_FATAL;
359 : }
360 :
361 0 : switch (status) {
362 : case PSFS_ERR_FATAL:
363 : /* If this first cycle simply fails then there's something wrong with the filter.
364 : Pull the filter off the chain and leave the read buffer alone. */
365 0 : if (chain->head == filter) {
366 0 : chain->head = NULL;
367 0 : chain->tail = NULL;
368 : } else {
369 0 : filter->prev->next = NULL;
370 0 : chain->tail = filter->prev;
371 : }
372 0 : php_stream_bucket_unlink(bucket TSRMLS_CC);
373 0 : php_stream_bucket_delref(bucket TSRMLS_CC);
374 0 : php_error_docref(NULL TSRMLS_CC, E_WARNING, "Filter failed to process pre-buffered data. Not adding to filterchain.");
375 0 : break;
376 : case PSFS_FEED_ME:
377 : /* We don't actually need data yet,
378 : leave this filter in a feed me state until data is needed.
379 : Reset stream's internal read buffer since the filter is "holding" it. */
380 0 : stream->readpos = 0;
381 0 : stream->writepos = 0;
382 0 : break;
383 : case PSFS_PASS_ON:
384 : /* Put any filtered data onto the readbuffer stack.
385 : Previously read data has been at least partially consumed. */
386 0 : stream->readpos += consumed;
387 :
388 0 : if (stream->writepos == stream->readpos) {
389 : /* Entirely consumed */
390 0 : stream->writepos = 0;
391 0 : stream->readpos = 0;
392 : }
393 :
394 0 : while (brig_outp->head) {
395 0 : bucket = brig_outp->head;
396 : /* Grow buffer to hold this bucket if need be.
397 : TODO: See warning in main/stream/streams.c::php_stream_fill_read_buffer */
398 0 : if (stream->readbuflen - stream->writepos < bucket->buflen) {
399 0 : stream->readbuflen += bucket->buflen;
400 0 : stream->readbuf = perealloc(stream->readbuf, stream->readbuflen, stream->is_persistent);
401 : }
402 0 : memcpy(stream->readbuf + stream->writepos, bucket->buf, bucket->buflen);
403 0 : stream->writepos += bucket->buflen;
404 :
405 0 : php_stream_bucket_unlink(bucket TSRMLS_CC);
406 0 : php_stream_bucket_delref(bucket TSRMLS_CC);
407 : }
408 : break;
409 : }
410 : }
411 :
412 13 : }
413 :
414 : PHPAPI int _php_stream_filter_flush(php_stream_filter *filter, int finish TSRMLS_DC)
415 0 : {
416 0 : php_stream_bucket_brigade brig_a = { NULL, NULL }, brig_b = { NULL, NULL }, *inp = &brig_a, *outp = &brig_b, *brig_temp;
417 : php_stream_bucket *bucket;
418 : php_stream_filter_chain *chain;
419 : php_stream_filter *current;
420 : php_stream *stream;
421 0 : size_t flushed_size = 0;
422 0 : long flags = (finish ? PSFS_FLAG_FLUSH_CLOSE : PSFS_FLAG_FLUSH_INC);
423 :
424 0 : if (!filter->chain || !filter->chain->stream) {
425 : /* Filter is not attached to a chain, or chain is somehow not part of a stream */
426 0 : return FAILURE;
427 : }
428 :
429 0 : chain = filter->chain;
430 0 : stream = chain->stream;
431 :
432 0 : for(current = filter; current; current = current->next) {
433 : php_stream_filter_status_t status;
434 :
435 0 : status = filter->fops->filter(stream, filter, inp, outp, NULL, flags TSRMLS_CC);
436 0 : if (status == PSFS_FEED_ME) {
437 : /* We've flushed the data far enough */
438 0 : return SUCCESS;
439 : }
440 0 : if (status == PSFS_ERR_FATAL) {
441 0 : return FAILURE;
442 : }
443 : /* Otherwise we have data available to PASS_ON
444 : Swap the brigades and continue */
445 0 : brig_temp = inp;
446 0 : inp = outp;
447 0 : outp = brig_temp;
448 0 : outp->head = NULL;
449 0 : outp->tail = NULL;
450 :
451 0 : flags = PSFS_FLAG_NORMAL;
452 : }
453 :
454 : /* Last filter returned data via PSFS_PASS_ON
455 : Do something with it */
456 :
457 0 : for(bucket = inp->head; bucket; bucket = bucket->next) {
458 0 : flushed_size += bucket->buflen;
459 : }
460 :
461 0 : if (flushed_size == 0) {
462 : /* Unlikely, but possible */
463 0 : return SUCCESS;
464 : }
465 :
466 0 : if (chain == &(stream->readfilters)) {
467 : /* Dump any newly flushed data to the read buffer */
468 0 : if (stream->readpos > 0) {
469 : /* Back the buffer up */
470 0 : memcpy(stream->readbuf, stream->readbuf + stream->readpos, stream->writepos - stream->readpos);
471 0 : stream->readpos = 0;
472 0 : stream->writepos -= stream->readpos;
473 : }
474 0 : if (flushed_size > (stream->readbuflen - stream->writepos)) {
475 : /* Grow the buffer */
476 0 : stream->readbuf = perealloc(stream->readbuf, stream->writepos + flushed_size + stream->chunk_size, stream->is_persistent);
477 : }
478 0 : while ((bucket = inp->head)) {
479 0 : memcpy(stream->readbuf + stream->writepos, bucket->buf, bucket->buflen);
480 0 : stream->writepos += bucket->buflen;
481 0 : php_stream_bucket_unlink(bucket TSRMLS_CC);
482 0 : php_stream_bucket_delref(bucket TSRMLS_CC);
483 : }
484 0 : } else if (chain == &(stream->writefilters)) {
485 : /* Send flushed data to the stream */
486 0 : while ((bucket = inp->head)) {
487 0 : stream->ops->write(stream, bucket->buf, bucket->buflen TSRMLS_CC);
488 0 : php_stream_bucket_unlink(bucket TSRMLS_CC);
489 0 : php_stream_bucket_delref(bucket TSRMLS_CC);
490 : }
491 : }
492 :
493 0 : return SUCCESS;
494 : }
495 :
496 : PHPAPI php_stream_filter *php_stream_filter_remove(php_stream_filter *filter, int call_dtor TSRMLS_DC)
497 13 : {
498 13 : if (filter->prev) {
499 0 : filter->prev->next = filter->next;
500 : } else {
501 13 : filter->chain->head = filter->next;
502 : }
503 13 : if (filter->next) {
504 7 : filter->next->prev = filter->prev;
505 : } else {
506 6 : filter->chain->tail = filter->prev;
507 : }
508 :
509 13 : if (filter->rsrc_id > 0) {
510 13 : zend_list_delete(filter->rsrc_id);
511 : }
512 :
513 13 : if (call_dtor) {
514 13 : php_stream_filter_free(filter TSRMLS_CC);
515 13 : return NULL;
516 : }
517 0 : return filter;
518 : }
519 :
520 : /*
521 : * Local variables:
522 : * tab-width: 4
523 : * c-basic-offset: 4
524 : * End:
525 : * vim600: noet sw=4 ts=4 fdm=marker
526 : * vim<600: noet sw=4 ts=4
527 : */
|