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_encoding_api.c,v 1.62 2007/02/07 11:50:26 mike Exp $ */
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 220 : {
27 220 : HTTP_LONG_CONSTANT("HTTP_DEFLATE_LEVEL_DEF", HTTP_DEFLATE_LEVEL_DEF);
28 220 : HTTP_LONG_CONSTANT("HTTP_DEFLATE_LEVEL_MIN", HTTP_DEFLATE_LEVEL_MIN);
29 220 : HTTP_LONG_CONSTANT("HTTP_DEFLATE_LEVEL_MAX", HTTP_DEFLATE_LEVEL_MAX);
30 220 : HTTP_LONG_CONSTANT("HTTP_DEFLATE_TYPE_ZLIB", HTTP_DEFLATE_TYPE_ZLIB);
31 220 : HTTP_LONG_CONSTANT("HTTP_DEFLATE_TYPE_GZIP", HTTP_DEFLATE_TYPE_GZIP);
32 220 : HTTP_LONG_CONSTANT("HTTP_DEFLATE_TYPE_RAW", HTTP_DEFLATE_TYPE_RAW);
33 220 : HTTP_LONG_CONSTANT("HTTP_DEFLATE_STRATEGY_DEF", HTTP_DEFLATE_STRATEGY_DEF);
34 220 : HTTP_LONG_CONSTANT("HTTP_DEFLATE_STRATEGY_FILT", HTTP_DEFLATE_STRATEGY_FILT);
35 220 : HTTP_LONG_CONSTANT("HTTP_DEFLATE_STRATEGY_HUFF", HTTP_DEFLATE_STRATEGY_HUFF);
36 220 : HTTP_LONG_CONSTANT("HTTP_DEFLATE_STRATEGY_RLE", HTTP_DEFLATE_STRATEGY_RLE);
37 220 : HTTP_LONG_CONSTANT("HTTP_DEFLATE_STRATEGY_FIXED", HTTP_DEFLATE_STRATEGY_FIXED);
38 :
39 220 : HTTP_LONG_CONSTANT("HTTP_ENCODING_STREAM_FLUSH_NONE", HTTP_ENCODING_STREAM_FLUSH_NONE);
40 220 : HTTP_LONG_CONSTANT("HTTP_ENCODING_STREAM_FLUSH_SYNC", HTTP_ENCODING_STREAM_FLUSH_SYNC);
41 220 : HTTP_LONG_CONSTANT("HTTP_ENCODING_STREAM_FLUSH_FULL", HTTP_ENCODING_STREAM_FLUSH_FULL);
42 :
43 220 : return SUCCESS;
44 : }
45 :
46 : PHP_RINIT_FUNCTION(http_encoding)
47 219 : {
48 219 : if (HTTP_G->send.inflate.start_auto) {
49 0 : php_ob_set_internal_handler(_http_ob_inflatehandler, HTTP_INFLATE_BUFFER_SIZE, "http inflate", 0 TSRMLS_CC);
50 : }
51 219 : if (HTTP_G->send.deflate.start_auto) {
52 0 : php_ob_set_internal_handler(_http_ob_deflatehandler, HTTP_DEFLATE_BUFFER_SIZE, "http deflate", 0 TSRMLS_CC);
53 : }
54 219 : return SUCCESS;
55 : }
56 :
57 : PHP_RSHUTDOWN_FUNCTION(http_encoding)
58 219 : {
59 219 : if (HTTP_G->send.deflate.stream) {
60 0 : http_encoding_deflate_stream_free((http_encoding_stream **) &HTTP_G->send.deflate.stream);
61 : }
62 219 : if (HTTP_G->send.inflate.stream) {
63 0 : http_encoding_inflate_stream_free((http_encoding_stream **) &HTTP_G->send.inflate.stream);
64 : }
65 219 : return SUCCESS;
66 : }
67 : #endif
68 : /* }}} */
69 :
70 : /* {{{ eol_match(char **, int *) */
71 : static inline int eol_match(char **line, int *eol_len)
72 656 : {
73 656 : char *ptr = *line;
74 :
75 656 : while (' ' == *ptr) ++ptr;
76 :
77 656 : if (ptr == http_locate_eol(*line, eol_len)) {
78 656 : *line = ptr;
79 656 : return 1;
80 : } else {
81 0 : 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 30 : {
89 30 : int eol_len = 0;
90 30 : char *n_ptr = NULL;
91 30 : const char *e_ptr = encoded;
92 :
93 30 : *decoded_len = 0;
94 30 : *decoded = ecalloc(1, encoded_len);
95 :
96 714 : while ((encoded + encoded_len - e_ptr) > 0) {
97 684 : ulong chunk_len = 0, rest;
98 :
99 684 : chunk_len = strtoul(e_ptr, &n_ptr, 16);
100 :
101 : /* we could not read in chunk size */
102 684 : 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 0 : if (e_ptr == encoded) {
109 0 : http_error(HE_NOTICE, HTTP_E_ENCODING, "Data does not seem to be chunked encoded");
110 0 : memcpy(*decoded, encoded, encoded_len);
111 0 : *decoded_len = encoded_len;
112 0 : return encoded + encoded_len;
113 : } else {
114 0 : efree(*decoded);
115 0 : 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 0 : return NULL;
117 : }
118 : }
119 :
120 : /* reached the end */
121 684 : if (!chunk_len) {
122 : /* move over '0' chunked encoding terminator */
123 28 : while (*e_ptr == '0') ++e_ptr;
124 28 : break;
125 : }
126 :
127 : /* there should be CRLF after the chunk size, but we'll ignore SP+ too */
128 656 : if (*n_ptr && !eol_match(&n_ptr, &eol_len)) {
129 0 : if (eol_len == 2) {
130 0 : 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 0 : 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 656 : n_ptr += eol_len;
136 :
137 : /* chunk size pretends more data than we actually got, so it's probably a truncated message */
138 656 : if (chunk_len > (rest = encoded + encoded_len - n_ptr)) {
139 1 : 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 1 : chunk_len = rest;
141 : }
142 :
143 : /* copy the chunk */
144 656 : memcpy(*decoded + *decoded_len, n_ptr, chunk_len);
145 656 : *decoded_len += chunk_len;
146 :
147 656 : if (chunk_len == rest) {
148 2 : e_ptr = n_ptr + chunk_len;
149 2 : break;
150 : } else {
151 : /* advance to next chunk */
152 654 : e_ptr = n_ptr + chunk_len + eol_len;
153 : }
154 : }
155 :
156 30 : 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 27 : {
163 27 : int response = HTTP_G->send.deflate.response;
164 27 : int ohandler = php_ob_handler_used("ob_gzhandler" TSRMLS_CC) || php_ob_handler_used("zlib output compression" TSRMLS_CC);
165 :
166 27 : if (!ohandler && !ignore_http_ohandler) {
167 26 : ohandler = php_ob_handler_used("ob_deflatehandler" TSRMLS_CC) || php_ob_handler_used("http deflate" TSRMLS_CC);
168 : }
169 :
170 30 : if (response && !ohandler) {
171 : #ifdef HTTP_HAVE_ZLIB
172 : HashTable *selected;
173 : zval zsupported;
174 :
175 3 : HTTP_G->send.deflate.encoding = 0;
176 :
177 3 : INIT_PZVAL(&zsupported);
178 3 : array_init(&zsupported);
179 3 : add_next_index_stringl(&zsupported, "gzip", lenof("gzip"), 1);
180 3 : add_next_index_stringl(&zsupported, "x-gzip", lenof("x-gzip"), 1);
181 3 : add_next_index_stringl(&zsupported, "deflate", lenof("deflate"), 1);
182 :
183 3 : if ((selected = http_negotiate_encoding(&zsupported))) {
184 3 : STATUS hs = FAILURE;
185 3 : char *encoding = NULL;
186 : ulong idx;
187 :
188 3 : if (HASH_KEY_IS_STRING == zend_hash_get_current_key(selected, &encoding, &idx, 0) && encoding) {
189 6 : if (!strcmp(encoding, "gzip") || !strcmp(encoding, "x-gzip")) {
190 3 : if (SUCCESS == (hs = http_send_header_string("Content-Encoding: gzip"))) {
191 3 : HTTP_G->send.deflate.encoding = HTTP_ENCODING_GZIP;
192 : }
193 0 : } else if (!strcmp(encoding, "deflate")) {
194 0 : if (SUCCESS == (hs = http_send_header_string("Content-Encoding: deflate"))) {
195 0 : HTTP_G->send.deflate.encoding = HTTP_ENCODING_DEFLATE;
196 : }
197 : }
198 3 : if (SUCCESS == hs) {
199 3 : http_send_header_string("Vary: Accept-Encoding");
200 : }
201 : }
202 :
203 3 : zend_hash_destroy(selected);
204 3 : FREE_HASHTABLE(selected);
205 : }
206 :
207 3 : 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 44 : } else if (content_length && !ohandler) {
213 : /* emit a content-length header */
214 : char cl_header_str[128];
215 : size_t cl_header_len;
216 20 : cl_header_len = snprintf(cl_header_str, sizeof(cl_header_str), "Content-Length: %zu", content_length);
217 20 : http_send_header_string_ex(cl_header_str, cl_header_len, 1);
218 : } else {
219 4 : HTTP_G->send.deflate.encoding = 0;
220 : }
221 :
222 27 : 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 29 : {
231 29 : int status = 0, round = 0;
232 : phpstr buffer;
233 :
234 29 : *buf = NULL;
235 29 : *len = 0;
236 :
237 29 : phpstr_init_ex(&buffer, Z->avail_in, PHPSTR_INIT_PREALLOC);
238 :
239 : do {
240 69 : if (PHPSTR_NOMEM == phpstr_resize_ex(&buffer, buffer.size, 0, 1)) {
241 0 : status = Z_MEM_ERROR;
242 : } else {
243 69 : Z->avail_out = buffer.free;
244 69 : 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 69 : status = inflate(Z, flush);
249 :
250 69 : buffer.used += buffer.free - Z->avail_out;
251 69 : 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 69 : HTTP_INFLATE_BUFFER_SIZE_ALIGN(buffer.size);
256 : }
257 69 : } while ((Z_BUF_ERROR == status || (Z_OK == status && Z->avail_in)) && ++round < HTTP_INFLATE_ROUNDS);
258 :
259 56 : if (status == Z_OK || status == Z_STREAM_END) {
260 27 : phpstr_shrink(&buffer);
261 27 : phpstr_fix(&buffer);
262 27 : *buf = buffer.data;
263 27 : *len = buffer.used;
264 : } else {
265 2 : phpstr_dtor(&buffer);
266 : }
267 :
268 29 : 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 4 : {
275 : int status, level, wbits, strategy;
276 : z_stream Z;
277 :
278 4 : HTTP_DEFLATE_LEVEL_SET(flags, level);
279 4 : HTTP_DEFLATE_WBITS_SET(flags, wbits);
280 4 : HTTP_DEFLATE_STRATEGY_SET(flags, strategy);
281 :
282 4 : memset(&Z, 0, sizeof(z_stream));
283 4 : *encoded = NULL;
284 4 : *encoded_len = 0;
285 :
286 4 : status = deflateInit2(&Z, level, Z_DEFLATED, wbits, MAX_MEM_LEVEL, strategy);
287 4 : if (Z_OK == status) {
288 4 : *encoded_len = HTTP_DEFLATE_BUFFER_SIZE_GUESS(data_len);
289 4 : *encoded = emalloc_rel(*encoded_len);
290 :
291 4 : Z.next_in = (Bytef *) data;
292 4 : Z.next_out = (Bytef *) *encoded;
293 4 : Z.avail_in = data_len;
294 4 : Z.avail_out = *encoded_len;
295 :
296 4 : status = deflate(&Z, Z_FINISH);
297 4 : deflateEnd(&Z);
298 :
299 4 : if (Z_STREAM_END == status) {
300 : /* size buffer down to actual length */
301 4 : *encoded = erealloc_rel(*encoded, Z.total_out + 1);
302 4 : (*encoded)[*encoded_len = Z.total_out] = '\0';
303 4 : return SUCCESS;
304 : } else {
305 0 : STR_SET(*encoded, NULL);
306 0 : *encoded_len = 0;
307 : }
308 : }
309 :
310 0 : http_error_ex(HE_WARNING, HTTP_E_ENCODING, "Could not deflate data: %s", zError(status));
311 0 : 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 17 : {
318 : z_stream Z;
319 17 : int status, wbits = HTTP_WINDOW_BITS_ANY;
320 :
321 17 : memset(&Z, 0, sizeof(z_stream));
322 :
323 19 : retry_raw_inflate:
324 19 : status = inflateInit2(&Z, wbits);
325 19 : if (Z_OK == status) {
326 19 : Z.next_in = (Bytef *) data;
327 19 : Z.avail_in = data_len;
328 :
329 19 : switch (status = http_inflate_rounds(&Z, Z_NO_FLUSH, decoded, decoded_len)) {
330 : case Z_OK:
331 : case Z_STREAM_END:
332 17 : inflateEnd(&Z);
333 17 : return SUCCESS;
334 :
335 : case Z_DATA_ERROR:
336 : /* raw deflated data? */
337 2 : if (HTTP_WINDOW_BITS_ANY == wbits) {
338 2 : inflateEnd(&Z);
339 2 : wbits = HTTP_WINDOW_BITS_RAW;
340 2 : goto retry_raw_inflate;
341 : }
342 : }
343 0 : inflateEnd(&Z);
344 : }
345 :
346 0 : http_error_ex(HE_WARNING, HTTP_E_ENCODING, "Could not inflate data: %s", zError(status));
347 0 : 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 12 : {
354 : int status, level, wbits, strategy, free_stream;
355 :
356 12 : if ((free_stream = !s)) {
357 9 : s = pemalloc_rel(sizeof(http_encoding_stream), (flags & HTTP_ENCODING_STREAM_PERSISTENT));
358 : }
359 12 : memset(s, 0, sizeof(http_encoding_stream));
360 12 : s->flags = flags;
361 :
362 12 : HTTP_DEFLATE_LEVEL_SET(flags, level);
363 12 : HTTP_DEFLATE_WBITS_SET(flags, wbits);
364 12 : HTTP_DEFLATE_STRATEGY_SET(flags, strategy);
365 :
366 12 : if (Z_OK == (status = deflateInit2(&s->stream, level, Z_DEFLATED, wbits, MAX_MEM_LEVEL, strategy))) {
367 12 : int p = (flags & HTTP_ENCODING_STREAM_PERSISTENT) ? PHPSTR_INIT_PERSISTENT:0;
368 :
369 12 : if ((s->stream.opaque = phpstr_init_ex(NULL, HTTP_DEFLATE_BUFFER_SIZE, p))) {
370 12 : return s;
371 : }
372 0 : deflateEnd(&s->stream);
373 0 : status = Z_MEM_ERROR;
374 : }
375 :
376 0 : http_error_ex(HE_WARNING, HTTP_E_ENCODING, "Failed to initialize deflate encoding stream: %s", zError(status));
377 0 : if (free_stream) {
378 0 : efree(s);
379 : }
380 0 : 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 7 : {
387 : int status, wbits, free_stream;
388 :
389 7 : if ((free_stream = !s)) {
390 4 : s = pemalloc_rel(sizeof(http_encoding_stream), (flags & HTTP_ENCODING_STREAM_PERSISTENT));
391 : }
392 7 : memset(s, 0, sizeof(http_encoding_stream));
393 7 : s->flags = flags;
394 :
395 7 : HTTP_INFLATE_WBITS_SET(flags, wbits);
396 :
397 7 : if (Z_OK == (status = inflateInit2(&s->stream, wbits))) {
398 7 : int p = (flags & HTTP_ENCODING_STREAM_PERSISTENT) ? PHPSTR_INIT_PERSISTENT:0;
399 :
400 7 : if ((s->stream.opaque = phpstr_init_ex(NULL, HTTP_DEFLATE_BUFFER_SIZE, p))) {
401 7 : return s;
402 : }
403 0 : inflateEnd(&s->stream);
404 0 : status = Z_MEM_ERROR;
405 : }
406 :
407 0 : http_error_ex(HE_WARNING, HTTP_E_ENCODING, "Failed to initialize inflate stream: %s", zError(status));
408 0 : if (free_stream) {
409 0 : efree(s);
410 : }
411 0 : 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 159 : {
418 : int status;
419 :
420 : /* append input to our buffer */
421 159 : phpstr_append(PHPSTR(s->stream.opaque), data, data_len);
422 :
423 159 : s->stream.next_in = (Bytef *) PHPSTR_VAL(s->stream.opaque);
424 159 : s->stream.avail_in = PHPSTR_LEN(s->stream.opaque);
425 :
426 : /* deflate */
427 159 : *encoded_len = HTTP_DEFLATE_BUFFER_SIZE_GUESS(data_len);
428 159 : *encoded = emalloc_rel(*encoded_len);
429 159 : s->stream.avail_out = *encoded_len;
430 159 : s->stream.next_out = (Bytef *) *encoded;
431 :
432 159 : 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 159 : if (s->stream.avail_in) {
437 0 : phpstr_cut(PHPSTR(s->stream.opaque), 0, PHPSTR_LEN(s->stream.opaque) - s->stream.avail_in);
438 : } else {
439 159 : phpstr_reset(PHPSTR(s->stream.opaque));
440 : }
441 :
442 : /* size buffer down to actual size */
443 159 : *encoded_len -= s->stream.avail_out;
444 159 : *encoded = erealloc_rel(*encoded, *encoded_len + 1);
445 159 : (*encoded)[*encoded_len] = '\0';
446 159 : return SUCCESS;
447 : }
448 :
449 0 : STR_SET(*encoded, NULL);
450 0 : *encoded_len = 0;
451 0 : http_error_ex(HE_WARNING, HTTP_E_ENCODING, "Failed to update deflate stream: %s", zError(status));
452 0 : 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 10 : {
459 : int status;
460 :
461 : /* append input to buffer */
462 10 : phpstr_append(PHPSTR(s->stream.opaque), data, data_len);
463 :
464 10 : retry_raw_inflate:
465 10 : s->stream.next_in = (Bytef *) PHPSTR_VAL(s->stream.opaque);
466 10 : s->stream.avail_in = PHPSTR_LEN(s->stream.opaque);
467 :
468 10 : 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 10 : if (s->stream.avail_in) {
473 0 : phpstr_cut(PHPSTR(s->stream.opaque), 0, PHPSTR_LEN(s->stream.opaque) - s->stream.avail_in);
474 : } else {
475 10 : phpstr_reset(PHPSTR(s->stream.opaque));
476 : }
477 10 : return SUCCESS;
478 :
479 : case Z_DATA_ERROR:
480 : /* raw deflated data ? */
481 0 : if (!(s->flags & HTTP_INFLATE_TYPE_RAW) && !s->stream.total_out) {
482 0 : inflateEnd(&s->stream);
483 0 : s->flags |= HTTP_INFLATE_TYPE_RAW;
484 0 : inflateInit2(&s->stream, HTTP_WINDOW_BITS_RAW);
485 0 : goto retry_raw_inflate;
486 : }
487 : }
488 :
489 0 : http_error_ex(HE_WARNING, HTTP_E_ENCODING, "Failed to update inflate stream: %s", zError(status));
490 0 : 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 4 : {
497 : int status;
498 :
499 4 : *encoded_len = HTTP_DEFLATE_BUFFER_SIZE;
500 4 : *encoded = emalloc_rel(*encoded_len);
501 :
502 4 : s->stream.avail_in = 0;
503 4 : s->stream.next_in = NULL;
504 4 : s->stream.avail_out = *encoded_len;
505 4 : s->stream.next_out = (Bytef *) *encoded;
506 :
507 4 : switch (status = deflate(&s->stream, Z_FULL_FLUSH)) {
508 : case Z_OK:
509 : case Z_STREAM_END:
510 4 : *encoded_len = HTTP_DEFLATE_BUFFER_SIZE - s->stream.avail_out;
511 4 : *encoded = erealloc_rel(*encoded, *encoded_len + 1);
512 4 : (*encoded)[*encoded_len] = '\0';
513 4 : return SUCCESS;
514 : }
515 :
516 0 : STR_SET(*encoded, NULL);
517 0 : *encoded_len = 0;
518 0 : http_error_ex(HE_WARNING, HTTP_E_ENCODING, "Failed to flush deflate stream: %s", zError(status));
519 0 : 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 0 : {
526 : /* noop */
527 0 : *decoded = estrndup("", *decoded_len = 0);
528 0 : 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 11 : {
535 : int status;
536 :
537 11 : *encoded_len = HTTP_DEFLATE_BUFFER_SIZE;
538 11 : *encoded = emalloc_rel(*encoded_len);
539 :
540 : /* deflate remaining input */
541 11 : s->stream.next_in = (Bytef *) PHPSTR_VAL(s->stream.opaque);
542 11 : s->stream.avail_in = PHPSTR_LEN(s->stream.opaque);
543 :
544 11 : s->stream.avail_out = *encoded_len;
545 11 : s->stream.next_out = (Bytef *) *encoded;
546 :
547 : do {
548 11 : status = deflate(&s->stream, Z_FINISH);
549 11 : } while (Z_OK == status);
550 :
551 11 : if (Z_STREAM_END == status) {
552 : /* cut processed intp off */
553 11 : phpstr_cut(PHPSTR(s->stream.opaque), 0, PHPSTR_LEN(s->stream.opaque) - s->stream.avail_in);
554 :
555 : /* size down */
556 11 : *encoded_len -= s->stream.avail_out;
557 11 : *encoded = erealloc_rel(*encoded, *encoded_len + 1);
558 11 : (*encoded)[*encoded_len] = '\0';
559 11 : return SUCCESS;
560 : }
561 :
562 0 : STR_SET(*encoded, NULL);
563 0 : *encoded_len = 0;
564 0 : http_error_ex(HE_WARNING, HTTP_E_ENCODING, "Failed to finish deflate stream: %s", zError(status));
565 0 : 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 6 : {
572 : int status;
573 :
574 6 : if (!PHPSTR_LEN(s->stream.opaque)) {
575 6 : *decoded = NULL;
576 6 : *decoded_len = 0;
577 6 : return SUCCESS;
578 : }
579 :
580 0 : *decoded_len = (PHPSTR_LEN(s->stream.opaque) + 1) * HTTP_INFLATE_ROUNDS;
581 0 : *decoded = emalloc_rel(*decoded_len);
582 :
583 : /* inflate remaining input */
584 0 : s->stream.next_in = (Bytef *) PHPSTR_VAL(s->stream.opaque);
585 0 : s->stream.avail_in = PHPSTR_LEN(s->stream.opaque);
586 :
587 0 : s->stream.avail_out = *decoded_len;
588 0 : s->stream.next_out = (Bytef *) *decoded;
589 :
590 0 : if (Z_STREAM_END == (status = inflate(&s->stream, Z_FINISH))) {
591 : /* cut processed input off */
592 0 : phpstr_cut(PHPSTR(s->stream.opaque), 0, PHPSTR_LEN(s->stream.opaque) - s->stream.avail_in);
593 :
594 : /* size down */
595 0 : *decoded_len -= s->stream.avail_out;
596 0 : *decoded = erealloc_rel(*decoded, *decoded_len + 1);
597 0 : (*decoded)[*decoded_len] = '\0';
598 0 : return SUCCESS;
599 : }
600 :
601 0 : STR_SET(*decoded, NULL);
602 0 : *decoded_len = 0;
603 0 : http_error_ex(HE_WARNING, HTTP_E_ENCODING, "Failed to finish inflate stream: %s", zError(status));
604 0 : 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 12 : {
611 12 : if (s) {
612 12 : if (s->stream.opaque) {
613 12 : phpstr_free((phpstr **) &s->stream.opaque);
614 : }
615 12 : deflateEnd(&s->stream);
616 : }
617 12 : }
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 7 : {
623 7 : if (s) {
624 7 : if (s->stream.opaque) {
625 7 : phpstr_free((phpstr **) &s->stream.opaque);
626 : }
627 7 : inflateEnd(&s->stream);
628 : }
629 7 : }
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 9 : {
635 9 : if (s) {
636 9 : http_encoding_deflate_stream_dtor(*s);
637 9 : if (*s) {
638 9 : pefree(*s, (*s)->flags & HTTP_ENCODING_STREAM_PERSISTENT);
639 : }
640 9 : *s = NULL;
641 : }
642 9 : }
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 4 : {
648 4 : if (s) {
649 4 : http_encoding_inflate_stream_dtor(*s);
650 4 : if (*s) {
651 4 : pefree(*s, (*s)->flags & HTTP_ENCODING_STREAM_PERSISTENT);
652 : }
653 4 : *s = NULL;
654 : }
655 4 : }
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 1 : {
661 : int encoding;
662 :
663 1 : *handled_output = NULL;
664 1 : *handled_output_len = 0;
665 :
666 1 : if (mode & PHP_OUTPUT_HANDLER_START) {
667 : int flags;
668 :
669 1 : if (HTTP_G->send.deflate.stream) {
670 0 : zend_error(E_ERROR, "ob_deflatehandler() can only be used once");
671 0 : return;
672 : }
673 :
674 1 : HTTP_G->send.deflate.response = 1;
675 1 : encoding = http_encoding_response_start(0, 1);
676 1 : HTTP_G->send.deflate.response = 0;
677 :
678 1 : switch (encoding) {
679 : case HTTP_ENCODING_GZIP:
680 1 : flags = HTTP_DEFLATE_TYPE_GZIP;
681 1 : break;
682 :
683 : case HTTP_ENCODING_DEFLATE:
684 0 : flags = HTTP_DEFLATE_TYPE_ZLIB;
685 0 : break;
686 :
687 : default:
688 0 : goto deflate_passthru_plain;
689 : }
690 :
691 1 : flags |= (HTTP_G->send.deflate.start_flags &~ 0xf0);
692 1 : HTTP_G->send.deflate.stream = http_encoding_deflate_stream_init(NULL, flags);
693 : }
694 :
695 1 : if (HTTP_G->send.deflate.stream) {
696 1 : if (output_len) {
697 1 : http_encoding_deflate_stream_update((http_encoding_stream *) HTTP_G->send.deflate.stream, output, output_len, handled_output, handled_output_len);
698 : }
699 :
700 1 : if (mode & PHP_OUTPUT_HANDLER_END) {
701 1 : char *remaining = NULL;
702 1 : size_t remaining_len = 0;
703 :
704 1 : http_encoding_deflate_stream_finish((http_encoding_stream *) HTTP_G->send.deflate.stream, &remaining, &remaining_len);
705 1 : http_encoding_deflate_stream_free((http_encoding_stream **) &HTTP_G->send.deflate.stream);
706 1 : if (remaining) {
707 1 : *handled_output = erealloc(*handled_output, *handled_output_len + remaining_len + 1);
708 1 : memcpy(*handled_output + *handled_output_len, remaining, remaining_len);
709 1 : (*handled_output)[*handled_output_len += remaining_len] = '\0';
710 1 : efree(remaining);
711 : }
712 : }
713 : } else {
714 0 : deflate_passthru_plain:
715 0 : *handled_output = estrndup(output, *handled_output_len = output_len);
716 : }
717 : }
718 : /* }}} */
719 :
720 : /* {{{ void http_ob_inflatehandler(char *, uint, char **, uint *, int) */
721 : void _http_ob_inflatehandler(char *output, uint output_len, char **handled_output, uint *handled_output_len, int mode TSRMLS_DC)
722 1 : {
723 1 : *handled_output = NULL;
724 1 : *handled_output_len = 0;
725 :
726 1 : if (mode & PHP_OUTPUT_HANDLER_START) {
727 1 : if (HTTP_G->send.inflate.stream) {
728 0 : zend_error(E_ERROR, "ob_inflatehandler() can only be used once");
729 0 : return;
730 : }
731 1 : HTTP_G->send.inflate.stream = http_encoding_inflate_stream_init(NULL, (HTTP_G->send.inflate.start_flags &~ 0xf0));
732 : }
733 :
734 1 : if (HTTP_G->send.inflate.stream) {
735 1 : if (output_len) {
736 1 : http_encoding_inflate_stream_update((http_encoding_stream *) HTTP_G->send.inflate.stream, output, output_len, handled_output, handled_output_len);
737 : }
738 :
739 1 : if (mode & PHP_OUTPUT_HANDLER_END) {
740 1 : char *remaining = NULL;
741 1 : size_t remaining_len = 0;
742 :
743 1 : http_encoding_inflate_stream_finish((http_encoding_stream *) HTTP_G->send.inflate.stream, &remaining, &remaining_len);
744 1 : http_encoding_inflate_stream_free((http_encoding_stream **) &HTTP_G->send.inflate.stream);
745 1 : if (remaining) {
746 0 : *handled_output = erealloc(*handled_output, *handled_output_len + remaining_len + 1);
747 0 : memcpy(*handled_output + *handled_output_len, remaining, remaining_len);
748 0 : (*handled_output)[*handled_output_len += remaining_len] = '\0';
749 0 : efree(remaining);
750 : }
751 : }
752 : } else {
753 0 : *handled_output = estrndup(output, *handled_output_len = output_len);
754 : }
755 : }
756 : /* }}} */
757 :
758 : #endif /* HTTP_HAVE_ZLIB */
759 :
760 : /*
761 : * Local variables:
762 : * tab-width: 4
763 : * c-basic-offset: 4
764 : * End:
765 : * vim600: noet sw=4 ts=4 fdm=marker
766 : * vim<600: noet sw=4 ts=4
767 : */
768 :
|