1: /*
2: +----------------------------------------------------------------------+
3: | PHP Version 5 |
4: +----------------------------------------------------------------------+
5: | Copyright (c) 1997-2014 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: bz2_filter.c,v 1.1.1.4 2014/06/15 20:03:41 misho Exp $ */
20:
21: #ifdef HAVE_CONFIG_H
22: #include "config.h"
23: #endif
24:
25: #include "php.h"
26: #include "php_bz2.h"
27:
28: /* {{{ data structure */
29:
30: enum strm_status {
31: PHP_BZ2_UNITIALIZED,
32: PHP_BZ2_RUNNING,
33: PHP_BZ2_FINISHED
34: };
35:
36: typedef struct _php_bz2_filter_data {
37: int persistent;
38: bz_stream strm;
39: char *inbuf;
40: size_t inbuf_len;
41: char *outbuf;
42: size_t outbuf_len;
43:
44: /* Decompress options */
45: enum strm_status status;
46: unsigned int small_footprint : 1;
47: unsigned int expect_concatenated : 1;
48: } php_bz2_filter_data;
49:
50: /* }}} */
51:
52: /* {{{ Memory management wrappers */
53:
54: static void *php_bz2_alloc(void *opaque, int items, int size)
55: {
56: return (void *)safe_pemalloc(items, size, 0, ((php_bz2_filter_data*)opaque)->persistent);
57: }
58:
59: static void php_bz2_free(void *opaque, void *address)
60: {
61: pefree((void *)address, ((php_bz2_filter_data*)opaque)->persistent);
62: }
63: /* }}} */
64:
65: /* {{{ bzip2.decompress filter implementation */
66:
67: static php_stream_filter_status_t php_bz2_decompress_filter(
68: php_stream *stream,
69: php_stream_filter *thisfilter,
70: php_stream_bucket_brigade *buckets_in,
71: php_stream_bucket_brigade *buckets_out,
72: size_t *bytes_consumed,
73: int flags
74: TSRMLS_DC)
75: {
76: php_bz2_filter_data *data;
77: php_stream_bucket *bucket;
78: size_t consumed = 0;
79: int status;
80: php_stream_filter_status_t exit_status = PSFS_FEED_ME;
81: bz_stream *streamp;
82:
83: if (!thisfilter || !thisfilter->abstract) {
84: /* Should never happen */
85: return PSFS_ERR_FATAL;
86: }
87:
88: data = (php_bz2_filter_data *)(thisfilter->abstract);
89: streamp = &(data->strm);
90:
91: while (buckets_in->head) {
92: size_t bin = 0, desired;
93:
94: bucket = php_stream_bucket_make_writeable(buckets_in->head TSRMLS_CC);
95: while (bin < bucket->buflen) {
96: if (data->status == PHP_BZ2_UNITIALIZED) {
97: status = BZ2_bzDecompressInit(streamp, 0, data->small_footprint);
98:
99: if (BZ_OK != status) {
100: php_stream_bucket_delref(bucket TSRMLS_CC);
101: return PSFS_ERR_FATAL;
102: }
103:
104: data->status = PHP_BZ2_RUNNING;
105: }
106:
107: if (data->status != PHP_BZ2_RUNNING) {
108: consumed += bucket->buflen;
109: break;
110: }
111:
112: desired = bucket->buflen - bin;
113: if (desired > data->inbuf_len) {
114: desired = data->inbuf_len;
115: }
116: memcpy(data->strm.next_in, bucket->buf + bin, desired);
117: data->strm.avail_in = desired;
118:
119: status = BZ2_bzDecompress(&(data->strm));
120:
121: if (status == BZ_STREAM_END) {
122: BZ2_bzDecompressEnd(&(data->strm));
123: if (data->expect_concatenated) {
124: data->status = PHP_BZ2_UNITIALIZED;
125: } else {
126: data->status = PHP_BZ2_FINISHED;
127: }
128: } else if (status != BZ_OK) {
129: /* Something bad happened */
130: php_stream_bucket_delref(bucket TSRMLS_CC);
131: return PSFS_ERR_FATAL;
132: }
133: desired -= data->strm.avail_in; /* desired becomes what we consumed this round through */
134: data->strm.next_in = data->inbuf;
135: data->strm.avail_in = 0;
136: consumed += desired;
137: bin += desired;
138:
139: if (data->strm.avail_out < data->outbuf_len) {
140: php_stream_bucket *out_bucket;
141: size_t bucketlen = data->outbuf_len - data->strm.avail_out;
142: out_bucket = php_stream_bucket_new(stream, estrndup(data->outbuf, bucketlen), bucketlen, 1, 0 TSRMLS_CC);
143: php_stream_bucket_append(buckets_out, out_bucket TSRMLS_CC);
144: data->strm.avail_out = data->outbuf_len;
145: data->strm.next_out = data->outbuf;
146: exit_status = PSFS_PASS_ON;
147: } else if (status == BZ_STREAM_END && data->strm.avail_out >= data->outbuf_len) {
148: /* no more data to decompress, and nothing was spat out */
149: php_stream_bucket_delref(bucket TSRMLS_CC);
150: return PSFS_PASS_ON;
151: }
152: }
153:
154: php_stream_bucket_delref(bucket TSRMLS_CC);
155: }
156:
157: if ((data->status == PHP_BZ2_RUNNING) && (flags & PSFS_FLAG_FLUSH_CLOSE)) {
158: /* Spit it out! */
159: status = BZ_OK;
160: while (status == BZ_OK) {
161: status = BZ2_bzDecompress(&(data->strm));
162: if (data->strm.avail_out < data->outbuf_len) {
163: size_t bucketlen = data->outbuf_len - data->strm.avail_out;
164:
165: bucket = php_stream_bucket_new(stream, estrndup(data->outbuf, bucketlen), bucketlen, 1, 0 TSRMLS_CC);
166: php_stream_bucket_append(buckets_out, bucket TSRMLS_CC);
167: data->strm.avail_out = data->outbuf_len;
168: data->strm.next_out = data->outbuf;
169: exit_status = PSFS_PASS_ON;
170: } else if (status == BZ_OK) {
171: break;
172: }
173: }
174: }
175:
176: if (bytes_consumed) {
177: *bytes_consumed = consumed;
178: }
179:
180: return exit_status;
181: }
182:
183: static void php_bz2_decompress_dtor(php_stream_filter *thisfilter TSRMLS_DC)
184: {
185: if (thisfilter && thisfilter->abstract) {
186: php_bz2_filter_data *data = thisfilter->abstract;
187: if (data->status == PHP_BZ2_RUNNING) {
188: BZ2_bzDecompressEnd(&(data->strm));
189: }
190: pefree(data->inbuf, data->persistent);
191: pefree(data->outbuf, data->persistent);
192: pefree(data, data->persistent);
193: }
194: }
195:
196: static php_stream_filter_ops php_bz2_decompress_ops = {
197: php_bz2_decompress_filter,
198: php_bz2_decompress_dtor,
199: "bzip2.decompress"
200: };
201: /* }}} */
202:
203: /* {{{ bzip2.compress filter implementation */
204:
205: static php_stream_filter_status_t php_bz2_compress_filter(
206: php_stream *stream,
207: php_stream_filter *thisfilter,
208: php_stream_bucket_brigade *buckets_in,
209: php_stream_bucket_brigade *buckets_out,
210: size_t *bytes_consumed,
211: int flags
212: TSRMLS_DC)
213: {
214: php_bz2_filter_data *data;
215: php_stream_bucket *bucket;
216: size_t consumed = 0;
217: int status;
218: php_stream_filter_status_t exit_status = PSFS_FEED_ME;
219: bz_stream *streamp;
220:
221: if (!thisfilter || !thisfilter->abstract) {
222: /* Should never happen */
223: return PSFS_ERR_FATAL;
224: }
225:
226: data = (php_bz2_filter_data *)(thisfilter->abstract);
227: streamp = &(data->strm);
228:
229: while (buckets_in->head) {
230: size_t bin = 0, desired;
231:
232: bucket = php_stream_bucket_make_writeable(buckets_in->head TSRMLS_CC);
233:
234: while (bin < bucket->buflen) {
235: desired = bucket->buflen - bin;
236: if (desired > data->inbuf_len) {
237: desired = data->inbuf_len;
238: }
239: memcpy(data->strm.next_in, bucket->buf + bin, desired);
240: data->strm.avail_in = desired;
241:
242: status = BZ2_bzCompress(&(data->strm), flags & PSFS_FLAG_FLUSH_CLOSE ? BZ_FINISH : (flags & PSFS_FLAG_FLUSH_INC ? BZ_FLUSH : BZ_RUN));
243: if (status != BZ_RUN_OK && status != BZ_FLUSH_OK && status != BZ_FINISH_OK) {
244: /* Something bad happened */
245: php_stream_bucket_delref(bucket TSRMLS_CC);
246: return PSFS_ERR_FATAL;
247: }
248: desired -= data->strm.avail_in; /* desired becomes what we consumed this round through */
249: data->strm.next_in = data->inbuf;
250: data->strm.avail_in = 0;
251: consumed += desired;
252: bin += desired;
253:
254: if (data->strm.avail_out < data->outbuf_len) {
255: php_stream_bucket *out_bucket;
256: size_t bucketlen = data->outbuf_len - data->strm.avail_out;
257:
258: out_bucket = php_stream_bucket_new(stream, estrndup(data->outbuf, bucketlen), bucketlen, 1, 0 TSRMLS_CC);
259: php_stream_bucket_append(buckets_out, 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: php_stream_bucket_delref(bucket TSRMLS_CC);
266: }
267:
268: if (flags & PSFS_FLAG_FLUSH_CLOSE) {
269: /* Spit it out! */
270: status = BZ_FINISH_OK;
271: while (status == BZ_FINISH_OK) {
272: status = BZ2_bzCompress(&(data->strm), BZ_FINISH);
273: if (data->strm.avail_out < data->outbuf_len) {
274: size_t bucketlen = data->outbuf_len - data->strm.avail_out;
275:
276: bucket = php_stream_bucket_new(stream, estrndup(data->outbuf, bucketlen), bucketlen, 1, 0 TSRMLS_CC);
277: php_stream_bucket_append(buckets_out, bucket TSRMLS_CC);
278: data->strm.avail_out = data->outbuf_len;
279: data->strm.next_out = data->outbuf;
280: exit_status = PSFS_PASS_ON;
281: }
282: }
283: }
284:
285: if (bytes_consumed) {
286: *bytes_consumed = consumed;
287: }
288: return exit_status;
289: }
290:
291: static void php_bz2_compress_dtor(php_stream_filter *thisfilter TSRMLS_DC)
292: {
293: if (thisfilter && thisfilter->abstract) {
294: php_bz2_filter_data *data = thisfilter->abstract;
295: BZ2_bzCompressEnd(&(data->strm));
296: pefree(data->inbuf, data->persistent);
297: pefree(data->outbuf, data->persistent);
298: pefree(data, data->persistent);
299: }
300: }
301:
302: static php_stream_filter_ops php_bz2_compress_ops = {
303: php_bz2_compress_filter,
304: php_bz2_compress_dtor,
305: "bzip2.compress"
306: };
307:
308: /* }}} */
309:
310: /* {{{ bzip2.* common factory */
311:
312: static php_stream_filter *php_bz2_filter_create(const char *filtername, zval *filterparams, int persistent TSRMLS_DC)
313: {
314: php_stream_filter_ops *fops = NULL;
315: php_bz2_filter_data *data;
316: int status = BZ_OK;
317:
318: /* Create this filter */
319: data = pecalloc(1, sizeof(php_bz2_filter_data), persistent);
320: if (!data) {
321: php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed allocating %zu bytes", sizeof(php_bz2_filter_data));
322: return NULL;
323: }
324:
325: /* Circular reference */
326: data->strm.opaque = (void *) data;
327:
328: data->strm.bzalloc = php_bz2_alloc;
329: data->strm.bzfree = php_bz2_free;
330: data->persistent = persistent;
331: data->strm.avail_out = data->outbuf_len = data->inbuf_len = 2048;
332: data->strm.next_in = data->inbuf = (char *) pemalloc(data->inbuf_len, persistent);
333: if (!data->inbuf) {
334: php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed allocating %zu bytes", data->inbuf_len);
335: pefree(data, persistent);
336: return NULL;
337: }
338: data->strm.avail_in = 0;
339: data->strm.next_out = data->outbuf = (char *) pemalloc(data->outbuf_len, persistent);
340: if (!data->outbuf) {
341: php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed allocating %zu bytes", data->outbuf_len);
342: pefree(data->inbuf, persistent);
343: pefree(data, persistent);
344: return NULL;
345: }
346:
347: if (strcasecmp(filtername, "bzip2.decompress") == 0) {
348: data->small_footprint = 0;
349: data->expect_concatenated = 0;
350:
351: if (filterparams) {
352: zval **tmpzval = NULL;
353:
354: if (Z_TYPE_P(filterparams) == IS_ARRAY || Z_TYPE_P(filterparams) == IS_OBJECT) {
355:
356: if (SUCCESS == zend_hash_find(HASH_OF(filterparams), "concatenated", sizeof("concatenated"), (void **) &tmpzval) ) {
357: zval tmp, *tmp2;
358:
359: tmp = **tmpzval;
360: zval_copy_ctor(&tmp);
361: tmp2 = &tmp;
362: convert_to_boolean_ex(&tmp2);
363: data->expect_concatenated = Z_LVAL(tmp);
364: tmpzval = NULL;
365: }
366:
367: zend_hash_find(HASH_OF(filterparams), "small", sizeof("small"), (void **) &tmpzval);
368: } else {
369: tmpzval = &filterparams;
370: }
371:
372: if (tmpzval) {
373: zval tmp, *tmp2;
374:
375: tmp = **tmpzval;
376: zval_copy_ctor(&tmp);
377: tmp2 = &tmp;
378: convert_to_boolean_ex(&tmp2);
379: data->small_footprint = Z_LVAL(tmp);
380: }
381: }
382:
383: data->status = PHP_BZ2_UNITIALIZED;
384: fops = &php_bz2_decompress_ops;
385: } else if (strcasecmp(filtername, "bzip2.compress") == 0) {
386: int blockSize100k = PHP_BZ2_FILTER_DEFAULT_BLOCKSIZE;
387: int workFactor = PHP_BZ2_FILTER_DEFAULT_WORKFACTOR;
388:
389: if (filterparams) {
390: zval **tmpzval;
391:
392: if (Z_TYPE_P(filterparams) == IS_ARRAY || Z_TYPE_P(filterparams) == IS_OBJECT) {
393: if (zend_hash_find(HASH_OF(filterparams), "blocks", sizeof("blocks"), (void**) &tmpzval) == SUCCESS) {
394: /* How much memory to allocate (1 - 9) x 100kb */
395: zval tmp;
396:
397: tmp = **tmpzval;
398: zval_copy_ctor(&tmp);
399: convert_to_long(&tmp);
400: if (Z_LVAL(tmp) < 1 || Z_LVAL(tmp) > 9) {
401: php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid parameter given for number of blocks to allocate. (%ld)", Z_LVAL_PP(tmpzval));
402: } else {
403: blockSize100k = Z_LVAL(tmp);
404: }
405: }
406:
407: if (zend_hash_find(HASH_OF(filterparams), "work", sizeof("work"), (void**) &tmpzval) == SUCCESS) {
408: /* Work Factor (0 - 250) */
409: zval tmp;
410:
411: tmp = **tmpzval;
412: zval_copy_ctor(&tmp);
413: convert_to_long(&tmp);
414:
415: if (Z_LVAL(tmp) < 0 || Z_LVAL(tmp) > 250) {
416: php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid parameter given for work factor. (%ld)", Z_LVAL(tmp));
417: } else {
418: workFactor = Z_LVAL(tmp);
419: }
420: }
421: }
422: }
423:
424: status = BZ2_bzCompressInit(&(data->strm), blockSize100k, 0, workFactor);
425: fops = &php_bz2_compress_ops;
426: } else {
427: status = BZ_DATA_ERROR;
428: }
429:
430: if (status != BZ_OK) {
431: /* Unspecified (probably strm) error, let stream-filter error do its own whining */
432: pefree(data->strm.next_in, persistent);
433: pefree(data->strm.next_out, persistent);
434: pefree(data, persistent);
435: return NULL;
436: }
437:
438: return php_stream_filter_alloc(fops, data, persistent);
439: }
440:
441: php_stream_filter_factory php_bz2_filter_factory = {
442: php_bz2_filter_create
443: };
444: /* }}} */
445:
446: /*
447: * Local variables:
448: * tab-width: 4
449: * c-basic-offset: 4
450: * End:
451: * vim600: sw=4 ts=4 fdm=marker
452: * vim<600: sw=4 ts=4
453: */
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>