- fix segv with http.send.deflate.start_auto=1 and http_send_*()
[m6w6/ext-http] / http_encoding_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-2006, Michael Wallner <mike@php.net> |
10 +--------------------------------------------------------------------+
11 */
12
13 /* $Id$ */
14
15 #define HTTP_WANT_ZLIB
16 #include "php_http.h"
17
18 #include "php_http_api.h"
19 #include "php_http_encoding_api.h"
20 #include "php_http_send_api.h"
21 #include "php_http_headers_api.h"
22
23 /* {{{ */
24 #ifdef HTTP_HAVE_ZLIB
25 PHP_MINIT_FUNCTION(http_encoding)
26 {
27 HTTP_LONG_CONSTANT("HTTP_DEFLATE_LEVEL_DEF", HTTP_DEFLATE_LEVEL_DEF);
28 HTTP_LONG_CONSTANT("HTTP_DEFLATE_LEVEL_MIN", HTTP_DEFLATE_LEVEL_MIN);
29 HTTP_LONG_CONSTANT("HTTP_DEFLATE_LEVEL_MAX", HTTP_DEFLATE_LEVEL_MAX);
30 HTTP_LONG_CONSTANT("HTTP_DEFLATE_TYPE_ZLIB", HTTP_DEFLATE_TYPE_ZLIB);
31 HTTP_LONG_CONSTANT("HTTP_DEFLATE_TYPE_GZIP", HTTP_DEFLATE_TYPE_GZIP);
32 HTTP_LONG_CONSTANT("HTTP_DEFLATE_TYPE_RAW", HTTP_DEFLATE_TYPE_RAW);
33 HTTP_LONG_CONSTANT("HTTP_DEFLATE_STRATEGY_DEF", HTTP_DEFLATE_STRATEGY_DEF);
34 HTTP_LONG_CONSTANT("HTTP_DEFLATE_STRATEGY_FILT", HTTP_DEFLATE_STRATEGY_FILT);
35 HTTP_LONG_CONSTANT("HTTP_DEFLATE_STRATEGY_HUFF", HTTP_DEFLATE_STRATEGY_HUFF);
36 HTTP_LONG_CONSTANT("HTTP_DEFLATE_STRATEGY_RLE", HTTP_DEFLATE_STRATEGY_RLE);
37 HTTP_LONG_CONSTANT("HTTP_DEFLATE_STRATEGY_FIXED", HTTP_DEFLATE_STRATEGY_FIXED);
38
39 HTTP_LONG_CONSTANT("HTTP_ENCODING_STREAM_FLUSH_NONE", HTTP_ENCODING_STREAM_FLUSH_NONE);
40 HTTP_LONG_CONSTANT("HTTP_ENCODING_STREAM_FLUSH_SYNC", HTTP_ENCODING_STREAM_FLUSH_SYNC);
41 HTTP_LONG_CONSTANT("HTTP_ENCODING_STREAM_FLUSH_FULL", HTTP_ENCODING_STREAM_FLUSH_FULL);
42
43 return SUCCESS;
44 }
45
46 PHP_RINIT_FUNCTION(http_encoding)
47 {
48 if (HTTP_G->send.inflate.start_auto) {
49 php_ob_set_internal_handler(_http_ob_inflatehandler, HTTP_INFLATE_BUFFER_SIZE, "http inflate", 0 TSRMLS_CC);
50 }
51 if (HTTP_G->send.deflate.start_auto) {
52 php_ob_set_internal_handler(_http_ob_deflatehandler, HTTP_DEFLATE_BUFFER_SIZE, "http deflate", 0 TSRMLS_CC);
53 }
54 return SUCCESS;
55 }
56
57 PHP_RSHUTDOWN_FUNCTION(http_encoding)
58 {
59 if (HTTP_G->send.deflate.stream) {
60 http_encoding_deflate_stream_free((http_encoding_stream **) &HTTP_G->send.deflate.stream);
61 }
62 if (HTTP_G->send.inflate.stream) {
63 http_encoding_inflate_stream_free((http_encoding_stream **) &HTTP_G->send.inflate.stream);
64 }
65 return SUCCESS;
66 }
67 #endif
68 /* }}} */
69
70 /* {{{ eol_match(char **, int *) */
71 static inline int eol_match(char **line, int *eol_len)
72 {
73 char *ptr = *line;
74
75 while (' ' == *ptr) ++ptr;
76
77 if (ptr == http_locate_eol(*line, eol_len)) {
78 *line = ptr;
79 return 1;
80 } else {
81 return 0;
82 }
83 }
84 /* }}} */
85
86 /* {{{ char *http_encoding_dechunk(char *, size_t, char **, size_t *) */
87 PHP_HTTP_API const char *_http_encoding_dechunk(const char *encoded, size_t encoded_len, char **decoded, size_t *decoded_len TSRMLS_DC)
88 {
89 int eol_len = 0;
90 char *n_ptr = NULL;
91 const char *e_ptr = encoded;
92
93 *decoded_len = 0;
94 *decoded = ecalloc(1, encoded_len);
95
96 while ((encoded + encoded_len - e_ptr) > 0) {
97 ulong chunk_len = 0, rest;
98
99 chunk_len = strtoul(e_ptr, &n_ptr, 16);
100
101 /* we could not read in chunk size */
102 if (n_ptr == e_ptr) {
103 /*
104 * if this is the first turn and there doesn't seem to be a chunk
105 * size at the begining of the body, do not fail on apparently
106 * not encoded data and return a copy
107 */
108 if (e_ptr == encoded) {
109 http_error(HE_NOTICE, HTTP_E_ENCODING, "Data does not seem to be chunked encoded");
110 memcpy(*decoded, encoded, encoded_len);
111 *decoded_len = encoded_len;
112 return encoded + encoded_len;
113 } else {
114 efree(*decoded);
115 http_error_ex(HE_WARNING, HTTP_E_ENCODING, "Expected chunk size at pos %tu of %zu but got trash", n_ptr - encoded, encoded_len);
116 return NULL;
117 }
118 }
119
120 /* reached the end */
121 if (!chunk_len) {
122 /* move over '0' chunked encoding terminator */
123 while (*e_ptr == '0') ++e_ptr;
124 break;
125 }
126
127 /* there should be CRLF after the chunk size, but we'll ignore SP+ too */
128 if (*n_ptr && !eol_match(&n_ptr, &eol_len)) {
129 if (eol_len == 2) {
130 http_error_ex(HE_WARNING, HTTP_E_ENCODING, "Expected CRLF at pos %tu of %zu but got 0x%02X 0x%02X", n_ptr - encoded, encoded_len, *n_ptr, *(n_ptr + 1));
131 } else {
132 http_error_ex(HE_WARNING, HTTP_E_ENCODING, "Expected LF at pos %tu of %zu but got 0x%02X", n_ptr - encoded, encoded_len, *n_ptr);
133 }
134 }
135 n_ptr += eol_len;
136
137 /* chunk size pretends more data than we actually got, so it's probably a truncated message */
138 if (chunk_len > (rest = encoded + encoded_len - n_ptr)) {
139 http_error_ex(HE_WARNING, HTTP_E_ENCODING, "Truncated message: chunk size %lu exceeds remaining data size %lu at pos %tu of %zu", chunk_len, rest, n_ptr - encoded, encoded_len);
140 chunk_len = rest;
141 }
142
143 /* copy the chunk */
144 memcpy(*decoded + *decoded_len, n_ptr, chunk_len);
145 *decoded_len += chunk_len;
146
147 if (chunk_len == rest) {
148 e_ptr = n_ptr + chunk_len;
149 break;
150 } else {
151 /* advance to next chunk */
152 e_ptr = n_ptr + chunk_len + eol_len;
153 }
154 }
155
156 return e_ptr;
157 }
158 /* }}} */
159
160 /* {{{ int http_encoding_response_start(size_t) */
161 PHP_HTTP_API int _http_encoding_response_start(size_t content_length TSRMLS_DC)
162 {
163 if ( php_ob_handler_used("ob_gzhandler" TSRMLS_CC) ||
164 php_ob_handler_used("zlib output compression" TSRMLS_CC)) {
165 HTTP_G->send.deflate.encoding = 0;
166 } else if (HTTP_G->send.deflate.encoding) {
167 HTTP_G->send.deflate.encoding = 0;
168 #ifdef HTTP_HAVE_ZLIB
169 HashTable *selected;
170 zval zsupported;
171
172 INIT_PZVAL(&zsupported);
173 array_init(&zsupported);
174 add_next_index_stringl(&zsupported, "gzip", lenof("gzip"), 1);
175 add_next_index_stringl(&zsupported, "x-gzip", lenof("x-gzip"), 1);
176 add_next_index_stringl(&zsupported, "deflate", lenof("deflate"), 1);
177
178 if ((selected = http_negotiate_encoding(&zsupported))) {
179 STATUS hs = FAILURE;
180 char *encoding = NULL;
181 ulong idx;
182
183 if (HASH_KEY_IS_STRING == zend_hash_get_current_key(selected, &encoding, &idx, 0) && encoding) {
184 if (!strcmp(encoding, "gzip") || !strcmp(encoding, "x-gzip")) {
185 if (SUCCESS == (hs = http_send_header_string("Content-Encoding: gzip"))) {
186 HTTP_G->send.deflate.encoding = HTTP_ENCODING_GZIP;
187 }
188 } else if (!strcmp(encoding, "deflate")) {
189 if (SUCCESS == (hs = http_send_header_string("Content-Encoding: deflate"))) {
190 HTTP_G->send.deflate.encoding = HTTP_ENCODING_DEFLATE;
191 }
192 }
193 if (SUCCESS == hs) {
194 http_send_header_string("Vary: Accept-Encoding");
195 }
196 }
197
198 zend_hash_destroy(selected);
199 FREE_HASHTABLE(selected);
200 }
201
202 zval_dtor(&zsupported);
203 #else
204 php_start_ob_buffer_named("ob_gzhandler", 0, 0 TSRMLS_CC);
205 #endif /* HTTP_HAVE_ZLIB */
206 } else {
207 HTTP_G->send.deflate.encoding = 0;
208 if (content_length) {
209 /* emit a content-length header */
210 char cl_header_str[128];
211 size_t cl_header_len;
212 cl_header_len = snprintf(cl_header_str, lenof(cl_header_str), "Content-Length: %zu", content_length);
213 http_send_header_string_ex(cl_header_str, cl_header_len, 1);
214 }
215 }
216 return HTTP_G->send.deflate.encoding;
217 }
218 /* }}} */
219
220 #ifdef HTTP_HAVE_ZLIB
221
222 /* {{{ inline int http_inflate_rounds */
223 static inline int http_inflate_rounds(z_stream *Z, int flush, char **buf, size_t *len)
224 {
225 int status = 0, round = 0;
226 phpstr buffer;
227
228 *buf = NULL;
229 *len = 0;
230
231 phpstr_init_ex(&buffer, Z->avail_in, PHPSTR_INIT_PREALLOC);
232
233 do {
234 if (PHPSTR_NOMEM == phpstr_resize_ex(&buffer, buffer.size, 0, 1)) {
235 status = Z_MEM_ERROR;
236 } else {
237 Z->avail_out = buffer.free;
238 Z->next_out = (Bytef *) buffer.data + buffer.used;
239 #if 0
240 fprintf(stderr, "\n%3d: %3d PRIOR: size=%7lu,\tfree=%7lu,\tused=%7lu,\tavail_in=%7lu,\tavail_out=%7lu\n", round, status, buffer.size, buffer.free, buffer.used, Z->avail_in, Z->avail_out);
241 #endif
242 status = inflate(Z, flush);
243
244 buffer.used += buffer.free - Z->avail_out;
245 buffer.free = Z->avail_out;
246 #if 0
247 fprintf(stderr, "%3d: %3d AFTER: size=%7lu,\tfree=%7lu,\tused=%7lu,\tavail_in=%7lu,\tavail_out=%7lu\n", round, status, buffer.size, buffer.free, buffer.used, Z->avail_in, Z->avail_out);
248 #endif
249 HTTP_INFLATE_BUFFER_SIZE_ALIGN(buffer.size);
250 }
251 } while ((Z_BUF_ERROR == status || (Z_OK == status && Z->avail_in)) && ++round < HTTP_INFLATE_ROUNDS);
252
253 if (status == Z_OK || status == Z_STREAM_END) {
254 phpstr_shrink(&buffer);
255 phpstr_fix(&buffer);
256 *buf = buffer.data;
257 *len = buffer.used;
258 } else {
259 phpstr_dtor(&buffer);
260 }
261
262 return status;
263 }
264 /* }}} */
265
266 /* {{{ STATUS http_encoding_deflate(int, char *, size_t, char **, size_t *) */
267 PHP_HTTP_API STATUS _http_encoding_deflate(int flags, const char *data, size_t data_len, char **encoded, size_t *encoded_len ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC TSRMLS_DC)
268 {
269 int status, level, wbits, strategy;
270 z_stream Z;
271
272 HTTP_DEFLATE_LEVEL_SET(flags, level);
273 HTTP_DEFLATE_WBITS_SET(flags, wbits);
274 HTTP_DEFLATE_STRATEGY_SET(flags, strategy);
275
276 memset(&Z, 0, sizeof(z_stream));
277 *encoded = NULL;
278 *encoded_len = 0;
279
280 status = deflateInit2(&Z, level, Z_DEFLATED, wbits, MAX_MEM_LEVEL, strategy);
281 if (Z_OK == status) {
282 *encoded_len = HTTP_DEFLATE_BUFFER_SIZE_GUESS(data_len);
283 *encoded = emalloc_rel(*encoded_len);
284
285 Z.next_in = (Bytef *) data;
286 Z.next_out = (Bytef *) *encoded;
287 Z.avail_in = data_len;
288 Z.avail_out = *encoded_len;
289
290 status = deflate(&Z, Z_FINISH);
291 deflateEnd(&Z);
292
293 if (Z_STREAM_END == status) {
294 /* size buffer down to actual length */
295 *encoded = erealloc_rel(*encoded, Z.total_out + 1);
296 (*encoded)[*encoded_len = Z.total_out] = '\0';
297 return SUCCESS;
298 } else {
299 STR_SET(*encoded, NULL);
300 *encoded_len = 0;
301 }
302 }
303
304 http_error_ex(HE_WARNING, HTTP_E_ENCODING, "Could not deflate data: %s", zError(status));
305 return FAILURE;
306 }
307 /* }}} */
308
309 /* {{{ STATUS http_encoding_inflate(char *, size_t, char **, size_t) */
310 PHP_HTTP_API STATUS _http_encoding_inflate(const char *data, size_t data_len, char **decoded, size_t *decoded_len ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC TSRMLS_DC)
311 {
312 z_stream Z;
313 int status, wbits = HTTP_WINDOW_BITS_ANY;
314
315 memset(&Z, 0, sizeof(z_stream));
316
317 retry_raw_inflate:
318 status = inflateInit2(&Z, wbits);
319 if (Z_OK == status) {
320 Z.next_in = (Bytef *) data;
321 Z.avail_in = data_len;
322
323 switch (status = http_inflate_rounds(&Z, Z_NO_FLUSH, decoded, decoded_len)) {
324 case Z_OK:
325 case Z_STREAM_END:
326 inflateEnd(&Z);
327 return SUCCESS;
328
329 case Z_DATA_ERROR:
330 /* raw deflated data? */
331 if (HTTP_WINDOW_BITS_ANY == wbits) {
332 inflateEnd(&Z);
333 wbits = HTTP_WINDOW_BITS_RAW;
334 goto retry_raw_inflate;
335 }
336 }
337 inflateEnd(&Z);
338 }
339
340 http_error_ex(HE_WARNING, HTTP_E_ENCODING, "Could not inflate data: %s", zError(status));
341 return FAILURE;
342 }
343 /* }}} */
344
345 /* {{{ http_encoding_stream *_http_encoding_deflate_stream_init(http_encoding_stream *, int) */
346 PHP_HTTP_API http_encoding_stream *_http_encoding_deflate_stream_init(http_encoding_stream *s, int flags ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC TSRMLS_DC)
347 {
348 int status, level, wbits, strategy, free_stream;
349
350 if ((free_stream = !s)) {
351 s = pemalloc_rel(sizeof(http_encoding_stream), (flags & HTTP_ENCODING_STREAM_PERSISTENT));
352 }
353 memset(s, 0, sizeof(http_encoding_stream));
354 s->flags = flags;
355
356 HTTP_DEFLATE_LEVEL_SET(flags, level);
357 HTTP_DEFLATE_WBITS_SET(flags, wbits);
358 HTTP_DEFLATE_STRATEGY_SET(flags, strategy);
359
360 if (Z_OK == (status = deflateInit2(&s->stream, level, Z_DEFLATED, wbits, MAX_MEM_LEVEL, strategy))) {
361 int p = (flags & HTTP_ENCODING_STREAM_PERSISTENT) ? PHPSTR_INIT_PERSISTENT:0;
362
363 if ((s->stream.opaque = phpstr_init_ex(NULL, HTTP_DEFLATE_BUFFER_SIZE, p))) {
364 return s;
365 }
366 deflateEnd(&s->stream);
367 status = Z_MEM_ERROR;
368 }
369
370 http_error_ex(HE_WARNING, HTTP_E_ENCODING, "Failed to initialize deflate encoding stream: %s", zError(status));
371 if (free_stream) {
372 efree(s);
373 }
374 return NULL;
375 }
376 /* }}} */
377
378 /* {{{ http_encoding_stream *http_encoding_inflate_stream_init(http_encoding_stream *, int) */
379 PHP_HTTP_API http_encoding_stream *_http_encoding_inflate_stream_init(http_encoding_stream *s, int flags ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC TSRMLS_DC)
380 {
381 int status, wbits, free_stream;
382
383 if ((free_stream = !s)) {
384 s = pemalloc_rel(sizeof(http_encoding_stream), (flags & HTTP_ENCODING_STREAM_PERSISTENT));
385 }
386 memset(s, 0, sizeof(http_encoding_stream));
387 s->flags = flags;
388
389 HTTP_INFLATE_WBITS_SET(flags, wbits);
390
391 if (Z_OK == (status = inflateInit2(&s->stream, wbits))) {
392 int p = (flags & HTTP_ENCODING_STREAM_PERSISTENT) ? PHPSTR_INIT_PERSISTENT:0;
393
394 if ((s->stream.opaque = phpstr_init_ex(NULL, HTTP_DEFLATE_BUFFER_SIZE, p))) {
395 return s;
396 }
397 inflateEnd(&s->stream);
398 status = Z_MEM_ERROR;
399 }
400
401 http_error_ex(HE_WARNING, HTTP_E_ENCODING, "Failed to initialize inflate stream: %s", zError(status));
402 if (free_stream) {
403 efree(s);
404 }
405 return NULL;
406 }
407 /* }}} */
408
409 /* {{{ STATUS http_encoding_deflate_stream_update(http_encoding_stream *, char *, size_t, char **, size_t *) */
410 PHP_HTTP_API STATUS _http_encoding_deflate_stream_update(http_encoding_stream *s, const char *data, size_t data_len, char **encoded, size_t *encoded_len ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC TSRMLS_DC)
411 {
412 int status;
413
414 /* append input to our buffer */
415 phpstr_append(PHPSTR(s->stream.opaque), data, data_len);
416
417 s->stream.next_in = (Bytef *) PHPSTR_VAL(s->stream.opaque);
418 s->stream.avail_in = PHPSTR_LEN(s->stream.opaque);
419
420 /* deflate */
421 *encoded_len = HTTP_DEFLATE_BUFFER_SIZE_GUESS(data_len);
422 *encoded = emalloc_rel(*encoded_len);
423 s->stream.avail_out = *encoded_len;
424 s->stream.next_out = (Bytef *) *encoded;
425
426 switch (status = deflate(&s->stream, HTTP_ENCODING_STREAM_FLUSH_FLAG(s->flags))) {
427 case Z_OK:
428 case Z_STREAM_END:
429 /* cut processed chunk off the buffer */
430 if (s->stream.avail_in) {
431 phpstr_cut(PHPSTR(s->stream.opaque), 0, PHPSTR_LEN(s->stream.opaque) - s->stream.avail_in);
432 } else {
433 phpstr_reset(PHPSTR(s->stream.opaque));
434 }
435
436 /* size buffer down to actual size */
437 *encoded_len -= s->stream.avail_out;
438 *encoded = erealloc_rel(*encoded, *encoded_len + 1);
439 (*encoded)[*encoded_len] = '\0';
440 return SUCCESS;
441 }
442
443 STR_SET(*encoded, NULL);
444 *encoded_len = 0;
445 http_error_ex(HE_WARNING, HTTP_E_ENCODING, "Failed to update deflate stream: %s", zError(status));
446 return FAILURE;
447 }
448 /* }}} */
449
450 /* {{{ STATUS http_encoding_inflate_stream_update(http_encoding_stream *, char *, size_t, char **, size_t *) */
451 PHP_HTTP_API STATUS _http_encoding_inflate_stream_update(http_encoding_stream *s, const char *data, size_t data_len, char **decoded, size_t *decoded_len ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC TSRMLS_DC)
452 {
453 int status;
454
455 /* append input to buffer */
456 phpstr_append(PHPSTR(s->stream.opaque), data, data_len);
457
458 retry_raw_inflate:
459 s->stream.next_in = (Bytef *) PHPSTR_VAL(s->stream.opaque);
460 s->stream.avail_in = PHPSTR_LEN(s->stream.opaque);
461
462 switch (status = http_inflate_rounds(&s->stream, HTTP_ENCODING_STREAM_FLUSH_FLAG(s->flags), decoded, decoded_len)) {
463 case Z_OK:
464 case Z_STREAM_END:
465 /* cut off */
466 if (s->stream.avail_in) {
467 phpstr_cut(PHPSTR(s->stream.opaque), 0, PHPSTR_LEN(s->stream.opaque) - s->stream.avail_in);
468 } else {
469 phpstr_reset(PHPSTR(s->stream.opaque));
470 }
471 return SUCCESS;
472
473 case Z_DATA_ERROR:
474 /* raw deflated data ? */
475 if (!(s->flags & HTTP_INFLATE_TYPE_RAW) && !s->stream.total_out) {
476 inflateEnd(&s->stream);
477 s->flags |= HTTP_INFLATE_TYPE_RAW;
478 inflateInit2(&s->stream, HTTP_WINDOW_BITS_RAW);
479 goto retry_raw_inflate;
480 }
481 }
482
483 http_error_ex(HE_WARNING, HTTP_E_ENCODING, "Failed to update inflate stream: %s", zError(status));
484 return FAILURE;
485 }
486 /* }}} */
487
488 /* {{{ STATUS http_encoding_deflate_stream_flush(http_encoding_stream *, char **, size_t *) */
489 PHP_HTTP_API STATUS _http_encoding_deflate_stream_flush(http_encoding_stream *s, char **encoded, size_t *encoded_len ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC TSRMLS_DC)
490 {
491 int status;
492
493 *encoded_len = HTTP_DEFLATE_BUFFER_SIZE;
494 *encoded = emalloc_rel(*encoded_len);
495
496 s->stream.avail_in = 0;
497 s->stream.next_in = NULL;
498 s->stream.avail_out = *encoded_len;
499 s->stream.next_out = (Bytef *) *encoded;
500
501 switch (status = deflate(&s->stream, Z_FULL_FLUSH)) {
502 case Z_OK:
503 case Z_STREAM_END:
504 *encoded_len = HTTP_DEFLATE_BUFFER_SIZE - s->stream.avail_out;
505 *encoded = erealloc_rel(*encoded, *encoded_len + 1);
506 (*encoded)[*encoded_len] = '\0';
507 return SUCCESS;
508 }
509
510 STR_SET(*encoded, NULL);
511 *encoded_len = 0;
512 http_error_ex(HE_WARNING, HTTP_E_ENCODING, "Failed to flush deflate stream: %s", zError(status));
513 return FAILURE;
514 }
515 /* }}} */
516
517 /* {{{ STATUS http_encoding_inflate_straem_flush(http_encoding_stream *, char **, size_t *) */
518 PHP_HTTP_API STATUS _http_encoding_inflate_stream_flush(http_encoding_stream *s, char **decoded, size_t *decoded_len ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC TSRMLS_DC)
519 {
520 /* noop */
521 *decoded = estrndup("", *decoded_len = 0);
522 return SUCCESS;
523 }
524 /* }}} */
525
526 /* {{{ STATUS http_encoding_deflate_stream_finish(http_encoding_stream *, char **, size_t *) */
527 PHP_HTTP_API STATUS _http_encoding_deflate_stream_finish(http_encoding_stream *s, char **encoded, size_t *encoded_len ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC TSRMLS_DC)
528 {
529 int status;
530
531 *encoded_len = HTTP_DEFLATE_BUFFER_SIZE;
532 *encoded = emalloc_rel(*encoded_len);
533
534 /* deflate remaining input */
535 s->stream.next_in = (Bytef *) PHPSTR_VAL(s->stream.opaque);
536 s->stream.avail_in = PHPSTR_LEN(s->stream.opaque);
537
538 s->stream.avail_out = *encoded_len;
539 s->stream.next_out = (Bytef *) *encoded;
540
541 do {
542 status = deflate(&s->stream, Z_FINISH);
543 } while (Z_OK == status);
544
545 if (Z_STREAM_END == status) {
546 /* cut processed intp off */
547 phpstr_cut(PHPSTR(s->stream.opaque), 0, PHPSTR_LEN(s->stream.opaque) - s->stream.avail_in);
548
549 /* size down */
550 *encoded_len -= s->stream.avail_out;
551 *encoded = erealloc_rel(*encoded, *encoded_len + 1);
552 (*encoded)[*encoded_len] = '\0';
553 return SUCCESS;
554 }
555
556 STR_SET(*encoded, NULL);
557 *encoded_len = 0;
558 http_error_ex(HE_WARNING, HTTP_E_ENCODING, "Failed to finish deflate stream: %s", zError(status));
559 return FAILURE;
560 }
561 /* }}} */
562
563 /* {{{ STATUS http_encoding_inflate_stream_finish(http_encoding_stream *, char **, size_t *) */
564 PHP_HTTP_API STATUS _http_encoding_inflate_stream_finish(http_encoding_stream *s, char **decoded, size_t *decoded_len ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC TSRMLS_DC)
565 {
566 int status;
567
568 if (!PHPSTR_LEN(s->stream.opaque)) {
569 *decoded = NULL;
570 *decoded_len = 0;
571 return SUCCESS;
572 }
573
574 *decoded_len = (PHPSTR_LEN(s->stream.opaque) + 1) * HTTP_INFLATE_ROUNDS;
575 *decoded = emalloc_rel(*decoded_len);
576
577 /* inflate remaining input */
578 s->stream.next_in = (Bytef *) PHPSTR_VAL(s->stream.opaque);
579 s->stream.avail_in = PHPSTR_LEN(s->stream.opaque);
580
581 s->stream.avail_out = *decoded_len;
582 s->stream.next_out = (Bytef *) *decoded;
583
584 if (Z_STREAM_END == (status = inflate(&s->stream, Z_FINISH))) {
585 /* cut processed input off */
586 phpstr_cut(PHPSTR(s->stream.opaque), 0, PHPSTR_LEN(s->stream.opaque) - s->stream.avail_in);
587
588 /* size down */
589 *decoded_len -= s->stream.avail_out;
590 *decoded = erealloc_rel(*decoded, *decoded_len + 1);
591 (*decoded)[*decoded_len] = '\0';
592 return SUCCESS;
593 }
594
595 STR_SET(*decoded, NULL);
596 *decoded_len = 0;
597 http_error_ex(HE_WARNING, HTTP_E_ENCODING, "Failed to finish inflate stream: %s", zError(status));
598 return FAILURE;
599 }
600 /* }}} */
601
602 /* {{{ void http_encoding_deflate_stream_dtor(http_encoding_stream *) */
603 PHP_HTTP_API void _http_encoding_deflate_stream_dtor(http_encoding_stream *s TSRMLS_DC)
604 {
605 if (s) {
606 if (s->stream.opaque) {
607 phpstr_free((phpstr **) &s->stream.opaque);
608 }
609 deflateEnd(&s->stream);
610 }
611 }
612 /* }}} */
613
614 /* {{{ void http_encoding_inflate_stream_dtor(http_encoding_stream *) */
615 PHP_HTTP_API void _http_encoding_inflate_stream_dtor(http_encoding_stream *s TSRMLS_DC)
616 {
617 if (s) {
618 if (s->stream.opaque) {
619 phpstr_free((phpstr **) &s->stream.opaque);
620 }
621 inflateEnd(&s->stream);
622 }
623 }
624 /* }}} */
625
626 /* {{{ void http_encoding_deflate_stream_free(http_encoding_stream **) */
627 PHP_HTTP_API void _http_encoding_deflate_stream_free(http_encoding_stream **s TSRMLS_DC)
628 {
629 if (s) {
630 http_encoding_deflate_stream_dtor(*s);
631 if (*s) {
632 pefree(*s, (*s)->flags & HTTP_ENCODING_STREAM_PERSISTENT);
633 }
634 *s = NULL;
635 }
636 }
637 /* }}} */
638
639 /* {{{ void http_encoding_inflate_stream_free(http_encoding_stream **) */
640 PHP_HTTP_API void _http_encoding_inflate_stream_free(http_encoding_stream **s TSRMLS_DC)
641 {
642 if (s) {
643 http_encoding_inflate_stream_dtor(*s);
644 if (*s) {
645 pefree(*s, (*s)->flags & HTTP_ENCODING_STREAM_PERSISTENT);
646 }
647 *s = NULL;
648 }
649 }
650 /* }}} */
651
652 /* {{{ void http_ob_deflatehandler(char *, uint, char **, uint *, int) */
653 void _http_ob_deflatehandler(char *output, uint output_len, char **handled_output, uint *handled_output_len, int mode TSRMLS_DC)
654 {
655 *handled_output = NULL;
656 *handled_output_len = 0;
657
658 if (mode & PHP_OUTPUT_HANDLER_START) {
659 int flags;
660
661 if (HTTP_G->send.deflate.stream) {
662 zend_error(E_ERROR, "ob_deflatehandler() can only be used once");
663 return;
664 }
665
666 HTTP_G->send.deflate.encoding = !0;
667
668 switch (http_encoding_response_start(0)) {
669 case HTTP_ENCODING_GZIP:
670 flags = HTTP_DEFLATE_TYPE_GZIP;
671 break;
672
673 case HTTP_ENCODING_DEFLATE:
674 flags = HTTP_DEFLATE_TYPE_ZLIB;
675 break;
676
677 default:
678 goto deflate_passthru_plain;
679 }
680
681 flags |= (HTTP_G->send.deflate.start_flags &~ 0xf0);
682 HTTP_G->send.deflate.stream = http_encoding_deflate_stream_init(NULL, flags);
683 }
684
685 if (HTTP_G->send.deflate.stream) {
686 if (output_len) {
687 http_encoding_deflate_stream_update((http_encoding_stream *) HTTP_G->send.deflate.stream, output, output_len, handled_output, handled_output_len);
688 }
689
690 if (mode & PHP_OUTPUT_HANDLER_END) {
691 char *remaining = NULL;
692 size_t remaining_len = 0;
693
694 http_encoding_deflate_stream_finish((http_encoding_stream *) HTTP_G->send.deflate.stream, &remaining, &remaining_len);
695 http_encoding_deflate_stream_free((http_encoding_stream **) &HTTP_G->send.deflate.stream);
696 if (remaining) {
697 *handled_output = erealloc(*handled_output, *handled_output_len + remaining_len + 1);
698 memcpy(*handled_output + *handled_output_len, remaining, remaining_len);
699 (*handled_output)[*handled_output_len += remaining_len] = '\0';
700 efree(remaining);
701 }
702 }
703 } else {
704 deflate_passthru_plain:
705 *handled_output = estrndup(output, *handled_output_len = output_len);
706 }
707 }
708 /* }}} */
709
710 /* {{{ void http_ob_inflatehandler(char *, uint, char **, uint *, int) */
711 void _http_ob_inflatehandler(char *output, uint output_len, char **handled_output, uint *handled_output_len, int mode TSRMLS_DC)
712 {
713 *handled_output = NULL;
714 *handled_output_len = 0;
715
716 if (mode & PHP_OUTPUT_HANDLER_START) {
717 if (HTTP_G->send.inflate.stream) {
718 zend_error(E_ERROR, "ob_inflatehandler() can only be used once");
719 return;
720 }
721 HTTP_G->send.inflate.stream = http_encoding_inflate_stream_init(NULL, (HTTP_G->send.inflate.start_flags &~ 0xf0));
722 }
723
724 if (HTTP_G->send.inflate.stream) {
725 if (output_len) {
726 http_encoding_inflate_stream_update((http_encoding_stream *) HTTP_G->send.inflate.stream, output, output_len, handled_output, handled_output_len);
727 }
728
729 if (mode & PHP_OUTPUT_HANDLER_END) {
730 char *remaining = NULL;
731 size_t remaining_len = 0;
732
733 http_encoding_inflate_stream_finish((http_encoding_stream *) HTTP_G->send.inflate.stream, &remaining, &remaining_len);
734 http_encoding_inflate_stream_free((http_encoding_stream **) &HTTP_G->send.inflate.stream);
735 if (remaining) {
736 *handled_output = erealloc(*handled_output, *handled_output_len + remaining_len + 1);
737 memcpy(*handled_output + *handled_output_len, remaining, remaining_len);
738 (*handled_output)[*handled_output_len += remaining_len] = '\0';
739 efree(remaining);
740 }
741 }
742 } else {
743 *handled_output = estrndup(output, *handled_output_len = output_len);
744 }
745 }
746 /* }}} */
747
748 #endif /* HTTP_HAVE_ZLIB */
749
750 /*
751 * Local variables:
752 * tab-width: 4
753 * c-basic-offset: 4
754 * End:
755 * vim600: noet sw=4 ts=4 fdm=marker
756 * vim<600: noet sw=4 ts=4
757 */
758