* attempt to fix pecl Bug #16826 http_date strange behavior and crash
[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-2007, 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, zend_bool ignore_http_ohandler TSRMLS_DC)
162 {
163 int response = HTTP_G->send.deflate.response;
164 int ohandler = php_ob_handler_used("ob_gzhandler" TSRMLS_CC) || php_ob_handler_used("zlib output compression" TSRMLS_CC);
165
166 if (!ohandler && !ignore_http_ohandler) {
167 ohandler = php_ob_handler_used("ob_deflatehandler" TSRMLS_CC) || php_ob_handler_used("http deflate" TSRMLS_CC);
168 }
169
170 if (response && !ohandler) {
171 #ifdef HTTP_HAVE_ZLIB
172 HashTable *selected;
173 zval zsupported;
174
175 HTTP_G->send.deflate.encoding = 0;
176
177 INIT_PZVAL(&zsupported);
178 array_init(&zsupported);
179 add_next_index_stringl(&zsupported, "gzip", lenof("gzip"), 1);
180 add_next_index_stringl(&zsupported, "x-gzip", lenof("x-gzip"), 1);
181 add_next_index_stringl(&zsupported, "deflate", lenof("deflate"), 1);
182
183 if ((selected = http_negotiate_encoding(&zsupported))) {
184 STATUS hs = FAILURE;
185 char *encoding = NULL;
186 ulong idx;
187
188 if (HASH_KEY_IS_STRING == zend_hash_get_current_key(selected, &encoding, &idx, 0) && encoding) {
189 if (!strcmp(encoding, "gzip") || !strcmp(encoding, "x-gzip")) {
190 if (SUCCESS == (hs = http_send_header_string("Content-Encoding: gzip"))) {
191 HTTP_G->send.deflate.encoding = HTTP_ENCODING_GZIP;
192 }
193 } else if (!strcmp(encoding, "deflate")) {
194 if (SUCCESS == (hs = http_send_header_string("Content-Encoding: deflate"))) {
195 HTTP_G->send.deflate.encoding = HTTP_ENCODING_DEFLATE;
196 }
197 }
198 if (SUCCESS == hs) {
199 http_send_header_string("Vary: Accept-Encoding");
200 }
201 }
202
203 zend_hash_destroy(selected);
204 FREE_HASHTABLE(selected);
205 }
206
207 zval_dtor(&zsupported);
208 #else
209 HTTP_G->send.deflate.encoding = 0;
210 php_start_ob_buffer_named("ob_gzhandler", 0, 0 TSRMLS_CC);
211 #endif /* HTTP_HAVE_ZLIB */
212 } else if (content_length && !ohandler) {
213 /* emit a content-length header */
214 char cl_header_str[128];
215 size_t cl_header_len;
216 cl_header_len = snprintf(cl_header_str, sizeof(cl_header_str), "Content-Length: %zu", content_length);
217 http_send_header_string_ex(cl_header_str, cl_header_len, 1);
218 } else {
219 HTTP_G->send.deflate.encoding = 0;
220 }
221
222 return HTTP_G->send.deflate.encoding;
223 }
224 /* }}} */
225
226 #ifdef HTTP_HAVE_ZLIB
227
228 /* {{{ inline int http_inflate_rounds */
229 static inline int http_inflate_rounds(z_stream *Z, int flush, char **buf, size_t *len)
230 {
231 int status = 0, round = 0;
232 phpstr buffer;
233
234 *buf = NULL;
235 *len = 0;
236
237 phpstr_init_ex(&buffer, Z->avail_in, PHPSTR_INIT_PREALLOC);
238
239 do {
240 if (PHPSTR_NOMEM == phpstr_resize_ex(&buffer, buffer.size, 0, 1)) {
241 status = Z_MEM_ERROR;
242 } else {
243 Z->avail_out = buffer.free;
244 Z->next_out = (Bytef *) buffer.data + buffer.used;
245 #if 0
246 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);
247 #endif
248 status = inflate(Z, flush);
249
250 buffer.used += buffer.free - Z->avail_out;
251 buffer.free = Z->avail_out;
252 #if 0
253 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);
254 #endif
255 HTTP_INFLATE_BUFFER_SIZE_ALIGN(buffer.size);
256 }
257 } while ((Z_BUF_ERROR == status || (Z_OK == status && Z->avail_in)) && ++round < HTTP_INFLATE_ROUNDS);
258
259 if (status == Z_OK || status == Z_STREAM_END) {
260 phpstr_shrink(&buffer);
261 phpstr_fix(&buffer);
262 *buf = buffer.data;
263 *len = buffer.used;
264 } else {
265 phpstr_dtor(&buffer);
266 }
267
268 return status;
269 }
270 /* }}} */
271
272 /* {{{ STATUS http_encoding_deflate(int, char *, size_t, char **, size_t *) */
273 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)
274 {
275 int status, level, wbits, strategy;
276 z_stream Z;
277
278 HTTP_DEFLATE_LEVEL_SET(flags, level);
279 HTTP_DEFLATE_WBITS_SET(flags, wbits);
280 HTTP_DEFLATE_STRATEGY_SET(flags, strategy);
281
282 memset(&Z, 0, sizeof(z_stream));
283 *encoded = NULL;
284 *encoded_len = 0;
285
286 status = deflateInit2(&Z, level, Z_DEFLATED, wbits, MAX_MEM_LEVEL, strategy);
287 if (Z_OK == status) {
288 *encoded_len = HTTP_DEFLATE_BUFFER_SIZE_GUESS(data_len);
289 *encoded = emalloc_rel(*encoded_len);
290
291 Z.next_in = (Bytef *) data;
292 Z.next_out = (Bytef *) *encoded;
293 Z.avail_in = data_len;
294 Z.avail_out = *encoded_len;
295
296 status = deflate(&Z, Z_FINISH);
297 deflateEnd(&Z);
298
299 if (Z_STREAM_END == status) {
300 /* size buffer down to actual length */
301 *encoded = erealloc_rel(*encoded, Z.total_out + 1);
302 (*encoded)[*encoded_len = Z.total_out] = '\0';
303 return SUCCESS;
304 } else {
305 STR_SET(*encoded, NULL);
306 *encoded_len = 0;
307 }
308 }
309
310 http_error_ex(HE_WARNING, HTTP_E_ENCODING, "Could not deflate data: %s", zError(status));
311 return FAILURE;
312 }
313 /* }}} */
314
315 /* {{{ STATUS http_encoding_inflate(char *, size_t, char **, size_t) */
316 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)
317 {
318 z_stream Z;
319 int status, wbits = HTTP_WINDOW_BITS_ANY;
320
321 memset(&Z, 0, sizeof(z_stream));
322
323 retry_raw_inflate:
324 status = inflateInit2(&Z, wbits);
325 if (Z_OK == status) {
326 Z.next_in = (Bytef *) data;
327 Z.avail_in = data_len;
328
329 switch (status = http_inflate_rounds(&Z, Z_NO_FLUSH, decoded, decoded_len)) {
330 case Z_STREAM_END:
331 inflateEnd(&Z);
332 return SUCCESS;
333
334 case Z_OK:
335 status = Z_DATA_ERROR;
336 break;
337
338 case Z_DATA_ERROR:
339 /* raw deflated data? */
340 if (HTTP_WINDOW_BITS_ANY == wbits) {
341 inflateEnd(&Z);
342 wbits = HTTP_WINDOW_BITS_RAW;
343 goto retry_raw_inflate;
344 }
345 }
346 inflateEnd(&Z);
347
348 if (decoded_len && *decoded) {
349 efree(*decoded);
350 }
351 }
352
353 http_error_ex(HE_WARNING, HTTP_E_ENCODING, "Could not inflate data: %s", zError(status));
354 return FAILURE;
355 }
356 /* }}} */
357
358 /* {{{ http_encoding_stream *_http_encoding_deflate_stream_init(http_encoding_stream *, int) */
359 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)
360 {
361 int status, level, wbits, strategy, free_stream;
362
363 if ((free_stream = !s)) {
364 s = pemalloc_rel(sizeof(http_encoding_stream), (flags & HTTP_ENCODING_STREAM_PERSISTENT));
365 }
366 memset(s, 0, sizeof(http_encoding_stream));
367 s->flags = flags;
368
369 HTTP_DEFLATE_LEVEL_SET(flags, level);
370 HTTP_DEFLATE_WBITS_SET(flags, wbits);
371 HTTP_DEFLATE_STRATEGY_SET(flags, strategy);
372
373 if (Z_OK == (status = deflateInit2(&s->stream, level, Z_DEFLATED, wbits, MAX_MEM_LEVEL, strategy))) {
374 int p = (flags & HTTP_ENCODING_STREAM_PERSISTENT) ? PHPSTR_INIT_PERSISTENT:0;
375
376 if ((s->stream.opaque = phpstr_init_ex(NULL, HTTP_DEFLATE_BUFFER_SIZE, p))) {
377 return s;
378 }
379 deflateEnd(&s->stream);
380 status = Z_MEM_ERROR;
381 }
382
383 http_error_ex(HE_WARNING, HTTP_E_ENCODING, "Failed to initialize deflate encoding stream: %s", zError(status));
384 if (free_stream) {
385 efree(s);
386 }
387 return NULL;
388 }
389 /* }}} */
390
391 /* {{{ http_encoding_stream *http_encoding_inflate_stream_init(http_encoding_stream *, int) */
392 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)
393 {
394 int status, wbits, free_stream;
395
396 if ((free_stream = !s)) {
397 s = pemalloc_rel(sizeof(http_encoding_stream), (flags & HTTP_ENCODING_STREAM_PERSISTENT));
398 }
399 memset(s, 0, sizeof(http_encoding_stream));
400 s->flags = flags;
401
402 HTTP_INFLATE_WBITS_SET(flags, wbits);
403
404 if (Z_OK == (status = inflateInit2(&s->stream, wbits))) {
405 int p = (flags & HTTP_ENCODING_STREAM_PERSISTENT) ? PHPSTR_INIT_PERSISTENT:0;
406
407 if ((s->stream.opaque = phpstr_init_ex(NULL, HTTP_DEFLATE_BUFFER_SIZE, p))) {
408 return s;
409 }
410 inflateEnd(&s->stream);
411 status = Z_MEM_ERROR;
412 }
413
414 http_error_ex(HE_WARNING, HTTP_E_ENCODING, "Failed to initialize inflate stream: %s", zError(status));
415 if (free_stream) {
416 efree(s);
417 }
418 return NULL;
419 }
420 /* }}} */
421
422 /* {{{ STATUS http_encoding_deflate_stream_update(http_encoding_stream *, char *, size_t, char **, size_t *) */
423 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)
424 {
425 int status;
426
427 /* append input to our buffer */
428 phpstr_append(PHPSTR(s->stream.opaque), data, data_len);
429
430 s->stream.next_in = (Bytef *) PHPSTR_VAL(s->stream.opaque);
431 s->stream.avail_in = PHPSTR_LEN(s->stream.opaque);
432
433 /* deflate */
434 *encoded_len = HTTP_DEFLATE_BUFFER_SIZE_GUESS(data_len);
435 *encoded = emalloc_rel(*encoded_len);
436 s->stream.avail_out = *encoded_len;
437 s->stream.next_out = (Bytef *) *encoded;
438
439 switch (status = deflate(&s->stream, HTTP_ENCODING_STREAM_FLUSH_FLAG(s->flags))) {
440 case Z_OK:
441 case Z_STREAM_END:
442 /* cut processed chunk off the buffer */
443 if (s->stream.avail_in) {
444 phpstr_cut(PHPSTR(s->stream.opaque), 0, PHPSTR_LEN(s->stream.opaque) - s->stream.avail_in);
445 } else {
446 phpstr_reset(PHPSTR(s->stream.opaque));
447 }
448
449 /* size buffer down to actual size */
450 *encoded_len -= s->stream.avail_out;
451 *encoded = erealloc_rel(*encoded, *encoded_len + 1);
452 (*encoded)[*encoded_len] = '\0';
453 return SUCCESS;
454 }
455
456 STR_SET(*encoded, NULL);
457 *encoded_len = 0;
458 http_error_ex(HE_WARNING, HTTP_E_ENCODING, "Failed to update deflate stream: %s", zError(status));
459 return FAILURE;
460 }
461 /* }}} */
462
463 /* {{{ STATUS http_encoding_inflate_stream_update(http_encoding_stream *, char *, size_t, char **, size_t *) */
464 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)
465 {
466 int status;
467
468 /* append input to buffer */
469 phpstr_append(PHPSTR(s->stream.opaque), data, data_len);
470
471 retry_raw_inflate:
472 s->stream.next_in = (Bytef *) PHPSTR_VAL(s->stream.opaque);
473 s->stream.avail_in = PHPSTR_LEN(s->stream.opaque);
474
475 switch (status = http_inflate_rounds(&s->stream, HTTP_ENCODING_STREAM_FLUSH_FLAG(s->flags), decoded, decoded_len)) {
476 case Z_OK:
477 case Z_STREAM_END:
478 /* cut off */
479 if (s->stream.avail_in) {
480 phpstr_cut(PHPSTR(s->stream.opaque), 0, PHPSTR_LEN(s->stream.opaque) - s->stream.avail_in);
481 } else {
482 phpstr_reset(PHPSTR(s->stream.opaque));
483 }
484 return SUCCESS;
485
486 case Z_DATA_ERROR:
487 /* raw deflated data ? */
488 if (!(s->flags & HTTP_INFLATE_TYPE_RAW) && !s->stream.total_out) {
489 inflateEnd(&s->stream);
490 s->flags |= HTTP_INFLATE_TYPE_RAW;
491 inflateInit2(&s->stream, HTTP_WINDOW_BITS_RAW);
492 goto retry_raw_inflate;
493 }
494 }
495
496 http_error_ex(HE_WARNING, HTTP_E_ENCODING, "Failed to update inflate stream: %s", zError(status));
497 return FAILURE;
498 }
499 /* }}} */
500
501 /* {{{ STATUS http_encoding_deflate_stream_flush(http_encoding_stream *, char **, size_t *) */
502 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)
503 {
504 int status;
505
506 *encoded_len = HTTP_DEFLATE_BUFFER_SIZE;
507 *encoded = emalloc_rel(*encoded_len);
508
509 s->stream.avail_in = 0;
510 s->stream.next_in = NULL;
511 s->stream.avail_out = *encoded_len;
512 s->stream.next_out = (Bytef *) *encoded;
513
514 switch (status = deflate(&s->stream, Z_FULL_FLUSH)) {
515 case Z_OK:
516 case Z_STREAM_END:
517 *encoded_len = HTTP_DEFLATE_BUFFER_SIZE - s->stream.avail_out;
518 *encoded = erealloc_rel(*encoded, *encoded_len + 1);
519 (*encoded)[*encoded_len] = '\0';
520 return SUCCESS;
521 }
522
523 STR_SET(*encoded, NULL);
524 *encoded_len = 0;
525 http_error_ex(HE_WARNING, HTTP_E_ENCODING, "Failed to flush deflate stream: %s", zError(status));
526 return FAILURE;
527 }
528 /* }}} */
529
530 /* {{{ STATUS http_encoding_inflate_straem_flush(http_encoding_stream *, char **, size_t *) */
531 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)
532 {
533 /* noop */
534 *decoded = estrndup("", *decoded_len = 0);
535 return SUCCESS;
536 }
537 /* }}} */
538
539 /* {{{ STATUS http_encoding_deflate_stream_finish(http_encoding_stream *, char **, size_t *) */
540 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)
541 {
542 int status;
543
544 *encoded_len = HTTP_DEFLATE_BUFFER_SIZE;
545 *encoded = emalloc_rel(*encoded_len);
546
547 /* deflate remaining input */
548 s->stream.next_in = (Bytef *) PHPSTR_VAL(s->stream.opaque);
549 s->stream.avail_in = PHPSTR_LEN(s->stream.opaque);
550
551 s->stream.avail_out = *encoded_len;
552 s->stream.next_out = (Bytef *) *encoded;
553
554 do {
555 status = deflate(&s->stream, Z_FINISH);
556 } while (Z_OK == status);
557
558 if (Z_STREAM_END == status) {
559 /* cut processed intp off */
560 phpstr_cut(PHPSTR(s->stream.opaque), 0, PHPSTR_LEN(s->stream.opaque) - s->stream.avail_in);
561
562 /* size down */
563 *encoded_len -= s->stream.avail_out;
564 *encoded = erealloc_rel(*encoded, *encoded_len + 1);
565 (*encoded)[*encoded_len] = '\0';
566 return SUCCESS;
567 }
568
569 STR_SET(*encoded, NULL);
570 *encoded_len = 0;
571 http_error_ex(HE_WARNING, HTTP_E_ENCODING, "Failed to finish deflate stream: %s", zError(status));
572 return FAILURE;
573 }
574 /* }}} */
575
576 /* {{{ STATUS http_encoding_inflate_stream_finish(http_encoding_stream *, char **, size_t *) */
577 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)
578 {
579 int status;
580
581 if (!PHPSTR_LEN(s->stream.opaque)) {
582 *decoded = NULL;
583 *decoded_len = 0;
584 return SUCCESS;
585 }
586
587 *decoded_len = (PHPSTR_LEN(s->stream.opaque) + 1) * HTTP_INFLATE_ROUNDS;
588 *decoded = emalloc_rel(*decoded_len);
589
590 /* inflate remaining input */
591 s->stream.next_in = (Bytef *) PHPSTR_VAL(s->stream.opaque);
592 s->stream.avail_in = PHPSTR_LEN(s->stream.opaque);
593
594 s->stream.avail_out = *decoded_len;
595 s->stream.next_out = (Bytef *) *decoded;
596
597 if (Z_STREAM_END == (status = inflate(&s->stream, Z_FINISH))) {
598 /* cut processed input off */
599 phpstr_cut(PHPSTR(s->stream.opaque), 0, PHPSTR_LEN(s->stream.opaque) - s->stream.avail_in);
600
601 /* size down */
602 *decoded_len -= s->stream.avail_out;
603 *decoded = erealloc_rel(*decoded, *decoded_len + 1);
604 (*decoded)[*decoded_len] = '\0';
605 return SUCCESS;
606 }
607
608 STR_SET(*decoded, NULL);
609 *decoded_len = 0;
610 http_error_ex(HE_WARNING, HTTP_E_ENCODING, "Failed to finish inflate stream: %s", zError(status));
611 return FAILURE;
612 }
613 /* }}} */
614
615 /* {{{ void http_encoding_deflate_stream_dtor(http_encoding_stream *) */
616 PHP_HTTP_API void _http_encoding_deflate_stream_dtor(http_encoding_stream *s TSRMLS_DC)
617 {
618 if (s) {
619 if (s->stream.opaque) {
620 phpstr_free((phpstr **) &s->stream.opaque);
621 }
622 deflateEnd(&s->stream);
623 }
624 }
625 /* }}} */
626
627 /* {{{ void http_encoding_inflate_stream_dtor(http_encoding_stream *) */
628 PHP_HTTP_API void _http_encoding_inflate_stream_dtor(http_encoding_stream *s TSRMLS_DC)
629 {
630 if (s) {
631 if (s->stream.opaque) {
632 phpstr_free((phpstr **) &s->stream.opaque);
633 }
634 inflateEnd(&s->stream);
635 }
636 }
637 /* }}} */
638
639 /* {{{ void http_encoding_deflate_stream_free(http_encoding_stream **) */
640 PHP_HTTP_API void _http_encoding_deflate_stream_free(http_encoding_stream **s TSRMLS_DC)
641 {
642 if (s) {
643 http_encoding_deflate_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_encoding_inflate_stream_free(http_encoding_stream **) */
653 PHP_HTTP_API void _http_encoding_inflate_stream_free(http_encoding_stream **s TSRMLS_DC)
654 {
655 if (s) {
656 http_encoding_inflate_stream_dtor(*s);
657 if (*s) {
658 pefree(*s, (*s)->flags & HTTP_ENCODING_STREAM_PERSISTENT);
659 }
660 *s = NULL;
661 }
662 }
663 /* }}} */
664
665 /* {{{ void http_ob_deflatehandler(char *, uint, char **, uint *, int) */
666 void _http_ob_deflatehandler(char *output, uint output_len, char **handled_output, uint *handled_output_len, int mode TSRMLS_DC)
667 {
668 int encoding;
669
670 *handled_output = NULL;
671 *handled_output_len = 0;
672
673 if (mode & PHP_OUTPUT_HANDLER_START) {
674 int flags;
675
676 if (HTTP_G->send.deflate.stream) {
677 zend_error(E_ERROR, "ob_deflatehandler() can only be used once");
678 return;
679 }
680
681 HTTP_G->send.deflate.response = 1;
682 encoding = http_encoding_response_start(0, 1);
683 HTTP_G->send.deflate.response = 0;
684
685 switch (encoding) {
686 case HTTP_ENCODING_GZIP:
687 flags = HTTP_DEFLATE_TYPE_GZIP;
688 break;
689
690 case HTTP_ENCODING_DEFLATE:
691 flags = HTTP_DEFLATE_TYPE_ZLIB;
692 break;
693
694 default:
695 goto deflate_passthru_plain;
696 }
697
698 flags |= (HTTP_G->send.deflate.start_flags &~ 0xf0);
699 HTTP_G->send.deflate.stream = http_encoding_deflate_stream_init(NULL, flags);
700 }
701
702 if (HTTP_G->send.deflate.stream) {
703 if (output_len) {
704 size_t tmp_len;
705
706 http_encoding_deflate_stream_update((http_encoding_stream *) HTTP_G->send.deflate.stream, output, output_len, handled_output, &tmp_len);
707 *handled_output_len = tmp_len;
708 }
709
710 if (mode & PHP_OUTPUT_HANDLER_END) {
711 char *remaining = NULL;
712 size_t remaining_len = 0;
713
714 http_encoding_deflate_stream_finish((http_encoding_stream *) HTTP_G->send.deflate.stream, &remaining, &remaining_len);
715 http_encoding_deflate_stream_free((http_encoding_stream **) &HTTP_G->send.deflate.stream);
716 if (remaining) {
717 *handled_output = erealloc(*handled_output, *handled_output_len + remaining_len + 1);
718 memcpy(*handled_output + *handled_output_len, remaining, remaining_len);
719 (*handled_output)[*handled_output_len += remaining_len] = '\0';
720 efree(remaining);
721 }
722 }
723 } else {
724 deflate_passthru_plain:
725 *handled_output = estrndup(output, *handled_output_len = output_len);
726 }
727 }
728 /* }}} */
729
730 /* {{{ void http_ob_inflatehandler(char *, uint, char **, uint *, int) */
731 void _http_ob_inflatehandler(char *output, uint output_len, char **handled_output, uint *handled_output_len, int mode TSRMLS_DC)
732 {
733 *handled_output = NULL;
734 *handled_output_len = 0;
735
736 if (mode & PHP_OUTPUT_HANDLER_START) {
737 if (HTTP_G->send.inflate.stream) {
738 zend_error(E_ERROR, "ob_inflatehandler() can only be used once");
739 return;
740 }
741 HTTP_G->send.inflate.stream = http_encoding_inflate_stream_init(NULL, (HTTP_G->send.inflate.start_flags &~ 0xf0));
742 }
743
744 if (HTTP_G->send.inflate.stream) {
745 if (output_len) {
746 size_t tmp_len;
747
748 http_encoding_inflate_stream_update((http_encoding_stream *) HTTP_G->send.inflate.stream, output, output_len, handled_output, &tmp_len);
749 *handled_output_len = tmp_len;
750 }
751
752 if (mode & PHP_OUTPUT_HANDLER_END) {
753 char *remaining = NULL;
754 size_t remaining_len = 0;
755
756 http_encoding_inflate_stream_finish((http_encoding_stream *) HTTP_G->send.inflate.stream, &remaining, &remaining_len);
757 http_encoding_inflate_stream_free((http_encoding_stream **) &HTTP_G->send.inflate.stream);
758 if (remaining) {
759 *handled_output = erealloc(*handled_output, *handled_output_len + remaining_len + 1);
760 memcpy(*handled_output + *handled_output_len, remaining, remaining_len);
761 (*handled_output)[*handled_output_len += remaining_len] = '\0';
762 efree(remaining);
763 }
764 }
765 } else {
766 *handled_output = estrndup(output, *handled_output_len = output_len);
767 }
768 }
769 /* }}} */
770
771 #endif /* HTTP_HAVE_ZLIB */
772
773 /*
774 * Local variables:
775 * tab-width: 4
776 * c-basic-offset: 4
777 * End:
778 * vim600: noet sw=4 ts=4 fdm=marker
779 * vim<600: noet sw=4 ts=4
780 */
781