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