root/ext/zlib/zlib_filter.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. php_zlib_alloc
  2. php_zlib_free
  3. php_zlib_inflate_filter
  4. php_zlib_inflate_dtor
  5. php_zlib_deflate_filter
  6. php_zlib_deflate_dtor
  7. php_zlib_filter_create

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

/* [<][>][^][v][top][bottom][index][help] */