1 : /*
2 : +----------------------------------------------------------------------+
3 : | PHP Version 5 |
4 : +----------------------------------------------------------------------+
5 : | Copyright (c) 1997-2009 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 275246 2009-02-05 21:45:43Z sixd $ */
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 142 : {
56 142 : 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 142 : {
61 142 : pefree((void *)address, ((php_bz2_filter_data*)opaque)->persistent);
62 142 : }
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 59 : {
76 : php_bz2_filter_data *data;
77 : php_stream_bucket *bucket;
78 59 : size_t consumed = 0;
79 : int status;
80 59 : php_stream_filter_status_t exit_status = PSFS_FEED_ME;
81 : bz_stream *streamp;
82 :
83 59 : if (!thisfilter || !thisfilter->abstract) {
84 : /* Should never happen */
85 0 : return PSFS_ERR_FATAL;
86 : }
87 :
88 59 : data = (php_bz2_filter_data *)(thisfilter->abstract);
89 59 : streamp = &(data->strm);
90 :
91 141 : while (buckets_in->head) {
92 23 : size_t bin = 0, desired;
93 :
94 23 : bucket = php_stream_bucket_make_writeable(buckets_in->head TSRMLS_CC);
95 83 : while (bin < bucket->buflen) {
96 38 : if (data->status == PHP_BZ2_UNITIALIZED) {
97 23 : status = BZ2_bzDecompressInit(streamp, 0, data->small_footprint);
98 :
99 23 : if (BZ_OK != status) {
100 0 : return PSFS_ERR_FATAL;
101 : }
102 :
103 23 : data->status = PHP_BZ2_RUNNING;
104 : }
105 :
106 38 : if (data->status != PHP_BZ2_RUNNING) {
107 1 : consumed += bucket->buflen;
108 1 : break;
109 : }
110 :
111 37 : desired = bucket->buflen - bin;
112 37 : if (desired > data->inbuf_len) {
113 2 : desired = data->inbuf_len;
114 : }
115 37 : memcpy(data->strm.next_in, bucket->buf + bin, desired);
116 37 : data->strm.avail_in = desired;
117 :
118 37 : status = BZ2_bzDecompress(&(data->strm));
119 :
120 37 : if (status == BZ_STREAM_END) {
121 23 : BZ2_bzDecompressEnd(&(data->strm));
122 23 : if (data->expect_concatenated) {
123 0 : data->status = PHP_BZ2_UNITIALIZED;
124 : } else {
125 23 : data->status = PHP_BZ2_FINISHED;
126 : }
127 14 : } else if (status != BZ_OK) {
128 : /* Something bad happened */
129 0 : php_stream_bucket_delref(bucket TSRMLS_CC);
130 0 : return PSFS_ERR_FATAL;
131 : }
132 37 : desired -= data->strm.avail_in; /* desired becomes what we consumed this round through */
133 37 : data->strm.next_in = data->inbuf;
134 37 : data->strm.avail_in = 0;
135 37 : consumed += desired;
136 37 : bin += desired;
137 :
138 37 : if (data->strm.avail_out < data->outbuf_len) {
139 : php_stream_bucket *out_bucket;
140 36 : size_t bucketlen = data->outbuf_len - data->strm.avail_out;
141 36 : out_bucket = php_stream_bucket_new(stream, estrndup(data->outbuf, bucketlen), bucketlen, 1, 0 TSRMLS_CC);
142 36 : php_stream_bucket_append(buckets_out, out_bucket TSRMLS_CC);
143 36 : data->strm.avail_out = data->outbuf_len;
144 36 : data->strm.next_out = data->outbuf;
145 36 : exit_status = PSFS_PASS_ON;
146 1 : } 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 0 : php_stream_bucket_delref(bucket TSRMLS_CC);
149 0 : return PSFS_PASS_ON;
150 : }
151 : }
152 :
153 23 : php_stream_bucket_delref(bucket TSRMLS_CC);
154 : }
155 :
156 59 : if ((data->status == PHP_BZ2_RUNNING) && (flags & PSFS_FLAG_FLUSH_CLOSE)) {
157 : /* Spit it out! */
158 0 : status = BZ_OK;
159 0 : while (status == BZ_OK) {
160 0 : status = BZ2_bzDecompress(&(data->strm));
161 0 : if (data->strm.avail_out < data->outbuf_len) {
162 0 : size_t bucketlen = data->outbuf_len - data->strm.avail_out;
163 :
164 0 : bucket = php_stream_bucket_new(stream, estrndup(data->outbuf, bucketlen), bucketlen, 1, 0 TSRMLS_CC);
165 0 : php_stream_bucket_append(buckets_out, bucket TSRMLS_CC);
166 0 : data->strm.avail_out = data->outbuf_len;
167 0 : data->strm.next_out = data->outbuf;
168 0 : exit_status = PSFS_PASS_ON;
169 0 : } else if (status == BZ_OK) {
170 0 : break;
171 : }
172 : }
173 : }
174 :
175 59 : if (bytes_consumed) {
176 34 : *bytes_consumed = consumed;
177 : }
178 :
179 59 : return exit_status;
180 : }
181 :
182 : static void php_bz2_decompress_dtor(php_stream_filter *thisfilter TSRMLS_DC)
183 23 : {
184 23 : if (thisfilter && thisfilter->abstract) {
185 23 : php_bz2_filter_data *data = thisfilter->abstract;
186 23 : if (data->status == PHP_BZ2_RUNNING) {
187 0 : BZ2_bzDecompressEnd(&(data->strm));
188 : }
189 23 : pefree(data->inbuf, data->persistent);
190 23 : pefree(data->outbuf, data->persistent);
191 23 : pefree(data, data->persistent);
192 : }
193 23 : }
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 :
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 64 : {
213 : php_bz2_filter_data *data;
214 : php_stream_bucket *bucket;
215 64 : size_t consumed = 0;
216 : int status;
217 64 : php_stream_filter_status_t exit_status = PSFS_FEED_ME;
218 : bz_stream *streamp;
219 :
220 64 : if (!thisfilter || !thisfilter->abstract) {
221 : /* Should never happen */
222 0 : return PSFS_ERR_FATAL;
223 : }
224 :
225 64 : data = (php_bz2_filter_data *)(thisfilter->abstract);
226 64 : streamp = &(data->strm);
227 :
228 152 : while (buckets_in->head) {
229 24 : size_t bin = 0, desired;
230 :
231 24 : bucket = php_stream_bucket_make_writeable(buckets_in->head TSRMLS_CC);
232 :
233 82 : while (bin < bucket->buflen) {
234 34 : desired = bucket->buflen - bin;
235 34 : if (desired > data->inbuf_len) {
236 10 : desired = data->inbuf_len;
237 : }
238 34 : memcpy(data->strm.next_in, bucket->buf + bin, desired);
239 34 : data->strm.avail_in = desired;
240 :
241 34 : status = BZ2_bzCompress(&(data->strm), flags & PSFS_FLAG_FLUSH_CLOSE ? BZ_FINISH : (flags & PSFS_FLAG_FLUSH_INC ? BZ_FLUSH : BZ_RUN));
242 34 : if (status != BZ_RUN_OK && status != BZ_FLUSH_OK && status != BZ_FINISH_OK) {
243 : /* Something bad happened */
244 0 : php_stream_bucket_delref(bucket TSRMLS_CC);
245 0 : return PSFS_ERR_FATAL;
246 : }
247 34 : desired -= data->strm.avail_in; /* desired becomes what we consumed this round through */
248 34 : data->strm.next_in = data->inbuf;
249 34 : data->strm.avail_in = 0;
250 34 : consumed += desired;
251 34 : bin += desired;
252 :
253 34 : if (data->strm.avail_out < data->outbuf_len) {
254 : php_stream_bucket *out_bucket;
255 0 : size_t bucketlen = data->outbuf_len - data->strm.avail_out;
256 :
257 0 : out_bucket = php_stream_bucket_new(stream, estrndup(data->outbuf, bucketlen), bucketlen, 1, 0 TSRMLS_CC);
258 0 : php_stream_bucket_append(buckets_out, out_bucket TSRMLS_CC);
259 0 : data->strm.avail_out = data->outbuf_len;
260 0 : data->strm.next_out = data->outbuf;
261 0 : exit_status = PSFS_PASS_ON;
262 : }
263 : }
264 24 : php_stream_bucket_delref(bucket TSRMLS_CC);
265 : }
266 :
267 64 : if (flags & PSFS_FLAG_FLUSH_CLOSE) {
268 : /* Spit it out! */
269 24 : status = BZ_FINISH_OK;
270 73 : while (status == BZ_FINISH_OK) {
271 25 : status = BZ2_bzCompress(&(data->strm), BZ_FINISH);
272 25 : if (data->strm.avail_out < data->outbuf_len) {
273 25 : size_t bucketlen = data->outbuf_len - data->strm.avail_out;
274 :
275 25 : bucket = php_stream_bucket_new(stream, estrndup(data->outbuf, bucketlen), bucketlen, 1, 0 TSRMLS_CC);
276 25 : php_stream_bucket_append(buckets_out, bucket TSRMLS_CC);
277 25 : data->strm.avail_out = data->outbuf_len;
278 25 : data->strm.next_out = data->outbuf;
279 25 : exit_status = PSFS_PASS_ON;
280 : }
281 : }
282 : }
283 :
284 64 : if (bytes_consumed) {
285 41 : *bytes_consumed = consumed;
286 : }
287 64 : return exit_status;
288 : }
289 :
290 : static void php_bz2_compress_dtor(php_stream_filter *thisfilter TSRMLS_DC)
291 24 : {
292 24 : if (thisfilter && thisfilter->abstract) {
293 24 : php_bz2_filter_data *data = thisfilter->abstract;
294 24 : BZ2_bzCompressEnd(&(data->strm));
295 24 : pefree(data->inbuf, data->persistent);
296 24 : pefree(data->outbuf, data->persistent);
297 24 : pefree(data, data->persistent);
298 : }
299 24 : }
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 :
311 : static php_stream_filter *php_bz2_filter_create(const char *filtername, zval *filterparams, int persistent TSRMLS_DC)
312 47 : {
313 47 : php_stream_filter_ops *fops = NULL;
314 : php_bz2_filter_data *data;
315 47 : int status = BZ_OK;
316 :
317 : /* Create this filter */
318 47 : data = pecalloc(1, sizeof(php_bz2_filter_data), persistent);
319 47 : if (!data) {
320 0 : php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed allocating %zu bytes", sizeof(php_bz2_filter_data));
321 0 : return NULL;
322 : }
323 :
324 : /* Circular reference */
325 47 : data->strm.opaque = (void *) data;
326 :
327 47 : data->strm.bzalloc = php_bz2_alloc;
328 47 : data->strm.bzfree = php_bz2_free;
329 47 : data->persistent = persistent;
330 47 : data->strm.avail_out = data->outbuf_len = data->inbuf_len = 2048;
331 47 : data->strm.next_in = data->inbuf = (char *) pemalloc(data->inbuf_len, persistent);
332 47 : if (!data->inbuf) {
333 0 : php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed allocating %zu bytes", data->inbuf_len);
334 0 : pefree(data, persistent);
335 0 : return NULL;
336 : }
337 47 : data->strm.avail_in = 0;
338 47 : data->strm.next_out = data->outbuf = (char *) pemalloc(data->outbuf_len, persistent);
339 47 : if (!data->outbuf) {
340 0 : php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed allocating %zu bytes", data->outbuf_len);
341 0 : pefree(data->inbuf, persistent);
342 0 : pefree(data, persistent);
343 0 : return NULL;
344 : }
345 :
346 47 : if (strcasecmp(filtername, "bzip2.decompress") == 0) {
347 23 : data->small_footprint = 0;
348 23 : data->expect_concatenated = 0;
349 :
350 23 : if (filterparams) {
351 0 : zval **tmpzval = NULL;
352 :
353 0 : if (Z_TYPE_P(filterparams) == IS_ARRAY || Z_TYPE_P(filterparams) == IS_OBJECT) {
354 :
355 0 : if (SUCCESS == zend_hash_find(HASH_OF(filterparams), "concatenated", sizeof("concatenated"), (void **) &tmpzval) ) {
356 : zval tmp, *tmp2;
357 :
358 0 : tmp = **tmpzval;
359 0 : zval_copy_ctor(&tmp);
360 0 : tmp2 = &tmp;
361 0 : convert_to_boolean_ex(&tmp2);
362 0 : data->expect_concatenated = Z_LVAL(tmp);
363 0 : tmpzval = NULL;
364 : }
365 :
366 0 : zend_hash_find(HASH_OF(filterparams), "small", sizeof("small"), (void **) &tmpzval);
367 : } else {
368 0 : tmpzval = &filterparams;
369 : }
370 :
371 0 : if (tmpzval) {
372 : zval tmp, *tmp2;
373 :
374 0 : tmp = **tmpzval;
375 0 : zval_copy_ctor(&tmp);
376 0 : tmp2 = &tmp;
377 0 : convert_to_boolean_ex(&tmp2);
378 0 : data->small_footprint = Z_LVAL(tmp);
379 : }
380 : }
381 :
382 23 : data->status = PHP_BZ2_UNITIALIZED;
383 23 : fops = &php_bz2_decompress_ops;
384 24 : } else if (strcasecmp(filtername, "bzip2.compress") == 0) {
385 24 : int blockSize100k = PHP_BZ2_FILTER_DEFAULT_BLOCKSIZE;
386 24 : int workFactor = PHP_BZ2_FILTER_DEFAULT_WORKFACTOR;
387 :
388 24 : if (filterparams) {
389 : zval **tmpzval;
390 :
391 0 : if (Z_TYPE_P(filterparams) == IS_ARRAY || Z_TYPE_P(filterparams) == IS_OBJECT) {
392 0 : 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 0 : tmp = **tmpzval;
397 0 : zval_copy_ctor(&tmp);
398 0 : convert_to_long(&tmp);
399 0 : if (Z_LVAL(tmp) < 1 || Z_LVAL(tmp) > 9) {
400 0 : 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 0 : blockSize100k = Z_LVAL(tmp);
403 : }
404 : }
405 :
406 0 : if (zend_hash_find(HASH_OF(filterparams), "work", sizeof("work"), (void**) &tmpzval) == SUCCESS) {
407 : /* Work Factor (0 - 250) */
408 : zval tmp;
409 :
410 0 : tmp = **tmpzval;
411 0 : zval_copy_ctor(&tmp);
412 0 : convert_to_long(&tmp);
413 :
414 0 : if (Z_LVAL(tmp) < 0 || Z_LVAL(tmp) > 250) {
415 0 : php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid parameter given for work factor. (%ld)", Z_LVAL(tmp));
416 : } else {
417 0 : workFactor = Z_LVAL(tmp);
418 : }
419 : }
420 : }
421 : }
422 :
423 24 : status = BZ2_bzCompressInit(&(data->strm), blockSize100k, 0, workFactor);
424 24 : fops = &php_bz2_compress_ops;
425 : } else {
426 0 : status = BZ_DATA_ERROR;
427 : }
428 :
429 47 : if (status != BZ_OK) {
430 : /* Unspecified (probably strm) error, let stream-filter error do its own whining */
431 0 : pefree(data->strm.next_in, persistent);
432 0 : pefree(data->strm.next_out, persistent);
433 0 : pefree(data, persistent);
434 0 : return NULL;
435 : }
436 :
437 47 : 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 : */
|