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

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: 
1.1.1.2 ! misho      19: /* $Id$ */
1.1       misho      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: 
1.1.1.2 ! misho      83:                bucket = buckets_in->head;
        !            84: 
1.1       misho      85:                bucket = php_stream_bucket_make_writeable(buckets_in->head TSRMLS_CC);
1.1.1.2 ! misho      86: 
        !            87:                while (bin < (unsigned int) bucket->buflen) {
1.1       misho      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:                        }
1.1.1.2 ! misho     131: 
1.1       misho     132:                }
1.1.1.2 ! misho     133:                consumed += bucket->buflen;
1.1       misho     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: 
                    161: static 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: 
                    174: static 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: 
                    183: static 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: 
1.1.1.2 ! misho     212:                bucket = buckets_in->head;
        !           213: 
        !           214:                bucket = php_stream_bucket_make_writeable(bucket TSRMLS_CC);
1.1       misho     215: 
1.1.1.2 ! misho     216:                while (bin < (unsigned int) bucket->buflen) {
1.1       misho     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:                }
1.1.1.2 ! misho     246:                consumed += bucket->buflen;
1.1       misho     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:        }
1.1.1.2 ! misho     270: 
1.1       misho     271:        return exit_status;
                    272: }
                    273: 
                    274: static 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: 
                    285: static 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: 
                    295: static 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) {
1.1.1.2 ! misho     304:                php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed allocating %zd bytes", sizeof(php_zlib_filter_data));
1.1       misho     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) {
1.1.1.2 ! misho     316:                php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed allocating %zd bytes", data->inbuf_len);
1.1       misho     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) {
1.1.1.2 ! misho     323:                php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed allocating %zd bytes", data->outbuf_len);
1.1       misho     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;
                    410: factory_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:
1.1.1.2 ! misho     422:                                        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid filter parameter, ignored");
1.1       misho     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: 
                    442: php_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:  */

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