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