Annotation of embedaddon/php/ext/zlib/zlib_filter.c, revision 1.1.1.1

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:  */

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>