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$ */
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
php_bz2_alloc(void * opaque,int items,int size)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
php_bz2_free(void * opaque,void * address)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
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)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
php_bz2_decompress_dtor(php_stream_filter * thisfilter TSRMLS_DC)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
php_bz2_compress_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)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
php_bz2_compress_dtor(php_stream_filter * thisfilter TSRMLS_DC)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
php_bz2_filter_create(const char * filtername,zval * filterparams,int persistent TSRMLS_DC)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 */
454