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