1 : /*
2 : +--------------------------------------------------------------------+
3 : | PECL :: http |
4 : +--------------------------------------------------------------------+
5 : | Redistribution and use in source and binary forms, with or without |
6 : | modification, are permitted provided that the conditions mentioned |
7 : | in the accompanying LICENSE file are met. |
8 : +--------------------------------------------------------------------+
9 : | Copyright (c) 2004-2007, Michael Wallner <mike@php.net> |
10 : +--------------------------------------------------------------------+
11 : */
12 :
13 : /* $Id: http_filter_api.c,v 1.23 2007/02/07 11:50:26 mike Exp $ */
14 :
15 : #define HTTP_WANT_ZLIB
16 : #include "php_http.h"
17 :
18 : #ifdef ZEND_ENGINE_2
19 :
20 : #include "php_streams.h"
21 : #include "php_http_api.h"
22 : #include "php_http_encoding_api.h"
23 : #include "php_http_filter_api.h"
24 :
25 : PHP_MINIT_FUNCTION(http_filter)
26 220 : {
27 220 : php_stream_filter_register_factory("http.*", &http_filter_factory TSRMLS_CC);
28 220 : return SUCCESS;
29 : }
30 :
31 : /*
32 : -
33 : */
34 :
35 : #define HTTP_FILTER_PARAMS \
36 : php_stream *stream, \
37 : php_stream_filter *this, \
38 : php_stream_bucket_brigade *buckets_in, \
39 : php_stream_bucket_brigade *buckets_out, \
40 : size_t *bytes_consumed, int flags \
41 : TSRMLS_DC
42 : #define HTTP_FILTER_OP(filter) \
43 : http_filter_op_ ##filter
44 : #define HTTP_FILTER_OPS(filter) \
45 : php_stream_filter_ops HTTP_FILTER_OP(filter)
46 : #define HTTP_FILTER_DTOR(filter) \
47 : http_filter_ ##filter## _dtor
48 : #define HTTP_FILTER_DESTRUCTOR(filter) \
49 : void HTTP_FILTER_DTOR(filter)(php_stream_filter *this TSRMLS_DC)
50 : #define HTTP_FILTER_FUNC(filter) \
51 : http_filter_ ##filter
52 : #define HTTP_FILTER_FUNCTION(filter) \
53 : php_stream_filter_status_t HTTP_FILTER_FUNC(filter)(HTTP_FILTER_PARAMS)
54 : #define HTTP_FILTER_BUFFER(filter) \
55 : http_filter_ ##filter## _buffer
56 :
57 : #define NEW_BUCKET(data, length) \
58 : { \
59 : char *__data; \
60 : php_stream_bucket *__buck; \
61 : \
62 : __data = pemalloc(length, this->is_persistent); \
63 : if (!__data) { \
64 : return PSFS_ERR_FATAL; \
65 : } \
66 : memcpy(__data, data, length); \
67 : \
68 : __buck = php_stream_bucket_new(stream, __data, length, 1, this->is_persistent TSRMLS_CC); \
69 : if (!__buck) { \
70 : pefree(__data, this->is_persistent); \
71 : return PSFS_ERR_FATAL; \
72 : } \
73 : \
74 : php_stream_bucket_append(buckets_out, __buck TSRMLS_CC); \
75 : }
76 :
77 : typedef struct _http_chunked_decode_filter_buffer_t {
78 : phpstr buffer;
79 : ulong hexlen;
80 : } HTTP_FILTER_BUFFER(chunked_decode);
81 :
82 : #ifdef HTTP_HAVE_ZLIB
83 : typedef http_encoding_stream HTTP_FILTER_BUFFER(deflate);
84 : typedef http_encoding_stream HTTP_FILTER_BUFFER(inflate);
85 : #endif /* HTTP_HAVE_ZLIB */
86 :
87 :
88 : static HTTP_FILTER_FUNCTION(chunked_decode)
89 4 : {
90 4 : int out_avail = 0;
91 : php_stream_bucket *ptr, *nxt;
92 4 : HTTP_FILTER_BUFFER(chunked_decode) *buffer = (HTTP_FILTER_BUFFER(chunked_decode) *) (this->abstract);
93 :
94 4 : if (bytes_consumed) {
95 0 : *bytes_consumed = 0;
96 : }
97 :
98 : /* new data available? */
99 4 : if (buckets_in->head) {
100 :
101 : /* fetch available bucket data */
102 6 : for (ptr = buckets_in->head; ptr; ptr = nxt) {
103 3 : nxt = ptr->next;
104 3 : if (bytes_consumed) {
105 0 : *bytes_consumed += ptr->buflen;
106 : }
107 :
108 3 : if (PHPSTR_NOMEM == phpstr_append(PHPSTR(buffer), ptr->buf, ptr->buflen)) {
109 0 : return PSFS_ERR_FATAL;
110 : }
111 :
112 3 : php_stream_bucket_unlink(ptr TSRMLS_CC);
113 3 : php_stream_bucket_delref(ptr TSRMLS_CC);
114 : }
115 : }
116 4 : if (!phpstr_fix(PHPSTR(buffer))) {
117 0 : return PSFS_ERR_FATAL;
118 : }
119 :
120 : /* we have data in our buffer */
121 85 : while (PHPSTR_LEN(buffer)) {
122 :
123 : /* we already know the size of the chunk and are waiting for data */
124 77 : if (buffer->hexlen) {
125 :
126 : /* not enough data buffered */
127 37 : if (PHPSTR_LEN(buffer) < buffer->hexlen) {
128 :
129 : /* flush anyway? */
130 0 : if (flags & PSFS_FLAG_FLUSH_INC) {
131 :
132 : /* flush all data (should only be chunk data) */
133 0 : out_avail = 1;
134 0 : NEW_BUCKET(PHPSTR_VAL(buffer), PHPSTR_LEN(buffer));
135 :
136 : /* waiting for less data now */
137 0 : buffer->hexlen -= PHPSTR_LEN(buffer);
138 : /* no more buffered data */
139 0 : phpstr_reset(PHPSTR(buffer));
140 : /* break */
141 : }
142 :
143 : /* we have too less data and don't need to flush */
144 : else {
145 0 : break;
146 : }
147 : }
148 :
149 : /* we seem to have all data of the chunk */
150 : else {
151 37 : out_avail = 1;
152 37 : NEW_BUCKET(PHPSTR_VAL(buffer), buffer->hexlen);
153 :
154 : /* remove outgoing data from the buffer */
155 37 : phpstr_cut(PHPSTR(buffer), 0, buffer->hexlen);
156 : /* reset hexlen */
157 37 : buffer->hexlen = 0;
158 : /* continue */
159 : }
160 : }
161 :
162 : /* we don't know the length of the chunk yet */
163 : else {
164 40 : size_t off = 0;
165 :
166 : /* ignore preceeding CRLFs (too loose?) */
167 154 : while (off < PHPSTR_LEN(buffer) && (
168 : PHPSTR_VAL(buffer)[off] == '\n' ||
169 : PHPSTR_VAL(buffer)[off] == '\r')) {
170 74 : ++off;
171 : }
172 40 : if (off) {
173 37 : phpstr_cut(PHPSTR(buffer), 0, off);
174 : }
175 :
176 : /* still data there? */
177 40 : if (PHPSTR_LEN(buffer)) {
178 : int eollen;
179 : const char *eolstr;
180 :
181 : /* we need eol, so we can be sure we have all hex digits */
182 40 : phpstr_fix(PHPSTR(buffer));
183 40 : if ((eolstr = http_locate_eol(PHPSTR_VAL(buffer), &eollen))) {
184 40 : char *stop = NULL;
185 :
186 : /* read in chunk size */
187 40 : buffer->hexlen = strtoul(PHPSTR_VAL(buffer), &stop, 16);
188 :
189 : /* if strtoul() stops at the beginning of the buffered data
190 : there's domething oddly wrong, i.e. bad input */
191 40 : if (stop == PHPSTR_VAL(buffer)) {
192 0 : return PSFS_ERR_FATAL;
193 : }
194 :
195 : /* cut out <chunk size hex><chunk extension><eol> */
196 40 : phpstr_cut(PHPSTR(buffer), 0, eolstr + eollen - PHPSTR_VAL(buffer));
197 : /* buffer->hexlen is 0 now or contains the size of the next chunk */
198 : /* continue */
199 : } else {
200 : /* we have not enough data buffered to read in chunk size */
201 0 : break;
202 : }
203 : }
204 : /* break */
205 : }
206 : }
207 :
208 : /* flush before close, but only if we are already waiting for more data */
209 4 : if ((flags & PSFS_FLAG_FLUSH_CLOSE) && buffer->hexlen && PHPSTR_LEN(buffer)) {
210 0 : out_avail = 1;
211 0 : NEW_BUCKET(PHPSTR_VAL(buffer), PHPSTR_LEN(buffer));
212 0 : phpstr_reset(PHPSTR(buffer));
213 0 : buffer->hexlen = 0;
214 : }
215 :
216 4 : return out_avail ? PSFS_PASS_ON : PSFS_FEED_ME;
217 : }
218 :
219 : static HTTP_FILTER_DESTRUCTOR(chunked_decode)
220 3 : {
221 3 : HTTP_FILTER_BUFFER(chunked_decode) *b = (HTTP_FILTER_BUFFER(chunked_decode) *) (this->abstract);
222 :
223 3 : phpstr_dtor(PHPSTR(b));
224 3 : pefree(b, this->is_persistent);
225 3 : }
226 :
227 : static HTTP_FILTER_FUNCTION(chunked_encode)
228 54 : {
229 54 : int out_avail = 0;
230 : php_stream_bucket *ptr, *nxt;
231 :
232 54 : if (bytes_consumed) {
233 52 : *bytes_consumed = 0;
234 : }
235 :
236 : /* new data available? */
237 54 : if (buckets_in->head) {
238 : phpstr buf;
239 37 : out_avail = 1;
240 :
241 37 : phpstr_init(&buf);
242 :
243 : /* fetch available bucket data */
244 74 : for (ptr = buckets_in->head; ptr; ptr = nxt) {
245 37 : nxt = ptr->next;
246 37 : if (bytes_consumed) {
247 35 : *bytes_consumed += ptr->buflen;
248 : }
249 :
250 37 : phpstr_appendf(&buf, "%x" HTTP_CRLF, ptr->buflen);
251 37 : phpstr_append(&buf, ptr->buf, ptr->buflen);
252 37 : phpstr_appends(&buf, HTTP_CRLF);
253 :
254 : /* pass through */
255 37 : NEW_BUCKET(PHPSTR_VAL(&buf), PHPSTR_LEN(&buf));
256 : /* reset */
257 37 : phpstr_reset(&buf);
258 :
259 37 : php_stream_bucket_unlink(ptr TSRMLS_CC);
260 37 : php_stream_bucket_delref(ptr TSRMLS_CC);
261 : }
262 :
263 : /* free buffer */
264 37 : phpstr_dtor(&buf);
265 : }
266 :
267 : /* terminate with "0" */
268 54 : if (flags & PSFS_FLAG_FLUSH_CLOSE) {
269 3 : out_avail = 1;
270 3 : NEW_BUCKET("0" HTTP_CRLF, lenof("0" HTTP_CRLF));
271 : }
272 :
273 54 : return out_avail ? PSFS_PASS_ON : PSFS_FEED_ME;
274 : }
275 :
276 : static HTTP_FILTER_OPS(chunked_decode) = {
277 : HTTP_FILTER_FUNC(chunked_decode),
278 : HTTP_FILTER_DTOR(chunked_decode),
279 : "http.chunked_decode"
280 : };
281 :
282 : static HTTP_FILTER_OPS(chunked_encode) = {
283 : HTTP_FILTER_FUNC(chunked_encode),
284 : NULL,
285 : "http.chunked_encode"
286 : };
287 :
288 : #ifdef HTTP_HAVE_ZLIB
289 :
290 : static HTTP_FILTER_FUNCTION(deflate)
291 47 : {
292 47 : int out_avail = 0;
293 : php_stream_bucket *ptr, *nxt;
294 47 : HTTP_FILTER_BUFFER(inflate) *buffer = (HTTP_FILTER_BUFFER(deflate) *) this->abstract;
295 :
296 47 : if (bytes_consumed) {
297 12 : *bytes_consumed = 0;
298 : }
299 :
300 : /* new data available? */
301 47 : if (buckets_in->head) {
302 :
303 : /* fetch available bucket data */
304 83 : for (ptr = buckets_in->head; ptr; ptr = nxt) {
305 42 : char *encoded = NULL;
306 42 : size_t encoded_len = 0;
307 :
308 42 : nxt = ptr->next;
309 42 : if (bytes_consumed) {
310 6 : *bytes_consumed += ptr->buflen;
311 : }
312 :
313 42 : if (ptr->buflen) {
314 42 : http_encoding_deflate_stream_update(buffer, ptr->buf, ptr->buflen, &encoded, &encoded_len);
315 42 : if (encoded) {
316 42 : if (encoded_len) {
317 4 : out_avail = 1;
318 4 : NEW_BUCKET(encoded, encoded_len);
319 : }
320 42 : efree(encoded);
321 : }
322 : }
323 :
324 42 : php_stream_bucket_unlink(ptr TSRMLS_CC);
325 42 : php_stream_bucket_delref(ptr TSRMLS_CC);
326 : }
327 : }
328 :
329 : /* flush & close */
330 47 : if (flags & PSFS_FLAG_FLUSH_INC) {
331 3 : char *encoded = NULL;
332 3 : size_t encoded_len = 0;
333 :
334 3 : http_encoding_deflate_stream_flush(buffer, &encoded, &encoded_len);
335 3 : if (encoded) {
336 3 : if (encoded_len) {
337 3 : out_avail = 1;
338 3 : NEW_BUCKET(encoded, encoded_len);
339 : }
340 3 : efree(encoded);
341 : }
342 : }
343 :
344 47 : if (flags & PSFS_FLAG_FLUSH_CLOSE) {
345 5 : char *encoded = NULL;
346 5 : size_t encoded_len = 0;
347 :
348 5 : http_encoding_deflate_stream_finish(buffer, &encoded, &encoded_len);
349 5 : if (encoded) {
350 5 : if (encoded_len) {
351 5 : out_avail = 1;
352 5 : NEW_BUCKET(encoded, encoded_len);
353 : }
354 5 : efree(encoded);
355 : }
356 : }
357 :
358 47 : return out_avail ? PSFS_PASS_ON : PSFS_FEED_ME;
359 : }
360 :
361 : static HTTP_FILTER_FUNCTION(inflate)
362 3 : {
363 3 : int out_avail = 0;
364 : php_stream_bucket *ptr, *nxt;
365 3 : HTTP_FILTER_BUFFER(inflate) *buffer = (HTTP_FILTER_BUFFER(inflate) *) this->abstract;
366 :
367 3 : if (bytes_consumed) {
368 0 : *bytes_consumed = 0;
369 : }
370 :
371 : /* new data available? */
372 3 : if (buckets_in->head) {
373 :
374 : /* fetch available bucket data */
375 7 : for (ptr = buckets_in->head; ptr; ptr = nxt) {
376 4 : char *decoded = NULL;
377 4 : size_t decoded_len = 0;
378 :
379 4 : nxt = ptr->next;
380 4 : if (bytes_consumed) {
381 0 : *bytes_consumed += ptr->buflen;
382 : }
383 :
384 4 : if (ptr->buflen) {
385 4 : http_encoding_inflate_stream_update(buffer, ptr->buf, ptr->buflen, &decoded, &decoded_len);
386 4 : if (decoded) {
387 4 : if (decoded_len) {
388 2 : out_avail = 1;
389 2 : NEW_BUCKET(decoded, decoded_len);
390 : }
391 4 : efree(decoded);
392 : }
393 : }
394 :
395 4 : php_stream_bucket_unlink(ptr TSRMLS_CC);
396 4 : php_stream_bucket_delref(ptr TSRMLS_CC);
397 : }
398 : }
399 :
400 : /* flush & close */
401 3 : if (flags & PSFS_FLAG_FLUSH_INC) {
402 0 : char *decoded = NULL;
403 0 : size_t decoded_len = 0;
404 :
405 0 : http_encoding_inflate_stream_flush(buffer, &decoded, &decoded_len);
406 0 : if (decoded) {
407 0 : if (decoded_len) {
408 0 : out_avail = 1;
409 0 : NEW_BUCKET(decoded, decoded_len);
410 : }
411 0 : efree(decoded);
412 : }
413 : }
414 :
415 3 : if (flags & PSFS_FLAG_FLUSH_CLOSE) {
416 2 : char *decoded = NULL;
417 2 : size_t decoded_len = 0;
418 :
419 2 : http_encoding_inflate_stream_finish(buffer, &decoded, &decoded_len);
420 2 : if (decoded) {
421 0 : if (decoded_len) {
422 0 : out_avail = 1;
423 0 : NEW_BUCKET(decoded, decoded_len);
424 : }
425 0 : efree(decoded);
426 : }
427 : }
428 :
429 3 : return out_avail ? PSFS_PASS_ON : PSFS_FEED_ME;
430 : }
431 :
432 : static HTTP_FILTER_DESTRUCTOR(deflate)
433 5 : {
434 5 : HTTP_FILTER_BUFFER(deflate) *buffer = (HTTP_FILTER_BUFFER(deflate) *) this->abstract;
435 5 : http_encoding_deflate_stream_free(&buffer);
436 5 : }
437 :
438 : static HTTP_FILTER_DESTRUCTOR(inflate)
439 2 : {
440 2 : HTTP_FILTER_BUFFER(inflate) *buffer = (HTTP_FILTER_BUFFER(inflate) *) this->abstract;
441 2 : http_encoding_inflate_stream_free(&buffer);
442 2 : }
443 :
444 : static HTTP_FILTER_OPS(deflate) = {
445 : HTTP_FILTER_FUNC(deflate),
446 : HTTP_FILTER_DTOR(deflate),
447 : "http.deflate"
448 : };
449 :
450 : static HTTP_FILTER_OPS(inflate) = {
451 : HTTP_FILTER_FUNC(inflate),
452 : HTTP_FILTER_DTOR(inflate),
453 : "http.inflate"
454 : };
455 :
456 : #endif /* HTTP_HAVE_ZLIB */
457 :
458 : static php_stream_filter *http_filter_create(const char *name, zval *params, int p TSRMLS_DC)
459 13 : {
460 13 : zval **tmp = ¶ms;
461 13 : php_stream_filter *f = NULL;
462 :
463 13 : if (!strcasecmp(name, "http.chunked_decode")) {
464 3 : HTTP_FILTER_BUFFER(chunked_decode) *b = NULL;
465 :
466 3 : if ((b = pecalloc(1, sizeof(HTTP_FILTER_BUFFER(chunked_decode)), p))) {
467 3 : phpstr_init_ex(PHPSTR(b), 4096, p ? PHPSTR_INIT_PERSISTENT : 0);
468 3 : if (!(f = php_stream_filter_alloc(&HTTP_FILTER_OP(chunked_decode), b, p))) {
469 0 : pefree(b, p);
470 : }
471 : }
472 : } else
473 :
474 10 : if (!strcasecmp(name, "http.chunked_encode")) {
475 3 : f = php_stream_filter_alloc(&HTTP_FILTER_OP(chunked_encode), NULL, p);
476 : #ifdef HTTP_HAVE_ZLIB
477 : } else
478 :
479 7 : if (!strcasecmp(name, "http.inflate")) {
480 2 : int flags = p ? HTTP_ENCODING_STREAM_PERSISTENT : 0;
481 2 : HTTP_FILTER_BUFFER(inflate) *b = NULL;
482 :
483 2 : if ((b = http_encoding_inflate_stream_init(NULL, flags))) {
484 2 : if (!(f = php_stream_filter_alloc(&HTTP_FILTER_OP(inflate), b, p))) {
485 0 : http_encoding_inflate_stream_free(&b);
486 : }
487 : }
488 : } else
489 :
490 5 : if (!strcasecmp(name, "http.deflate")) {
491 5 : int flags = p ? HTTP_ENCODING_STREAM_PERSISTENT : 0;
492 5 : HTTP_FILTER_BUFFER(deflate) *b = NULL;
493 :
494 5 : if (params) {
495 2 : switch (Z_TYPE_P(params)) {
496 : case IS_ARRAY:
497 : case IS_OBJECT:
498 0 : if (SUCCESS != zend_hash_find(HASH_OF(params), "flags", sizeof("flags"), (void *) &tmp)) {
499 0 : break;
500 : }
501 : default:
502 : {
503 2 : zval *orig = *tmp;
504 :
505 2 : convert_to_long_ex(tmp);
506 2 : flags |= (Z_LVAL_PP(tmp) & 0x0fffffff);
507 2 : if (orig != *tmp) zval_ptr_dtor(tmp);
508 : }
509 : }
510 : }
511 5 : if ((b = http_encoding_deflate_stream_init(NULL, flags))) {
512 5 : if (!(f = php_stream_filter_alloc(&HTTP_FILTER_OP(deflate), b, p))) {
513 0 : http_encoding_deflate_stream_free(&b);
514 : }
515 : }
516 : #endif /* HTTP_HAVE_ZLIB */
517 : }
518 :
519 13 : return f;
520 : }
521 :
522 : php_stream_filter_factory http_filter_factory = {
523 : http_filter_create
524 : };
525 :
526 : #endif /* ZEND_ENGINE_2 */
527 :
528 : /*
529 : * Local variables:
530 : * tab-width: 4
531 : * c-basic-offset: 4
532 : * End:
533 : * vim600: noet sw=4 ts=4 fdm=marker
534 : * vim<600: noet sw=4 ts=4
535 : */
|