1/*
2   +----------------------------------------------------------------------+
3   | PHP Version 5                                                        |
4   +----------------------------------------------------------------------+
5   | Copyright (c) 1997-2014 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: Rasmus Lerdorf <rasmus@lerdorf.on.ca>                       |
16   |          Stefan Röhrich <sr@linux.de>                                |
17   |          Zeev Suraski <zeev@zend.com>                                |
18   |          Jade Nicoletti <nicoletti@nns.ch>                           |
19   |          Michael Wallner <mike@php.net>                              |
20   +----------------------------------------------------------------------+
21 */
22
23/* $Id$ */
24
25#ifdef HAVE_CONFIG_H
26#include "config.h"
27#endif
28
29#include "php.h"
30#include "SAPI.h"
31#include "php_ini.h"
32#include "ext/standard/info.h"
33#include "ext/standard/file.h"
34#include "ext/standard/php_string.h"
35#include "php_zlib.h"
36
37ZEND_DECLARE_MODULE_GLOBALS(zlib);
38
39/* {{{ Memory management wrappers */
40
41static voidpf php_zlib_alloc(voidpf opaque, uInt items, uInt size)
42{
43    return (voidpf)safe_emalloc(items, size, 0);
44}
45
46static void php_zlib_free(voidpf opaque, voidpf address)
47{
48    efree((void*)address);
49}
50/* }}} */
51
52/* {{{ php_zlib_output_conflict_check() */
53static int php_zlib_output_conflict_check(const char *handler_name, size_t handler_name_len TSRMLS_DC)
54{
55    if (php_output_get_level(TSRMLS_C) > 0) {
56        if (php_output_handler_conflict(handler_name, handler_name_len, ZEND_STRL(PHP_ZLIB_OUTPUT_HANDLER_NAME) TSRMLS_CC)
57        ||  php_output_handler_conflict(handler_name, handler_name_len, ZEND_STRL("ob_gzhandler") TSRMLS_CC)
58        ||  php_output_handler_conflict(handler_name, handler_name_len, ZEND_STRL("mb_output_handler") TSRMLS_CC)
59        ||  php_output_handler_conflict(handler_name, handler_name_len, ZEND_STRL("URL-Rewriter") TSRMLS_CC)) {
60            return FAILURE;
61        }
62    }
63    return SUCCESS;
64}
65/* }}} */
66
67/* {{{ php_zlib_output_encoding() */
68static int php_zlib_output_encoding(TSRMLS_D)
69{
70    zval *enc;
71
72    if (!ZLIBG(compression_coding)) {
73        zend_string *name = zend_string_init("_SERVER", sizeof("_SERVER") - 1, 0);
74        zend_is_auto_global(name TSRMLS_CC);
75        zend_string_release(name);
76        if (Z_TYPE(PG(http_globals)[TRACK_VARS_SERVER]) == IS_ARRAY &&
77            (enc = zend_hash_str_find(Z_ARRVAL(PG(http_globals)[TRACK_VARS_SERVER]), "HTTP_ACCEPT_ENCODING", sizeof("HTTP_ACCEPT_ENCODING") - 1))) {
78            convert_to_string(enc);
79            if (strstr(Z_STRVAL_P(enc), "gzip")) {
80                ZLIBG(compression_coding) = PHP_ZLIB_ENCODING_GZIP;
81            } else if (strstr(Z_STRVAL_P(enc), "deflate")) {
82                ZLIBG(compression_coding) = PHP_ZLIB_ENCODING_DEFLATE;
83            }
84        }
85    }
86    return ZLIBG(compression_coding);
87}
88/* }}} */
89
90/* {{{ php_zlib_output_handler_ex() */
91static int php_zlib_output_handler_ex(php_zlib_context *ctx, php_output_context *output_context)
92{
93    int flags = Z_SYNC_FLUSH;
94    PHP_OUTPUT_TSRMLS(output_context);
95
96    if (output_context->op & PHP_OUTPUT_HANDLER_START) {
97        /* start up */
98        if (Z_OK != deflateInit2(&ctx->Z, ZLIBG(output_compression_level), Z_DEFLATED, ZLIBG(compression_coding), MAX_MEM_LEVEL, Z_DEFAULT_STRATEGY)) {
99            return FAILURE;
100        }
101    }
102
103    if (output_context->op & PHP_OUTPUT_HANDLER_CLEAN) {
104        /* free buffers */
105        deflateEnd(&ctx->Z);
106
107        if (output_context->op & PHP_OUTPUT_HANDLER_FINAL) {
108            /* discard */
109            return SUCCESS;
110        } else {
111            /* restart */
112            if (Z_OK != deflateInit2(&ctx->Z, ZLIBG(output_compression_level), Z_DEFLATED, ZLIBG(compression_coding), MAX_MEM_LEVEL, Z_DEFAULT_STRATEGY)) {
113                return FAILURE;
114            }
115            ctx->buffer.used = 0;
116        }
117    } else {
118        if (output_context->in.used) {
119            /* append input */
120            if (ctx->buffer.free < output_context->in.used) {
121                if (!(ctx->buffer.aptr = erealloc_recoverable(ctx->buffer.data, ctx->buffer.used + ctx->buffer.free + output_context->in.used))) {
122                    deflateEnd(&ctx->Z);
123                    return FAILURE;
124                }
125                ctx->buffer.data = ctx->buffer.aptr;
126                ctx->buffer.free += output_context->in.used;
127            }
128            memcpy(ctx->buffer.data + ctx->buffer.used, output_context->in.data, output_context->in.used);
129            ctx->buffer.free -= output_context->in.used;
130            ctx->buffer.used += output_context->in.used;
131        }
132        output_context->out.size = PHP_ZLIB_BUFFER_SIZE_GUESS(output_context->in.used);
133        output_context->out.data = emalloc(output_context->out.size);
134        output_context->out.free = 1;
135        output_context->out.used = 0;
136
137        ctx->Z.avail_in = ctx->buffer.used;
138        ctx->Z.next_in = (Bytef *) ctx->buffer.data;
139        ctx->Z.avail_out = output_context->out.size;
140        ctx->Z.next_out = (Bytef *) output_context->out.data;
141
142        if (output_context->op & PHP_OUTPUT_HANDLER_FINAL) {
143            flags = Z_FINISH;
144        } else if (output_context->op & PHP_OUTPUT_HANDLER_FLUSH) {
145            flags = Z_FULL_FLUSH;
146        }
147
148        switch (deflate(&ctx->Z, flags)) {
149            case Z_OK:
150                if (flags == Z_FINISH) {
151                    deflateEnd(&ctx->Z);
152                    return FAILURE;
153                }
154            case Z_STREAM_END:
155                if (ctx->Z.avail_in) {
156                    memmove(ctx->buffer.data, ctx->buffer.data + ctx->buffer.used - ctx->Z.avail_in, ctx->Z.avail_in);
157                }
158                ctx->buffer.free += ctx->buffer.used - ctx->Z.avail_in;
159                ctx->buffer.used = ctx->Z.avail_in;
160                output_context->out.used = output_context->out.size - ctx->Z.avail_out;
161                break;
162            default:
163                deflateEnd(&ctx->Z);
164                return FAILURE;
165        }
166
167        if (output_context->op & PHP_OUTPUT_HANDLER_FINAL) {
168            deflateEnd(&ctx->Z);
169        }
170    }
171
172    return SUCCESS;
173}
174/* }}} */
175
176/* {{{ php_zlib_output_handler() */
177static int php_zlib_output_handler(void **handler_context, php_output_context *output_context)
178{
179    php_zlib_context *ctx = *(php_zlib_context **) handler_context;
180    PHP_OUTPUT_TSRMLS(output_context);
181
182    if (!php_zlib_output_encoding(TSRMLS_C)) {
183        /* "Vary: Accept-Encoding" header sent along uncompressed content breaks caching in MSIE,
184            so let's just send it with successfully compressed content or unless the complete
185            buffer gets discarded, see http://bugs.php.net/40325;
186
187            Test as follows:
188            +Vary: $ HTTP_ACCEPT_ENCODING=gzip ./sapi/cgi/php <<<'<?php ob_start("ob_gzhandler"); echo "foo\n";'
189            +Vary: $ HTTP_ACCEPT_ENCODING= ./sapi/cgi/php <<<'<?php ob_start("ob_gzhandler"); echo "foo\n";'
190            -Vary: $ HTTP_ACCEPT_ENCODING=gzip ./sapi/cgi/php <<<'<?php ob_start("ob_gzhandler"); echo "foo\n"; ob_end_clean();'
191            -Vary: $ HTTP_ACCEPT_ENCODING= ./sapi/cgi/php <<<'<?php ob_start("ob_gzhandler"); echo "foo\n"; ob_end_clean();'
192        */
193        if ((output_context->op & PHP_OUTPUT_HANDLER_START)
194        &&  (output_context->op != (PHP_OUTPUT_HANDLER_START|PHP_OUTPUT_HANDLER_CLEAN|PHP_OUTPUT_HANDLER_FINAL))
195        ) {
196            sapi_add_header_ex(ZEND_STRL("Vary: Accept-Encoding"), 1, 0 TSRMLS_CC);
197        }
198        return FAILURE;
199    }
200
201    if (SUCCESS != php_zlib_output_handler_ex(ctx, output_context)) {
202        return FAILURE;
203    }
204
205    if (!(output_context->op & PHP_OUTPUT_HANDLER_CLEAN)) {
206        int flags;
207
208        if (SUCCESS == php_output_handler_hook(PHP_OUTPUT_HANDLER_HOOK_GET_FLAGS, &flags TSRMLS_CC)) {
209            /* only run this once */
210            if (!(flags & PHP_OUTPUT_HANDLER_STARTED)) {
211                if (SG(headers_sent) || !ZLIBG(output_compression)) {
212                    deflateEnd(&ctx->Z);
213                    return FAILURE;
214                }
215                switch (ZLIBG(compression_coding)) {
216                    case PHP_ZLIB_ENCODING_GZIP:
217                        sapi_add_header_ex(ZEND_STRL("Content-Encoding: gzip"), 1, 1 TSRMLS_CC);
218                        break;
219                    case PHP_ZLIB_ENCODING_DEFLATE:
220                        sapi_add_header_ex(ZEND_STRL("Content-Encoding: deflate"), 1, 1 TSRMLS_CC);
221                        break;
222                    default:
223                        deflateEnd(&ctx->Z);
224                        return FAILURE;
225                }
226                sapi_add_header_ex(ZEND_STRL("Vary: Accept-Encoding"), 1, 0 TSRMLS_CC);
227                php_output_handler_hook(PHP_OUTPUT_HANDLER_HOOK_IMMUTABLE, NULL TSRMLS_CC);
228            }
229        }
230    }
231
232    return SUCCESS;
233}
234/* }}} */
235
236/* {{{ php_zlib_output_handler_context_init() */
237static php_zlib_context *php_zlib_output_handler_context_init(TSRMLS_D)
238{
239    php_zlib_context *ctx = (php_zlib_context *) ecalloc(1, sizeof(php_zlib_context));
240    ctx->Z.zalloc = php_zlib_alloc;
241    ctx->Z.zfree = php_zlib_free;
242    return ctx;
243}
244/* }}} */
245
246/* {{{ php_zlib_output_handler_context_dtor() */
247static void php_zlib_output_handler_context_dtor(void *opaq TSRMLS_DC)
248{
249    php_zlib_context *ctx = (php_zlib_context *) opaq;
250
251    if (ctx) {
252        if (ctx->buffer.data) {
253            efree(ctx->buffer.data);
254        }
255        efree(ctx);
256    }
257}
258/* }}} */
259
260/* {{{ php_zlib_output_handler_init() */
261static php_output_handler *php_zlib_output_handler_init(const char *handler_name, size_t handler_name_len, size_t chunk_size, int flags TSRMLS_DC)
262{
263    php_output_handler *h = NULL;
264
265    if (!ZLIBG(output_compression)) {
266        ZLIBG(output_compression) = chunk_size ? chunk_size : PHP_OUTPUT_HANDLER_DEFAULT_SIZE;
267    }
268
269    ZLIBG(handler_registered) = 1;
270
271    if ((h = php_output_handler_create_internal(handler_name, handler_name_len, php_zlib_output_handler, chunk_size, flags TSRMLS_CC))) {
272        php_output_handler_set_context(h, php_zlib_output_handler_context_init(TSRMLS_C), php_zlib_output_handler_context_dtor TSRMLS_CC);
273    }
274
275    return h;
276}
277/* }}} */
278
279/* {{{ php_zlib_output_compression_start() */
280static void php_zlib_output_compression_start(TSRMLS_D)
281{
282    zval zoh;
283    php_output_handler *h;
284
285    switch (ZLIBG(output_compression)) {
286        case 0:
287            break;
288        case 1:
289            ZLIBG(output_compression) = PHP_OUTPUT_HANDLER_DEFAULT_SIZE;
290            /* break omitted intentionally */
291        default:
292            if (    php_zlib_output_encoding(TSRMLS_C) &&
293                    (h = php_zlib_output_handler_init(ZEND_STRL(PHP_ZLIB_OUTPUT_HANDLER_NAME), ZLIBG(output_compression), PHP_OUTPUT_HANDLER_STDFLAGS TSRMLS_CC)) &&
294                    (SUCCESS == php_output_handler_start(h TSRMLS_CC))) {
295                if (ZLIBG(output_handler) && *ZLIBG(output_handler)) {
296                    ZVAL_STRING(&zoh, ZLIBG(output_handler));
297                    php_output_start_user(&zoh, ZLIBG(output_compression), PHP_OUTPUT_HANDLER_STDFLAGS TSRMLS_CC);
298                    zval_ptr_dtor(&zoh);
299                }
300            }
301            break;
302    }
303}
304/* }}} */
305
306/* {{{ php_zlib_encode() */
307static zend_string *php_zlib_encode(const char *in_buf, size_t in_len, int encoding, int level TSRMLS_DC)
308{
309    int status;
310    z_stream Z;
311    zend_string *out;
312
313    memset(&Z, 0, sizeof(z_stream));
314    Z.zalloc = php_zlib_alloc;
315    Z.zfree = php_zlib_free;
316
317    if (Z_OK == (status = deflateInit2(&Z, level, Z_DEFLATED, encoding, MAX_MEM_LEVEL, Z_DEFAULT_STRATEGY))) {
318        out = zend_string_alloc(PHP_ZLIB_BUFFER_SIZE_GUESS(in_len), 0);
319
320        Z.next_in = (Bytef *) in_buf;
321        Z.next_out = (Bytef *) out->val;
322        Z.avail_in = in_len;
323        Z.avail_out = out->len;
324
325        status = deflate(&Z, Z_FINISH);
326        deflateEnd(&Z);
327
328        if (Z_STREAM_END == status) {
329            /* size buffer down to actual length */
330            out = zend_string_realloc(out, Z.total_out, 0);
331            out->val[out->len] = '\0';
332            return out;
333        } else {
334            zend_string_free(out);
335        }
336    }
337
338    php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", zError(status));
339    return NULL;
340}
341/* }}} */
342
343/* {{{ php_zlib_inflate_rounds() */
344static inline int php_zlib_inflate_rounds(z_stream *Z, size_t max, char **buf, size_t *len)
345{
346    int status, round = 0;
347    php_zlib_buffer buffer = {NULL, NULL, 0, 0, 0};
348
349    *buf = NULL;
350    *len = 0;
351
352    buffer.size = (max && (max < Z->avail_in)) ? max : Z->avail_in;
353
354    do {
355        if ((max && (max <= buffer.used)) || !(buffer.aptr = erealloc_recoverable(buffer.data, buffer.size))) {
356            status = Z_MEM_ERROR;
357        } else {
358            buffer.data = buffer.aptr;
359            Z->avail_out = buffer.free = buffer.size - buffer.used;
360            Z->next_out = (Bytef *) buffer.data + buffer.used;
361#if 0
362            fprintf(stderr, "\n%3d: %3d PRIOR: size=%7lu,\tfree=%7lu,\tused=%7lu,\tavail_in=%7lu,\tavail_out=%7lu\n", round, status, buffer.size, buffer.free, buffer.used, Z->avail_in, Z->avail_out);
363#endif
364            status = inflate(Z, Z_NO_FLUSH);
365
366            buffer.used += buffer.free - Z->avail_out;
367            buffer.free = Z->avail_out;
368#if 0
369            fprintf(stderr, "%3d: %3d AFTER: size=%7lu,\tfree=%7lu,\tused=%7lu,\tavail_in=%7lu,\tavail_out=%7lu\n", round, status, buffer.size, buffer.free, buffer.used, Z->avail_in, Z->avail_out);
370#endif
371            buffer.size += (buffer.size >> 3) + 1;
372        }
373    } while ((Z_BUF_ERROR == status || (Z_OK == status && Z->avail_in)) && ++round < 100);
374
375    if (status == Z_STREAM_END) {
376        buffer.data = erealloc(buffer.data, buffer.used + 1);
377        buffer.data[buffer.used] = '\0';
378        *buf = buffer.data;
379        *len = buffer.used;
380    } else {
381        if (buffer.data) {
382            efree(buffer.data);
383        }
384        /* HACK: See zlib/examples/zpipe.c inf() function for explanation. */
385        /* This works as long as this function is not used for streaming. Required to catch very short invalid data. */
386        status = (status == Z_OK) ? Z_DATA_ERROR : status;
387    }
388    return status;
389}
390/* }}} */
391
392/* {{{ php_zlib_decode() */
393static int php_zlib_decode(const char *in_buf, size_t in_len, char **out_buf, size_t *out_len, int encoding, size_t max_len TSRMLS_DC)
394{
395    int status = Z_DATA_ERROR;
396    z_stream Z;
397
398    memset(&Z, 0, sizeof(z_stream));
399    Z.zalloc = php_zlib_alloc;
400    Z.zfree = php_zlib_free;
401
402    if (in_len) {
403retry_raw_inflate:
404        status = inflateInit2(&Z, encoding);
405        if (Z_OK == status) {
406            Z.next_in = (Bytef *) in_buf;
407            Z.avail_in = in_len + 1; /* NOTE: data must be zero terminated */
408
409            switch (status = php_zlib_inflate_rounds(&Z, max_len, out_buf, out_len)) {
410                case Z_STREAM_END:
411                    inflateEnd(&Z);
412                    return SUCCESS;
413
414                case Z_DATA_ERROR:
415                    /* raw deflated data? */
416                    if (PHP_ZLIB_ENCODING_ANY == encoding) {
417                        inflateEnd(&Z);
418                        encoding = PHP_ZLIB_ENCODING_RAW;
419                        goto retry_raw_inflate;
420                    }
421            }
422            inflateEnd(&Z);
423        }
424    }
425
426    *out_buf = NULL;
427    *out_len = 0;
428
429    php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", zError(status));
430    return FAILURE;
431}
432/* }}} */
433
434/* {{{ php_zlib_cleanup_ob_gzhandler_mess() */
435static void php_zlib_cleanup_ob_gzhandler_mess(TSRMLS_D)
436{
437    if (ZLIBG(ob_gzhandler)) {
438        deflateEnd(&(ZLIBG(ob_gzhandler)->Z));
439        php_zlib_output_handler_context_dtor(ZLIBG(ob_gzhandler) TSRMLS_CC);
440        ZLIBG(ob_gzhandler) = NULL;
441    }
442}
443/* }}} */
444
445/* {{{ proto string ob_gzhandler(string data, int flags)
446   Legacy hack */
447static PHP_FUNCTION(ob_gzhandler)
448{
449    char *in_str;
450    size_t in_len;
451    zend_long flags = 0;
452    php_output_context ctx = {0};
453    int encoding, rv;
454
455    /*
456     * NOTE that the real ob_gzhandler is an alias to "zlib output compression".
457     * This is a really bad hack, because
458     * - we have to initialize a php_zlib_context on demand
459     * - we have to clean it up in RSHUTDOWN
460     * - OG(running) is not set or set to any other output handler
461     * - we have to mess around with php_output_context */
462
463    if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sl", &in_str, &in_len, &flags)) {
464        RETURN_FALSE;
465    }
466
467    if (!(encoding = php_zlib_output_encoding(TSRMLS_C))) {
468        RETURN_FALSE;
469    }
470
471    if (flags & PHP_OUTPUT_HANDLER_START) {
472        switch (encoding) {
473            case PHP_ZLIB_ENCODING_GZIP:
474                sapi_add_header_ex(ZEND_STRL("Content-Encoding: gzip"), 1, 1 TSRMLS_CC);
475                break;
476            case PHP_ZLIB_ENCODING_DEFLATE:
477                sapi_add_header_ex(ZEND_STRL("Content-Encoding: deflate"), 1, 1 TSRMLS_CC);
478                break;
479        }
480        sapi_add_header_ex(ZEND_STRL("Vary: Accept-Encoding"), 1, 0 TSRMLS_CC);
481    }
482
483    if (!ZLIBG(ob_gzhandler)) {
484        ZLIBG(ob_gzhandler) = php_zlib_output_handler_context_init(TSRMLS_C);
485    }
486
487    TSRMLS_SET_CTX(ctx.tsrm_ls);
488    ctx.op = flags;
489    ctx.in.data = in_str;
490    ctx.in.used = in_len;
491
492    rv = php_zlib_output_handler_ex(ZLIBG(ob_gzhandler), &ctx);
493
494    if (SUCCESS != rv) {
495        if (ctx.out.data && ctx.out.free) {
496            efree(ctx.out.data);
497        }
498        php_zlib_cleanup_ob_gzhandler_mess(TSRMLS_C);
499        RETURN_FALSE;
500    }
501
502    if (ctx.out.data) {
503        RETVAL_STRINGL(ctx.out.data, ctx.out.used);
504        if (ctx.out.free) {
505            efree(ctx.out.data);
506        }
507    } else {
508        RETVAL_EMPTY_STRING();
509    }
510}
511/* }}} */
512
513/* {{{ proto string zlib_get_coding_type(void)
514   Returns the coding type used for output compression */
515static PHP_FUNCTION(zlib_get_coding_type)
516{
517    if (zend_parse_parameters_none() == FAILURE) {
518        return;
519    }
520    switch (ZLIBG(compression_coding)) {
521        case PHP_ZLIB_ENCODING_GZIP:
522            RETURN_STRINGL("gzip", sizeof("gzip") - 1);
523        case PHP_ZLIB_ENCODING_DEFLATE:
524            RETURN_STRINGL("deflate", sizeof("deflate") - 1);
525        default:
526            RETURN_FALSE;
527    }
528}
529/* }}} */
530
531/* {{{ proto array gzfile(string filename [, int use_include_path])
532   Read and uncompress entire .gz-file into an array */
533static PHP_FUNCTION(gzfile)
534{
535    char *filename;
536    size_t filename_len;
537    int flags = REPORT_ERRORS;
538    char buf[8192] = {0};
539    register int i = 0;
540    zend_long use_include_path = 0;
541    php_stream *stream;
542
543    if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "p|l", &filename, &filename_len, &use_include_path)) {
544        return;
545    }
546
547    if (use_include_path) {
548        flags |= USE_PATH;
549    }
550
551    /* using a stream here is a bit more efficient (resource wise) than php_gzopen_wrapper */
552    stream = php_stream_gzopen(NULL, filename, "rb", flags, NULL, NULL STREAMS_CC TSRMLS_CC);
553
554    if (!stream) {
555        /* Error reporting is already done by stream code */
556        RETURN_FALSE;
557    }
558
559    /* Initialize return array */
560    array_init(return_value);
561
562    /* Now loop through the file and do the magic quotes thing if needed */
563    memset(buf, 0, sizeof(buf));
564
565    while (php_stream_gets(stream, buf, sizeof(buf) - 1) != NULL) {
566        add_index_string(return_value, i++, buf);
567    }
568    php_stream_close(stream);
569}
570/* }}} */
571
572/* {{{ proto resource gzopen(string filename, string mode [, int use_include_path])
573   Open a .gz-file and return a .gz-file pointer */
574static PHP_FUNCTION(gzopen)
575{
576    char *filename;
577    char *mode;
578    size_t filename_len, mode_len;
579    int flags = REPORT_ERRORS;
580    php_stream *stream;
581    zend_long use_include_path = 0;
582
583    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss|l", &filename, &filename_len, &mode, &mode_len, &use_include_path) == FAILURE) {
584        return;
585    }
586
587    if (use_include_path) {
588        flags |= USE_PATH;
589    }
590
591    stream = php_stream_gzopen(NULL, filename, mode, flags, NULL, NULL STREAMS_CC TSRMLS_CC);
592
593    if (!stream) {
594        RETURN_FALSE;
595    }
596    php_stream_to_zval(stream, return_value);
597}
598/* }}} */
599
600/* {{{ proto int readgzfile(string filename [, int use_include_path])
601   Output a .gz-file */
602static PHP_FUNCTION(readgzfile)
603{
604    char *filename;
605    size_t filename_len;
606    int flags = REPORT_ERRORS;
607    php_stream *stream;
608    int size;
609    zend_long use_include_path = 0;
610
611    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|l", &filename, &filename_len, &use_include_path) == FAILURE) {
612        return;
613    }
614
615    if (use_include_path) {
616        flags |= USE_PATH;
617    }
618
619    stream = php_stream_gzopen(NULL, filename, "rb", flags, NULL, NULL STREAMS_CC TSRMLS_CC);
620
621    if (!stream) {
622        RETURN_FALSE;
623    }
624    size = php_stream_passthru(stream);
625    php_stream_close(stream);
626    RETURN_LONG(size);
627}
628/* }}} */
629
630#define PHP_ZLIB_ENCODE_FUNC(name, default_encoding) \
631static PHP_FUNCTION(name) \
632{ \
633    zend_string *in, *out; \
634    zend_long level = -1; \
635    zend_long encoding = default_encoding; \
636    if (default_encoding) { \
637        if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "S|ll", &in, &level, &encoding)) { \
638            return; \
639        } \
640    } else { \
641        if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Sl|l", &in, &encoding, &level)) { \
642            return; \
643        } \
644    } \
645    if (level < -1 || level > 9) { \
646        php_error_docref(NULL TSRMLS_CC, E_WARNING, "compression level (%pd) must be within -1..9", level); \
647        RETURN_FALSE; \
648    } \
649    switch (encoding) { \
650        case PHP_ZLIB_ENCODING_RAW: \
651        case PHP_ZLIB_ENCODING_GZIP: \
652        case PHP_ZLIB_ENCODING_DEFLATE: \
653            break; \
654        default: \
655            php_error_docref(NULL TSRMLS_CC, E_WARNING, "encoding mode must be either ZLIB_ENCODING_RAW, ZLIB_ENCODING_GZIP or ZLIB_ENCODING_DEFLATE"); \
656            RETURN_FALSE; \
657    } \
658    if ((out = php_zlib_encode(in->val, in->len, encoding, level TSRMLS_CC)) == NULL) { \
659        RETURN_FALSE; \
660    } \
661    RETURN_STR(out); \
662}
663
664#define PHP_ZLIB_DECODE_FUNC(name, encoding) \
665static PHP_FUNCTION(name) \
666{ \
667    char *in_buf, *out_buf; \
668    size_t in_len; \
669    size_t out_len; \
670    zend_long max_len = 0; \
671    if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|l", &in_buf, &in_len, &max_len)) { \
672        return; \
673    } \
674    if (max_len < 0) { \
675        php_error_docref(NULL TSRMLS_CC, E_WARNING, "length (%pd) must be greater or equal zero", max_len); \
676        RETURN_FALSE; \
677    } \
678    if (SUCCESS != php_zlib_decode(in_buf, in_len, &out_buf, &out_len, encoding, max_len TSRMLS_CC)) { \
679        RETURN_FALSE; \
680    } \
681    RETVAL_STRINGL(out_buf, out_len); \
682    efree(out_buf); \
683}
684
685/* {{{ proto binary zlib_encode(binary data, int encoding[, int level = -1])
686   Compress data with the specified encoding */
687PHP_ZLIB_ENCODE_FUNC(zlib_encode, 0);
688/* }}} */
689
690/* {{{ proto binary zlib_decode(binary data[, int max_decoded_len])
691   Uncompress any raw/gzip/zlib encoded data */
692PHP_ZLIB_DECODE_FUNC(zlib_decode, PHP_ZLIB_ENCODING_ANY);
693/* }}} */
694
695/* NOTE: The naming of these userland functions was quite unlucky */
696/* {{{ proto binary gzdeflate(binary data[, int level = -1[, int encoding = ZLIB_ENCODING_RAW])
697   Encode data with the raw deflate encoding */
698PHP_ZLIB_ENCODE_FUNC(gzdeflate, PHP_ZLIB_ENCODING_RAW);
699/* }}} */
700
701/* {{{ proto binary gzencode(binary data[, int level = -1[, int encoding = ZLIB_ENCODING_GZIP])
702   Encode data with the gzip encoding */
703PHP_ZLIB_ENCODE_FUNC(gzencode, PHP_ZLIB_ENCODING_GZIP);
704/* }}} */
705
706/* {{{ proto binary gzcompress(binary data[, int level = -1[, int encoding = ZLIB_ENCODING_DEFLATE])
707   Encode data with the zlib encoding */
708PHP_ZLIB_ENCODE_FUNC(gzcompress, PHP_ZLIB_ENCODING_DEFLATE);
709/* }}} */
710
711/* {{{ proto binary gzinflate(binary data[, int max_decoded_len])
712   Decode raw deflate encoded data */
713PHP_ZLIB_DECODE_FUNC(gzinflate, PHP_ZLIB_ENCODING_RAW);
714/* }}} */
715
716/* {{{ proto binary gzdecode(binary data[, int max_decoded_len])
717   Decode gzip encoded data */
718PHP_ZLIB_DECODE_FUNC(gzdecode, PHP_ZLIB_ENCODING_GZIP);
719/* }}} */
720
721/* {{{ proto binary gzuncompress(binary data[, int max_decoded_len])
722   Decode zlib encoded data */
723PHP_ZLIB_DECODE_FUNC(gzuncompress, PHP_ZLIB_ENCODING_DEFLATE);
724/* }}} */
725
726#ifdef COMPILE_DL_ZLIB
727ZEND_GET_MODULE(php_zlib)
728#endif
729
730/* {{{ arginfo */
731ZEND_BEGIN_ARG_INFO_EX(arginfo_ob_gzhandler, 0, 0, 2)
732    ZEND_ARG_INFO(0, data)
733    ZEND_ARG_INFO(0, flags)
734ZEND_END_ARG_INFO()
735
736ZEND_BEGIN_ARG_INFO(arginfo_zlib_get_coding_type, 0)
737ZEND_END_ARG_INFO()
738
739ZEND_BEGIN_ARG_INFO_EX(arginfo_gzfile, 0, 0, 1)
740    ZEND_ARG_INFO(0, filename)
741    ZEND_ARG_INFO(0, use_include_path)
742ZEND_END_ARG_INFO()
743
744ZEND_BEGIN_ARG_INFO_EX(arginfo_gzopen, 0, 0, 2)
745    ZEND_ARG_INFO(0, filename)
746    ZEND_ARG_INFO(0, mode)
747    ZEND_ARG_INFO(0, use_include_path)
748ZEND_END_ARG_INFO()
749
750ZEND_BEGIN_ARG_INFO_EX(arginfo_readgzfile, 0, 0, 1)
751    ZEND_ARG_INFO(0, filename)
752    ZEND_ARG_INFO(0, use_include_path)
753ZEND_END_ARG_INFO()
754
755ZEND_BEGIN_ARG_INFO_EX(arginfo_zlib_encode, 0, 0, 2)
756    ZEND_ARG_INFO(0, data)
757    ZEND_ARG_INFO(0, encoding)
758    ZEND_ARG_INFO(0, level)
759ZEND_END_ARG_INFO()
760
761ZEND_BEGIN_ARG_INFO_EX(arginfo_zlib_decode, 0, 0, 1)
762    ZEND_ARG_INFO(0, data)
763    ZEND_ARG_INFO(0, max_decoded_len)
764ZEND_END_ARG_INFO()
765
766ZEND_BEGIN_ARG_INFO_EX(arginfo_gzdeflate, 0, 0, 1)
767    ZEND_ARG_INFO(0, data)
768    ZEND_ARG_INFO(0, level)
769    ZEND_ARG_INFO(0, encoding)
770ZEND_END_ARG_INFO()
771
772ZEND_BEGIN_ARG_INFO_EX(arginfo_gzencode, 0, 0, 1)
773    ZEND_ARG_INFO(0, data)
774    ZEND_ARG_INFO(0, level)
775    ZEND_ARG_INFO(0, encoding)
776ZEND_END_ARG_INFO()
777
778ZEND_BEGIN_ARG_INFO_EX(arginfo_gzcompress, 0, 0, 1)
779    ZEND_ARG_INFO(0, data)
780    ZEND_ARG_INFO(0, level)
781    ZEND_ARG_INFO(0, encoding)
782ZEND_END_ARG_INFO()
783
784ZEND_BEGIN_ARG_INFO_EX(arginfo_gzinflate, 0, 0, 1)
785    ZEND_ARG_INFO(0, data)
786    ZEND_ARG_INFO(0, max_decoded_len)
787ZEND_END_ARG_INFO()
788
789ZEND_BEGIN_ARG_INFO_EX(arginfo_gzdecode, 0, 0, 1)
790    ZEND_ARG_INFO(0, data)
791    ZEND_ARG_INFO(0, max_decoded_len)
792ZEND_END_ARG_INFO()
793
794ZEND_BEGIN_ARG_INFO_EX(arginfo_gzuncompress, 0, 0, 1)
795    ZEND_ARG_INFO(0, data)
796    ZEND_ARG_INFO(0, max_decoded_len)
797ZEND_END_ARG_INFO()
798
799ZEND_BEGIN_ARG_INFO_EX(arginfo_gzputs, 0, 0, 2)
800    ZEND_ARG_INFO(0, fp)
801    ZEND_ARG_INFO(0, str)
802    ZEND_ARG_INFO(0, length)
803ZEND_END_ARG_INFO()
804
805ZEND_BEGIN_ARG_INFO(arginfo_gzpassthru, 0)
806    ZEND_ARG_INFO(0, fp)
807ZEND_END_ARG_INFO()
808
809ZEND_BEGIN_ARG_INFO_EX(arginfo_gzseek, 0, 0, 2)
810    ZEND_ARG_INFO(0, fp)
811    ZEND_ARG_INFO(0, offset)
812    ZEND_ARG_INFO(0, whence)
813ZEND_END_ARG_INFO()
814
815ZEND_BEGIN_ARG_INFO(arginfo_gzread, 0)
816    ZEND_ARG_INFO(0, fp)
817    ZEND_ARG_INFO(0, length)
818ZEND_END_ARG_INFO()
819
820ZEND_BEGIN_ARG_INFO_EX(arginfo_gzgetss, 0, 0, 1)
821    ZEND_ARG_INFO(0, fp)
822    ZEND_ARG_INFO(0, length)
823    ZEND_ARG_INFO(0, allowable_tags)
824ZEND_END_ARG_INFO()
825
826ZEND_BEGIN_ARG_INFO_EX(arginfo_gzgets, 0, 0, 1)
827    ZEND_ARG_INFO(0, fp)
828    ZEND_ARG_INFO(0, length)
829ZEND_END_ARG_INFO()
830/* }}} */
831
832/* {{{ php_zlib_functions[] */
833static const zend_function_entry php_zlib_functions[] = {
834    PHP_FE(readgzfile,                      arginfo_readgzfile)
835    PHP_FALIAS(gzrewind,    rewind,         arginfo_gzpassthru)
836    PHP_FALIAS(gzclose,     fclose,         arginfo_gzpassthru)
837    PHP_FALIAS(gzeof,       feof,           arginfo_gzpassthru)
838    PHP_FALIAS(gzgetc,      fgetc,          arginfo_gzpassthru)
839    PHP_FALIAS(gzgets,      fgets,          arginfo_gzgets)
840    PHP_FALIAS(gzgetss,     fgetss,         arginfo_gzgetss)
841    PHP_FALIAS(gzread,      fread,          arginfo_gzread)
842    PHP_FE(gzopen,                          arginfo_gzopen)
843    PHP_FALIAS(gzpassthru,  fpassthru,      arginfo_gzpassthru)
844    PHP_FALIAS(gzseek,      fseek,          arginfo_gzseek)
845    PHP_FALIAS(gztell,      ftell,          arginfo_gzpassthru)
846    PHP_FALIAS(gzwrite,     fwrite,         arginfo_gzputs)
847    PHP_FALIAS(gzputs,      fwrite,         arginfo_gzputs)
848    PHP_FE(gzfile,                          arginfo_gzfile)
849    PHP_FE(gzcompress,                      arginfo_gzcompress)
850    PHP_FE(gzuncompress,                    arginfo_gzuncompress)
851    PHP_FE(gzdeflate,                       arginfo_gzdeflate)
852    PHP_FE(gzinflate,                       arginfo_gzinflate)
853    PHP_FE(gzencode,                        arginfo_gzencode)
854    PHP_FE(gzdecode,                        arginfo_gzdecode)
855    PHP_FE(zlib_encode,                     arginfo_zlib_encode)
856    PHP_FE(zlib_decode,                     arginfo_zlib_decode)
857    PHP_FE(zlib_get_coding_type,            arginfo_zlib_get_coding_type)
858    PHP_FE(ob_gzhandler,                    arginfo_ob_gzhandler)
859    PHP_FE_END
860};
861/* }}} */
862
863/* {{{ OnUpdate_zlib_output_compression */
864static PHP_INI_MH(OnUpdate_zlib_output_compression)
865{
866    int status, int_value;
867    char *ini_value;
868
869    if (new_value == NULL) {
870        return FAILURE;
871    }
872
873    if (!strncasecmp(new_value, "off", sizeof("off"))) {
874        new_value = "0";
875        new_value_length = sizeof("0");
876    } else if (!strncasecmp(new_value, "on", sizeof("on"))) {
877        new_value = "1";
878        new_value_length = sizeof("1");
879    }
880
881    int_value = zend_atoi(new_value, new_value_length);
882    ini_value = zend_ini_string("output_handler", sizeof("output_handler"), 0);
883
884    if (ini_value && *ini_value && int_value) {
885        php_error_docref("ref.outcontrol" TSRMLS_CC, E_CORE_ERROR, "Cannot use both zlib.output_compression and output_handler together!!");
886        return FAILURE;
887    }
888    if (stage == PHP_INI_STAGE_RUNTIME) {
889        status = php_output_get_status(TSRMLS_C);
890        if (status & PHP_OUTPUT_SENT) {
891            php_error_docref("ref.outcontrol" TSRMLS_CC, E_WARNING, "Cannot change zlib.output_compression - headers already sent");
892            return FAILURE;
893        }
894    }
895
896    status = OnUpdateLong(entry, new_value, new_value_length, mh_arg1, mh_arg2, mh_arg3, stage TSRMLS_CC);
897
898    ZLIBG(output_compression) = ZLIBG(output_compression_default);
899    if (stage == PHP_INI_STAGE_RUNTIME && int_value) {
900        if (!php_output_handler_started(ZEND_STRL(PHP_ZLIB_OUTPUT_HANDLER_NAME) TSRMLS_CC)) {
901            php_zlib_output_compression_start(TSRMLS_C);
902        }
903    }
904
905    return status;
906}
907/* }}} */
908
909/* {{{ OnUpdate_zlib_output_handler */
910static PHP_INI_MH(OnUpdate_zlib_output_handler)
911{
912    if (stage == PHP_INI_STAGE_RUNTIME && (php_output_get_status(TSRMLS_C) & PHP_OUTPUT_SENT)) {
913        php_error_docref("ref.outcontrol" TSRMLS_CC, E_WARNING, "Cannot change zlib.output_handler - headers already sent");
914        return FAILURE;
915    }
916
917    return OnUpdateString(entry, new_value, new_value_length, mh_arg1, mh_arg2, mh_arg3, stage TSRMLS_CC);
918}
919/* }}} */
920
921/* {{{ INI */
922PHP_INI_BEGIN()
923    STD_PHP_INI_BOOLEAN("zlib.output_compression",      "0", PHP_INI_ALL, OnUpdate_zlib_output_compression,       output_compression_default,       zend_zlib_globals, zlib_globals)
924    STD_PHP_INI_ENTRY("zlib.output_compression_level", "-1", PHP_INI_ALL, OnUpdateLong,                           output_compression_level, zend_zlib_globals, zlib_globals)
925    STD_PHP_INI_ENTRY("zlib.output_handler",             "", PHP_INI_ALL, OnUpdate_zlib_output_handler,           output_handler,           zend_zlib_globals, zlib_globals)
926PHP_INI_END()
927
928/* }}} */
929
930/* {{{ PHP_MINIT_FUNCTION */
931static PHP_MINIT_FUNCTION(zlib)
932{
933    php_register_url_stream_wrapper("compress.zlib", &php_stream_gzip_wrapper TSRMLS_CC);
934    php_stream_filter_register_factory("zlib.*", &php_zlib_filter_factory TSRMLS_CC);
935
936    php_output_handler_alias_register(ZEND_STRL("ob_gzhandler"), php_zlib_output_handler_init TSRMLS_CC);
937    php_output_handler_conflict_register(ZEND_STRL("ob_gzhandler"), php_zlib_output_conflict_check TSRMLS_CC);
938    php_output_handler_conflict_register(ZEND_STRL(PHP_ZLIB_OUTPUT_HANDLER_NAME), php_zlib_output_conflict_check TSRMLS_CC);
939
940    REGISTER_LONG_CONSTANT("FORCE_GZIP", PHP_ZLIB_ENCODING_GZIP, CONST_CS|CONST_PERSISTENT);
941    REGISTER_LONG_CONSTANT("FORCE_DEFLATE", PHP_ZLIB_ENCODING_DEFLATE, CONST_CS|CONST_PERSISTENT);
942
943    REGISTER_LONG_CONSTANT("ZLIB_ENCODING_RAW", PHP_ZLIB_ENCODING_RAW, CONST_CS|CONST_PERSISTENT);
944    REGISTER_LONG_CONSTANT("ZLIB_ENCODING_GZIP", PHP_ZLIB_ENCODING_GZIP, CONST_CS|CONST_PERSISTENT);
945    REGISTER_LONG_CONSTANT("ZLIB_ENCODING_DEFLATE", PHP_ZLIB_ENCODING_DEFLATE, CONST_CS|CONST_PERSISTENT);
946    REGISTER_INI_ENTRIES();
947    return SUCCESS;
948}
949/* }}} */
950
951/* {{{ PHP_MSHUTDOWN_FUNCTION */
952static PHP_MSHUTDOWN_FUNCTION(zlib)
953{
954    php_unregister_url_stream_wrapper("zlib" TSRMLS_CC);
955    php_stream_filter_unregister_factory("zlib.*" TSRMLS_CC);
956
957    UNREGISTER_INI_ENTRIES();
958
959    return SUCCESS;
960}
961/* }}} */
962
963/* {{{ PHP_RINIT_FUNCTION */
964static PHP_RINIT_FUNCTION(zlib)
965{
966    ZLIBG(compression_coding) = 0;
967    if (!ZLIBG(handler_registered)) {
968        ZLIBG(output_compression) = ZLIBG(output_compression_default);
969        php_zlib_output_compression_start(TSRMLS_C);
970    }
971
972    return SUCCESS;
973}
974/* }}} */
975
976/* {{{ PHP_RSHUTDOWN_FUNCTION */
977static PHP_RSHUTDOWN_FUNCTION(zlib)
978{
979    php_zlib_cleanup_ob_gzhandler_mess(TSRMLS_C);
980    ZLIBG(handler_registered) = 0;
981
982    return SUCCESS;
983}
984/* }}} */
985
986/* {{{ PHP_MINFO_FUNCTION */
987static PHP_MINFO_FUNCTION(zlib)
988{
989    php_info_print_table_start();
990    php_info_print_table_header(2, "ZLib Support", "enabled");
991    php_info_print_table_row(2, "Stream Wrapper", "compress.zlib://");
992    php_info_print_table_row(2, "Stream Filter", "zlib.inflate, zlib.deflate");
993    php_info_print_table_row(2, "Compiled Version", ZLIB_VERSION);
994    php_info_print_table_row(2, "Linked Version", (char *) zlibVersion());
995    php_info_print_table_end();
996
997    DISPLAY_INI_ENTRIES();
998}
999/* }}} */
1000
1001/* {{{ ZEND_MODULE_GLOBALS_CTOR */
1002static ZEND_MODULE_GLOBALS_CTOR_D(zlib)
1003{
1004    zlib_globals->ob_gzhandler = NULL;
1005    zlib_globals->handler_registered = 0;
1006}
1007/* }}} */
1008
1009/* {{{ php_zlib_module_entry */
1010zend_module_entry php_zlib_module_entry = {
1011    STANDARD_MODULE_HEADER,
1012    "zlib",
1013    php_zlib_functions,
1014    PHP_MINIT(zlib),
1015    PHP_MSHUTDOWN(zlib),
1016    PHP_RINIT(zlib),
1017    PHP_RSHUTDOWN(zlib),
1018    PHP_MINFO(zlib),
1019    "2.0",
1020    PHP_MODULE_GLOBALS(zlib),
1021    ZEND_MODULE_GLOBALS_CTOR_N(zlib),
1022    NULL,
1023    NULL,
1024    STANDARD_MODULE_PROPERTIES_EX
1025};
1026/* }}} */
1027
1028/*
1029 * Local variables:
1030 * tab-width: 4
1031 * c-basic-offset: 4
1032 * End:
1033 * vim600: sw=4 ts=4 fdm=marker
1034 * vim<600: sw=4 ts=4
1035 */
1036