2 +----------------------------------------------------------------------+
4 +----------------------------------------------------------------------+
5 | This source file is subject to version 3.0 of the PHP license, that |
6 | is bundled with this package in the file LICENSE, and is available |
7 | through the world-wide-web at http://www.php.net/license/3_0.txt. |
8 | If you did not receive a copy of the PHP license and are unable to |
9 | obtain it through the world-wide-web, please send a note to |
10 | license@php.net so we can mail you a copy immediately. |
11 +----------------------------------------------------------------------+
12 | Copyright (c) 2004-2005 Michael Wallner <mike@php.net> |
13 +----------------------------------------------------------------------+
23 #include "php_http_encoding_api.h"
25 #include "php_http_api.h"
28 # include "php_http_send_api.h"
29 # include "php_http_headers_api.h"
33 ZEND_EXTERN_MODULE_GLOBALS(http
);
35 /* {{{ char *http_encoding_dechunk(char *, size_t, char **, size_t *) */
36 PHP_HTTP_API
const char *_http_encoding_dechunk(const char *encoded
, size_t encoded_len
, char **decoded
, size_t *decoded_len TSRMLS_DC
)
43 *decoded
= ecalloc(1, encoded_len
);
47 while ((rest
= encoded
+ encoded_len
- e_ptr
) > 0) {
49 int EOL_len
= 0, eol_mismatch
= 0;
52 chunk_len
= strtol(e_ptr
, &n_ptr
, 16);
55 * - we could not read in chunk size
56 * - we got a negative chunk size
57 * - chunk size is greater then remaining size
58 * - chunk size is not followed by (CR)LF|NUL
60 if ( (n_ptr
== e_ptr
) || (chunk_len
< 0) || (chunk_len
> rest
) ||
61 (*n_ptr
&& (eol_mismatch
= (n_ptr
!= http_locate_eol(e_ptr
, &EOL_len
))))) {
62 /* don't fail on apperently not encoded data */
63 if (e_ptr
== encoded
) {
64 memcpy(*decoded
, encoded
, encoded_len
);
65 *decoded_len
= encoded_len
;
66 return encoded
+ encoded_len
;
71 http_error_ex(HE_WARNING
, HTTP_E_ENCODING
, "Invalid character (expected 0x0D 0x0A; got: 0x%X 0x%X)", *n_ptr
, *(n_ptr
+ 1));
73 http_error_ex(HE_WARNING
, HTTP_E_ENCODING
, "Invalid character (expected 0x0A; got: 0x%X)", *n_ptr
);
76 char *error
= estrndup(n_ptr
, strcspn(n_ptr
, "\r\n "));
77 http_error_ex(HE_WARNING
, HTTP_E_ENCODING
, "Invalid chunk size: '%s' at pos %d", error
, n_ptr
- encoded
);
91 memcpy(d_ptr
, e_ptr
+= EOL_len
, chunk_len
);
93 e_ptr
+= chunk_len
+ EOL_len
;
94 *decoded_len
+= chunk_len
;
101 #ifdef HTTP_HAVE_ZLIB
103 static const char http_encoding_gzip_header
[] = {
104 (const char) 0x1f, // fixed value
105 (const char) 0x8b, // fixed value
106 (const char) Z_DEFLATED
, // compression algorithm
107 (const char) 0, // none of the possible flags defined by the GZIP "RFC"
108 (const char) 0, // no MTIME available (4 bytes)
109 (const char) 0, // =*=
110 (const char) 0, // =*=
111 (const char) 0, // =*=
112 (const char) 0, // two possible flag values for 9 compression levels? o_O
113 (const char) 0x03 // assume *nix OS
116 inline void http_init_gzencode_buffer(z_stream
*Z
, const char *data
, size_t data_len
, char **buf_ptr
)
122 Z
->next_in
= (Bytef
*) data
;
123 Z
->avail_in
= data_len
;
124 Z
->avail_out
= HTTP_ENCODING_BUFLEN(data_len
) + HTTP_ENCODING_SAFPAD
- 1;
126 *buf_ptr
= emalloc(HTTP_ENCODING_BUFLEN(data_len
) + sizeof(http_encoding_gzip_header
) + HTTP_ENCODING_SAFPAD
);
127 memcpy(*buf_ptr
, http_encoding_gzip_header
, sizeof(http_encoding_gzip_header
));
129 Z
->next_out
= *buf_ptr
+ sizeof(http_encoding_gzip_header
);
132 inline void http_init_deflate_buffer(z_stream
*Z
, const char *data
, size_t data_len
, char **buf_ptr
)
138 Z
->data_type
= Z_UNKNOWN
;
139 Z
->next_in
= (Bytef
*) data
;
140 Z
->avail_in
= data_len
;
141 Z
->avail_out
= HTTP_ENCODING_BUFLEN(data_len
) - 1;
142 Z
->next_out
= emalloc(HTTP_ENCODING_BUFLEN(data_len
));
144 *buf_ptr
= Z
->next_out
;
147 inline void http_init_uncompress_buffer(size_t data_len
, char **buf_ptr
, size_t *buf_len
, int *iteration
)
150 *buf_len
= data_len
* 2;
151 *buf_ptr
= emalloc(*buf_len
+ 1);
153 size_t new_len
= *buf_len
<< 2;
154 char *new_ptr
= erealloc(*buf_ptr
, new_len
+ 1);
160 *iteration
= INT_MAX
;
165 inline void http_init_inflate_buffer(z_stream
*Z
, const char *data
, size_t data_len
, char **buf_ptr
, size_t *buf_len
, int *iteration
)
170 http_init_uncompress_buffer(data_len
, buf_ptr
, buf_len
, iteration
);
172 Z
->next_in
= (Bytef
*) data
;
173 Z
->avail_in
= data_len
;
174 Z
->avail_out
= *buf_len
;
175 Z
->next_out
= *buf_ptr
;
178 inline size_t http_finish_buffer(size_t buf_len
, char **buf_ptr
)
180 (*buf_ptr
)[buf_len
] = '\0';
184 inline size_t http_finish_gzencode_buffer(z_stream
*Z
, const char *data
, size_t data_len
, char **buf_ptr
)
189 crc
= crc32(0L, Z_NULL
, 0);
190 crc
= crc32(crc
, (const Bytef
*) data
, data_len
);
192 trailer
= *buf_ptr
+ sizeof(http_encoding_gzip_header
) + Z
->total_out
;
195 trailer
[0] = (char) (crc
& 0xFF);
196 trailer
[1] = (char) ((crc
>> 8) & 0xFF);
197 trailer
[2] = (char) ((crc
>> 16) & 0xFF);
198 trailer
[3] = (char) ((crc
>> 24) & 0xFF);
199 trailer
[4] = (char) ((Z
->total_in
) & 0xFF);
200 trailer
[5] = (char) ((Z
->total_in
>> 8) & 0xFF);
201 trailer
[6] = (char) ((Z
->total_in
>> 16) & 0xFF);
202 trailer
[7] = (char) ((Z
->total_in
>> 24) & 0xFF);
204 return http_finish_buffer(Z
->total_out
+ sizeof(http_encoding_gzip_header
) + 8, buf_ptr
);
207 inline STATUS
http_verify_gzencode_buffer(const char *data
, size_t data_len
, const char **encoded
, size_t *encoded_len
, int error_level TSRMLS_DC
)
209 size_t offset
= sizeof(http_encoding_gzip_header
);
211 if (data_len
< offset
) {
212 goto really_bad_gzip_header
;
215 if (data
[0] != (const char) 0x1F || data
[1] != (const char) 0x8B) {
216 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));
220 if (data
[2] != (const char) Z_DEFLATED
) {
221 http_error_ex(error_level TSRMLS_CC
, HTTP_E_ENCODING
, "Unrecognized compression format (%d)", (int) (data
[2] & 0xFF));
222 /* still try to decode */
224 if ((data
[3] & 0x4) == 0x4) {
225 if (data_len
< offset
+ 2) {
226 goto really_bad_gzip_header
;
228 /* there are extra fields, the length follows the common header as 2 bytes LSB */
229 offset
+= (unsigned) ((data
[offset
] & 0xFF));
231 offset
+= (unsigned) ((data
[offset
] & 0xFF) << 8);
234 if ((data
[3] & 0x8) == 0x8) {
235 if (data_len
<= offset
) {
236 goto really_bad_gzip_header
;
238 /* there's a file name */
239 offset
+= strlen(&data
[offset
]) + 1 /*NUL*/;
241 if ((data
[3] & 0x10) == 0x10) {
242 if (data_len
<= offset
) {
243 goto really_bad_gzip_header
;
245 /* there's a comment */
246 offset
+= strlen(&data
[offset
]) + 1 /* NUL */;
248 if ((data
[3] & 0x2) == 0x2) {
249 /* there's a CRC16 of the header */
251 if (data_len
<= offset
) {
252 goto really_bad_gzip_header
;
254 unsigned long crc
, cmp
;
256 cmp
= (unsigned) ((data
[offset
-2] & 0xFF));
257 cmp
+= (unsigned) ((data
[offset
-1] & 0xFF) << 8);
259 crc
= crc32(0L, Z_NULL
, 0);
260 crc
= crc32(crc
, data
, sizeof(http_encoding_gzip_header
));
262 if (cmp
!= (crc
& 0xFFFF)) {
263 http_error_ex(error_level TSRMLS_CC
, HTTP_E_ENCODING
, "GZIP headers CRC checksums so not match (%lu, %lu)", cmp
, crc
& 0xFFFF);
269 if (data_len
< offset
+ 8) {
270 http_error(error_level TSRMLS_CC
, HTTP_E_ENCODING
, "Missing or truncated GZIP footer");
275 *encoded
= data
+ offset
;
278 *encoded_len
= data_len
- offset
- 8 /* size of the assumed GZIP footer */;
283 really_bad_gzip_header
:
284 http_error(error_level TSRMLS_CC
, HTTP_E_ENCODING
, "Missing or truncated GZIP header");
288 inline STATUS
http_verify_gzdecode_buffer(const char *data
, size_t data_len
, const char *decoded
, size_t decoded_len
, int error_level TSRMLS_DC
)
290 STATUS status
= SUCCESS
;
291 unsigned long len
, cmp
, crc
;
293 crc
= crc32(0L, Z_NULL
, 0);
294 crc
= crc32(crc
, (const Bytef
*) decoded
, decoded_len
);
296 cmp
= (unsigned) ((data
[data_len
-8] & 0xFF));
297 cmp
+= (unsigned) ((data
[data_len
-7] & 0xFF) << 8);
298 cmp
+= (unsigned) ((data
[data_len
-6] & 0xFF) << 16);
299 cmp
+= (unsigned) ((data
[data_len
-5] & 0xFF) << 24);
300 len
= (unsigned) ((data
[data_len
-4] & 0xFF));
301 len
+= (unsigned) ((data
[data_len
-3] & 0xFF) << 8);
302 len
+= (unsigned) ((data
[data_len
-2] & 0xFF) << 16);
303 len
+= (unsigned) ((data
[data_len
-1] & 0xFF) << 24);
306 http_error_ex(error_level TSRMLS_CC
, HTTP_E_ENCODING
, "Could not verify data integrity: CRC checksums do not match (%lu, %lu)", cmp
, crc
);
309 if (len
!= decoded_len
) {
310 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
);
316 PHP_HTTP_API STATUS
_http_encode(http_encoding_type type
, int level
, const char *data
, size_t data_len
, char **encoded
, size_t *encoded_len TSRMLS_DC
)
318 STATUS status
= SUCCESS
;
322 case HTTP_ENCODING_ANY
:
323 case HTTP_ENCODING_GZIP
:
324 status
= http_encoding_gzencode(level
, data
, data_len
, encoded
, encoded_len
);
327 case HTTP_ENCODING_DEFLATE
:
328 status
= http_encoding_deflate(level
, data
, data_len
, encoded
, encoded_len
);
331 case HTTP_ENCODING_COMPRESS
:
332 status
= http_encoding_compress(level
, data
, data_len
, encoded
, encoded_len
);
335 case HTTP_ENCODING_NONE
:
337 *encoded
= estrndup(data
, data_len
);
338 *encoded_len
= data_len
;
345 PHP_HTTP_API STATUS
_http_decode(http_encoding_type type
, const char *data
, size_t data_len
, char **decoded
, size_t *decoded_len TSRMLS_DC
)
347 STATUS status
= SUCCESS
;
351 case HTTP_ENCODING_ANY
:
352 if ( SUCCESS
!= http_encoding_gzdecode(data
, data_len
, decoded
, decoded_len
) &&
353 SUCCESS
!= http_encoding_inflate(data
, data_len
, decoded
, decoded_len
) &&
354 SUCCESS
!= http_encoding_uncompress(data
, data_len
, decoded
, decoded_len
)) {
359 case HTTP_ENCODING_GZIP
:
360 status
= http_encoding_gzdecode(data
, data_len
, decoded
, decoded_len
);
363 case HTTP_ENCODING_DEFLATE
:
364 status
= http_encoding_inflate(data
, data_len
, decoded
, decoded_len
);
367 case HTTP_ENCODING_COMPRESS
:
368 status
= http_encoding_uncompress(data
, data_len
, decoded
, decoded_len
);
371 case HTTP_ENCODING_NONE
:
373 *decoded
= estrndup(data
, data_len
);
374 *decoded_len
= data_len
;
381 PHP_HTTP_API STATUS
_http_encoding_gzencode(int level
, const char *data
, size_t data_len
, char **encoded
, size_t *encoded_len TSRMLS_DC
)
384 STATUS status
= Z_OK
;
386 http_init_gzencode_buffer(&Z
, data
, data_len
, encoded
);
388 if ( (Z_OK
== (status
= deflateInit2(&Z
, level
, Z_DEFLATED
, -MAX_WBITS
, MAX_MEM_LEVEL
, Z_DEFAULT_STRATEGY
))) &&
389 (Z_STREAM_END
== (status
= deflate(&Z
, Z_FINISH
))) &&
390 (Z_OK
== (status
= deflateEnd(&Z
)))) {
391 *encoded_len
= http_finish_gzencode_buffer(&Z
, data
, data_len
, encoded
);
396 http_error_ex(HE_WARNING
, HTTP_E_ENCODING
, "Could not gzencode data: %s", zError(status
));
400 PHP_HTTP_API STATUS
_http_encoding_deflate(int level
, const char *data
, size_t data_len
, char **encoded
, size_t *encoded_len TSRMLS_DC
)
403 STATUS status
= Z_OK
;
405 http_init_deflate_buffer(&Z
, data
, data_len
, encoded
);
407 if ( (Z_OK
== (status
= deflateInit2(&Z
, level
, Z_DEFLATED
, -MAX_WBITS
, MAX_MEM_LEVEL
, Z_DEFAULT_STRATEGY
))) &&
408 (Z_STREAM_END
== (status
= deflate(&Z
, Z_FINISH
))) &&
409 (Z_OK
== (status
= deflateEnd(&Z
)))) {
410 *encoded_len
= http_finish_buffer(Z
.total_out
, encoded
);
415 http_error_ex(HE_WARNING
, HTTP_E_ENCODING
, "Could not deflate data: %s", zError(status
));
419 PHP_HTTP_API STATUS
_http_encoding_compress(int level
, const char *data
, size_t data_len
, char **encoded
, size_t *encoded_len TSRMLS_DC
)
423 *encoded
= emalloc(*encoded_len
= HTTP_ENCODING_BUFLEN(data_len
));
425 if (Z_OK
== (status
= compress2(*encoded
, encoded_len
, data
, data_len
, level
))) {
426 http_finish_buffer(*encoded_len
, encoded
);
431 http_error_ex(HE_WARNING
, HTTP_E_ENCODING
, "Could not compress data: %s", zError(status
));
435 PHP_HTTP_API STATUS
_http_encoding_gzdecode(const char *data
, size_t data_len
, char **decoded
, size_t *decoded_len TSRMLS_DC
)
440 if ( (SUCCESS
== http_verify_gzencode_buffer(data
, data_len
, &encoded
, &encoded_len
, HE_NOTICE
)) &&
441 (SUCCESS
== http_encoding_inflate(encoded
, encoded_len
, decoded
, decoded_len
))) {
442 http_verify_gzdecode_buffer(data
, data_len
, *decoded
, *decoded_len
, HE_NOTICE
);
449 PHP_HTTP_API STATUS
_http_encoding_inflate(const char *data
, size_t data_len
, char **decoded
, size_t *decoded_len TSRMLS_DC
)
456 http_init_inflate_buffer(&Z
, data
, data_len
, decoded
, decoded_len
, &max
);
457 if (Z_OK
== (status
= inflateInit2(&Z
, -MAX_WBITS
))) {
458 if (Z_STREAM_END
== (status
= inflate(&Z
, Z_FINISH
))) {
459 if (Z_OK
== (status
= inflateEnd(&Z
))) {
460 *decoded_len
= http_finish_buffer(Z
.total_out
, decoded
);
465 } while (++max
< HTTP_ENCODING_MAXTRY
&& status
== Z_BUF_ERROR
);
468 http_error_ex(HE_WARNING
, HTTP_E_ENCODING
, "Could not inflate data: %s", zError(status
));
472 PHP_HTTP_API STATUS
_http_encoding_uncompress(const char *data
, size_t data_len
, char **decoded
, size_t *decoded_len TSRMLS_DC
)
478 http_init_uncompress_buffer(data_len
, decoded
, decoded_len
, &max
);
479 if (Z_OK
== (status
= uncompress(*decoded
, decoded_len
, data
, data_len
))) {
480 http_finish_buffer(*decoded_len
, decoded
);
483 } while (++max
< HTTP_ENCODING_MAXTRY
&& status
== Z_BUF_ERROR
);
486 http_error_ex(HE_WARNING
, HTTP_E_ENCODING
, "Could not uncompress data: %s", zError(status
));
490 #define HTTP_ENCODING_STREAM_ERROR(status, tofree) \
492 if (tofree) efree(tofree); \
493 http_error_ex(HE_WARNING, HTTP_E_ENCODING, "GZIP stream error: %s", zError(status)); \
497 PHP_HTTP_API STATUS
_http_encoding_stream_init(http_encoding_stream
*s
, int gzip
, int level
, char **encoded
, size_t *encoded_len TSRMLS_DC
)
501 memset(s
, 0, sizeof(http_encoding_stream
));
502 if (Z_OK
!= (status
= deflateInit2(&s
->Z
, level
, Z_DEFLATED
, -MAX_WBITS
, MAX_MEM_LEVEL
, Z_DEFAULT_STRATEGY
))) {
503 HTTP_ENCODING_STREAM_ERROR(status
, NULL
);
506 if (s
->gzip
= gzip
) {
507 s
->crc
= crc32(0L, Z_NULL
, 0);
508 *encoded_len
= sizeof(http_encoding_gzip_header
);
509 *encoded
= emalloc(*encoded_len
);
510 memcpy(*encoded
, http_encoding_gzip_header
, *encoded_len
);
519 PHP_HTTP_API STATUS
_http_encoding_stream_update(http_encoding_stream
*s
, const char *data
, size_t data_len
, char **encoded
, size_t *encoded_len TSRMLS_DC
)
523 *encoded_len
= HTTP_ENCODING_BUFLEN(data_len
);
524 *encoded
= emalloc(*encoded_len
);
526 s
->Z
.next_in
= (Bytef
*) data
;
527 s
->Z
.avail_in
= data_len
;
528 s
->Z
.next_out
= *encoded
;
529 s
->Z
.avail_out
= *encoded_len
;
531 status
= deflate(&s
->Z
, Z_SYNC_FLUSH
);
533 if (Z_OK
!= status
&& Z_STREAM_END
!= status
) {
534 HTTP_ENCODING_STREAM_ERROR(status
, *encoded
);
536 *encoded_len
-= s
->Z
.avail_out
;
539 s
->crc
= crc32(s
->crc
, (const Bytef
*) data
, data_len
);
545 PHP_HTTP_API STATUS
_http_encoding_stream_finish(http_encoding_stream
*s
, char **encoded
, size_t *encoded_len TSRMLS_DC
)
550 *encoded
= emalloc(*encoded_len
);
552 s
->Z
.next_out
= *encoded
;
553 s
->Z
.avail_out
= *encoded_len
;
555 if (Z_STREAM_END
!= (status
= deflate(&s
->Z
, Z_FINISH
)) || Z_OK
!= (status
= deflateEnd(&s
->Z
))) {
556 HTTP_ENCODING_STREAM_ERROR(status
, *encoded
);
559 *encoded_len
-= s
->Z
.avail_out
;
561 if (s
->Z
.avail_out
< 8) {
562 *encoded
= erealloc(*encoded
, *encoded_len
+ 8);
564 (*encoded
)[(*encoded_len
)++] = (char) (s
->crc
& 0xFF);
565 (*encoded
)[(*encoded_len
)++] = (char) ((s
->crc
>> 8) & 0xFF);
566 (*encoded
)[(*encoded_len
)++] = (char) ((s
->crc
>> 16) & 0xFF);
567 (*encoded
)[(*encoded_len
)++] = (char) ((s
->crc
>> 24) & 0xFF);
568 (*encoded
)[(*encoded_len
)++] = (char) ((s
->Z
.total_in
) & 0xFF);
569 (*encoded
)[(*encoded_len
)++] = (char) ((s
->Z
.total_in
>> 8) & 0xFF);
570 (*encoded
)[(*encoded_len
)++] = (char) ((s
->Z
.total_in
>> 16) & 0xFF);
571 (*encoded
)[(*encoded_len
)++] = (char) ((s
->Z
.total_in
>> 24) & 0xFF);
577 #endif /* HTTP_HAVE_ZLIB */
579 PHP_HTTP_API zend_bool
_http_encoding_response_start(size_t content_length TSRMLS_DC
)
581 if ( php_ob_handler_used("ob_gzhandler" TSRMLS_CC
) ||
582 php_ob_handler_used("zlib output compression" TSRMLS_CC
)) {
583 HTTP_G(send
).gzip_encoding
= 0;
585 if (!HTTP_G(send
).gzip_encoding
) {
586 /* emit a content-length header */
587 if (content_length
) {
588 char cl_header_str
[128];
589 size_t cl_header_len
;
590 cl_header_len
= snprintf(cl_header_str
, lenof(cl_header_str
), "Content-Length: %lu", (unsigned long) content_length
);
591 http_send_header_string_ex(cl_header_str
, cl_header_len
, 1);
594 #ifndef HTTP_HAVE_ZLIB
595 php_start_ob_buffer_named("ob_gzhandler", 0, 0 TSRMLS_CC
);
600 INIT_PZVAL(&zsupported
);
601 array_init(&zsupported
);
602 add_next_index_stringl(&zsupported
, "gzip", lenof("gzip"), 1);
603 add_next_index_stringl(&zsupported
, "deflate", lenof("deflate"), 1);
605 if (selected
= http_negotiate_encoding(&zsupported
)) {
607 char *encoding
= NULL
;
610 if (HASH_KEY_IS_STRING
== zend_hash_get_current_key(selected
, &encoding
, &idx
, 0) && encoding
) {
611 if (!strcmp(encoding
, "gzip")) {
612 if (SUCCESS
== (hs
= http_send_header_string("Content-Encoding: gzip"))) {
613 HTTP_G(send
).gzip_encoding
= HTTP_ENCODING_GZIP
;
615 } else if (!strcmp(encoding
, "deflate")) {
616 if (SUCCESS
== (hs
= http_send_header_string("Content-Encoding: deflate"))) {
617 HTTP_G(send
).gzip_encoding
= HTTP_ENCODING_DEFLATE
;
621 http_send_header_string("Vary: Accept-Encoding");
623 HTTP_G(send
).gzip_encoding
= 0;
627 zend_hash_destroy(selected
);
628 FREE_HASHTABLE(selected
);
631 zval_dtor(&zsupported
);
644 * vim600: noet sw=4 ts=4 fdm=marker
645 * vim<600: noet sw=4 ts=4