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 #include "php.h"
22 #include "php_zlib.h"
23
24 /* {{{ data structure */
25
26 /* Passed as opaque in malloc callbacks */
27 typedef struct _php_zlib_filter_data {
28 int persistent;
29 z_stream strm;
30 char *inbuf;
31 size_t inbuf_len;
32 char *outbuf;
33 size_t outbuf_len;
34 zend_bool finished;
35 } php_zlib_filter_data;
36
37 /* }}} */
38
39 /* {{{ Memory management wrappers */
40
php_zlib_alloc(voidpf opaque,uInt items,uInt size)41 static voidpf php_zlib_alloc(voidpf opaque, uInt items, uInt size)
42 {
43 return (voidpf)safe_pemalloc(items, size, 0, ((php_zlib_filter_data*)opaque)->persistent);
44 }
45
php_zlib_free(voidpf opaque,voidpf address)46 static void php_zlib_free(voidpf opaque, voidpf address)
47 {
48 pefree((void*)address, ((php_zlib_filter_data*)opaque)->persistent);
49 }
50 /* }}} */
51
52 /* {{{ zlib.inflate filter implementation */
53
php_zlib_inflate_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)54 static php_stream_filter_status_t php_zlib_inflate_filter(
55 php_stream *stream,
56 php_stream_filter *thisfilter,
57 php_stream_bucket_brigade *buckets_in,
58 php_stream_bucket_brigade *buckets_out,
59 size_t *bytes_consumed,
60 int flags
61 TSRMLS_DC)
62 {
63 php_zlib_filter_data *data;
64 php_stream_bucket *bucket;
65 size_t consumed = 0, original_out, original_in;
66 int status;
67 php_stream_filter_status_t exit_status = PSFS_FEED_ME;
68 z_stream *streamp;
69
70 if (!thisfilter || !thisfilter->abstract) {
71 /* Should never happen */
72 return PSFS_ERR_FATAL;
73 }
74
75 data = (php_zlib_filter_data *)(thisfilter->abstract);
76 streamp = &(data->strm);
77 original_in = data->strm.total_in;
78 original_out = data->strm.total_out;
79
80 while (buckets_in->head) {
81 size_t bin = 0, desired;
82
83 bucket = php_stream_bucket_make_writeable(buckets_in->head TSRMLS_CC);
84 while (bin < bucket->buflen) {
85
86 if (data->finished) {
87 consumed += bucket->buflen;
88 break;
89 }
90
91 desired = bucket->buflen - bin;
92 if (desired > data->inbuf_len) {
93 desired = data->inbuf_len;
94 }
95 memcpy(data->strm.next_in, bucket->buf + bin, desired);
96 data->strm.avail_in = desired;
97
98 status = inflate(&(data->strm), flags & PSFS_FLAG_FLUSH_CLOSE ? Z_FINISH : Z_SYNC_FLUSH);
99 if (status == Z_STREAM_END) {
100 inflateEnd(&(data->strm));
101 data->finished = '\1';
102 } else if (status != Z_OK) {
103 /* Something bad happened */
104 php_stream_bucket_delref(bucket TSRMLS_CC);
105 /* reset these because despite the error the filter may be used again */
106 data->strm.next_in = data->inbuf;
107 data->strm.avail_in = 0;
108 return PSFS_ERR_FATAL;
109 }
110 desired -= data->strm.avail_in; /* desired becomes what we consumed this round through */
111 data->strm.next_in = data->inbuf;
112 data->strm.avail_in = 0;
113 consumed += desired;
114 bin += desired;
115
116 if (data->strm.avail_out < data->outbuf_len) {
117 php_stream_bucket *out_bucket;
118 size_t bucketlen = data->outbuf_len - data->strm.avail_out;
119 out_bucket = php_stream_bucket_new(stream, estrndup(data->outbuf, bucketlen), bucketlen, 1, 0 TSRMLS_CC);
120 php_stream_bucket_append(buckets_out, out_bucket TSRMLS_CC);
121 data->strm.avail_out = data->outbuf_len;
122 data->strm.next_out = data->outbuf;
123 exit_status = PSFS_PASS_ON;
124 } else if (status == Z_STREAM_END && data->strm.avail_out >= data->outbuf_len) {
125 /* no more data to decompress, and nothing was spat out */
126 php_stream_bucket_delref(bucket TSRMLS_CC);
127 return PSFS_PASS_ON;
128 }
129 }
130 php_stream_bucket_delref(bucket TSRMLS_CC);
131 }
132
133 if (!data->finished && flags & PSFS_FLAG_FLUSH_CLOSE) {
134 /* Spit it out! */
135 status = Z_OK;
136 while (status == Z_OK) {
137 status = inflate(&(data->strm), Z_FINISH);
138 if (data->strm.avail_out < data->outbuf_len) {
139 size_t bucketlen = data->outbuf_len - data->strm.avail_out;
140
141 bucket = php_stream_bucket_new(stream, estrndup(data->outbuf, bucketlen), bucketlen, 1, 0 TSRMLS_CC);
142 php_stream_bucket_append(buckets_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 }
147 }
148 }
149
150 if (bytes_consumed) {
151 *bytes_consumed = consumed;
152 }
153
154 return exit_status;
155 }
156
php_zlib_inflate_dtor(php_stream_filter * thisfilter TSRMLS_DC)157 static void php_zlib_inflate_dtor(php_stream_filter *thisfilter TSRMLS_DC)
158 {
159 if (thisfilter && thisfilter->abstract) {
160 php_zlib_filter_data *data = thisfilter->abstract;
161 if (!data->finished) {
162 inflateEnd(&(data->strm));
163 }
164 pefree(data->inbuf, data->persistent);
165 pefree(data->outbuf, data->persistent);
166 pefree(data, data->persistent);
167 }
168 }
169
170 static php_stream_filter_ops php_zlib_inflate_ops = {
171 php_zlib_inflate_filter,
172 php_zlib_inflate_dtor,
173 "zlib.inflate"
174 };
175 /* }}} */
176
177 /* {{{ zlib.inflate filter implementation */
178
php_zlib_deflate_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)179 static php_stream_filter_status_t php_zlib_deflate_filter(
180 php_stream *stream,
181 php_stream_filter *thisfilter,
182 php_stream_bucket_brigade *buckets_in,
183 php_stream_bucket_brigade *buckets_out,
184 size_t *bytes_consumed,
185 int flags
186 TSRMLS_DC)
187 {
188 php_zlib_filter_data *data;
189 php_stream_bucket *bucket;
190 size_t consumed = 0, original_out, original_in;
191 int status;
192 php_stream_filter_status_t exit_status = PSFS_FEED_ME;
193 z_stream *streamp;
194
195 if (!thisfilter || !thisfilter->abstract) {
196 /* Should never happen */
197 return PSFS_ERR_FATAL;
198 }
199
200 data = (php_zlib_filter_data *)(thisfilter->abstract);
201 streamp = &(data->strm);
202 original_in = data->strm.total_in;
203 original_out = data->strm.total_out;
204
205 while (buckets_in->head) {
206 size_t bin = 0, desired;
207
208 bucket = php_stream_bucket_make_writeable(buckets_in->head TSRMLS_CC);
209
210 while (bin < bucket->buflen) {
211 desired = bucket->buflen - bin;
212 if (desired > data->inbuf_len) {
213 desired = data->inbuf_len;
214 }
215 memcpy(data->strm.next_in, bucket->buf + bin, desired);
216 data->strm.avail_in = desired;
217
218 status = deflate(&(data->strm), flags & PSFS_FLAG_FLUSH_CLOSE ? Z_FULL_FLUSH : (flags & PSFS_FLAG_FLUSH_INC ? Z_SYNC_FLUSH : Z_NO_FLUSH));
219 if (status != Z_OK) {
220 /* Something bad happened */
221 php_stream_bucket_delref(bucket TSRMLS_CC);
222 return PSFS_ERR_FATAL;
223 }
224 desired -= data->strm.avail_in; /* desired becomes what we consumed this round through */
225 data->strm.next_in = data->inbuf;
226 data->strm.avail_in = 0;
227 consumed += desired;
228 bin += desired;
229
230 if (data->strm.avail_out < data->outbuf_len) {
231 php_stream_bucket *out_bucket;
232 size_t bucketlen = data->outbuf_len - data->strm.avail_out;
233
234 out_bucket = php_stream_bucket_new(stream, estrndup(data->outbuf, bucketlen), bucketlen, 1, 0 TSRMLS_CC);
235 php_stream_bucket_append(buckets_out, out_bucket TSRMLS_CC);
236 data->strm.avail_out = data->outbuf_len;
237 data->strm.next_out = data->outbuf;
238 exit_status = PSFS_PASS_ON;
239 }
240 }
241 php_stream_bucket_delref(bucket TSRMLS_CC);
242 }
243
244 if (flags & PSFS_FLAG_FLUSH_CLOSE) {
245 /* Spit it out! */
246 status = Z_OK;
247 while (status == Z_OK) {
248 status = deflate(&(data->strm), Z_FINISH);
249 if (data->strm.avail_out < data->outbuf_len) {
250 size_t bucketlen = data->outbuf_len - data->strm.avail_out;
251
252 bucket = php_stream_bucket_new(stream, estrndup(data->outbuf, bucketlen), bucketlen, 1, 0 TSRMLS_CC);
253 php_stream_bucket_append(buckets_out, bucket TSRMLS_CC);
254 data->strm.avail_out = data->outbuf_len;
255 data->strm.next_out = data->outbuf;
256 exit_status = PSFS_PASS_ON;
257 }
258 }
259 }
260
261 if (bytes_consumed) {
262 *bytes_consumed = consumed;
263 }
264 return exit_status;
265 }
266
php_zlib_deflate_dtor(php_stream_filter * thisfilter TSRMLS_DC)267 static void php_zlib_deflate_dtor(php_stream_filter *thisfilter TSRMLS_DC)
268 {
269 if (thisfilter && thisfilter->abstract) {
270 php_zlib_filter_data *data = thisfilter->abstract;
271 deflateEnd(&(data->strm));
272 pefree(data->inbuf, data->persistent);
273 pefree(data->outbuf, data->persistent);
274 pefree(data, data->persistent);
275 }
276 }
277
278 static php_stream_filter_ops php_zlib_deflate_ops = {
279 php_zlib_deflate_filter,
280 php_zlib_deflate_dtor,
281 "zlib.deflate"
282 };
283
284 /* }}} */
285
286 /* {{{ zlib.* common factory */
287
php_zlib_filter_create(const char * filtername,zval * filterparams,int persistent TSRMLS_DC)288 static php_stream_filter *php_zlib_filter_create(const char *filtername, zval *filterparams, int persistent TSRMLS_DC)
289 {
290 php_stream_filter_ops *fops = NULL;
291 php_zlib_filter_data *data;
292 int status;
293
294 /* Create this filter */
295 data = pecalloc(1, sizeof(php_zlib_filter_data), persistent);
296 if (!data) {
297 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed allocating %zd bytes.", sizeof(php_zlib_filter_data));
298 return NULL;
299 }
300
301 /* Circular reference */
302 data->strm.opaque = (voidpf) data;
303
304 data->strm.zalloc = (alloc_func) php_zlib_alloc;
305 data->strm.zfree = (free_func) php_zlib_free;
306 data->strm.avail_out = data->outbuf_len = data->inbuf_len = 2048;
307 data->strm.next_in = data->inbuf = (Bytef *) pemalloc(data->inbuf_len, persistent);
308 if (!data->inbuf) {
309 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed allocating %zd bytes.", data->inbuf_len);
310 pefree(data, persistent);
311 return NULL;
312 }
313 data->strm.avail_in = 0;
314 data->strm.next_out = data->outbuf = (Bytef *) pemalloc(data->outbuf_len, persistent);
315 if (!data->outbuf) {
316 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed allocating %zd bytes.", data->outbuf_len);
317 pefree(data->inbuf, persistent);
318 pefree(data, persistent);
319 return NULL;
320 }
321
322 data->strm.data_type = Z_ASCII;
323
324 if (strcasecmp(filtername, "zlib.inflate") == 0) {
325 int windowBits = -MAX_WBITS;
326
327 if (filterparams) {
328 zval **tmpzval;
329
330 if ((Z_TYPE_P(filterparams) == IS_ARRAY || Z_TYPE_P(filterparams) == IS_OBJECT) &&
331 zend_hash_find(HASH_OF(filterparams), "window", sizeof("window"), (void **) &tmpzval) == SUCCESS) {
332 zval tmp;
333
334 /* log-2 base of history window (9 - 15) */
335 tmp = **tmpzval;
336 zval_copy_ctor(&tmp);
337 convert_to_long(&tmp);
338 if (Z_LVAL(tmp) < -MAX_WBITS || Z_LVAL(tmp) > MAX_WBITS + 32) {
339 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid parameter give for window size. (%ld)", Z_LVAL(tmp));
340 } else {
341 windowBits = Z_LVAL(tmp);
342 }
343 }
344 }
345
346 /* RFC 1951 Inflate */
347 data->finished = '\0';
348 status = inflateInit2(&(data->strm), windowBits);
349 fops = &php_zlib_inflate_ops;
350 } else if (strcasecmp(filtername, "zlib.deflate") == 0) {
351 /* RFC 1951 Deflate */
352 int level = Z_DEFAULT_COMPRESSION;
353 int windowBits = -MAX_WBITS;
354 int memLevel = MAX_MEM_LEVEL;
355
356
357 if (filterparams) {
358 zval **tmpzval, tmp;
359
360 /* filterparams can either be a scalar value to indicate compression level (shortcut method)
361 Or can be a hash containing one or more of 'window', 'memory', and/or 'level' members. */
362
363 switch (Z_TYPE_P(filterparams)) {
364 case IS_ARRAY:
365 case IS_OBJECT:
366 if (zend_hash_find(HASH_OF(filterparams), "memory", sizeof("memory"), (void**) &tmpzval) == SUCCESS) {
367 tmp = **tmpzval;
368 zval_copy_ctor(&tmp);
369 convert_to_long(&tmp);
370
371 /* Memory Level (1 - 9) */
372 if (Z_LVAL(tmp) < 1 || Z_LVAL(tmp) > MAX_MEM_LEVEL) {
373 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid parameter give for memory level. (%ld)", Z_LVAL(tmp));
374 } else {
375 memLevel = Z_LVAL(tmp);
376 }
377 }
378
379 if (zend_hash_find(HASH_OF(filterparams), "window", sizeof("window"), (void**) &tmpzval) == SUCCESS) {
380 tmp = **tmpzval;
381 zval_copy_ctor(&tmp);
382 convert_to_long(&tmp);
383
384 /* log-2 base of history window (9 - 15) */
385 if (Z_LVAL(tmp) < -MAX_WBITS || Z_LVAL(tmp) > MAX_WBITS + 16) {
386 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid parameter give for window size. (%ld)", Z_LVAL(tmp));
387 } else {
388 windowBits = Z_LVAL(tmp);
389 }
390 }
391
392 if (zend_hash_find(HASH_OF(filterparams), "level", sizeof("level"), (void**) &tmpzval) == SUCCESS) {
393 tmp = **tmpzval;
394
395 /* Psuedo pass through to catch level validating code */
396 goto factory_setlevel;
397 }
398 break;
399 case IS_STRING:
400 case IS_DOUBLE:
401 case IS_LONG:
402 tmp = *filterparams;
403 factory_setlevel:
404 zval_copy_ctor(&tmp);
405 convert_to_long(&tmp);
406
407 /* Set compression level within reason (-1 == default, 0 == none, 1-9 == least to most compression */
408 if (Z_LVAL(tmp) < -1 || Z_LVAL(tmp) > 9) {
409 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid compression level specified. (%ld)", Z_LVAL(tmp));
410 } else {
411 level = Z_LVAL(tmp);
412 }
413 break;
414 default:
415 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid filter parameter, ignored.");
416 }
417 }
418 status = deflateInit2(&(data->strm), level, Z_DEFLATED, windowBits, memLevel, 0);
419 fops = &php_zlib_deflate_ops;
420 } else {
421 status = Z_DATA_ERROR;
422 }
423
424 if (status != Z_OK) {
425 /* Unspecified (probably strm) error, let stream-filter error do its own whining */
426 pefree(data->strm.next_in, persistent);
427 pefree(data->strm.next_out, persistent);
428 pefree(data, persistent);
429 return NULL;
430 }
431
432 return php_stream_filter_alloc(fops, data, persistent);
433 }
434
435 php_stream_filter_factory php_zlib_filter_factory = {
436 php_zlib_filter_create
437 };
438 /* }}} */
439
440 /*
441 * Local variables:
442 * tab-width: 4
443 * c-basic-offset: 4
444 * End:
445 * vim600: sw=4 ts=4 fdm=marker
446 * vim<600: sw=4 ts=4
447 */
448