xref: /PHP-5.3/ext/bz2/bz2_filter.c (revision a2045ff3)
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