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