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"
27 ZEND_EXTERN_MODULE_GLOBALS(http
);
29 /* {{{ char *http_encoding_dechunk(char *, size_t, char **, size_t *) */
30 PHP_HTTP_API
const char *_http_encoding_dechunk(const char *encoded
, size_t encoded_len
, char **decoded
, size_t *decoded_len TSRMLS_DC
)
37 *decoded
= ecalloc(1, encoded_len
);
41 while ((rest
= encoded
+ encoded_len
- e_ptr
) > 0) {
43 int EOL_len
= 0, eol_mismatch
= 0;
46 chunk_len
= strtol(e_ptr
, &n_ptr
, 16);
49 * - we could not read in chunk size
50 * - we got a negative chunk size
51 * - chunk size is greater then remaining size
52 * - chunk size is not followed by (CR)LF|NUL
54 if ( (n_ptr
== e_ptr
) || (chunk_len
< 0) || (chunk_len
> rest
) ||
55 (*n_ptr
&& (eol_mismatch
= (n_ptr
!= http_locate_eol(e_ptr
, &EOL_len
))))) {
56 /* don't fail on apperently not encoded data */
57 if (e_ptr
== encoded
) {
58 memcpy(*decoded
, encoded
, encoded_len
);
59 *decoded_len
= encoded_len
;
60 return encoded
+ encoded_len
;
65 http_error_ex(HE_WARNING
, HTTP_E_ENCODING
, "Invalid character (expected 0x0D 0x0A; got: 0x%X 0x%X)", *n_ptr
, *(n_ptr
+ 1));
67 http_error_ex(HE_WARNING
, HTTP_E_ENCODING
, "Invalid character (expected 0x0A; got: 0x%X)", *n_ptr
);
70 char *error
= estrndup(n_ptr
, strcspn(n_ptr
, "\r\n "));
71 http_error_ex(HE_WARNING
, HTTP_E_ENCODING
, "Invalid chunk size: '%s' at pos %d", error
, n_ptr
- encoded
);
85 memcpy(d_ptr
, e_ptr
+= EOL_len
, chunk_len
);
87 e_ptr
+= chunk_len
+ EOL_len
;
88 *decoded_len
+= chunk_len
;
98 /* max count of uncompress trials, alloc_size <<= 2 for each try */
99 #define HTTP_GZMAXTRY 10
101 #define HTTP_GZSAFPAD 10
102 /* add 1% extra space in case we need to encode widely differing (binary) data */
103 #define HTTP_GZBUFLEN(l) (l + (l / 100) + HTTP_GZSAFPAD)
105 static const char http_gzencode_header
[] = {
106 (const char) 0x1f, // fixed value
107 (const char) 0x8b, // fixed value
108 (const char) Z_DEFLATED
, // compression algorithm
109 (const char) 0, // none of the possible flags defined by the GZIP "RFC"
110 (const char) 0, // no MTIME available (4 bytes)
111 (const char) 0, // =*=
112 (const char) 0, // =*=
113 (const char) 0, // =*=
114 (const char) 0, // two possible flag values for 9 compression levels? o_O
115 (const char) 0x03 // assume *nix OS
118 inline void http_init_gzencode_buffer(z_stream
*Z
, const char *data
, size_t data_len
, char **buf_ptr
)
124 Z
->next_in
= (Bytef
*) data
;
125 Z
->avail_in
= data_len
;
126 Z
->avail_out
= HTTP_GZBUFLEN(data_len
) + HTTP_GZSAFPAD
- 1;
128 *buf_ptr
= emalloc(HTTP_GZBUFLEN(data_len
) + sizeof(http_gzencode_header
) + HTTP_GZSAFPAD
);
129 memcpy(*buf_ptr
, http_gzencode_header
, sizeof(http_gzencode_header
));
131 Z
->next_out
= *buf_ptr
+ sizeof(http_gzencode_header
);
134 inline void http_init_deflate_buffer(z_stream
*Z
, const char *data
, size_t data_len
, char **buf_ptr
)
140 Z
->data_type
= Z_UNKNOWN
;
141 Z
->next_in
= (Bytef
*) data
;
142 Z
->avail_in
= data_len
;
143 Z
->avail_out
= HTTP_GZBUFLEN(data_len
) - 1;
144 Z
->next_out
= emalloc(HTTP_GZBUFLEN(data_len
));
146 *buf_ptr
= Z
->next_out
;
149 inline void http_init_uncompress_buffer(size_t data_len
, char **buf_ptr
, size_t *buf_len
, int iteration
)
152 *buf_len
= data_len
* 2;
153 *buf_ptr
= emalloc(*buf_len
+ 1);
156 *buf_ptr
= erealloc(*buf_ptr
, *buf_len
+ 1);
160 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
)
165 http_init_uncompress_buffer(data_len
, buf_ptr
, buf_len
, iteration
);
167 Z
->next_in
= (Bytef
*) data
;
168 Z
->avail_in
= data_len
;
169 Z
->avail_out
= *buf_len
;
170 Z
->next_out
= *buf_ptr
;
173 inline size_t http_finish_buffer(size_t buf_len
, char **buf_ptr
)
175 (*buf_ptr
)[buf_len
] = '\0';
179 inline size_t http_finish_gzencode_buffer(z_stream
*Z
, const char *data
, size_t data_len
, char **buf_ptr
)
184 crc
= crc32(0L, Z_NULL
, 0);
185 crc
= crc32(crc
, (const Bytef
*) data
, data_len
);
187 trailer
= *buf_ptr
+ sizeof(http_gzencode_header
) + Z
->total_out
;
190 trailer
[0] = (char) (crc
& 0xFF);
191 trailer
[1] = (char) ((crc
>> 8) & 0xFF);
192 trailer
[2] = (char) ((crc
>> 16) & 0xFF);
193 trailer
[3] = (char) ((crc
>> 24) & 0xFF);
194 trailer
[4] = (char) ((Z
->total_in
) & 0xFF);
195 trailer
[5] = (char) ((Z
->total_in
>> 8) & 0xFF);
196 trailer
[6] = (char) ((Z
->total_in
>> 16) & 0xFF);
197 trailer
[7] = (char) ((Z
->total_in
>> 24) & 0xFF);
199 return http_finish_buffer(Z
->total_out
+ sizeof(http_gzencode_header
) + 8, buf_ptr
);
202 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
)
204 size_t offset
= sizeof(http_gzencode_header
);
206 if (data_len
< offset
) {
207 goto really_bad_gzip_header
;
210 if (data
[0] != (const char) 0x1F || data
[1] != (const char) 0x8B) {
211 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));
215 if (data
[2] != (const char) Z_DEFLATED
) {
216 http_error_ex(error_level TSRMLS_CC
, HTTP_E_ENCODING
, "Unrecognized compression format (%d)", (int) (data
[2] & 0xFF));
217 /* still try to decode */
219 if ((data
[3] & 0x4) == 0x4) {
220 if (data_len
< offset
+ 2) {
221 goto really_bad_gzip_header
;
223 /* there are extra fields, the length follows the common header as 2 bytes LSB */
224 offset
+= (unsigned) ((data
[offset
] & 0xFF));
226 offset
+= (unsigned) ((data
[offset
] & 0xFF) << 8);
229 if ((data
[3] & 0x8) == 0x8) {
230 if (data_len
<= offset
) {
231 goto really_bad_gzip_header
;
233 /* there's a file name */
234 offset
+= strlen(&data
[offset
]) + 1 /*NUL*/;
236 if ((data
[3] & 0x10) == 0x10) {
237 if (data_len
<= offset
) {
238 goto really_bad_gzip_header
;
240 /* there's a comment */
241 offset
+= strlen(&data
[offset
]) + 1 /* NUL */;
243 if ((data
[3] & 0x2) == 0x2) {
244 /* there's a CRC16 of the header */
246 if (data_len
<= offset
) {
247 goto really_bad_gzip_header
;
249 unsigned long crc
, cmp
;
251 cmp
= (unsigned) ((data
[offset
-2] & 0xFF));
252 cmp
+= (unsigned) ((data
[offset
-1] & 0xFF) << 8);
254 crc
= crc32(0L, Z_NULL
, 0);
255 crc
= crc32(crc
, data
, sizeof(http_gzencode_header
));
257 if (cmp
!= (crc
& 0xFFFF)) {
258 http_error_ex(error_level TSRMLS_CC
, HTTP_E_ENCODING
, "GZIP headers CRC checksums so not match (%lu, %lu)", cmp
, crc
& 0xFFFF);
264 if (data_len
< offset
+ 8) {
265 http_error(error_level TSRMLS_CC
, HTTP_E_ENCODING
, "Missing or truncated GZIP footer");
270 *encoded
= data
+ offset
;
273 *encoded_len
= data_len
- offset
- 8 /* size of the assumed GZIP footer */;
278 really_bad_gzip_header
:
279 http_error(error_level TSRMLS_CC
, HTTP_E_ENCODING
, "Missing or truncated GZIP header");
283 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
)
285 STATUS status
= SUCCESS
;
286 unsigned long len
, cmp
, crc
;
288 crc
= crc32(0L, Z_NULL
, 0);
289 crc
= crc32(crc
, (const Bytef
*) decoded
, decoded_len
);
291 cmp
= (unsigned) ((data
[data_len
-8] & 0xFF));
292 cmp
+= (unsigned) ((data
[data_len
-7] & 0xFF) << 8);
293 cmp
+= (unsigned) ((data
[data_len
-6] & 0xFF) << 16);
294 cmp
+= (unsigned) ((data
[data_len
-5] & 0xFF) << 24);
295 len
= (unsigned) ((data
[data_len
-4] & 0xFF));
296 len
+= (unsigned) ((data
[data_len
-3] & 0xFF) << 8);
297 len
+= (unsigned) ((data
[data_len
-2] & 0xFF) << 16);
298 len
+= (unsigned) ((data
[data_len
-1] & 0xFF) << 24);
301 http_error_ex(error_level TSRMLS_CC
, HTTP_E_ENCODING
, "Could not verify data integrity: CRC checksums do not match (%lu, %lu)", cmp
, crc
);
304 if (len
!= decoded_len
) {
305 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
);
311 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
)
313 STATUS status
= SUCCESS
;
317 case HTTP_ENCODING_ANY
:
318 case HTTP_ENCODING_GZIP
:
319 status
= http_encoding_gzencode(level
, data
, data_len
, encoded
, encoded_len
);
322 case HTTP_ENCODING_DEFLATE
:
323 status
= http_encoding_deflate(level
, data
, data_len
, encoded
, encoded_len
);
326 case HTTP_ENCODING_COMPRESS
:
327 status
= http_encoding_compress(level
, data
, data_len
, encoded
, encoded_len
);
330 case HTTP_ENCODING_NONE
:
332 *encoded
= estrndup(data
, data_len
);
333 *encoded_len
= data_len
;
340 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
)
342 STATUS status
= SUCCESS
;
346 case HTTP_ENCODING_ANY
:
347 if ( SUCCESS
!= http_encoding_gzdecode(data
, data_len
, decoded
, decoded_len
) &&
348 SUCCESS
!= http_encoding_inflate(data
, data_len
, decoded
, decoded_len
) &&
349 SUCCESS
!= http_encoding_uncompress(data
, data_len
, decoded
, decoded_len
)) {
354 case HTTP_ENCODING_GZIP
:
355 status
= http_encoding_gzdecode(data
, data_len
, decoded
, decoded_len
);
358 case HTTP_ENCODING_DEFLATE
:
359 status
= http_encoding_inflate(data
, data_len
, decoded
, decoded_len
);
362 case HTTP_ENCODING_COMPRESS
:
363 status
= http_encoding_uncompress(data
, data_len
, decoded
, decoded_len
);
366 case HTTP_ENCODING_NONE
:
368 *decoded
= estrndup(data
, data_len
);
369 *decoded_len
= data_len
;
376 PHP_HTTP_API STATUS
_http_encoding_gzencode(int level
, const char *data
, size_t data_len
, char **encoded
, size_t *encoded_len TSRMLS_DC
)
379 STATUS status
= Z_OK
;
381 http_init_gzencode_buffer(&Z
, data
, data_len
, encoded
);
383 if ( (Z_OK
== (status
= deflateInit2(&Z
, level
, Z_DEFLATED
, -MAX_WBITS
, MAX_MEM_LEVEL
, Z_DEFAULT_STRATEGY
))) &&
384 (Z_STREAM_END
== (status
= deflate(&Z
, Z_FINISH
))) &&
385 (Z_OK
== (status
= deflateEnd(&Z
)))) {
386 *encoded_len
= http_finish_gzencode_buffer(&Z
, data
, data_len
, encoded
);
391 http_error_ex(HE_WARNING
, HTTP_E_ENCODING
, "Could not gzencode data: %s", zError(status
));
395 PHP_HTTP_API STATUS
_http_encoding_deflate(int level
, const char *data
, size_t data_len
, char **encoded
, size_t *encoded_len TSRMLS_DC
)
398 STATUS status
= Z_OK
;
400 http_init_deflate_buffer(&Z
, data
, data_len
, encoded
);
402 if ( (Z_OK
== (status
= deflateInit2(&Z
, level
, Z_DEFLATED
, -MAX_WBITS
, MAX_MEM_LEVEL
, Z_DEFAULT_STRATEGY
))) &&
403 (Z_STREAM_END
== (status
= deflate(&Z
, Z_FINISH
))) &&
404 (Z_OK
== (status
= deflateEnd(&Z
)))) {
405 *encoded_len
= http_finish_buffer(Z
.total_out
, encoded
);
410 http_error_ex(HE_WARNING
, HTTP_E_ENCODING
, "Could not deflate data: %s", zError(status
));
414 PHP_HTTP_API STATUS
_http_encoding_compress(int level
, const char *data
, size_t data_len
, char **encoded
, size_t *encoded_len TSRMLS_DC
)
418 *encoded
= emalloc(*encoded_len
= HTTP_GZBUFLEN(data_len
));
420 if (Z_OK
== (status
= compress2(*encoded
, encoded_len
, data
, data_len
, level
))) {
421 http_finish_buffer(*encoded_len
, encoded
);
426 http_error_ex(HE_WARNING
, HTTP_E_ENCODING
, "Could not compress data: %s", zError(status
));
430 PHP_HTTP_API STATUS
_http_encoding_gzdecode(const char *data
, size_t data_len
, char **decoded
, size_t *decoded_len TSRMLS_DC
)
435 if ( (SUCCESS
== http_verify_gzencode_buffer(data
, data_len
, &encoded
, &encoded_len
, HE_NOTICE
)) &&
436 (SUCCESS
== http_encoding_inflate(encoded
, encoded_len
, decoded
, decoded_len
))) {
437 http_verify_gzdecode_buffer(data
, data_len
, *decoded
, *decoded_len
, HE_NOTICE
);
444 PHP_HTTP_API STATUS
_http_encoding_inflate(const char *data
, size_t data_len
, char **decoded
, size_t *decoded_len TSRMLS_DC
)
451 http_init_inflate_buffer(&Z
, data
, data_len
, decoded
, decoded_len
, max
++);
452 if (Z_OK
== (status
= inflateInit2(&Z
, -MAX_WBITS
))) {
453 if (Z_STREAM_END
== (status
= inflate(&Z
, Z_FINISH
))) {
454 if (Z_OK
== (status
= inflateEnd(&Z
))) {
455 *decoded_len
= http_finish_buffer(Z
.total_out
, decoded
);
460 } while (max
< HTTP_GZMAXTRY
&& status
== Z_BUF_ERROR
);
463 http_error_ex(HE_WARNING
, HTTP_E_ENCODING
, "Could not inflate data: %s", zError(status
));
467 PHP_HTTP_API STATUS
_http_encoding_uncompress(const char *data
, size_t data_len
, char **decoded
, size_t *decoded_len TSRMLS_DC
)
473 http_init_uncompress_buffer(data_len
, decoded
, decoded_len
, max
++);
474 if (Z_OK
== (status
= uncompress(*decoded
, decoded_len
, data
, data_len
))) {
475 http_finish_buffer(*decoded_len
, decoded
);
478 } while (max
< HTTP_GZMAXTRY
&& status
== Z_BUF_ERROR
);
481 http_error_ex(HE_WARNING
, HTTP_E_ENCODING
, "Could not uncompress data: %s", zError(status
));
485 #endif /* HTTP_HAVE_ZLIB */
492 * vim600: noet sw=4 ts=4 fdm=marker
493 * vim<600: noet sw=4 ts=4