PHPAPI int php_stream_filter_append_ex(php_stream_filter_chain *chain, php_stream_filter *filter) { php_stream *stream = chain->stream; filter->prev = chain->tail; filter->next = NULL; if (chain->tail) { chain->tail->next = filter; } else { chain->head = filter; } chain->tail = filter; filter->chain = chain; if (&(stream->readfilters) == chain && (stream->writepos - stream->readpos) > 0) { /* Let's going ahead and wind anything in the buffer through this filter */ php_stream_bucket_brigade brig_in = { NULL, NULL }, brig_out = { NULL, NULL }; php_stream_bucket_brigade *brig_inp = &brig_in, *brig_outp = &brig_out; php_stream_filter_status_t status; php_stream_bucket *bucket; size_t consumed = 0; bucket = php_stream_bucket_new(stream, (char*) stream->readbuf + stream->readpos, stream->writepos - stream->readpos, 0, 0); php_stream_bucket_append(brig_inp, bucket); status = filter->fops->filter(stream, filter, brig_inp, brig_outp, &consumed, PSFS_FLAG_NORMAL); if (stream->readpos + consumed > (uint)stream->writepos) { /* No behaving filter should cause this. */ status = PSFS_ERR_FATAL; } switch (status) { case PSFS_ERR_FATAL: while (brig_in.head) { bucket = brig_in.head; php_stream_bucket_unlink(bucket); php_stream_bucket_delref(bucket); } while (brig_out.head) { bucket = brig_out.head; php_stream_bucket_unlink(bucket); php_stream_bucket_delref(bucket); } php_error_docref(NULL, E_WARNING, "Filter failed to process pre-buffered data"); return FAILURE; case PSFS_FEED_ME: /* We don't actually need data yet, leave this filter in a feed me state until data is needed. Reset stream's internal read buffer since the filter is "holding" it. */ stream->readpos = 0; stream->writepos = 0; break; case PSFS_PASS_ON: /* If any data is consumed, we cannot rely upon the existing read buffer, as the filtered data must replace the existing data, so invalidate the cache */ /* note that changes here should be reflected in main/streams/streams.c::php_stream_fill_read_buffer */ stream->writepos = 0; stream->readpos = 0; while (brig_outp->head) { bucket = brig_outp->head; /* Grow buffer to hold this bucket if need be. TODO: See warning in main/stream/streams.c::php_stream_fill_read_buffer */ if (stream->readbuflen - stream->writepos < bucket->buflen) { stream->readbuflen += bucket->buflen; stream->readbuf = perealloc(stream->readbuf, stream->readbuflen, stream->is_persistent); } memcpy(stream->readbuf + stream->writepos, bucket->buf, bucket->buflen); stream->writepos += bucket->buflen; php_stream_bucket_unlink(bucket); php_stream_bucket_delref(bucket); } break; } } return SUCCESS; }
static php_stream_filter_status_t php_zlib_inflate_filter( php_stream *stream, php_stream_filter *thisfilter, php_stream_bucket_brigade *buckets_in, php_stream_bucket_brigade *buckets_out, size_t *bytes_consumed, int flags TSRMLS_DC) { php_zlib_filter_data *data; php_stream_bucket *bucket; size_t consumed = 0, original_out, original_in; int status; php_stream_filter_status_t exit_status = PSFS_FEED_ME; z_stream *streamp; if (!thisfilter || !thisfilter->abstract) { /* Should never happen */ return PSFS_ERR_FATAL; } data = (php_zlib_filter_data *)(thisfilter->abstract); streamp = &(data->strm); original_in = data->strm.total_in; original_out = data->strm.total_out; while (buckets_in->head) { size_t bin = 0, desired; bucket = php_stream_bucket_make_writeable(buckets_in->head TSRMLS_CC); while (bin < bucket->buflen) { desired = bucket->buflen - bin; if (desired > data->inbuf_len) { desired = data->inbuf_len; } memcpy(data->strm.next_in, bucket->buf + bin, desired); data->strm.avail_in = desired; status = inflate(&(data->strm), flags & PSFS_FLAG_FLUSH_CLOSE ? Z_FINISH : Z_SYNC_FLUSH); if (status != Z_OK && status != Z_STREAM_END) { /* Something bad happened */ php_stream_bucket_delref(bucket TSRMLS_CC); return PSFS_ERR_FATAL; } desired -= data->strm.avail_in; /* desired becomes what we consumed this round through */ data->strm.next_in = data->inbuf; data->strm.avail_in = 0; consumed += desired; bin += desired; if (!desired) { flags |= PSFS_FLAG_FLUSH_CLOSE; break; } if (data->strm.avail_out < data->outbuf_len) { php_stream_bucket *out_bucket; size_t bucketlen = data->outbuf_len - data->strm.avail_out; out_bucket = php_stream_bucket_new(stream, estrndup(data->outbuf, bucketlen), bucketlen, 1, 0 TSRMLS_CC); php_stream_bucket_append(buckets_out, out_bucket TSRMLS_CC); data->strm.avail_out = data->outbuf_len; data->strm.next_out = data->outbuf; exit_status = PSFS_PASS_ON; } } php_stream_bucket_delref(bucket TSRMLS_CC); } if (flags & PSFS_FLAG_FLUSH_CLOSE) { /* Spit it out! */ status = Z_OK; while (status == Z_OK) { status = inflate(&(data->strm), Z_FINISH); if (data->strm.avail_out < data->outbuf_len) { size_t bucketlen = data->outbuf_len - data->strm.avail_out; bucket = php_stream_bucket_new(stream, estrndup(data->outbuf, bucketlen), bucketlen, 1, 0 TSRMLS_CC); php_stream_bucket_append(buckets_out, bucket TSRMLS_CC); data->strm.avail_out = data->outbuf_len; data->strm.next_out = data->outbuf; exit_status = PSFS_PASS_ON; } } } if (bytes_consumed) { *bytes_consumed = consumed; } return exit_status; }
static php_stream_filter_status_t php_bz2_decompress_filter( php_stream *stream, php_stream_filter *thisfilter, php_stream_bucket_brigade *buckets_in, php_stream_bucket_brigade *buckets_out, size_t *bytes_consumed, int flags TSRMLS_DC) { php_bz2_filter_data *data; php_stream_bucket *bucket; size_t consumed = 0; int status; php_stream_filter_status_t exit_status = PSFS_FEED_ME; bz_stream *streamp; if (!Z_PTR(thisfilter->abstract)) { /* Should never happen */ return PSFS_ERR_FATAL; } data = (php_bz2_filter_data *)Z_PTR(thisfilter->abstract); streamp = &(data->strm); while (buckets_in->head) { size_t bin = 0, desired; bucket = php_stream_bucket_make_writeable(buckets_in->head TSRMLS_CC); while (bin < bucket->buflen) { if (data->status == PHP_BZ2_UNITIALIZED) { status = BZ2_bzDecompressInit(streamp, 0, data->small_footprint); if (BZ_OK != status) { php_stream_bucket_delref(bucket TSRMLS_CC); return PSFS_ERR_FATAL; } data->status = PHP_BZ2_RUNNING; } if (data->status != PHP_BZ2_RUNNING) { consumed += bucket->buflen; break; } desired = bucket->buflen - bin; if (desired > data->inbuf_len) { desired = data->inbuf_len; } memcpy(data->strm.next_in, bucket->buf + bin, desired); data->strm.avail_in = desired; status = BZ2_bzDecompress(&(data->strm)); if (status == BZ_STREAM_END) { BZ2_bzDecompressEnd(&(data->strm)); if (data->expect_concatenated) { data->status = PHP_BZ2_UNITIALIZED; } else { data->status = PHP_BZ2_FINISHED; } } else if (status != BZ_OK) { /* Something bad happened */ php_stream_bucket_delref(bucket TSRMLS_CC); return PSFS_ERR_FATAL; } desired -= data->strm.avail_in; /* desired becomes what we consumed this round through */ data->strm.next_in = data->inbuf; data->strm.avail_in = 0; consumed += desired; bin += desired; if (data->strm.avail_out < data->outbuf_len) { php_stream_bucket *out_bucket; size_t bucketlen = data->outbuf_len - data->strm.avail_out; out_bucket = php_stream_bucket_new(stream, estrndup(data->outbuf, bucketlen), bucketlen, 1, 0 TSRMLS_CC); php_stream_bucket_append(buckets_out, out_bucket TSRMLS_CC); data->strm.avail_out = data->outbuf_len; data->strm.next_out = data->outbuf; exit_status = PSFS_PASS_ON; } else if (status == BZ_STREAM_END && data->strm.avail_out >= data->outbuf_len) { /* no more data to decompress, and nothing was spat out */ php_stream_bucket_delref(bucket TSRMLS_CC); return PSFS_PASS_ON; } } php_stream_bucket_delref(bucket TSRMLS_CC); } if ((data->status == PHP_BZ2_RUNNING) && (flags & PSFS_FLAG_FLUSH_CLOSE)) { /* Spit it out! */ status = BZ_OK; while (status == BZ_OK) { status = BZ2_bzDecompress(&(data->strm)); if (data->strm.avail_out < data->outbuf_len) { size_t bucketlen = data->outbuf_len - data->strm.avail_out; bucket = php_stream_bucket_new(stream, estrndup(data->outbuf, bucketlen), bucketlen, 1, 0 TSRMLS_CC); php_stream_bucket_append(buckets_out, bucket TSRMLS_CC); data->strm.avail_out = data->outbuf_len; data->strm.next_out = data->outbuf; exit_status = PSFS_PASS_ON; } else if (status == BZ_OK) { break; } } } if (bytes_consumed) { *bytes_consumed = consumed; } return exit_status; }
static php_stream_filter_status_t php_zlib_inflate_filter( php_stream *stream, php_stream_filter *thisfilter, php_stream_bucket_brigade *buckets_in, php_stream_bucket_brigade *buckets_out, size_t *bytes_consumed, int flags ) { php_zlib_filter_data *data; php_stream_bucket *bucket; size_t consumed = 0; int status; php_stream_filter_status_t exit_status = PSFS_FEED_ME; if (!thisfilter || !Z_PTR(thisfilter->abstract)) { /* Should never happen */ return PSFS_ERR_FATAL; } data = (php_zlib_filter_data *)(Z_PTR(thisfilter->abstract)); while (buckets_in->head) { size_t bin = 0, desired; bucket = php_stream_bucket_make_writeable(buckets_in->head); while (bin < (unsigned int) bucket->buflen) { if (data->finished) { consumed += bucket->buflen; break; } desired = bucket->buflen - bin; if (desired > data->inbuf_len) { desired = data->inbuf_len; } memcpy(data->strm.next_in, bucket->buf + bin, desired); data->strm.avail_in = desired; status = inflate(&(data->strm), flags & PSFS_FLAG_FLUSH_CLOSE ? Z_FINISH : Z_SYNC_FLUSH); if (status == Z_STREAM_END) { inflateEnd(&(data->strm)); data->finished = '\1'; } else if (status != Z_OK) { /* Something bad happened */ php_stream_bucket_delref(bucket); /* reset these because despite the error the filter may be used again */ data->strm.next_in = data->inbuf; data->strm.avail_in = 0; return PSFS_ERR_FATAL; } desired -= data->strm.avail_in; /* desired becomes what we consumed this round through */ data->strm.next_in = data->inbuf; data->strm.avail_in = 0; bin += desired; if (data->strm.avail_out < data->outbuf_len) { php_stream_bucket *out_bucket; size_t bucketlen = data->outbuf_len - data->strm.avail_out; out_bucket = php_stream_bucket_new( stream, estrndup((char *) data->outbuf, bucketlen), bucketlen, 1, 0); php_stream_bucket_append(buckets_out, out_bucket); data->strm.avail_out = data->outbuf_len; data->strm.next_out = data->outbuf; exit_status = PSFS_PASS_ON; } else if (status == Z_STREAM_END && data->strm.avail_out >= data->outbuf_len) { /* no more data to decompress, and nothing was spat out */ php_stream_bucket_delref(bucket); return PSFS_PASS_ON; } } consumed += bucket->buflen; php_stream_bucket_delref(bucket); } if (!data->finished && flags & PSFS_FLAG_FLUSH_CLOSE) { /* Spit it out! */ status = Z_OK; while (status == Z_OK) { status = inflate(&(data->strm), Z_FINISH); if (data->strm.avail_out < data->outbuf_len) { size_t bucketlen = data->outbuf_len - data->strm.avail_out; bucket = php_stream_bucket_new( stream, estrndup((char *) data->outbuf, bucketlen), bucketlen, 1, 0); php_stream_bucket_append(buckets_out, bucket); data->strm.avail_out = data->outbuf_len; data->strm.next_out = data->outbuf; exit_status = PSFS_PASS_ON; } } } if (bytes_consumed) { *bytes_consumed = consumed; } return exit_status; }
static php_stream_filter_status_t php_zlib_deflate_filter( php_stream *stream, php_stream_filter *thisfilter, php_stream_bucket_brigade *buckets_in, php_stream_bucket_brigade *buckets_out, size_t *bytes_consumed, int flags ) { php_zlib_filter_data *data; php_stream_bucket *bucket; size_t consumed = 0; int status; php_stream_filter_status_t exit_status = PSFS_FEED_ME; if (!thisfilter || !Z_PTR(thisfilter->abstract)) { /* Should never happen */ return PSFS_ERR_FATAL; } data = (php_zlib_filter_data *)(Z_PTR(thisfilter->abstract)); while (buckets_in->head) { size_t bin = 0, desired; bucket = buckets_in->head; bucket = php_stream_bucket_make_writeable(bucket); while (bin < (unsigned int) bucket->buflen) { desired = bucket->buflen - bin; if (desired > data->inbuf_len) { desired = data->inbuf_len; } memcpy(data->strm.next_in, bucket->buf + bin, desired); data->strm.avail_in = desired; status = deflate(&(data->strm), flags & PSFS_FLAG_FLUSH_CLOSE ? Z_FULL_FLUSH : (flags & PSFS_FLAG_FLUSH_INC ? Z_SYNC_FLUSH : Z_NO_FLUSH)); if (status != Z_OK) { /* Something bad happened */ php_stream_bucket_delref(bucket); return PSFS_ERR_FATAL; } desired -= data->strm.avail_in; /* desired becomes what we consumed this round through */ data->strm.next_in = data->inbuf; data->strm.avail_in = 0; bin += desired; if (data->strm.avail_out < data->outbuf_len) { php_stream_bucket *out_bucket; size_t bucketlen = data->outbuf_len - data->strm.avail_out; out_bucket = php_stream_bucket_new( stream, estrndup((char *) data->outbuf, bucketlen), bucketlen, 1, 0); php_stream_bucket_append(buckets_out, out_bucket); data->strm.avail_out = data->outbuf_len; data->strm.next_out = data->outbuf; exit_status = PSFS_PASS_ON; } } consumed += bucket->buflen; php_stream_bucket_delref(bucket); } if (flags & PSFS_FLAG_FLUSH_CLOSE) { /* Spit it out! */ status = Z_OK; while (status == Z_OK) { status = deflate(&(data->strm), Z_FINISH); if (data->strm.avail_out < data->outbuf_len) { size_t bucketlen = data->outbuf_len - data->strm.avail_out; bucket = php_stream_bucket_new( stream, estrndup((char *) data->outbuf, bucketlen), bucketlen, 1, 0); php_stream_bucket_append(buckets_out, bucket); data->strm.avail_out = data->outbuf_len; data->strm.next_out = data->outbuf; exit_status = PSFS_PASS_ON; } } } if (bytes_consumed) { *bytes_consumed = consumed; } return exit_status; }
PHPAPI int _php_stream_filter_flush(php_stream_filter *filter, int finish) { php_stream_bucket_brigade brig_a = { NULL, NULL }, brig_b = { NULL, NULL }, *inp = &brig_a, *outp = &brig_b, *brig_temp; php_stream_bucket *bucket; php_stream_filter_chain *chain; php_stream_filter *current; php_stream *stream; size_t flushed_size = 0; long flags = (finish ? PSFS_FLAG_FLUSH_CLOSE : PSFS_FLAG_FLUSH_INC); if (!filter->chain || !filter->chain->stream) { /* Filter is not attached to a chain, or chain is somehow not part of a stream */ return FAILURE; } chain = filter->chain; stream = chain->stream; for(current = filter; current; current = current->next) { php_stream_filter_status_t status; status = filter->fops->filter(stream, current, inp, outp, NULL, flags); if (status == PSFS_FEED_ME) { /* We've flushed the data far enough */ return SUCCESS; } if (status == PSFS_ERR_FATAL) { return FAILURE; } /* Otherwise we have data available to PASS_ON Swap the brigades and continue */ brig_temp = inp; inp = outp; outp = brig_temp; outp->head = NULL; outp->tail = NULL; flags = PSFS_FLAG_NORMAL; } /* Last filter returned data via PSFS_PASS_ON Do something with it */ for(bucket = inp->head; bucket; bucket = bucket->next) { flushed_size += bucket->buflen; } if (flushed_size == 0) { /* Unlikely, but possible */ return SUCCESS; } if (chain == &(stream->readfilters)) { /* Dump any newly flushed data to the read buffer */ if (stream->readpos > 0) { /* Back the buffer up */ memcpy(stream->readbuf, stream->readbuf + stream->readpos, stream->writepos - stream->readpos); stream->readpos = 0; stream->writepos -= stream->readpos; } if (flushed_size > (stream->readbuflen - stream->writepos)) { /* Grow the buffer */ stream->readbuf = perealloc(stream->readbuf, stream->writepos + flushed_size + stream->chunk_size, stream->is_persistent); } while ((bucket = inp->head)) { memcpy(stream->readbuf + stream->writepos, bucket->buf, bucket->buflen); stream->writepos += bucket->buflen; php_stream_bucket_unlink(bucket); php_stream_bucket_delref(bucket); } } else if (chain == &(stream->writefilters)) { /* Send flushed data to the stream */ while ((bucket = inp->head)) { stream->ops->write(stream, bucket->buf, bucket->buflen); php_stream_bucket_unlink(bucket); php_stream_bucket_delref(bucket); } } return SUCCESS; }
static php_stream_filter_status_t php_mcrypt_filter( php_stream *stream, php_stream_filter *thisfilter, php_stream_bucket_brigade *buckets_in, php_stream_bucket_brigade *buckets_out, size_t *bytes_consumed, int flags) { php_mcrypt_filter_data *data; php_stream_bucket *bucket; size_t consumed = 0; php_stream_filter_status_t exit_status = PSFS_FEED_ME; if (!thisfilter || !Z_PTR(thisfilter->abstract)) { /* Should never happen */ return PSFS_ERR_FATAL; } data = (php_mcrypt_filter_data *)(Z_PTR(thisfilter->abstract)); while(buckets_in->head) { bucket = buckets_in->head; consumed += bucket->buflen; if (data->blocksize) { /* Blockmode cipher */ char *outchunk; int chunklen = (int)(bucket->buflen + data->block_used), n; php_stream_bucket *newbucket; outchunk = pemalloc(chunklen, data->persistent); if (data->block_used) { memcpy(outchunk, data->block_buffer, data->block_used); } memcpy(outchunk + data->block_used, bucket->buf, bucket->buflen); for(n=0; (n + data->blocksize) <= chunklen; n += data->blocksize) { if (data->encrypt) { mcrypt_generic(data->module, outchunk + n, data->blocksize); } else { mdecrypt_generic(data->module, outchunk + n, data->blocksize); } } data->block_used = chunklen - n; memcpy(data->block_buffer, outchunk + n, data->block_used); newbucket = php_stream_bucket_new(stream, outchunk, n, 1, data->persistent); php_stream_bucket_append(buckets_out, newbucket); exit_status = PSFS_PASS_ON; php_stream_bucket_unlink(bucket); php_stream_bucket_delref(bucket); } else { /* Stream cipher */ php_stream_bucket_make_writeable(bucket); if (data->encrypt) { mcrypt_generic(data->module, bucket->buf, (int)bucket->buflen); } else { mdecrypt_generic(data->module, bucket->buf, (int)bucket->buflen); } php_stream_bucket_append(buckets_out, bucket); exit_status = PSFS_PASS_ON; } } if ((flags & PSFS_FLAG_FLUSH_CLOSE) && data->blocksize && data->block_used) { php_stream_bucket *newbucket; memset(data->block_buffer + data->block_used, 0, data->blocksize - data->block_used); if (data->encrypt) { mcrypt_generic(data->module, data->block_buffer, data->blocksize); } else { mdecrypt_generic(data->module, data->block_buffer, data->blocksize); } newbucket = php_stream_bucket_new(stream, data->block_buffer, data->blocksize, 0, data->persistent); php_stream_bucket_append(buckets_out, newbucket); exit_status = PSFS_PASS_ON; } if (bytes_consumed) { *bytes_consumed = consumed; } return exit_status; }
php_stream_filter_status_t userfilter_filter( php_stream *stream, php_stream_filter *thisfilter, php_stream_bucket_brigade *buckets_in, php_stream_bucket_brigade *buckets_out, size_t *bytes_consumed, int flags ) { int ret = PSFS_ERR_FATAL; zval *obj = &thisfilter->abstract; zval func_name; zval retval; zval args[4]; zval zpropname; int call_result; /* the userfilter object probably doesn't exist anymore */ if (CG(unclean_shutdown)) { return ret; } if (!zend_hash_str_exists(Z_OBJPROP_P(obj), "stream", sizeof("stream")-1)) { zval tmp; /* Give the userfilter class a hook back to the stream */ php_stream_to_zval(stream, &tmp); zval_copy_ctor(&tmp); add_property_zval(obj, "stream", &tmp); /* add_property_zval increments the refcount which is unwanted here */ zval_ptr_dtor(&tmp); } ZVAL_STRINGL(&func_name, "filter", sizeof("filter")-1); /* Setup calling arguments */ ZVAL_RES(&args[0], zend_register_resource(buckets_in, le_bucket_brigade)); ZVAL_RES(&args[1], zend_register_resource(buckets_out, le_bucket_brigade)); if (bytes_consumed) { ZVAL_LONG(&args[2], *bytes_consumed); } else { ZVAL_NULL(&args[2]); } ZVAL_BOOL(&args[3], flags & PSFS_FLAG_FLUSH_CLOSE); call_result = call_user_function_ex(NULL, obj, &func_name, &retval, 4, args, 0, NULL); zval_ptr_dtor(&func_name); if (call_result == SUCCESS && Z_TYPE(retval) != IS_UNDEF) { convert_to_long(&retval); ret = (int)Z_LVAL(retval); } else if (call_result == FAILURE) { php_error_docref(NULL, E_WARNING, "failed to call filter function"); } if (bytes_consumed) { *bytes_consumed = Z_LVAL_P(&args[2]); } if (buckets_in->head) { php_stream_bucket *bucket = buckets_in->head; php_error_docref(NULL, E_WARNING, "Unprocessed filter buckets remaining on input brigade"); while ((bucket = buckets_in->head)) { /* Remove unconsumed buckets from the brigade */ php_stream_bucket_unlink(bucket); php_stream_bucket_delref(bucket); } } if (ret != PSFS_PASS_ON) { php_stream_bucket *bucket = buckets_out->head; while (bucket != NULL) { php_stream_bucket_unlink(bucket); php_stream_bucket_delref(bucket); bucket = buckets_out->head; } } /* filter resources are cleaned up by the stream destructor, * keeping a reference to the stream resource here would prevent it * from being destroyed properly */ ZVAL_STRINGL(&zpropname, "stream", sizeof("stream")-1); Z_OBJ_HANDLER_P(obj, unset_property)(obj, &zpropname, NULL); zval_ptr_dtor(&zpropname); zval_ptr_dtor(&args[3]); zval_ptr_dtor(&args[2]); zval_ptr_dtor(&args[1]); zval_ptr_dtor(&args[0]); return ret; }