1/* 2 +----------------------------------------------------------------------+ 3 | PHP Version 5 | 4 +----------------------------------------------------------------------+ 5 | Copyright (c) 1997-2013 The PHP Group | 6 +----------------------------------------------------------------------+ 7 | This source file is subject to version 3.01 of the PHP license, | 8 | that is bundled with this package in the file LICENSE, and is | 9 | available through the world-wide-web at the following url: | 10 | http://www.php.net/license/3_01.txt | 11 | If you did not receive a copy of the PHP license and are unable to | 12 | obtain it through the world-wide-web, please send a note to | 13 | license@php.net so we can mail you a copy immediately. | 14 +----------------------------------------------------------------------+ 15 | Authors: Sara Golemon (pollita@php.net) | 16 +----------------------------------------------------------------------+ 17*/ 18 19/* $Id$ */ 20 21#include "php.h" 22#include "php_zlib.h" 23 24/* {{{ data structure */ 25 26/* Passed as opaque in malloc callbacks */ 27typedef struct _php_zlib_filter_data { 28 int persistent; 29 z_stream strm; 30 char *inbuf; 31 size_t inbuf_len; 32 char *outbuf; 33 size_t outbuf_len; 34 zend_bool finished; 35} php_zlib_filter_data; 36 37/* }}} */ 38 39/* {{{ Memory management wrappers */ 40 41static voidpf php_zlib_alloc(voidpf opaque, uInt items, uInt size) 42{ 43 return (voidpf)safe_pemalloc(items, size, 0, ((php_zlib_filter_data*)opaque)->persistent); 44} 45 46static void php_zlib_free(voidpf opaque, voidpf address) 47{ 48 pefree((void*)address, ((php_zlib_filter_data*)opaque)->persistent); 49} 50/* }}} */ 51 52/* {{{ zlib.inflate filter implementation */ 53 54static php_stream_filter_status_t php_zlib_inflate_filter( 55 php_stream *stream, 56 php_stream_filter *thisfilter, 57 php_stream_bucket_brigade *buckets_in, 58 php_stream_bucket_brigade *buckets_out, 59 size_t *bytes_consumed, 60 int flags 61 TSRMLS_DC) 62{ 63 php_zlib_filter_data *data; 64 php_stream_bucket *bucket; 65 size_t consumed = 0, original_out, original_in; 66 int status; 67 php_stream_filter_status_t exit_status = PSFS_FEED_ME; 68 z_stream *streamp; 69 70 if (!thisfilter || !thisfilter->abstract) { 71 /* Should never happen */ 72 return PSFS_ERR_FATAL; 73 } 74 75 data = (php_zlib_filter_data *)(thisfilter->abstract); 76 streamp = &(data->strm); 77 original_in = data->strm.total_in; 78 original_out = data->strm.total_out; 79 80 while (buckets_in->head) { 81 size_t bin = 0, desired; 82 83 bucket = buckets_in->head; 84 85 bucket = php_stream_bucket_make_writeable(buckets_in->head TSRMLS_CC); 86 87 while (bin < (unsigned int) bucket->buflen) { 88 89 if (data->finished) { 90 consumed += bucket->buflen; 91 break; 92 } 93 94 desired = bucket->buflen - bin; 95 if (desired > data->inbuf_len) { 96 desired = data->inbuf_len; 97 } 98 memcpy(data->strm.next_in, bucket->buf + bin, desired); 99 data->strm.avail_in = desired; 100 101 status = inflate(&(data->strm), flags & PSFS_FLAG_FLUSH_CLOSE ? Z_FINISH : Z_SYNC_FLUSH); 102 if (status == Z_STREAM_END) { 103 inflateEnd(&(data->strm)); 104 data->finished = '\1'; 105 } else if (status != Z_OK) { 106 /* Something bad happened */ 107 php_stream_bucket_delref(bucket TSRMLS_CC); 108 /* reset these because despite the error the filter may be used again */ 109 data->strm.next_in = data->inbuf; 110 data->strm.avail_in = 0; 111 return PSFS_ERR_FATAL; 112 } 113 desired -= data->strm.avail_in; /* desired becomes what we consumed this round through */ 114 data->strm.next_in = data->inbuf; 115 data->strm.avail_in = 0; 116 bin += desired; 117 118 if (data->strm.avail_out < data->outbuf_len) { 119 php_stream_bucket *out_bucket; 120 size_t bucketlen = data->outbuf_len - data->strm.avail_out; 121 out_bucket = php_stream_bucket_new(stream, estrndup(data->outbuf, bucketlen), bucketlen, 1, 0 TSRMLS_CC); 122 php_stream_bucket_append(buckets_out, out_bucket TSRMLS_CC); 123 data->strm.avail_out = data->outbuf_len; 124 data->strm.next_out = data->outbuf; 125 exit_status = PSFS_PASS_ON; 126 } else if (status == Z_STREAM_END && data->strm.avail_out >= data->outbuf_len) { 127 /* no more data to decompress, and nothing was spat out */ 128 php_stream_bucket_delref(bucket TSRMLS_CC); 129 return PSFS_PASS_ON; 130 } 131 132 } 133 consumed += bucket->buflen; 134 php_stream_bucket_delref(bucket TSRMLS_CC); 135 } 136 137 if (!data->finished && flags & PSFS_FLAG_FLUSH_CLOSE) { 138 /* Spit it out! */ 139 status = Z_OK; 140 while (status == Z_OK) { 141 status = inflate(&(data->strm), Z_FINISH); 142 if (data->strm.avail_out < data->outbuf_len) { 143 size_t bucketlen = data->outbuf_len - data->strm.avail_out; 144 145 bucket = php_stream_bucket_new(stream, estrndup(data->outbuf, bucketlen), bucketlen, 1, 0 TSRMLS_CC); 146 php_stream_bucket_append(buckets_out, bucket TSRMLS_CC); 147 data->strm.avail_out = data->outbuf_len; 148 data->strm.next_out = data->outbuf; 149 exit_status = PSFS_PASS_ON; 150 } 151 } 152 } 153 154 if (bytes_consumed) { 155 *bytes_consumed = consumed; 156 } 157 158 return exit_status; 159} 160 161static void php_zlib_inflate_dtor(php_stream_filter *thisfilter TSRMLS_DC) 162{ 163 if (thisfilter && thisfilter->abstract) { 164 php_zlib_filter_data *data = thisfilter->abstract; 165 if (!data->finished) { 166 inflateEnd(&(data->strm)); 167 } 168 pefree(data->inbuf, data->persistent); 169 pefree(data->outbuf, data->persistent); 170 pefree(data, data->persistent); 171 } 172} 173 174static php_stream_filter_ops php_zlib_inflate_ops = { 175 php_zlib_inflate_filter, 176 php_zlib_inflate_dtor, 177 "zlib.inflate" 178}; 179/* }}} */ 180 181/* {{{ zlib.inflate filter implementation */ 182 183static php_stream_filter_status_t php_zlib_deflate_filter( 184 php_stream *stream, 185 php_stream_filter *thisfilter, 186 php_stream_bucket_brigade *buckets_in, 187 php_stream_bucket_brigade *buckets_out, 188 size_t *bytes_consumed, 189 int flags 190 TSRMLS_DC) 191{ 192 php_zlib_filter_data *data; 193 php_stream_bucket *bucket; 194 size_t consumed = 0, original_out, original_in; 195 int status; 196 php_stream_filter_status_t exit_status = PSFS_FEED_ME; 197 z_stream *streamp; 198 199 if (!thisfilter || !thisfilter->abstract) { 200 /* Should never happen */ 201 return PSFS_ERR_FATAL; 202 } 203 204 data = (php_zlib_filter_data *)(thisfilter->abstract); 205 streamp = &(data->strm); 206 original_in = data->strm.total_in; 207 original_out = data->strm.total_out; 208 209 while (buckets_in->head) { 210 size_t bin = 0, desired; 211 212 bucket = buckets_in->head; 213 214 bucket = php_stream_bucket_make_writeable(bucket TSRMLS_CC); 215 216 while (bin < (unsigned int) bucket->buflen) { 217 desired = bucket->buflen - bin; 218 if (desired > data->inbuf_len) { 219 desired = data->inbuf_len; 220 } 221 memcpy(data->strm.next_in, bucket->buf + bin, desired); 222 data->strm.avail_in = desired; 223 224 status = deflate(&(data->strm), flags & PSFS_FLAG_FLUSH_CLOSE ? Z_FULL_FLUSH : (flags & PSFS_FLAG_FLUSH_INC ? Z_SYNC_FLUSH : Z_NO_FLUSH)); 225 if (status != Z_OK) { 226 /* Something bad happened */ 227 php_stream_bucket_delref(bucket TSRMLS_CC); 228 return PSFS_ERR_FATAL; 229 } 230 desired -= data->strm.avail_in; /* desired becomes what we consumed this round through */ 231 data->strm.next_in = data->inbuf; 232 data->strm.avail_in = 0; 233 bin += desired; 234 235 if (data->strm.avail_out < data->outbuf_len) { 236 php_stream_bucket *out_bucket; 237 size_t bucketlen = data->outbuf_len - data->strm.avail_out; 238 239 out_bucket = php_stream_bucket_new(stream, estrndup(data->outbuf, bucketlen), bucketlen, 1, 0 TSRMLS_CC); 240 php_stream_bucket_append(buckets_out, out_bucket TSRMLS_CC); 241 data->strm.avail_out = data->outbuf_len; 242 data->strm.next_out = data->outbuf; 243 exit_status = PSFS_PASS_ON; 244 } 245 } 246 consumed += bucket->buflen; 247 php_stream_bucket_delref(bucket TSRMLS_CC); 248 } 249 250 if (flags & PSFS_FLAG_FLUSH_CLOSE) { 251 /* Spit it out! */ 252 status = Z_OK; 253 while (status == Z_OK) { 254 status = deflate(&(data->strm), Z_FINISH); 255 if (data->strm.avail_out < data->outbuf_len) { 256 size_t bucketlen = data->outbuf_len - data->strm.avail_out; 257 258 bucket = php_stream_bucket_new(stream, estrndup(data->outbuf, bucketlen), bucketlen, 1, 0 TSRMLS_CC); 259 php_stream_bucket_append(buckets_out, bucket TSRMLS_CC); 260 data->strm.avail_out = data->outbuf_len; 261 data->strm.next_out = data->outbuf; 262 exit_status = PSFS_PASS_ON; 263 } 264 } 265 } 266 267 if (bytes_consumed) { 268 *bytes_consumed = consumed; 269 } 270 271 return exit_status; 272} 273 274static void php_zlib_deflate_dtor(php_stream_filter *thisfilter TSRMLS_DC) 275{ 276 if (thisfilter && thisfilter->abstract) { 277 php_zlib_filter_data *data = thisfilter->abstract; 278 deflateEnd(&(data->strm)); 279 pefree(data->inbuf, data->persistent); 280 pefree(data->outbuf, data->persistent); 281 pefree(data, data->persistent); 282 } 283} 284 285static php_stream_filter_ops php_zlib_deflate_ops = { 286 php_zlib_deflate_filter, 287 php_zlib_deflate_dtor, 288 "zlib.deflate" 289}; 290 291/* }}} */ 292 293/* {{{ zlib.* common factory */ 294 295static php_stream_filter *php_zlib_filter_create(const char *filtername, zval *filterparams, int persistent TSRMLS_DC) 296{ 297 php_stream_filter_ops *fops = NULL; 298 php_zlib_filter_data *data; 299 int status; 300 301 /* Create this filter */ 302 data = pecalloc(1, sizeof(php_zlib_filter_data), persistent); 303 if (!data) { 304 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed allocating %zd bytes", sizeof(php_zlib_filter_data)); 305 return NULL; 306 } 307 308 /* Circular reference */ 309 data->strm.opaque = (voidpf) data; 310 311 data->strm.zalloc = (alloc_func) php_zlib_alloc; 312 data->strm.zfree = (free_func) php_zlib_free; 313 data->strm.avail_out = data->outbuf_len = data->inbuf_len = 2048; 314 data->strm.next_in = data->inbuf = (Bytef *) pemalloc(data->inbuf_len, persistent); 315 if (!data->inbuf) { 316 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed allocating %zd bytes", data->inbuf_len); 317 pefree(data, persistent); 318 return NULL; 319 } 320 data->strm.avail_in = 0; 321 data->strm.next_out = data->outbuf = (Bytef *) pemalloc(data->outbuf_len, persistent); 322 if (!data->outbuf) { 323 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed allocating %zd bytes", data->outbuf_len); 324 pefree(data->inbuf, persistent); 325 pefree(data, persistent); 326 return NULL; 327 } 328 329 data->strm.data_type = Z_ASCII; 330 331 if (strcasecmp(filtername, "zlib.inflate") == 0) { 332 int windowBits = -MAX_WBITS; 333 334 if (filterparams) { 335 zval **tmpzval; 336 337 if ((Z_TYPE_P(filterparams) == IS_ARRAY || Z_TYPE_P(filterparams) == IS_OBJECT) && 338 zend_hash_find(HASH_OF(filterparams), "window", sizeof("window"), (void **) &tmpzval) == SUCCESS) { 339 zval tmp; 340 341 /* log-2 base of history window (9 - 15) */ 342 tmp = **tmpzval; 343 zval_copy_ctor(&tmp); 344 convert_to_long(&tmp); 345 if (Z_LVAL(tmp) < -MAX_WBITS || Z_LVAL(tmp) > MAX_WBITS + 32) { 346 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid parameter give for window size. (%ld)", Z_LVAL(tmp)); 347 } else { 348 windowBits = Z_LVAL(tmp); 349 } 350 } 351 } 352 353 /* RFC 1951 Inflate */ 354 data->finished = '\0'; 355 status = inflateInit2(&(data->strm), windowBits); 356 fops = &php_zlib_inflate_ops; 357 } else if (strcasecmp(filtername, "zlib.deflate") == 0) { 358 /* RFC 1951 Deflate */ 359 int level = Z_DEFAULT_COMPRESSION; 360 int windowBits = -MAX_WBITS; 361 int memLevel = MAX_MEM_LEVEL; 362 363 364 if (filterparams) { 365 zval **tmpzval, tmp; 366 367 /* filterparams can either be a scalar value to indicate compression level (shortcut method) 368 Or can be a hash containing one or more of 'window', 'memory', and/or 'level' members. */ 369 370 switch (Z_TYPE_P(filterparams)) { 371 case IS_ARRAY: 372 case IS_OBJECT: 373 if (zend_hash_find(HASH_OF(filterparams), "memory", sizeof("memory"), (void**) &tmpzval) == SUCCESS) { 374 tmp = **tmpzval; 375 zval_copy_ctor(&tmp); 376 convert_to_long(&tmp); 377 378 /* Memory Level (1 - 9) */ 379 if (Z_LVAL(tmp) < 1 || Z_LVAL(tmp) > MAX_MEM_LEVEL) { 380 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid parameter give for memory level. (%ld)", Z_LVAL(tmp)); 381 } else { 382 memLevel = Z_LVAL(tmp); 383 } 384 } 385 386 if (zend_hash_find(HASH_OF(filterparams), "window", sizeof("window"), (void**) &tmpzval) == SUCCESS) { 387 tmp = **tmpzval; 388 zval_copy_ctor(&tmp); 389 convert_to_long(&tmp); 390 391 /* log-2 base of history window (9 - 15) */ 392 if (Z_LVAL(tmp) < -MAX_WBITS || Z_LVAL(tmp) > MAX_WBITS + 16) { 393 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid parameter give for window size. (%ld)", Z_LVAL(tmp)); 394 } else { 395 windowBits = Z_LVAL(tmp); 396 } 397 } 398 399 if (zend_hash_find(HASH_OF(filterparams), "level", sizeof("level"), (void**) &tmpzval) == SUCCESS) { 400 tmp = **tmpzval; 401 402 /* Psuedo pass through to catch level validating code */ 403 goto factory_setlevel; 404 } 405 break; 406 case IS_STRING: 407 case IS_DOUBLE: 408 case IS_LONG: 409 tmp = *filterparams; 410factory_setlevel: 411 zval_copy_ctor(&tmp); 412 convert_to_long(&tmp); 413 414 /* Set compression level within reason (-1 == default, 0 == none, 1-9 == least to most compression */ 415 if (Z_LVAL(tmp) < -1 || Z_LVAL(tmp) > 9) { 416 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid compression level specified. (%ld)", Z_LVAL(tmp)); 417 } else { 418 level = Z_LVAL(tmp); 419 } 420 break; 421 default: 422 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid filter parameter, ignored"); 423 } 424 } 425 status = deflateInit2(&(data->strm), level, Z_DEFLATED, windowBits, memLevel, 0); 426 fops = &php_zlib_deflate_ops; 427 } else { 428 status = Z_DATA_ERROR; 429 } 430 431 if (status != Z_OK) { 432 /* Unspecified (probably strm) error, let stream-filter error do its own whining */ 433 pefree(data->strm.next_in, persistent); 434 pefree(data->strm.next_out, persistent); 435 pefree(data, persistent); 436 return NULL; 437 } 438 439 return php_stream_filter_alloc(fops, data, persistent); 440} 441 442php_stream_filter_factory php_zlib_filter_factory = { 443 php_zlib_filter_create 444}; 445/* }}} */ 446 447/* 448 * Local variables: 449 * tab-width: 4 450 * c-basic-offset: 4 451 * End: 452 * vim600: sw=4 ts=4 fdm=marker 453 * vim<600: sw=4 ts=4 454 */ 455