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