Return to zlib_filter.c CVS log | Up to [ELWIX - Embedded LightWeight unIX -] / embedaddon / php / ext / zlib |
1.1 ! misho 1: /* ! 2: +----------------------------------------------------------------------+ ! 3: | PHP Version 5 | ! 4: +----------------------------------------------------------------------+ ! 5: | Copyright (c) 1997-2012 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: zlib_filter.c 321634 2012-01-01 13:15:04Z felipe $ */ ! 20: ! 21: #include "php.h" ! 22: #include "php_zlib.h" ! 23: ! 24: /* {{{ data structure */ ! 25: ! 26: /* Passed as opaque in malloc callbacks */ ! 27: typedef 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: ! 41: static 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: ! 46: static 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: ! 54: static 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: ! 157: static 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: ! 170: static 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: ! 179: static 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: ! 267: static 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: ! 278: static 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: ! 288: static 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; ! 403: factory_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: ! 435: php_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: */