- add ob_(deflate|inflate)handler
[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-2005, Michael Wallner <mike@php.net> |
10 +--------------------------------------------------------------------+
11 */
12
13 /* $Id$ */
14
15 #ifdef HAVE_CONFIG_H
16 # include "config.h"
17 #endif
18
19 #define HTTP_WANT_ZLIB
20 #include "php_http.h"
21
22 #include "php_http_api.h"
23 #include "php_http_encoding_api.h"
24 #include "php_http_send_api.h"
25 #include "php_http_headers_api.h"
26
27 #ifdef HTTP_HAVE_ZLIB
28 PHP_MINIT_FUNCTION(http_encoding)
29 {
30 HTTP_LONG_CONSTANT("HTTP_DEFLATE_LEVEL_DEF", HTTP_DEFLATE_LEVEL_DEF);
31 HTTP_LONG_CONSTANT("HTTP_DEFLATE_LEVEL_MIN", HTTP_DEFLATE_LEVEL_MIN);
32 HTTP_LONG_CONSTANT("HTTP_DEFLATE_LEVEL_MAX", HTTP_DEFLATE_LEVEL_MAX);
33 HTTP_LONG_CONSTANT("HTTP_DEFLATE_TYPE_ZLIB", HTTP_DEFLATE_TYPE_ZLIB);
34 HTTP_LONG_CONSTANT("HTTP_DEFLATE_TYPE_GZIP", HTTP_DEFLATE_TYPE_GZIP);
35 HTTP_LONG_CONSTANT("HTTP_DEFLATE_TYPE_RAW", HTTP_DEFLATE_TYPE_RAW);
36 HTTP_LONG_CONSTANT("HTTP_DEFLATE_STRATEGY_DEF", HTTP_DEFLATE_STRATEGY_DEF);
37 HTTP_LONG_CONSTANT("HTTP_DEFLATE_STRATEGY_FILT", HTTP_DEFLATE_STRATEGY_FILT);
38 HTTP_LONG_CONSTANT("HTTP_DEFLATE_STRATEGY_HUFF", HTTP_DEFLATE_STRATEGY_HUFF);
39 HTTP_LONG_CONSTANT("HTTP_DEFLATE_STRATEGY_RLE", HTTP_DEFLATE_STRATEGY_RLE);
40 HTTP_LONG_CONSTANT("HTTP_DEFLATE_STRATEGY_FIXED", HTTP_DEFLATE_STRATEGY_FIXED);
41 return SUCCESS;
42 }
43
44 PHP_RINIT_FUNCTION(http_encoding)
45 {
46 if (HTTP_G(send).inflate.start_auto) {
47 php_ob_set_internal_handler(_http_ob_inflatehandler, 0x1000, "http inflate", 0 TSRMLS_CC);
48 }
49 if (HTTP_G(send).deflate.start_auto) {
50 php_ob_set_internal_handler(_http_ob_deflatehandler, 0x8000, "http deflate", 0 TSRMLS_CC);
51 }
52 return SUCCESS;
53 }
54
55 PHP_RSHUTDOWN_FUNCTION(http_encoding)
56 {
57 if (G->send.deflate.stream) {
58 http_encoding_deflate_stream_free((http_encoding_stream **) &G->send.deflate.stream);
59 }
60 if (G->send.inflate.stream) {
61 http_encoding_inflate_stream_free((http_encoding_stream **) &G->send.inflate.stream);
62 }
63 return SUCCESS;
64 }
65 #endif
66
67 static inline int eol_match(char **line, int *eol_len)
68 {
69 char *ptr = *line;
70
71 while (0x20 == *ptr) ++ptr;
72
73 if (ptr == http_locate_eol(*line, eol_len)) {
74 *line = ptr;
75 return 1;
76 } else {
77 return 0;
78 }
79 }
80
81 /* {{{ char *http_encoding_dechunk(char *, size_t, char **, size_t *) */
82 PHP_HTTP_API const char *_http_encoding_dechunk(const char *encoded, size_t encoded_len, char **decoded, size_t *decoded_len TSRMLS_DC)
83 {
84 int eol_len = 0;
85 char *n_ptr = NULL;
86 const char *e_ptr = encoded;
87
88 *decoded_len = 0;
89 *decoded = ecalloc(1, encoded_len);
90
91 while ((encoded + encoded_len - e_ptr) > 0) {
92 ulong chunk_len = 0, rest;
93
94 chunk_len = strtoul(e_ptr, &n_ptr, 16);
95
96 /* we could not read in chunk size */
97 if (n_ptr == e_ptr) {
98 /*
99 * if this is the first turn and there doesn't seem to be a chunk
100 * size at the begining of the body, do not fail on apparently
101 * not encoded data and return a copy
102 */
103 if (e_ptr == encoded) {
104 http_error(HE_NOTICE, HTTP_E_ENCODING, "Data does not seem to be chunked encoded");
105 memcpy(*decoded, encoded, encoded_len);
106 *decoded_len = encoded_len;
107 return encoded + encoded_len;
108 } else {
109 efree(*decoded);
110 http_error_ex(HE_WARNING, HTTP_E_ENCODING, "Expected chunk size at pos %tu of %zu but got trash", n_ptr - encoded, encoded_len);
111 return NULL;
112 }
113 }
114
115 /* reached the end */
116 if (!chunk_len) {
117 /* move over '0' chunked encoding terminator */
118 while (*e_ptr == '0') ++e_ptr;
119 break;
120 }
121
122 /* there should be CRLF after the chunk size, but we'll ignore SP+ too */
123 if (*n_ptr && !eol_match(&n_ptr, &eol_len)) {
124 if (eol_len == 2) {
125 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));
126 } else {
127 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);
128 }
129 }
130 n_ptr += eol_len;
131
132 /* chunk size pretends more data than we actually got, so it's probably a truncated message */
133 if (chunk_len > (rest = encoded + encoded_len - n_ptr)) {
134 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);
135 chunk_len = rest;
136 }
137
138 /* copy the chunk */
139 memcpy(*decoded + *decoded_len, n_ptr, chunk_len);
140 *decoded_len += chunk_len;
141
142 if (chunk_len == rest) {
143 e_ptr = n_ptr + chunk_len;
144 break;
145 } else {
146 /* advance to next chunk */
147 e_ptr = n_ptr + chunk_len + eol_len;
148 }
149 }
150
151 return e_ptr;
152 }
153 /* }}} */
154
155 #ifdef HTTP_HAVE_ZLIB
156
157 #define HTTP_DEFLATE_LEVEL_SET(flags, level) \
158 switch (flags & 0xf) \
159 { \
160 default: \
161 if ((flags & 0xf) < 10) { \
162 level = flags & 0xf; \
163 break; \
164 } \
165 case HTTP_DEFLATE_LEVEL_DEF: \
166 level = Z_DEFAULT_COMPRESSION; \
167 break; \
168 }
169
170 #define HTTP_DEFLATE_WBITS_SET(flags, wbits) \
171 switch (flags & 0xf0) \
172 { \
173 case HTTP_DEFLATE_TYPE_GZIP: \
174 wbits = HTTP_WINDOW_BITS_GZIP; \
175 break; \
176 case HTTP_DEFLATE_TYPE_RAW: \
177 wbits = HTTP_WINDOW_BITS_RAW; \
178 break; \
179 default: \
180 wbits = HTTP_WINDOW_BITS_ZLIB; \
181 break; \
182 }
183
184 #define HTTP_INFLATE_WBITS_SET(flags, wbits) \
185 if (flags & HTTP_INFLATE_TYPE_RAW) { \
186 wbits = HTTP_WINDOW_BITS_RAW; \
187 } else { \
188 wbits = HTTP_WINDOW_BITS_ANY; \
189 }
190
191 #define HTTP_DEFLATE_STRATEGY_SET(flags, strategy) \
192 switch (flags & 0xf00) \
193 { \
194 case HTTP_DEFLATE_STRATEGY_FILT: \
195 strategy = Z_FILTERED; \
196 break; \
197 case HTTP_DEFLATE_STRATEGY_HUFF: \
198 strategy = Z_HUFFMAN_ONLY; \
199 break; \
200 case HTTP_DEFLATE_STRATEGY_RLE: \
201 strategy = Z_RLE; \
202 break; \
203 case HTTP_DEFLATE_STRATEGY_FIXED: \
204 strategy = Z_FIXED; \
205 break; \
206 default: \
207 strategy = Z_DEFAULT_STRATEGY; \
208 break; \
209 }
210
211 #define HTTP_WINDOW_BITS_ZLIB 0x0000000f
212 #define HTTP_WINDOW_BITS_GZIP 0x0000001f
213 #define HTTP_WINDOW_BITS_ANY 0x0000002f
214 #define HTTP_WINDOW_BITS_RAW -0x000000f
215
216 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)
217 {
218 int status, level, wbits, strategy;
219 z_stream Z;
220
221 HTTP_DEFLATE_LEVEL_SET(flags, level);
222 HTTP_DEFLATE_WBITS_SET(flags, wbits);
223 HTTP_DEFLATE_STRATEGY_SET(flags, strategy);
224
225 memset(&Z, 0, sizeof(z_stream));
226 *encoded = NULL;
227 *encoded_len = 0;
228
229 status = deflateInit2(&Z, level, Z_DEFLATED, wbits, MAX_MEM_LEVEL, strategy);
230 if (Z_OK == status) {
231 *encoded_len = HTTP_ENCODING_BUFLEN(data_len);
232 *encoded = emalloc_rel(*encoded_len);
233
234 Z.next_in = (Bytef *) data;
235 Z.next_out = (Bytef *) *encoded;
236 Z.avail_in = data_len;
237 Z.avail_out = *encoded_len;
238
239 status = deflate(&Z, Z_FINISH);
240 deflateEnd(&Z);
241
242 if (Z_STREAM_END == status) {
243 /* size buffer down to actual length */
244 *encoded = erealloc_rel(*encoded, Z.total_out + 1);
245 (*encoded)[*encoded_len = Z.total_out] = '\0';
246 return SUCCESS;
247 } else {
248 STR_SET(*encoded, NULL);
249 *encoded_len = 0;
250 }
251 }
252
253 http_error_ex(HE_WARNING, HTTP_E_ENCODING, "Could not deflate data: %s (%s)", zError(status));
254 return FAILURE;
255 }
256
257 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)
258 {
259 int status, max = 0, wbits = HTTP_WINDOW_BITS_ANY;
260 z_stream Z;
261 phpstr buffer;
262
263 memset(&Z, 0, sizeof(z_stream));
264 *decoded = NULL;
265 *decoded_len = 0;
266
267 phpstr_init_ex(&buffer, data_len << 2, PHPSTR_INIT_PREALLOC);
268 buffer.size = data_len;
269
270 retry_inflate:
271 status = inflateInit2(&Z, wbits);
272 if (Z_OK == status) {
273 Z.next_in = (Bytef *) data;
274 Z.avail_in = data_len;
275
276 do {
277 phpstr_resize(&buffer, data_len << 2);
278
279 do {
280 Z.avail_out = (buffer.free -= Z.total_out - buffer.used);
281 Z.next_out = (Bytef *) buffer.data + (buffer.used = Z.total_out);
282 status = inflate(&Z, Z_NO_FLUSH);
283 } while (Z_OK == status);
284 } while (Z_BUF_ERROR == status && ++max < HTTP_ENCODING_MAXTRY);
285
286 if (Z_DATA_ERROR == status && HTTP_WINDOW_BITS_ANY == wbits) {
287 /* raw deflated data? */
288 inflateEnd(&Z);
289 wbits = HTTP_WINDOW_BITS_RAW;
290 goto retry_inflate;
291 }
292
293 inflateEnd(&Z);
294
295 if (Z_STREAM_END == status) {
296 *decoded_len = Z.total_out;
297 *decoded = erealloc_rel(buffer.data, *decoded_len + 1);
298 (*decoded)[*decoded_len] = '\0';
299 return SUCCESS;
300 } else {
301 phpstr_dtor(&buffer);
302 }
303 }
304
305 http_error_ex(HE_WARNING, HTTP_E_ENCODING, "Could not inflate data: %s", zError(status));
306 return FAILURE;
307 }
308
309
310 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)
311 {
312 int status, level, wbits, strategy, free_stream;
313
314 if ((free_stream = !s)) {
315 s = pemalloc_rel(sizeof(http_encoding_stream), (flags & HTTP_ENCODING_STREAM_PERSISTENT));
316 }
317 memset(s, 0, sizeof(http_encoding_stream));
318 s->flags = flags;
319
320 HTTP_DEFLATE_LEVEL_SET(flags, level);
321 HTTP_DEFLATE_WBITS_SET(flags, wbits);
322 HTTP_DEFLATE_STRATEGY_SET(flags, strategy);
323
324 if (Z_OK == (status = deflateInit2(&s->stream, level, Z_DEFLATED, wbits, MAX_MEM_LEVEL, strategy))) {
325 int p = (flags & HTTP_ENCODING_STREAM_PERSISTENT) ? PHPSTR_INIT_PERSISTENT:0;
326
327 if ((s->stream.opaque = phpstr_init_ex(NULL, 0x8000, p))) {
328 return s;
329 }
330 deflateEnd(&s->stream);
331 status = Z_MEM_ERROR;
332 }
333
334 http_error_ex(HE_WARNING, HTTP_E_ENCODING, "Failed to initialize deflate encoding stream: %s", zError(status));
335 if (free_stream) {
336 efree(s);
337 }
338 return NULL;
339 }
340
341 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)
342 {
343 int status, wbits, free_stream;
344
345 if ((free_stream = !s)) {
346 s = pemalloc_rel(sizeof(http_encoding_stream), (flags & HTTP_ENCODING_STREAM_PERSISTENT));
347 }
348 memset(s, 0, sizeof(http_encoding_stream));
349 s->flags = flags;
350
351 HTTP_INFLATE_WBITS_SET(flags, wbits);
352
353 if (Z_OK == (status = inflateInit2(&s->stream, wbits))) {
354 int p = (flags & HTTP_ENCODING_STREAM_PERSISTENT) ? PHPSTR_INIT_PERSISTENT:0;
355
356 if ((s->stream.opaque = phpstr_init_ex(NULL, 0x8000, p))) {
357 return s;
358 }
359 inflateEnd(&s->stream);
360 status = Z_MEM_ERROR;
361 }
362
363 http_error_ex(HE_WARNING, HTTP_E_ENCODING, "Failed to initialize inflate stream: %s", zError(status));
364 if (free_stream) {
365 efree(s);
366 }
367 return NULL;
368 }
369
370 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)
371 {
372 int status;
373
374 /* append input to our buffer */
375 phpstr_append(PHPSTR(s->stream.opaque), data, data_len);
376
377 s->stream.next_in = (Bytef *) PHPSTR_VAL(s->stream.opaque);
378 s->stream.avail_in = PHPSTR_LEN(s->stream.opaque);
379
380 /* deflate */
381 *encoded_len = HTTP_ENCODING_BUFLEN(data_len);
382 *encoded = emalloc_rel(*encoded_len);
383 s->stream.avail_out = *encoded_len;
384 s->stream.next_out = (Bytef *) *encoded;
385
386 switch (status = deflate(&s->stream, Z_NO_FLUSH))
387 {
388 case Z_OK:
389 case Z_STREAM_END:
390 /* cut processed chunk off the buffer */
391 phpstr_cut(PHPSTR(s->stream.opaque), 0, data_len - s->stream.avail_in);
392
393 /* size buffer down to actual size */
394 *encoded_len -= s->stream.avail_out;
395 *encoded = erealloc_rel(*encoded, *encoded_len + 1);
396 (*encoded)[*encoded_len] = '\0';
397 return SUCCESS;
398 break;
399 }
400
401 STR_SET(*encoded, NULL);
402 *encoded_len = 0;
403 http_error_ex(HE_WARNING, HTTP_E_ENCODING, "Failed to update deflate stream: %s", zError(status));
404 return FAILURE;
405 }
406
407 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)
408 {
409 int status, max = 0;
410
411 /* append input to buffer */
412 phpstr_append(PHPSTR(s->stream.opaque), data, data_len);
413
414 /* for realloc() */
415 *decoded = NULL;
416 *decoded_len = data_len << 1;
417
418 /* inflate */
419 do {
420 *decoded_len <<= 1;
421 *decoded = erealloc_rel(*decoded, *decoded_len);
422
423 retry_raw_inflate:
424 s->stream.next_in = (Bytef *) PHPSTR_VAL(s->stream.opaque);
425 s->stream.avail_in = PHPSTR_LEN(s->stream.opaque);
426
427 s->stream.next_out = (Bytef *) *decoded;
428 s->stream.avail_out = *decoded_len;
429
430 switch (status = inflate(&s->stream, Z_NO_FLUSH))
431 {
432 case Z_OK:
433 case Z_STREAM_END:
434 /* cut off */
435 phpstr_cut(PHPSTR(s->stream.opaque), 0, data_len - s->stream.avail_in);
436
437 /* size down */
438 *decoded_len -= s->stream.avail_out;
439 *decoded = erealloc_rel(*decoded, *decoded_len + 1);
440 (*decoded)[*decoded_len] = '\0';
441 return SUCCESS;
442 break;
443
444 case Z_DATA_ERROR:
445 /* raw deflated data ? */
446 if (!(s->flags & HTTP_INFLATE_TYPE_RAW) && !s->stream.total_out) {
447 inflateEnd(&s->stream);
448 s->flags |= HTTP_INFLATE_TYPE_RAW;
449 inflateInit2(&s->stream, HTTP_WINDOW_BITS_RAW);
450 goto retry_raw_inflate;
451 }
452 break;
453 }
454 } while (Z_BUF_ERROR == status && ++max < HTTP_ENCODING_MAXTRY);
455
456 STR_SET(*decoded, NULL);
457 *decoded_len = 0;
458 http_error_ex(HE_WARNING, HTTP_E_ENCODING, "Failed to update inflate stream: %s", zError(status));
459 return FAILURE;
460 }
461
462 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)
463 {
464 int status;
465
466 *encoded_len = 0x800;
467 *encoded = emalloc_rel(*encoded_len);
468
469 s->stream.avail_in = 0;
470 s->stream.next_in = NULL;
471 s->stream.avail_out = *encoded_len;
472 s->stream.next_out = (Bytef *) *encoded;
473
474 switch (status = deflate(&s->stream, Z_SYNC_FLUSH))
475 {
476 case Z_OK:
477 case Z_STREAM_END:
478 *encoded_len = 0x800 - s->stream.avail_out;
479 *encoded = erealloc_rel(*encoded, *encoded_len + 1);
480 (*encoded)[*encoded_len] = '\0';
481 return SUCCESS;
482 break;
483 }
484
485 STR_SET(*encoded, NULL);
486 *encoded_len = 0;
487 http_error_ex(HE_WARNING, HTTP_E_ENCODING, "Failed to flush deflate stream: %s", zError(status));
488 return FAILURE;
489 }
490
491 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)
492 {
493 int status;
494
495 *decoded_len = 0x800;
496 *decoded = emalloc_rel(*decoded_len);
497
498 s->stream.avail_in = 0;
499 s->stream.next_in = NULL;
500 s->stream.avail_out = *decoded_len;
501 s->stream.next_out = (Bytef *) *decoded;
502
503 switch (status = inflate(&s->stream, Z_SYNC_FLUSH))
504 {
505 case Z_OK:
506 case Z_STREAM_END:
507 *decoded_len = 0x800 - s->stream.avail_out;
508 *decoded = erealloc_rel(*decoded, *decoded_len + 1);
509 (*decoded)[*decoded_len] = '\0';
510 return SUCCESS;
511 break;
512 }
513
514 STR_SET(*decoded, NULL);
515 *decoded_len = 0;
516 http_error_ex(HE_WARNING, HTTP_E_ENCODING, "Failed to flush inflate stream: %s", zError(status));
517 return FAILURE;
518 }
519
520 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)
521 {
522 int status;
523
524 *encoded_len = 0x800;
525 *encoded = emalloc_rel(*encoded_len);
526
527 /* deflate remaining input */
528 s->stream.next_in = (Bytef *) PHPSTR_VAL(s->stream.opaque);
529 s->stream.avail_in = PHPSTR_LEN(s->stream.opaque);
530
531 s->stream.avail_out = *encoded_len;
532 s->stream.next_out = (Bytef *) *encoded;
533
534 do {
535 status = deflate(&s->stream, Z_FINISH);
536 } while (Z_OK == status);
537
538 if (Z_STREAM_END == status) {
539 /* cut processed intp off */
540 phpstr_cut(PHPSTR(s->stream.opaque), 0, PHPSTR_LEN(s->stream.opaque) - s->stream.avail_in);
541
542 /* size down */
543 *encoded_len -= s->stream.avail_out;
544 *encoded = erealloc_rel(*encoded, *encoded_len + 1);
545 (*encoded)[*encoded_len] = '\0';
546 return SUCCESS;
547 }
548
549 STR_SET(*encoded, NULL);
550 *encoded_len = 0;
551 http_error_ex(HE_WARNING, HTTP_E_ENCODING, "Failed to finish deflate stream: %s", zError(status));
552 return FAILURE;
553 }
554
555 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)
556 {
557 int status;
558
559 *decoded_len = s->stream.avail_in << 2;
560 *decoded = emalloc_rel(*decoded_len);
561
562 /* inflate remaining input */
563 s->stream.next_in = (Bytef *) PHPSTR_VAL(s->stream.opaque);
564 s->stream.avail_in = PHPSTR_LEN(s->stream.opaque);
565
566 s->stream.avail_out = *decoded_len;
567 s->stream.next_out = (Bytef *) *decoded;
568
569 if (Z_STREAM_END == (status = inflate(&s->stream, Z_FINISH))) {
570 /* cut processed input off */
571 phpstr_cut(PHPSTR(s->stream.opaque), 0, PHPSTR_LEN(s->stream.opaque) - s->stream.avail_in);
572
573 /* size down */
574 *decoded_len -= s->stream.avail_out;
575 *decoded = erealloc_rel(*decoded, *decoded_len + 1);
576 (*decoded)[*decoded_len] = '\0';
577 return SUCCESS;
578 }
579
580 STR_SET(*decoded, NULL);
581 *decoded_len = 0;
582 http_error_ex(HE_WARNING, HTTP_E_ENCODING, "Failed to finish inflate stream: %s", zError(status));
583 return FAILURE;
584 }
585
586 PHP_HTTP_API void _http_encoding_deflate_stream_dtor(http_encoding_stream *s TSRMLS_DC)
587 {
588 if (s) {
589 if (s->stream.opaque) {
590 phpstr_free((phpstr **) &s->stream.opaque);
591 }
592 deflateEnd(&s->stream);
593 }
594 }
595
596 PHP_HTTP_API void _http_encoding_inflate_stream_dtor(http_encoding_stream *s TSRMLS_DC)
597 {
598 if (s) {
599 if (s->stream.opaque) {
600 phpstr_free((phpstr **) &s->stream.opaque);
601 }
602 inflateEnd(&s->stream);
603 }
604 }
605
606 PHP_HTTP_API void _http_encoding_deflate_stream_free(http_encoding_stream **s TSRMLS_DC)
607 {
608 if (s) {
609 http_encoding_deflate_stream_dtor(*s);
610 if (*s) {
611 pefree(*s, (*s)->flags & HTTP_ENCODING_STREAM_PERSISTENT);
612 }
613 *s = NULL;
614 }
615 }
616
617 PHP_HTTP_API void _http_encoding_inflate_stream_free(http_encoding_stream **s TSRMLS_DC)
618 {
619 if (s) {
620 http_encoding_inflate_stream_dtor(*s);
621 if (*s) {
622 pefree(*s, (*s)->flags & HTTP_ENCODING_STREAM_PERSISTENT);
623 }
624 *s = NULL;
625 }
626 }
627
628 void _http_ob_deflatehandler(char *output, uint output_len, char **handled_output, uint *handled_output_len, int mode TSRMLS_DC)
629 {
630 getGlobals(G);
631
632 *handled_output = NULL;
633 *handled_output_len = 0;
634
635 if (mode & PHP_OUTPUT_HANDLER_START) {
636 int flags;
637
638 if (G->send.deflate.stream) {
639 zend_error(E_ERROR, "ob_deflatehandler() can only be used once");
640 return;
641 }
642
643 G->send.deflate.encoding = !0;
644
645 switch (http_encoding_response_start(0))
646 {
647 case HTTP_ENCODING_GZIP:
648 flags = HTTP_DEFLATE_TYPE_GZIP;
649 break;
650
651 case HTTP_ENCODING_DEFLATE:
652 flags = HTTP_DEFLATE_TYPE_ZLIB;
653 break;
654
655 default:
656 goto deflate_passthru_plain;
657 break;
658 }
659
660 flags |= (G->send.deflate.start_flags &~ 0xf);
661 G->send.deflate.stream = http_encoding_deflate_stream_init(NULL, flags);
662 }
663
664 if (G->send.deflate.stream) {
665 http_encoding_deflate_stream_update((http_encoding_stream *) G->send.deflate.stream, output, output_len, handled_output, handled_output_len);
666
667 if (mode & PHP_OUTPUT_HANDLER_END) {
668 char *remaining = NULL;
669 size_t remaining_len = 0;
670
671 http_encoding_deflate_stream_finish((http_encoding_stream *) G->send.deflate.stream, &remaining, &remaining_len);
672 http_encoding_deflate_stream_free((http_encoding_stream **) &G->send.deflate.stream);
673 if (remaining) {
674 *handled_output = erealloc(*handled_output, *handled_output_len + remaining_len + 1);
675 memcpy(*handled_output + *handled_output_len, remaining, remaining_len);
676 (*handled_output)[*handled_output_len += remaining_len] = '\0';
677 efree(remaining);
678 }
679 }
680 } else {
681 deflate_passthru_plain:
682 *handled_output = estrndup(output, *handled_output_len = output_len);
683 }
684 }
685
686 void _http_ob_inflatehandler(char *output, uint output_len, char **handled_output, uint *handled_output_len, int mode TSRMLS_DC)
687 {
688 getGlobals(G);
689
690 *handled_output = NULL;
691 *handled_output_len = 0;
692
693 if (mode & PHP_OUTPUT_HANDLER_START) {
694 if (G->send.inflate.stream) {
695 zend_error(E_ERROR, "ob_inflatehandler() can only be used once");
696 return;
697 }
698 G->send.inflate.stream = http_encoding_inflate_stream_init(NULL, (HTTP_G(send).inflate.start_flags &~ 0xf));
699 }
700
701 if (G->send.inflate.stream) {
702 http_encoding_inflate_stream_update((http_encoding_stream *) G->send.inflate.stream, output, output_len, handled_output, handled_output_len);
703
704 if (mode & PHP_OUTPUT_HANDLER_END) {
705 char *remaining = NULL;
706 size_t remaining_len = 0;
707
708 http_encoding_inflate_stream_finish((http_encoding_stream *) G->send.inflate.stream, &remaining, &remaining_len);
709 http_encoding_inflate_stream_free((http_encoding_stream **) &G->send.inflate.stream);
710 if (remaining) {
711 *handled_output = erealloc(*handled_output, *handled_output_len + remaining_len + 1);
712 memcpy(*handled_output + *handled_output_len, remaining, remaining_len);
713 (*handled_output)[*handled_output_len += remaining_len] = '\0';
714 efree(remaining);
715 }
716 }
717 } else {
718 *handled_output = estrndup(output, *handled_output_len = output_len);
719 }
720 }
721
722 static const char http_encoding_gzip_header[] = {
723 (const char) 0x1f, // fixed value
724 (const char) 0x8b, // fixed value
725 (const char) Z_DEFLATED, // compression algorithm
726 (const char) 0, // none of the possible flags defined by the GZIP "RFC"
727 (const char) 0, // MTIME
728 (const char) 0, // =*=
729 (const char) 0, // =*=
730 (const char) 0, // =*=
731 (const char) 0, // two possible flag values for 9 compression levels? o_O
732 #ifdef PHP_WIN32
733 (const char) 0x0b // OS_CODE
734 #else
735 (const char) 0x03 // OS_CODE
736 #endif
737 };
738
739 PHP_HTTP_API STATUS _http_encoding_gzencode_verify(const char *data, size_t data_len, const char **encoded, size_t *encoded_len, int error_level TSRMLS_DC)
740 {
741 size_t offset = sizeof(http_encoding_gzip_header);
742
743 if (data_len < offset) {
744 goto really_bad_gzip_header;
745 }
746
747 if (data[0] != (const char) 0x1F || data[1] != (const char) 0x8B) {
748 http_error_ex(error_level TSRMLS_CC, HTTP_E_ENCODING, "Unrecognized GZIP header start: 0x%02X 0x%02X", (int) data[0], (int) (data[1] & 0xFF));
749 return FAILURE;
750 }
751
752 if (data[2] != (const char) Z_DEFLATED) {
753 http_error_ex(error_level TSRMLS_CC, HTTP_E_ENCODING, "Unrecognized compression format (%d)", (int) (data[2] & 0xFF));
754 /* still try to decode */
755 }
756 if ((data[3] & 0x4) == 0x4) {
757 if (data_len < offset + 2) {
758 goto really_bad_gzip_header;
759 }
760 /* there are extra fields, the length follows the common header as 2 bytes LSB */
761 offset += (unsigned) ((data[offset] & 0xFF));
762 offset += 1;
763 offset += (unsigned) ((data[offset] & 0xFF) << 8);
764 offset += 1;
765 }
766 if ((data[3] & 0x8) == 0x8) {
767 if (data_len <= offset) {
768 goto really_bad_gzip_header;
769 }
770 /* there's a file name */
771 offset += strlen(&data[offset]) + 1 /*NUL*/;
772 }
773 if ((data[3] & 0x10) == 0x10) {
774 if (data_len <= offset) {
775 goto really_bad_gzip_header;
776 }
777 /* there's a comment */
778 offset += strlen(&data[offset]) + 1 /* NUL */;
779 }
780 if ((data[3] & 0x2) == 0x2) {
781 /* there's a CRC16 of the header */
782 offset += 2;
783 if (data_len <= offset) {
784 goto really_bad_gzip_header;
785 } else {
786 ulong crc, cmp;
787
788 cmp = (unsigned) ((data[offset-2] & 0xFF));
789 cmp += (unsigned) ((data[offset-1] & 0xFF) << 8);
790
791 crc = crc32(0L, Z_NULL, 0);
792 crc = crc32(crc, (const Bytef *) data, sizeof(http_encoding_gzip_header));
793
794 if (cmp != (crc & 0xFFFF)) {
795 http_error_ex(error_level TSRMLS_CC, HTTP_E_ENCODING, "GZIP headers CRC checksums so not match (%lu, %lu)", cmp, crc & 0xFFFF);
796 return FAILURE;
797 }
798 }
799 }
800
801 if (data_len < offset + 8) {
802 http_error(error_level TSRMLS_CC, HTTP_E_ENCODING, "Missing or truncated GZIP footer");
803 return FAILURE;
804 }
805
806 if (encoded) {
807 *encoded = data + offset;
808 }
809 if (encoded_len) {
810 *encoded_len = data_len - offset - 8 /* size of the assumed GZIP footer */;
811 }
812
813 return SUCCESS;
814
815 really_bad_gzip_header:
816 http_error(error_level TSRMLS_CC, HTTP_E_ENCODING, "Missing or truncated GZIP header");
817 return FAILURE;
818 }
819
820 PHP_HTTP_API STATUS _http_encoding_gzdecode_verify(const char *data, size_t data_len, const char *decoded, size_t decoded_len, int error_level TSRMLS_DC)
821 {
822 STATUS status = SUCCESS;
823 ulong len, cmp, crc;
824
825 crc = crc32(0L, Z_NULL, 0);
826 crc = crc32(crc, (const Bytef *) decoded, decoded_len);
827
828 cmp = (unsigned) ((data[data_len-8] & 0xFF));
829 cmp += (unsigned) ((data[data_len-7] & 0xFF) << 8);
830 cmp += (unsigned) ((data[data_len-6] & 0xFF) << 16);
831 cmp += (unsigned) ((data[data_len-5] & 0xFF) << 24);
832 len = (unsigned) ((data[data_len-4] & 0xFF));
833 len += (unsigned) ((data[data_len-3] & 0xFF) << 8);
834 len += (unsigned) ((data[data_len-2] & 0xFF) << 16);
835 len += (unsigned) ((data[data_len-1] & 0xFF) << 24);
836
837 if (cmp != crc) {
838 http_error_ex(error_level TSRMLS_CC, HTTP_E_ENCODING, "Could not verify data integrity: CRC checksums do not match (%lu, %lu)", cmp, crc);
839 status = FAILURE;
840 }
841 if (len != decoded_len) {
842 http_error_ex(error_level TSRMLS_CC, HTTP_E_ENCODING, "Could not verify data integrity: data sizes do not match (%lu, %lu)", len, decoded_len);
843 status = FAILURE;
844 }
845 return status;
846 }
847
848 #endif /* HTTP_HAVE_ZLIB */
849
850 PHP_HTTP_API int _http_encoding_response_start(size_t content_length TSRMLS_DC)
851 {
852 if ( php_ob_handler_used("ob_gzhandler" TSRMLS_CC) ||
853 php_ob_handler_used("zlib output compression" TSRMLS_CC)) {
854 HTTP_G(send).deflate.encoding = 0;
855 } else {
856 if (!HTTP_G(send).deflate.encoding) {
857 /* emit a content-length header */
858 if (content_length) {
859 char cl_header_str[128];
860 size_t cl_header_len;
861 cl_header_len = snprintf(cl_header_str, lenof(cl_header_str), "Content-Length: %zu", content_length);
862 http_send_header_string_ex(cl_header_str, cl_header_len, 1);
863 }
864 } else {
865 #ifndef HTTP_HAVE_ZLIB
866 HTTP_G(send).deflate.encoding = 0;
867 php_start_ob_buffer_named("ob_gzhandler", 0, 0 TSRMLS_CC);
868 #else
869 HashTable *selected;
870 zval zsupported;
871
872 INIT_PZVAL(&zsupported);
873 array_init(&zsupported);
874 add_next_index_stringl(&zsupported, "gzip", lenof("gzip"), 1);
875 add_next_index_stringl(&zsupported, "x-gzip", lenof("x-gzip"), 1);
876 add_next_index_stringl(&zsupported, "deflate", lenof("deflate"), 1);
877
878 HTTP_G(send).deflate.encoding = 0;
879
880 if ((selected = http_negotiate_encoding(&zsupported))) {
881 STATUS hs = FAILURE;
882 char *encoding = NULL;
883 ulong idx;
884
885 if (HASH_KEY_IS_STRING == zend_hash_get_current_key(selected, &encoding, &idx, 0) && encoding) {
886 if (!strcmp(encoding, "gzip") || !strcmp(encoding, "x-gzip")) {
887 if (SUCCESS == (hs = http_send_header_string("Content-Encoding: gzip"))) {
888 HTTP_G(send).deflate.encoding = HTTP_ENCODING_GZIP;
889 }
890 } else if (!strcmp(encoding, "deflate")) {
891 if (SUCCESS == (hs = http_send_header_string("Content-Encoding: deflate"))) {
892 HTTP_G(send).deflate.encoding = HTTP_ENCODING_DEFLATE;
893 }
894 }
895 if (SUCCESS == hs) {
896 http_send_header_string("Vary: Accept-Encoding");
897 }
898 }
899
900 zend_hash_destroy(selected);
901 FREE_HASHTABLE(selected);
902 }
903
904 zval_dtor(&zsupported);
905 return HTTP_G(send).deflate.encoding;
906 #endif
907 }
908 }
909 return 0;
910 }
911
912 /*
913 * Local variables:
914 * tab-width: 4
915 * c-basic-offset: 4
916 * End:
917 * vim600: noet sw=4 ts=4 fdm=marker
918 * vim<600: noet sw=4 ts=4
919 */
920