php-internal-docs 8.4.8
Unofficial docs for php/php-src
Loading...
Searching...
No Matches
zlib_filter.c
Go to the documentation of this file.
1/*
2 +----------------------------------------------------------------------+
3 | Copyright (c) The PHP Group |
4 +----------------------------------------------------------------------+
5 | This source file is subject to version 3.01 of the PHP license, |
6 | that is bundled with this package in the file LICENSE, and is |
7 | available through the world-wide-web at the following url: |
8 | https://www.php.net/license/3_01.txt |
9 | If you did not receive a copy of the PHP license and are unable to |
10 | obtain it through the world-wide-web, please send a note to |
11 | license@php.net so we can mail you a copy immediately. |
12 +----------------------------------------------------------------------+
13 | Authors: Sara Golemon (pollita@php.net) |
14 +----------------------------------------------------------------------+
15*/
16
17#include "php.h"
18#include "php_zlib.h"
19
20/* {{{ data structure */
21
22/* Passed as opaque in malloc callbacks */
23typedef struct _php_zlib_filter_data {
24 z_stream strm;
25 unsigned char *inbuf;
26 size_t inbuf_len;
27 unsigned char *outbuf;
28 size_t outbuf_len;
30 bool finished; /* for zlib.deflate: signals that no flush is pending */
32
33/* }}} */
34
35/* {{{ Memory management wrappers */
36
37static voidpf php_zlib_alloc(voidpf opaque, uInt items, uInt size)
38{
39 return (voidpf)safe_pemalloc(items, size, 0, ((php_zlib_filter_data*)opaque)->persistent);
40}
41
42static void php_zlib_free(voidpf opaque, voidpf address)
43{
44 pefree((void*)address, ((php_zlib_filter_data*)opaque)->persistent);
45}
46/* }}} */
47
48/* {{{ zlib.inflate filter implementation */
49
50static php_stream_filter_status_t php_zlib_inflate_filter(
51 php_stream *stream,
52 php_stream_filter *thisfilter,
53 php_stream_bucket_brigade *buckets_in,
54 php_stream_bucket_brigade *buckets_out,
55 size_t *bytes_consumed,
56 int flags
57 )
58{
60 php_stream_bucket *bucket;
61 size_t consumed = 0;
62 int status;
64
65 if (!thisfilter || !Z_PTR(thisfilter->abstract)) {
66 /* Should never happen */
67 return PSFS_ERR_FATAL;
68 }
69
70 data = (php_zlib_filter_data *)(Z_PTR(thisfilter->abstract));
71
72 while (buckets_in->head) {
73 size_t bin = 0, desired;
74
75 bucket = php_stream_bucket_make_writeable(buckets_in->head);
76
77 while (bin < (unsigned int) bucket->buflen && !data->finished) {
78
79 desired = bucket->buflen - bin;
80 if (desired > data->inbuf_len) {
81 desired = data->inbuf_len;
82 }
83 memcpy(data->strm.next_in, bucket->buf + bin, desired);
84 data->strm.avail_in = desired;
85
86 status = inflate(&(data->strm), flags & PSFS_FLAG_FLUSH_CLOSE ? Z_FINISH : Z_SYNC_FLUSH);
87 if (status == Z_STREAM_END) {
88 inflateEnd(&(data->strm));
89 data->finished = '\1';
90 exit_status = PSFS_PASS_ON;
91 } else if (status != Z_OK && status != Z_BUF_ERROR) {
92 /* Something bad happened */
93 php_error_docref(NULL, E_NOTICE, "zlib: %s", zError(status));
95 /* reset these because despite the error the filter may be used again */
96 data->strm.next_in = data->inbuf;
97 data->strm.avail_in = 0;
98 return PSFS_ERR_FATAL;
99 }
100 desired -= data->strm.avail_in; /* desired becomes what we consumed this round through */
101 data->strm.next_in = data->inbuf;
102 data->strm.avail_in = 0;
103 bin += desired;
104
105 if (data->strm.avail_out < data->outbuf_len) {
106 php_stream_bucket *out_bucket;
107 size_t bucketlen = data->outbuf_len - data->strm.avail_out;
108 out_bucket = php_stream_bucket_new(
109 stream, estrndup((char *) data->outbuf, bucketlen), bucketlen, 1, 0);
110 php_stream_bucket_append(buckets_out, out_bucket);
111 data->strm.avail_out = data->outbuf_len;
112 data->strm.next_out = data->outbuf;
113 exit_status = PSFS_PASS_ON;
114 }
115
116 }
117 consumed += bucket->buflen;
119 }
120
121 if (!data->finished && flags & PSFS_FLAG_FLUSH_CLOSE) {
122 /* Spit it out! */
123 status = Z_OK;
124 while (status == Z_OK) {
125 status = inflate(&(data->strm), Z_FINISH);
126 if (data->strm.avail_out < data->outbuf_len) {
127 size_t bucketlen = data->outbuf_len - data->strm.avail_out;
128
129 bucket = php_stream_bucket_new(
130 stream, estrndup((char *) data->outbuf, bucketlen), bucketlen, 1, 0);
131 php_stream_bucket_append(buckets_out, bucket);
132 data->strm.avail_out = data->outbuf_len;
133 data->strm.next_out = data->outbuf;
134 exit_status = PSFS_PASS_ON;
135 }
136 }
137 }
138
139 if (bytes_consumed) {
140 *bytes_consumed = consumed;
141 }
142
143 return exit_status;
144}
145
146static void php_zlib_inflate_dtor(php_stream_filter *thisfilter)
147{
148 if (thisfilter && Z_PTR(thisfilter->abstract)) {
149 php_zlib_filter_data *data = Z_PTR(thisfilter->abstract);
150 if (!data->finished) {
151 inflateEnd(&(data->strm));
152 }
153 pefree(data->inbuf, data->persistent);
154 pefree(data->outbuf, data->persistent);
155 pefree(data, data->persistent);
156 }
157}
158
159static const php_stream_filter_ops php_zlib_inflate_ops = {
160 php_zlib_inflate_filter,
161 php_zlib_inflate_dtor,
162 "zlib.inflate"
163};
164/* }}} */
165
166/* {{{ zlib.deflate filter implementation */
167
168static php_stream_filter_status_t php_zlib_deflate_filter(
169 php_stream *stream,
170 php_stream_filter *thisfilter,
171 php_stream_bucket_brigade *buckets_in,
172 php_stream_bucket_brigade *buckets_out,
173 size_t *bytes_consumed,
174 int flags
175 )
176{
178 php_stream_bucket *bucket;
179 size_t consumed = 0;
180 int status;
182
183 if (!thisfilter || !Z_PTR(thisfilter->abstract)) {
184 /* Should never happen */
185 return PSFS_ERR_FATAL;
186 }
187
188 data = (php_zlib_filter_data *)(Z_PTR(thisfilter->abstract));
189
190 while (buckets_in->head) {
191 size_t bin = 0, desired;
192
193 bucket = buckets_in->head;
194
195 bucket = php_stream_bucket_make_writeable(bucket);
196
197 while (bin < (unsigned int) bucket->buflen) {
198 int flush_mode;
199
200 desired = bucket->buflen - bin;
201 if (desired > data->inbuf_len) {
202 desired = data->inbuf_len;
203 }
204 memcpy(data->strm.next_in, bucket->buf + bin, desired);
205 data->strm.avail_in = desired;
206
207 flush_mode = flags & PSFS_FLAG_FLUSH_CLOSE ? Z_FULL_FLUSH : (flags & PSFS_FLAG_FLUSH_INC ? Z_SYNC_FLUSH : Z_NO_FLUSH);
208 data->finished = flush_mode != Z_NO_FLUSH;
209 status = deflate(&(data->strm), flush_mode);
210 if (status != Z_OK) {
211 /* Something bad happened */
213 return PSFS_ERR_FATAL;
214 }
215 desired -= data->strm.avail_in; /* desired becomes what we consumed this round through */
216 data->strm.next_in = data->inbuf;
217 data->strm.avail_in = 0;
218 bin += desired;
219
220 if (data->strm.avail_out < data->outbuf_len) {
221 php_stream_bucket *out_bucket;
222 size_t bucketlen = data->outbuf_len - data->strm.avail_out;
223
224 out_bucket = php_stream_bucket_new(
225 stream, estrndup((char *) data->outbuf, bucketlen), bucketlen, 1, 0);
226 php_stream_bucket_append(buckets_out, out_bucket);
227 data->strm.avail_out = data->outbuf_len;
228 data->strm.next_out = data->outbuf;
229 exit_status = PSFS_PASS_ON;
230 }
231 }
232 consumed += bucket->buflen;
234 }
235
236 if (flags & PSFS_FLAG_FLUSH_CLOSE || ((flags & PSFS_FLAG_FLUSH_INC) && !data->finished)) {
237 /* Spit it out! */
238 status = Z_OK;
239 while (status == Z_OK) {
240 status = deflate(&(data->strm), (flags & PSFS_FLAG_FLUSH_CLOSE ? Z_FINISH : Z_SYNC_FLUSH));
241 data->finished = 1;
242 if (data->strm.avail_out < data->outbuf_len) {
243 size_t bucketlen = data->outbuf_len - data->strm.avail_out;
244
245 bucket = php_stream_bucket_new(
246 stream, estrndup((char *) data->outbuf, bucketlen), bucketlen, 1, 0);
247 php_stream_bucket_append(buckets_out, bucket);
248 data->strm.avail_out = data->outbuf_len;
249 data->strm.next_out = data->outbuf;
250 exit_status = PSFS_PASS_ON;
251 }
252 }
253 }
254
255 if (bytes_consumed) {
256 *bytes_consumed = consumed;
257 }
258
259 return exit_status;
260}
261
262static void php_zlib_deflate_dtor(php_stream_filter *thisfilter)
263{
264 if (thisfilter && Z_PTR(thisfilter->abstract)) {
265 php_zlib_filter_data *data = Z_PTR(thisfilter->abstract);
266 deflateEnd(&(data->strm));
267 pefree(data->inbuf, data->persistent);
268 pefree(data->outbuf, data->persistent);
269 pefree(data, data->persistent);
270 }
271}
272
273static const php_stream_filter_ops php_zlib_deflate_ops = {
274 php_zlib_deflate_filter,
275 php_zlib_deflate_dtor,
276 "zlib.deflate"
277};
278
279/* }}} */
280
281/* {{{ zlib.* common factory */
282
283static php_stream_filter *php_zlib_filter_create(const char *filtername, zval *filterparams, uint8_t persistent)
284{
285 const php_stream_filter_ops *fops = NULL;
287 int status;
288
289 /* Create this filter */
291 if (!data) {
292 php_error_docref(NULL, E_WARNING, "Failed allocating %zd bytes", sizeof(php_zlib_filter_data));
293 return NULL;
294 }
295
296 /* Circular reference */
297 data->strm.opaque = (voidpf) data;
298
299 data->strm.zalloc = (alloc_func) php_zlib_alloc;
300 data->strm.zfree = (free_func) php_zlib_free;
301 data->strm.avail_out = data->outbuf_len = data->inbuf_len = 0x8000;
302 data->strm.next_in = data->inbuf = (Bytef *) pemalloc(data->inbuf_len, persistent);
303 if (!data->inbuf) {
304 php_error_docref(NULL, E_WARNING, "Failed allocating %zd bytes", data->inbuf_len);
306 return NULL;
307 }
308 data->strm.avail_in = 0;
309 data->strm.next_out = data->outbuf = (Bytef *) pemalloc(data->outbuf_len, persistent);
310 if (!data->outbuf) {
311 php_error_docref(NULL, E_WARNING, "Failed allocating %zd bytes", data->outbuf_len);
312 pefree(data->inbuf, persistent);
314 return NULL;
315 }
316
317 data->strm.data_type = Z_ASCII;
318
319 if (strcasecmp(filtername, "zlib.inflate") == 0) {
320 int windowBits = -MAX_WBITS;
321
322 if (filterparams) {
323 zval *tmpzval;
324
325 if ((Z_TYPE_P(filterparams) == IS_ARRAY || Z_TYPE_P(filterparams) == IS_OBJECT) &&
326 (tmpzval = zend_hash_str_find(HASH_OF(filterparams), "window", sizeof("window") - 1))) {
327 /* log-2 base of history window (9 - 15) */
328 zend_long tmp = zval_get_long(tmpzval);
329 if (tmp < -MAX_WBITS || tmp > MAX_WBITS + 32) {
330 php_error_docref(NULL, E_WARNING, "Invalid parameter given for window size (" ZEND_LONG_FMT ")", tmp);
331 } else {
332 windowBits = tmp;
333 }
334 }
335 }
336
337 /* RFC 1951 Inflate */
338 data->finished = '\0';
339 status = inflateInit2(&(data->strm), windowBits);
340 fops = &php_zlib_inflate_ops;
341 } else if (strcasecmp(filtername, "zlib.deflate") == 0) {
342 /* RFC 1951 Deflate */
343 int level = Z_DEFAULT_COMPRESSION;
344 int windowBits = -MAX_WBITS;
345 int memLevel = MAX_MEM_LEVEL;
346
347
348 if (filterparams) {
349 zval *tmpzval;
350 zend_long tmp;
351
352 /* filterparams can either be a scalar value to indicate compression level (shortcut method)
353 Or can be a hash containing one or more of 'window', 'memory', and/or 'level' members. */
354
355 switch (Z_TYPE_P(filterparams)) {
356 case IS_ARRAY:
357 case IS_OBJECT:
358 if ((tmpzval = zend_hash_str_find(HASH_OF(filterparams), "memory", sizeof("memory") -1))) {
359 /* Memory Level (1 - 9) */
360 tmp = zval_get_long(tmpzval);
361 if (tmp < 1 || tmp > MAX_MEM_LEVEL) {
362 php_error_docref(NULL, E_WARNING, "Invalid parameter given for memory level (" ZEND_LONG_FMT ")", tmp);
363 } else {
364 memLevel = tmp;
365 }
366 }
367
368 if ((tmpzval = zend_hash_str_find(HASH_OF(filterparams), "window", sizeof("window") - 1))) {
369 /* log-2 base of history window (9 - 15) */
370 tmp = zval_get_long(tmpzval);
371 if (tmp < -MAX_WBITS || tmp > MAX_WBITS + 16) {
372 php_error_docref(NULL, E_WARNING, "Invalid parameter given for window size (" ZEND_LONG_FMT ")", tmp);
373 } else {
374 windowBits = tmp;
375 }
376 }
377
378 if ((tmpzval = zend_hash_str_find(HASH_OF(filterparams), "level", sizeof("level") - 1))) {
379 tmp = zval_get_long(tmpzval);
380
381 /* Pseudo pass through to catch level validating code */
382 goto factory_setlevel;
383 }
384 break;
385 case IS_STRING:
386 case IS_DOUBLE:
387 case IS_LONG:
388 tmp = zval_get_long(filterparams);
389factory_setlevel:
390 /* Set compression level within reason (-1 == default, 0 == none, 1-9 == least to most compression */
391 if (tmp < -1 || tmp > 9) {
392 php_error_docref(NULL, E_WARNING, "Invalid compression level specified. (" ZEND_LONG_FMT ")", tmp);
393 } else {
394 level = tmp;
395 }
396 break;
397 default:
398 php_error_docref(NULL, E_WARNING, "Invalid filter parameter, ignored");
399 }
400 }
401 status = deflateInit2(&(data->strm), level, Z_DEFLATED, windowBits, memLevel, 0);
402 data->finished = 1;
403 fops = &php_zlib_deflate_ops;
404 } else {
405 status = Z_DATA_ERROR;
406 }
407
408 if (status != Z_OK) {
409 /* Unspecified (probably strm) error, let stream-filter error do its own whining */
410 pefree(data->strm.next_in, persistent);
411 pefree(data->strm.next_out, persistent);
413 return NULL;
414 }
415
417}
418
420 php_zlib_filter_create
421};
422/* }}} */
DNS_STATUS status
Definition dns_win32.c:49
new_type size
Definition ffi.c:4365
memcpy(ptr1, ptr2, size)
ffi persistent
Definition ffi.c:3633
#define NULL
Definition gdcache.h:45
PHPAPI php_stream_bucket * php_stream_bucket_new(php_stream *stream, char *buf, size_t buflen, uint8_t own_buf, uint8_t buf_persistent)
Definition filter.c:71
PHPAPI php_stream_bucket * php_stream_bucket_make_writeable(php_stream_bucket *bucket)
Definition filter.c:104
PHPAPI void php_stream_bucket_append(php_stream_bucket_brigade *brigade, php_stream_bucket *bucket)
Definition filter.c:174
PHPAPI void php_stream_bucket_delref(php_stream_bucket *bucket)
Definition filter.c:150
PHPAPI ZEND_COLD void php_error_docref(const char *docref, int type, const char *format,...)
Definition main.c:1173
#define MAX_WBITS
#define php_stream_filter_alloc(fops, thisptr, persistent)
#define PSFS_FLAG_FLUSH_CLOSE
#define PSFS_FLAG_FLUSH_INC
struct _php_stream_filter_ops php_stream_filter_ops
php_stream_filter_status_t
@ PSFS_ERR_FATAL
struct _php_stream_filter_factory php_stream_filter_factory
struct _php_stream_bucket php_stream_bucket
struct _php_stream_bucket_brigade php_stream_bucket_brigade
struct _php_stream php_stream
Definition php_streams.h:96
struct _php_stream_filter php_stream_filter
Definition php_streams.h:99
const php_stream_filter_factory php_zlib_filter_factory
zend_constant * data
unsigned char * inbuf
Definition zlib_filter.c:25
unsigned char * outbuf
Definition zlib_filter.c:27
#define HASH_OF(p)
Definition zend_API.h:1062
#define estrndup(s, length)
Definition zend_alloc.h:165
#define pefree(ptr, persistent)
Definition zend_alloc.h:191
#define pemalloc(size, persistent)
Definition zend_alloc.h:189
#define safe_pemalloc(nmemb, size, offset, persistent)
Definition zend_alloc.h:190
#define pecalloc(nmemb, size, persistent)
Definition zend_alloc.h:200
struct _zval_struct zval
#define strcasecmp(s1, s2)
#define E_NOTICE
Definition zend_errors.h:26
#define E_WARNING
Definition zend_errors.h:24
ZEND_API zval *ZEND_FASTCALL zend_hash_str_find(const HashTable *ht, const char *str, size_t len)
Definition zend_hash.c:2689
int32_t zend_long
Definition zend_long.h:42
#define ZEND_LONG_FMT
Definition zend_long.h:87
#define Z_TYPE_P(zval_p)
Definition zend_types.h:660
#define IS_STRING
Definition zend_types.h:606
#define IS_ARRAY
Definition zend_types.h:607
#define IS_DOUBLE
Definition zend_types.h:605
#define Z_PTR(zval)
#define IS_OBJECT
Definition zend_types.h:608
#define IS_LONG
Definition zend_types.h:604
struct _php_zlib_filter_data php_zlib_filter_data