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