1 : /*
2 : +----------------------------------------------------------------------+
3 : | PHP Version 6 |
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: Wez Furlong <wez@thebrainroom.com> |
16 : +----------------------------------------------------------------------+
17 : */
18 :
19 : /* $Id: filter.c 286624 2009-08-01 14:45:42Z kalle $ */
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 34046 : {
37 34046 : return &stream_filters_hash;
38 : }
39 :
40 : /* Normal hash selection/retrieval call */
41 : PHPAPI HashTable *_php_get_stream_filters_hash(TSRMLS_D)
42 32 : {
43 32 : 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 170070 : {
49 170070 : 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 153351 : {
54 153351 : 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 12 : {
60 12 : if (!FG(stream_filters)) {
61 : php_stream_filter_factory tmpfactory;
62 :
63 8 : ALLOC_HASHTABLE(FG(stream_filters));
64 8 : zend_hash_init(FG(stream_filters), zend_hash_num_elements(&stream_filters_hash), NULL, NULL, 1);
65 8 : zend_hash_copy(FG(stream_filters), &stream_filters_hash, NULL, &tmpfactory, sizeof(php_stream_filter_factory));
66 : }
67 :
68 12 : 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 16181 : {
75 16181 : int is_persistent = php_stream_is_persistent(stream);
76 : php_stream_bucket *bucket;
77 :
78 16181 : bucket = (php_stream_bucket*)pemalloc(sizeof(php_stream_bucket), is_persistent);
79 :
80 16181 : if (bucket == NULL) {
81 0 : return NULL;
82 : }
83 :
84 16181 : bucket->next = bucket->prev = NULL;
85 :
86 16181 : if (is_persistent && !buf_persistent) {
87 : /* all data in a persistent bucket must also be persistent */
88 0 : bucket->buf.s = pemalloc(buflen, 1);
89 :
90 0 : if (bucket->buf.s == NULL) {
91 0 : pefree(bucket, 1);
92 0 : return NULL;
93 : }
94 :
95 0 : memcpy(bucket->buf.s, buf, buflen);
96 0 : bucket->buflen = buflen;
97 0 : bucket->own_buf = 1;
98 : } else {
99 16181 : bucket->buf.s = buf;
100 16181 : bucket->buflen = buflen;
101 16181 : bucket->own_buf = own_buf;
102 : }
103 16181 : bucket->buf_type = IS_STRING;
104 16181 : bucket->is_persistent = is_persistent;
105 16181 : bucket->refcount = 1;
106 16181 : bucket->brigade = NULL;
107 :
108 16181 : return bucket;
109 : }
110 :
111 : PHPAPI php_stream_bucket *php_stream_bucket_new_unicode(php_stream *stream, UChar *buf, int32_t buflen, int own_buf, int buf_persistent TSRMLS_DC)
112 14935 : {
113 14935 : int is_persistent = php_stream_is_persistent(stream);
114 : php_stream_bucket *bucket;
115 :
116 14935 : bucket = (php_stream_bucket*)pemalloc(sizeof(php_stream_bucket), is_persistent);
117 :
118 14935 : if (bucket == NULL) {
119 0 : return NULL;
120 : }
121 :
122 14935 : bucket->next = bucket->prev = NULL;
123 :
124 14935 : if (is_persistent && !buf_persistent) {
125 : /* all data in a persistent bucket must also be persistent */
126 0 : bucket->buf.u = safe_pemalloc(sizeof(UChar), buflen, 0, 1);
127 :
128 0 : if (bucket->buf.u == NULL) {
129 0 : pefree(bucket, 1);
130 0 : return NULL;
131 : }
132 :
133 0 : memcpy(bucket->buf.u, buf, buflen);
134 0 : bucket->buflen = buflen;
135 0 : bucket->own_buf = 1;
136 : } else {
137 14935 : bucket->buf.u = buf;
138 14935 : bucket->buflen = buflen;
139 14935 : bucket->own_buf = own_buf;
140 : }
141 14935 : bucket->buf_type = IS_UNICODE;
142 14935 : bucket->is_persistent = is_persistent;
143 14935 : bucket->refcount = 1;
144 14935 : bucket->brigade = NULL;
145 :
146 14935 : return bucket;
147 : }
148 :
149 : /* Given a bucket, returns a version of that bucket with a writeable buffer.
150 : * If the original bucket has a refcount of 1 and owns its buffer, then it
151 : * is returned unchanged.
152 : * Otherwise, a copy of the buffer is made.
153 : * In both cases, the original bucket is unlinked from its brigade.
154 : * If a copy is made, the original bucket is delref'd.
155 : * */
156 : PHPAPI php_stream_bucket *php_stream_bucket_make_writeable(php_stream_bucket *bucket TSRMLS_DC)
157 191 : {
158 : php_stream_bucket *retval;
159 :
160 191 : php_stream_bucket_unlink(bucket TSRMLS_CC);
161 :
162 191 : if (bucket->refcount == 1 && bucket->own_buf) {
163 8 : return bucket;
164 : }
165 :
166 183 : retval = (php_stream_bucket*)pemalloc(sizeof(php_stream_bucket), bucket->is_persistent);
167 183 : memcpy(retval, bucket, sizeof(*retval));
168 :
169 183 : if (bucket->buf_type == IS_UNICODE) {
170 0 : retval->buf.u = safe_pemalloc(sizeof(UChar), retval->buflen, 0, retval->is_persistent);
171 0 : memcpy(retval->buf.u, bucket->buf.u, UBYTES(retval->buflen));
172 : } else {
173 183 : retval->buf.s = pemalloc(retval->buflen, retval->is_persistent);
174 183 : memcpy(retval->buf.s, bucket->buf.s, retval->buflen);
175 : }
176 :
177 183 : retval->refcount = 1;
178 183 : retval->own_buf = 1;
179 :
180 183 : php_stream_bucket_delref(bucket TSRMLS_CC);
181 :
182 183 : return retval;
183 : }
184 :
185 : PHPAPI int php_stream_bucket_split(php_stream_bucket *in, php_stream_bucket **left, php_stream_bucket **right, size_t length TSRMLS_DC)
186 0 : {
187 0 : *left = (php_stream_bucket*)pecalloc(1, sizeof(php_stream_bucket), in->is_persistent);
188 0 : *right = (php_stream_bucket*)pecalloc(1, sizeof(php_stream_bucket), in->is_persistent);
189 :
190 0 : if (*left == NULL || *right == NULL) {
191 : goto exit_fail;
192 : }
193 :
194 0 : if (in->buf_type == IS_UNICODE) {
195 0 : (*left)->buf.u = safe_pemalloc(sizeof(UChar), length, 0, in->is_persistent);
196 0 : (*left)->buflen = length;
197 0 : memcpy((*left)->buf.u, in->buf.u, UBYTES(length));
198 :
199 0 : (*right)->buflen = in->buflen - length;
200 0 : (*right)->buf.u = pemalloc(UBYTES((*right)->buflen), in->is_persistent);
201 0 : memcpy((*right)->buf.u, in->buf.u + length, UBYTES((*right)->buflen));
202 : } else {
203 0 : (*left)->buf.s = pemalloc(length, in->is_persistent);
204 0 : (*left)->buflen = length;
205 0 : memcpy((*left)->buf.s, in->buf.s, length);
206 :
207 0 : (*right)->buflen = in->buflen - length;
208 0 : (*right)->buf.s = pemalloc((*right)->buflen, in->is_persistent);
209 0 : memcpy((*right)->buf.s, in->buf.s + length, (*right)->buflen);
210 : }
211 :
212 0 : (*left)->refcount = 1;
213 0 : (*left)->own_buf = 1;
214 0 : (*left)->is_persistent = in->is_persistent;
215 0 : (*left)->buf_type = in->buf_type;
216 :
217 0 : (*right)->refcount = 1;
218 0 : (*right)->own_buf = 1;
219 0 : (*right)->is_persistent = in->is_persistent;
220 0 : (*right)->buf_type = in->buf_type;
221 :
222 0 : return SUCCESS;
223 :
224 0 : exit_fail:
225 0 : if (*right) {
226 0 : if ((*right)->buf.v) {
227 0 : pefree((*right)->buf.v, in->is_persistent);
228 : }
229 0 : pefree(*right, in->is_persistent);
230 : }
231 0 : if (*left) {
232 0 : if ((*left)->buf.v) {
233 0 : pefree((*left)->buf.v, in->is_persistent);
234 : }
235 0 : pefree(*left, in->is_persistent);
236 : }
237 0 : return FAILURE;
238 : }
239 :
240 : PHPAPI void php_stream_bucket_delref(php_stream_bucket *bucket TSRMLS_DC)
241 31313 : {
242 31313 : if (--bucket->refcount == 0) {
243 31299 : if (bucket->own_buf) {
244 15442 : pefree(bucket->buf.v, bucket->is_persistent);
245 : }
246 31299 : pefree(bucket, bucket->is_persistent);
247 : }
248 31313 : }
249 :
250 : PHPAPI void php_stream_bucket_prepend(php_stream_bucket_brigade *brigade, php_stream_bucket *bucket TSRMLS_DC)
251 0 : {
252 0 : bucket->next = brigade->head;
253 0 : bucket->prev = NULL;
254 :
255 0 : if (brigade->head) {
256 0 : brigade->head->prev = bucket;
257 : } else {
258 0 : brigade->tail = bucket;
259 : }
260 0 : brigade->head = bucket;
261 0 : bucket->brigade = brigade;
262 0 : }
263 :
264 : PHPAPI void php_stream_bucket_append(php_stream_bucket_brigade *brigade, php_stream_bucket *bucket TSRMLS_DC)
265 31778 : {
266 31778 : if (brigade->tail == bucket) {
267 1 : return;
268 : }
269 :
270 31777 : bucket->prev = brigade->tail;
271 31777 : bucket->next = NULL;
272 :
273 31777 : if (brigade->tail) {
274 166 : brigade->tail->next = bucket;
275 : } else {
276 31611 : brigade->head = bucket;
277 : }
278 31777 : brigade->tail = bucket;
279 31777 : bucket->brigade = brigade;
280 : }
281 :
282 : PHPAPI void php_stream_bucket_unlink(php_stream_bucket *bucket TSRMLS_DC)
283 31777 : {
284 31777 : if (bucket->prev) {
285 0 : bucket->prev->next = bucket->next;
286 31777 : } else if (bucket->brigade) {
287 31777 : bucket->brigade->head = bucket->next;
288 : }
289 31777 : if (bucket->next) {
290 166 : bucket->next->prev = bucket->prev;
291 31611 : } else if (bucket->brigade) {
292 31611 : bucket->brigade->tail = bucket->prev;
293 : }
294 31777 : bucket->brigade = NULL;
295 31777 : bucket->next = bucket->prev = NULL;
296 31777 : }
297 :
298 :
299 :
300 :
301 :
302 :
303 :
304 :
305 : /* We allow very simple pattern matching for filter factories:
306 : * if "convert.charset.utf-8/sjis" is requested, we search first for an exact
307 : * match. If that fails, we try "convert.charset.*", then "convert.*"
308 : * This means that we don't need to clog up the hashtable with a zillion
309 : * charsets (for example) but still be able to provide them all as filters */
310 : PHPAPI php_stream_filter *php_stream_filter_create(const char *filtername, zval *filterparams, int persistent TSRMLS_DC)
311 18962 : {
312 18962 : HashTable *filter_hash = (FG(stream_filters) ? FG(stream_filters) : &stream_filters_hash);
313 18962 : php_stream_filter_factory *factory = NULL;
314 18962 : php_stream_filter *filter = NULL;
315 : int n;
316 : char *period;
317 :
318 18962 : n = strlen(filtername);
319 :
320 18962 : if (SUCCESS == zend_hash_find(filter_hash, (char*)filtername, n + 1, (void**)&factory)) {
321 86 : filter = factory->create_filter(filtername, filterparams, persistent TSRMLS_CC);
322 18876 : } else if ((period = strrchr(filtername, '.'))) {
323 : /* try a wildcard */
324 : char *wildname;
325 :
326 18876 : wildname = emalloc(n+3);
327 18876 : memcpy(wildname, filtername, n+1);
328 18876 : period = wildname + (period - filtername);
329 75355 : while (period && !filter) {
330 37603 : *period = '\0';
331 37603 : strcat(wildname, ".*");
332 37603 : if (SUCCESS == zend_hash_find(filter_hash, wildname, strlen(wildname) + 1, (void**)&factory)) {
333 18876 : filter = factory->create_filter(filtername, filterparams, persistent TSRMLS_CC);
334 : }
335 :
336 37603 : *period = '\0';
337 37603 : period = strrchr(wildname, '.');
338 : }
339 18876 : efree(wildname);
340 : }
341 :
342 18962 : if (filter == NULL) {
343 : /* TODO: these need correct docrefs */
344 4 : if (factory == NULL) {
345 0 : php_error_docref(NULL TSRMLS_CC, E_WARNING, "unable to locate filter \"%s\"", filtername);
346 : } else {
347 4 : php_error_docref(NULL TSRMLS_CC, E_WARNING, "unable to create or locate filter \"%s\"", filtername);
348 : }
349 4 : return NULL;
350 : }
351 :
352 18958 : filter->name = pestrdup(filtername, filter->is_persistent);
353 :
354 18958 : return filter;
355 : }
356 :
357 : PHPAPI php_stream_filter *_php_stream_filter_alloc(php_stream_filter_ops *fops, void *abstract, int persistent STREAMS_DC TSRMLS_DC)
358 18958 : {
359 : php_stream_filter *filter;
360 :
361 18958 : filter = (php_stream_filter*) pemalloc_rel_orig(sizeof(php_stream_filter), persistent);
362 18958 : memset(filter, 0, sizeof(php_stream_filter));
363 :
364 18958 : filter->fops = fops;
365 18958 : filter->abstract = abstract;
366 18958 : filter->is_persistent = persistent;
367 :
368 18958 : return filter;
369 : }
370 :
371 : PHPAPI void php_stream_filter_free(php_stream_filter *filter TSRMLS_DC)
372 18958 : {
373 18958 : if (filter->fops->dtor)
374 18933 : filter->fops->dtor(filter TSRMLS_CC);
375 18958 : pefree(filter->name, filter->is_persistent);
376 18958 : pefree(filter, filter->is_persistent);
377 18958 : }
378 :
379 : PHPAPI int php_stream_filter_prepend_ex(php_stream_filter_chain *chain, php_stream_filter *filter TSRMLS_DC)
380 10 : {
381 10 : filter->next = chain->head;
382 10 : filter->prev = NULL;
383 :
384 10 : if (chain->head) {
385 2 : chain->head->prev = filter;
386 : } else {
387 8 : chain->tail = filter;
388 : }
389 10 : chain->head = filter;
390 10 : filter->chain = chain;
391 :
392 10 : return SUCCESS;
393 : }
394 :
395 : PHPAPI void _php_stream_filter_prepend(php_stream_filter_chain *chain, php_stream_filter *filter TSRMLS_DC)
396 0 : {
397 0 : php_stream_filter_prepend_ex(chain, filter TSRMLS_CC);
398 0 : }
399 :
400 : PHPAPI int php_stream_filter_append_ex(php_stream_filter_chain *chain, php_stream_filter *filter TSRMLS_DC)
401 18948 : {
402 18948 : php_stream *stream = chain->stream;
403 :
404 18948 : filter->prev = chain->tail;
405 18948 : filter->next = NULL;
406 18948 : if (chain->tail) {
407 33 : chain->tail->next = filter;
408 : } else {
409 18915 : chain->head = filter;
410 : }
411 18948 : chain->tail = filter;
412 18948 : filter->chain = chain;
413 :
414 18948 : if (&(stream->readfilters) == chain) {
415 : /* Let's going ahead and wind anything in the buffer through this filter */
416 12801 : php_stream_bucket_brigade brig_in = { NULL, NULL }, brig_out = { NULL, NULL };
417 12801 : php_stream_bucket_brigade *brig_inp = &brig_in, *brig_outp = &brig_out;
418 12801 : php_stream_filter_status_t status = PSFS_FEED_ME;
419 : php_stream_bucket *bucket;
420 12801 : size_t consumed = 0;
421 :
422 12801 : if ((stream->writepos - stream->readpos) > 0) {
423 8 : if (stream->readbuf_type == IS_UNICODE) {
424 0 : bucket = php_stream_bucket_new_unicode(stream, stream->readbuf.u + stream->readpos, stream->writepos - stream->readpos, 0, 0 TSRMLS_CC);
425 : } else {
426 8 : bucket = php_stream_bucket_new(stream, stream->readbuf.s + stream->readpos, stream->writepos - stream->readpos, 0, 0 TSRMLS_CC);
427 : }
428 8 : php_stream_bucket_append(brig_inp, bucket TSRMLS_CC);
429 8 : status = filter->fops->filter(stream, filter, brig_inp, brig_outp, &consumed, PSFS_FLAG_NORMAL TSRMLS_CC);
430 :
431 8 : if ((int) (stream->readpos + consumed) > stream->writepos || consumed < 0) {
432 : /* No behaving filter should cause this. */
433 0 : status = PSFS_ERR_FATAL;
434 : }
435 : }
436 :
437 12801 : if (status == PSFS_ERR_FATAL) {
438 14 : while (brig_in.head) {
439 0 : bucket = brig_in.head;
440 0 : php_stream_bucket_unlink(bucket TSRMLS_CC);
441 0 : php_stream_bucket_delref(bucket TSRMLS_CC);
442 : }
443 14 : while (brig_out.head) {
444 0 : bucket = brig_out.head;
445 0 : php_stream_bucket_unlink(bucket TSRMLS_CC);
446 0 : php_stream_bucket_delref(bucket TSRMLS_CC);
447 : }
448 7 : php_error_docref(NULL TSRMLS_CC, E_WARNING, "Filter failed to process pre-buffered data");
449 7 : return FAILURE;
450 : } else {
451 : /* This filter addition may change the readbuffer type.
452 : Since all the previously held data is in the bucket brigade,
453 : we can reappropriate the buffer that already exists (if one does) */
454 12802 : if (stream->readbuf_type == IS_UNICODE && (filter->fops->flags & PSFO_FLAG_OUTPUTS_UNICODE) == 0) {
455 : /* Buffer is currently based on unicode characters, but filter only outputs STRING adjust counting */
456 8 : stream->readbuf_type = IS_STRING;
457 8 : stream->readbuflen *= UBYTES(1);
458 12786 : } else if (stream->readbuf_type == IS_STRING && (filter->fops->flags & PSFO_FLAG_OUTPUTS_STRING) == 0) {
459 : /* Buffer is currently based on binary characters, but filter only outputs UNICODE adjust counting */
460 12734 : stream->readbuf_type = IS_UNICODE;
461 12734 : stream->readbuflen /= UBYTES(1);
462 : }
463 :
464 12794 : if (status == PSFS_FEED_ME) {
465 : /* We don't actually need data yet,
466 : leave this filter in a feed me state until data is needed.
467 : Reset stream's internal read buffer since the filter is "holding" it. */
468 12793 : stream->readpos = 0;
469 12793 : stream->writepos = 0;
470 1 : } else if (status == PSFS_PASS_ON) {
471 : /* If any data is consumed, we cannot rely upon the existing read buffer,
472 : as the filtered data must replace the existing data, so invalidate the cache */
473 : /* note that changes here should be reflected in
474 : main/streams/streams.c::php_stream_fill_read_buffer */
475 1 : stream->writepos = 0;
476 1 : stream->readpos = 0;
477 :
478 3 : while (brig_outp->head) {
479 1 : bucket = brig_outp->head;
480 :
481 : /* Convert for stream type */
482 1 : if (bucket->buf_type != stream->readbuf_type) {
483 : /* Stream expects different type than bucket contains, convert slopily */
484 0 : php_stream_bucket_convert_notranscode(bucket, stream->readbuf_type);
485 : }
486 :
487 : /* Grow buffer to hold this bucket if need be */
488 1 : if (stream->readbuflen - stream->writepos < (unsigned int)bucket->buflen) {
489 0 : stream->readbuflen += bucket->buflen;
490 0 : stream->readbuf.v = perealloc(stream->readbuf.v, PS_ULEN(stream->readbuf_type == IS_UNICODE, stream->readbuflen), stream->is_persistent);
491 : }
492 :
493 : /* Append to readbuf */
494 1 : if (stream->readbuf_type == IS_UNICODE) {
495 0 : memcpy(stream->readbuf.u + stream->writepos, bucket->buf.u, UBYTES(bucket->buflen));
496 : } else {
497 1 : memcpy(stream->readbuf.s + stream->writepos, bucket->buf.s, bucket->buflen);
498 : }
499 1 : stream->writepos += bucket->buflen;
500 :
501 1 : php_stream_bucket_unlink(bucket TSRMLS_CC);
502 1 : php_stream_bucket_delref(bucket TSRMLS_CC);
503 : }
504 : }
505 : }
506 : } /* end of readfilters specific code */
507 :
508 18941 : return SUCCESS;
509 : }
510 :
511 : PHPAPI void _php_stream_filter_append(php_stream_filter_chain *chain, php_stream_filter *filter TSRMLS_DC)
512 18856 : {
513 18856 : if (php_stream_filter_append_ex(chain, filter TSRMLS_CC) != SUCCESS) {
514 0 : if (chain->head == filter) {
515 0 : chain->head = NULL;
516 0 : chain->tail = NULL;
517 : } else {
518 0 : filter->prev->next = NULL;
519 0 : chain->tail = filter->prev;
520 : }
521 : }
522 18856 : }
523 :
524 : PHPAPI int _php_stream_filter_check_chain(php_stream_filter_chain *chain TSRMLS_DC)
525 98 : {
526 : php_stream_filter *filter;
527 98 : long last_output = PSFO_FLAG_OUTPUTS_ANY;
528 :
529 248 : for(filter = chain->head; filter; filter = filter->next) {
530 150 : if ((((filter->fops->flags & PSFO_FLAG_ACCEPT_MASK) << PSFO_FLAG_ACCEPT_SHIFT) & last_output) == 0) {
531 : /* Nothing which the last filter outputs is accepted by this filter */
532 0 : return FAILURE;
533 : }
534 150 : if (filter->fops->flags & PSFO_FLAG_OUTPUTS_SAME) {
535 150 : continue;
536 : }
537 0 : if (filter->fops->flags & PSFO_FLAG_OUTPUTS_OPPOSITE) {
538 0 : last_output = ((last_output & PSFO_FLAG_OUTPUTS_STRING) ? PSFO_FLAG_OUTPUTS_UNICODE : 0) |
539 : ((last_output & PSFO_FLAG_OUTPUTS_UNICODE) ? PSFO_FLAG_OUTPUTS_STRING : 0);
540 0 : continue;
541 : }
542 0 : last_output = filter->fops->flags & PSFO_FLAG_OUTPUTS_ANY;
543 : }
544 :
545 98 : return SUCCESS;
546 : }
547 :
548 : PHPAPI int _php_stream_filter_output_prefer_unicode(php_stream_filter *filter TSRMLS_DC)
549 0 : {
550 0 : php_stream_filter_chain *chain = filter->chain;
551 : php_stream_filter *f;
552 0 : int inverted = 0;
553 0 : int preferred = (chain == &chain->stream->readfilters ? 1 : 0);
554 :
555 0 : for (f = filter->next; f ; f = f->next) {
556 0 : if ((f->fops->flags & PSFO_FLAG_ACCEPTS_STRING) == 0) {
557 0 : return inverted ^= 1;
558 : }
559 0 : if ((f->fops->flags & PSFO_FLAG_ACCEPTS_UNICODE) == 0) {
560 0 : return inverted;
561 : }
562 0 : if (((f->fops->flags & PSFO_FLAG_OUTPUTS_SAME) == 0) &&
563 : ((f->fops->flags & PSFO_FLAG_OUTPUTS_OPPOSITE) == 0)) {
564 : /* Input type for next filter won't effect output -- Might as well go for unicode */
565 0 : return inverted ^ 1;
566 : }
567 0 : if (f->fops->flags & PSFO_FLAG_OUTPUTS_SAME) {
568 0 : continue;
569 : }
570 0 : if (f->fops->flags & PSFO_FLAG_OUTPUTS_OPPOSITE) {
571 0 : inverted ^= 1;
572 0 : continue;
573 : }
574 : }
575 :
576 0 : return preferred ^ inverted;
577 : }
578 :
579 : PHPAPI int _php_stream_filter_product(php_stream_filter_chain *chain, int type TSRMLS_DC)
580 34831 : {
581 : php_stream_filter *f;
582 :
583 40769 : for (f = chain->head; f; f = f->next) {
584 5938 : if ((type == IS_STRING && (f->fops->flags & PSFO_FLAG_ACCEPTS_STRING) == 0) ||
585 : (type == IS_UNICODE && (f->fops->flags & PSFO_FLAG_ACCEPTS_UNICODE) == 0)) {
586 : /* At some point, the type produced conflicts with the type accepted */
587 0 : return 0;
588 : }
589 :
590 5938 : if (f->fops->flags & PSFO_FLAG_OUTPUTS_OPPOSITE) {
591 5938 : type = (type == IS_STRING) ? IS_UNICODE : IS_STRING;
592 5938 : continue;
593 : }
594 0 : if ((f->fops->flags & PSFO_FLAG_OUTPUTS_SAME) ||
595 : (f->fops->flags & PSFO_FLAG_OUTPUTS_ANY)) {
596 : continue;
597 : }
598 0 : if (f->fops->flags & PSFO_FLAG_OUTPUTS_UNICODE) {
599 0 : type = IS_UNICODE;
600 0 : continue;
601 : }
602 0 : type = IS_STRING;
603 : }
604 :
605 34831 : return type;
606 : }
607 :
608 : PHPAPI int _php_stream_filter_flush(php_stream_filter *filter, int finish TSRMLS_DC)
609 144 : {
610 144 : php_stream_bucket_brigade brig_a = { NULL, NULL }, brig_b = { NULL, NULL }, *inp = &brig_a, *outp = &brig_b, *brig_temp;
611 : php_stream_bucket *bucket;
612 : php_stream_filter_chain *chain;
613 : php_stream_filter *current;
614 : php_stream *stream;
615 144 : size_t flushed_size = 0;
616 144 : long flags = (finish ? PSFS_FLAG_FLUSH_CLOSE : PSFS_FLAG_FLUSH_INC);
617 :
618 144 : if (!filter->chain || !filter->chain->stream) {
619 : /* Filter is not attached to a chain, or chain is somehow not part of a stream */
620 0 : return FAILURE;
621 : }
622 :
623 144 : chain = filter->chain;
624 144 : stream = chain->stream;
625 :
626 212 : for(current = filter; current; current = current->next) {
627 : php_stream_filter_status_t status;
628 :
629 144 : status = filter->fops->filter(stream, filter, inp, outp, NULL, flags TSRMLS_CC);
630 144 : if (status == PSFS_FEED_ME) {
631 : /* We've flushed the data far enough */
632 66 : return SUCCESS;
633 : }
634 78 : if (status == PSFS_ERR_FATAL) {
635 10 : return FAILURE;
636 : }
637 : /* Otherwise we have data available to PASS_ON
638 : Swap the brigades and continue */
639 68 : brig_temp = inp;
640 68 : inp = outp;
641 68 : outp = brig_temp;
642 68 : outp->head = NULL;
643 68 : outp->tail = NULL;
644 :
645 68 : flags = PSFS_FLAG_NORMAL;
646 : }
647 :
648 : /* Last filter returned data via PSFS_PASS_ON
649 : Do something with it */
650 :
651 137 : for(bucket = inp->head; bucket; bucket = bucket->next) {
652 69 : flushed_size += bucket->buflen;
653 : }
654 :
655 68 : if (flushed_size == 0) {
656 : /* Unlikely, but possible */
657 3 : return SUCCESS;
658 : }
659 :
660 65 : if (chain == &(stream->readfilters)) {
661 : /* Dump any newly flushed data to the read buffer */
662 0 : if ((unsigned int)stream->readpos > stream->chunk_size) {
663 : /* Back the buffer up */
664 0 : memcpy(stream->readbuf.s, stream->readbuf.s + PS_ULEN(stream->readbuf_type == IS_UNICODE, stream->readpos), PS_ULEN(stream->readbuf_type == IS_UNICODE, stream->writepos - stream->readpos));
665 0 : stream->writepos -= stream->readpos;
666 0 : stream->readpos = 0;
667 : }
668 0 : if (flushed_size > (stream->readbuflen - stream->writepos)) {
669 : /* Grow the buffer */
670 0 : stream->readbuf.v = perealloc(stream->readbuf.v, PS_ULEN(stream->readbuf_type == IS_UNICODE, stream->writepos + flushed_size + stream->chunk_size), stream->is_persistent);
671 : }
672 0 : while ((bucket = inp->head)) {
673 : /* Convert if necessary */
674 0 : if (bucket->buf_type != stream->readbuf_type) {
675 : /* Stream expects different type than what's in bucket, convert slopily */
676 0 : php_stream_bucket_convert_notranscode(bucket, stream->readbuf_type);
677 : }
678 :
679 : /* Append to readbuf */
680 0 : if (stream->readbuf_type == IS_UNICODE) {
681 0 : memcpy(stream->readbuf.u + stream->writepos, bucket->buf.u, UBYTES(bucket->buflen));
682 : } else {
683 0 : memcpy(stream->readbuf.s + stream->writepos, bucket->buf.s, bucket->buflen);
684 : }
685 0 : stream->writepos += bucket->buflen;
686 0 : php_stream_bucket_unlink(bucket TSRMLS_CC);
687 0 : php_stream_bucket_delref(bucket TSRMLS_CC);
688 : }
689 :
690 :
691 65 : } else if (chain == &(stream->writefilters)) {
692 : /* Send flushed data to the stream */
693 199 : while ((bucket = inp->head)) {
694 : /* Convert if necessary */
695 69 : if (bucket->buf_type == IS_UNICODE) {
696 : /* Force data to binary, adjusting buflen */
697 0 : php_stream_bucket_convert_notranscode(bucket, IS_STRING);
698 : }
699 :
700 : /* Must be binary by this point */
701 69 : stream->ops->write(stream, bucket->buf.s, bucket->buflen TSRMLS_CC);
702 :
703 69 : php_stream_bucket_unlink(bucket TSRMLS_CC);
704 69 : php_stream_bucket_delref(bucket TSRMLS_CC);
705 : }
706 : }
707 :
708 65 : return SUCCESS;
709 : }
710 :
711 : PHPAPI php_stream_filter *php_stream_filter_remove(php_stream_filter *filter, int call_dtor TSRMLS_DC)
712 18958 : {
713 : /* UTODO: Figure out a sane way to "defilter" so that unicode converters can be swapped around
714 : For now, at least fopen(,'b') + stream_encoding($fp, 'charset') works since there's nothing to remove */
715 :
716 18958 : if (filter->prev) {
717 1 : filter->prev->next = filter->next;
718 : } else {
719 18957 : filter->chain->head = filter->next;
720 : }
721 18958 : if (filter->next) {
722 34 : filter->next->prev = filter->prev;
723 : } else {
724 18924 : filter->chain->tail = filter->prev;
725 : }
726 :
727 18958 : if (filter->rsrc_id > 0) {
728 65 : zend_list_delete(filter->rsrc_id);
729 : }
730 :
731 18958 : if (call_dtor) {
732 18958 : php_stream_filter_free(filter TSRMLS_CC);
733 18958 : return NULL;
734 : }
735 0 : return filter;
736 : }
737 :
738 : PHPAPI int _php_stream_bucket_convert(php_stream_bucket *bucket, unsigned char type, UConverter *conv TSRMLS_DC)
739 1 : {
740 1 : if (bucket->buf_type == type) {
741 0 : return SUCCESS;
742 : }
743 :
744 1 : if (conv) {
745 : /* transcode current type using provided converter */
746 0 : if (type == IS_UNICODE) {
747 0 : UErrorCode status = U_ZERO_ERROR;
748 : UChar *dest;
749 : int destlen;
750 :
751 0 : zend_string_to_unicode_ex(conv, &dest, &destlen, bucket->buf.s, bucket->buflen, &status);
752 :
753 0 : if (bucket->own_buf) {
754 0 : pefree(bucket->buf.s, bucket->is_persistent);
755 : }
756 :
757 : /* Might be dangerous, double check this (or, better, get a persistent version of zend_string_to_unicode_ex() */
758 0 : bucket->is_persistent = 0;
759 :
760 0 : bucket->buf_type = IS_UNICODE;
761 0 : bucket->buf.u = dest;
762 0 : bucket->buflen = destlen;
763 0 : bucket->own_buf = 1;
764 0 : return SUCCESS;
765 : } else {
766 0 : UErrorCode status = U_ZERO_ERROR;
767 : char *dest;
768 : int destlen, num_conv;
769 :
770 0 : num_conv = zend_unicode_to_string_ex(conv, &dest, &destlen, bucket->buf.u, bucket->buflen, &status);
771 0 : if (U_FAILURE(status)) {
772 0 : int32_t offset = u_countChar32(bucket->buf.u, num_conv);
773 :
774 0 : zend_raise_conversion_error_ex("Could not convert Unicode string to binary string", conv, ZEND_FROM_UNICODE, offset TSRMLS_CC);
775 : }
776 :
777 0 : if (bucket->own_buf) {
778 0 : pefree(bucket->buf.u, bucket->is_persistent);
779 : }
780 :
781 : /* See above */
782 0 : bucket->is_persistent = 0;
783 :
784 0 : bucket->buf_type = IS_STRING;
785 0 : bucket->buf.s = dest;
786 0 : bucket->buflen = destlen;
787 0 : bucket->own_buf = 1;
788 0 : return SUCCESS;
789 : }
790 : } else {
791 : /* Convert without transcode, usually a bad idea as it creates ugly data,
792 : When binary streams receive unicode data (because of filters or writing unicode strings)
793 : this is the only option */
794 1 : if (type == IS_UNICODE) {
795 0 : if (bucket->buflen == 0) {
796 : /* Shortcut conversion for empty buckets */
797 0 : if (bucket->own_buf) {
798 0 : pefree(bucket->buf.s, bucket->is_persistent);
799 : }
800 0 : bucket->buf_type = IS_UNICODE;
801 0 : bucket->buf.u = EMPTY_STR;
802 0 : bucket->own_buf = 0;
803 0 : bucket->buflen = 0;
804 0 : return SUCCESS;
805 : }
806 :
807 0 : if (bucket->own_buf) {
808 : /* one UChar padding for partial units, one more for terminating NULL */
809 0 : bucket->buf.s = perealloc(bucket->buf.s, bucket->buflen + UBYTES(2), bucket->is_persistent);
810 : } else {
811 0 : int destlen = ceil(bucket->buflen / sizeof(UChar));
812 0 : UChar *dest = pemalloc(UBYTES(destlen + 2), bucket->is_persistent);
813 0 : memcpy(dest, bucket->buf.s, bucket->buflen);
814 0 : bucket->buf.u = dest;
815 0 : bucket->own_buf = 1;
816 : }
817 0 : memset(bucket->buf.s + bucket->buflen, 0, UBYTES(2));
818 0 : bucket->buf_type = IS_UNICODE;
819 0 : bucket->buflen = ceil(bucket->buflen / sizeof(UChar));
820 0 : return SUCCESS;
821 : } else { /* IS_STRING */
822 1 : bucket->buf_type = IS_STRING;
823 1 : bucket->buflen *= 2;
824 1 : return SUCCESS;
825 : }
826 : }
827 :
828 : /* Never reached */
829 : return FAILURE;
830 : }
831 :
832 : PHPAPI int _php_stream_encoding_apply(php_stream *stream, int writechain, const char *encoding, uint16_t error_mode, UChar *subst TSRMLS_DC)
833 18715 : {
834 18715 : int encoding_len = strlen(encoding);
835 18715 : int buflen = sizeof("unicode.from.") + encoding_len - 1; /* might be "to", but "from" is long enough for both */
836 18715 : char *buf = emalloc(buflen + 1);
837 : php_stream_filter *filter;
838 : zval *filterparams;
839 :
840 18715 : if (writechain) {
841 5981 : memcpy(buf, "unicode.to.", sizeof("unicode.to.") - 1);
842 5981 : memcpy(buf + sizeof("unicode.to.") - 1, encoding, encoding_len + 1);
843 : } else {
844 12734 : memcpy(buf, "unicode.from.", sizeof("unicode.from.") - 1);
845 12734 : memcpy(buf + sizeof("unicode.from.") - 1, encoding, encoding_len + 1);
846 : }
847 :
848 18715 : ALLOC_INIT_ZVAL(filterparams);
849 18715 : array_init(filterparams);
850 18715 : add_ascii_assoc_long(filterparams, "error_mode", error_mode);
851 18715 : if (subst) {
852 5981 : add_ascii_assoc_unicode(filterparams, "subst_char", subst, 1);
853 : }
854 18715 : filter = php_stream_filter_create(buf, filterparams, php_stream_is_persistent(stream) TSRMLS_CC);
855 18715 : efree(buf);
856 18715 : zval_ptr_dtor(&filterparams);
857 :
858 18715 : if (!filter) {
859 0 : php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to apply encoding for charset: %s\n", encoding);
860 0 : return FAILURE;
861 : }
862 :
863 18715 : php_stream_filter_append(writechain ? &stream->writefilters : &stream->readfilters, filter);
864 :
865 18715 : return SUCCESS;
866 : }
867 :
868 : /*
869 : * Local variables:
870 : * tab-width: 4
871 : * c-basic-offset: 4
872 : * End:
873 : * vim600: noet sw=4 ts=4 fdm=marker
874 : * vim<600: noet sw=4 ts=4
875 : */
|