1/*
2   +----------------------------------------------------------------------+
3   | PHP Version 7                                                        |
4   +----------------------------------------------------------------------+
5   | Copyright (c) 1997-2015 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
37/*
38 * zlib include files can define the following preprocessor defines which rename
39 * the corresponding PHP functions to gzopen64, gzseek64 and gztell64 and thereby
40 * breaking some software, most notably PEAR's Archive_Tar, which halts execution
41 * without error message on gzip compressed archivesa.
42 *
43 * This only seems to happen on 32bit systems with large file support.
44 */
45#undef gzopen
46#undef gzseek
47#undef gztell
48
49int le_deflate;
50int le_inflate;
51
52ZEND_DECLARE_MODULE_GLOBALS(zlib);
53
54/* {{{ Memory management wrappers */
55
56static voidpf php_zlib_alloc(voidpf opaque, uInt items, uInt size)
57{
58    return (voidpf)safe_emalloc(items, size, 0);
59}
60
61static void php_zlib_free(voidpf opaque, voidpf address)
62{
63    efree((void*)address);
64}
65/* }}} */
66
67/* {{{ Incremental deflate/inflate resource destructors */
68
69void deflate_rsrc_dtor(zend_resource *res)
70{
71    z_stream *ctx = zend_fetch_resource(res, NULL, le_deflate);
72    deflateEnd(ctx);
73    efree(ctx);
74}
75
76void inflate_rsrc_dtor(zend_resource *res)
77{
78    z_stream *ctx = zend_fetch_resource(res, NULL, le_inflate);
79    inflateEnd(ctx);
80    efree(ctx);
81}
82
83/* }}} */
84
85/* {{{ php_zlib_output_conflict_check() */
86static int php_zlib_output_conflict_check(const char *handler_name, size_t handler_name_len)
87{
88    if (php_output_get_level() > 0) {
89        if (php_output_handler_conflict(handler_name, handler_name_len, ZEND_STRL(PHP_ZLIB_OUTPUT_HANDLER_NAME))
90        ||  php_output_handler_conflict(handler_name, handler_name_len, ZEND_STRL("ob_gzhandler"))
91        ||  php_output_handler_conflict(handler_name, handler_name_len, ZEND_STRL("mb_output_handler"))
92        ||  php_output_handler_conflict(handler_name, handler_name_len, ZEND_STRL("URL-Rewriter"))) {
93            return FAILURE;
94        }
95    }
96    return SUCCESS;
97}
98/* }}} */
99
100/* {{{ php_zlib_output_encoding() */
101static int php_zlib_output_encoding(void)
102{
103    zval *enc;
104
105    if (!ZLIBG(compression_coding)) {
106        if ((Z_TYPE(PG(http_globals)[TRACK_VARS_SERVER]) == IS_ARRAY || zend_is_auto_global_str(ZEND_STRL("_SERVER"))) &&
107            (enc = zend_hash_str_find(Z_ARRVAL(PG(http_globals)[TRACK_VARS_SERVER]), "HTTP_ACCEPT_ENCODING", sizeof("HTTP_ACCEPT_ENCODING") - 1))) {
108            convert_to_string(enc);
109            if (strstr(Z_STRVAL_P(enc), "gzip")) {
110                ZLIBG(compression_coding) = PHP_ZLIB_ENCODING_GZIP;
111            } else if (strstr(Z_STRVAL_P(enc), "deflate")) {
112                ZLIBG(compression_coding) = PHP_ZLIB_ENCODING_DEFLATE;
113            }
114        }
115    }
116    return ZLIBG(compression_coding);
117}
118/* }}} */
119
120/* {{{ php_zlib_output_handler_ex() */
121static int php_zlib_output_handler_ex(php_zlib_context *ctx, php_output_context *output_context)
122{
123    int flags = Z_SYNC_FLUSH;
124    PHP_OUTPUT_TSRMLS(output_context);
125
126    if (output_context->op & PHP_OUTPUT_HANDLER_START) {
127        /* start up */
128        if (Z_OK != deflateInit2(&ctx->Z, ZLIBG(output_compression_level), Z_DEFLATED, ZLIBG(compression_coding), MAX_MEM_LEVEL, Z_DEFAULT_STRATEGY)) {
129            return FAILURE;
130        }
131    }
132
133    if (output_context->op & PHP_OUTPUT_HANDLER_CLEAN) {
134        /* free buffers */
135        deflateEnd(&ctx->Z);
136
137        if (output_context->op & PHP_OUTPUT_HANDLER_FINAL) {
138            /* discard */
139            return SUCCESS;
140        } else {
141            /* restart */
142            if (Z_OK != deflateInit2(&ctx->Z, ZLIBG(output_compression_level), Z_DEFLATED, ZLIBG(compression_coding), MAX_MEM_LEVEL, Z_DEFAULT_STRATEGY)) {
143                return FAILURE;
144            }
145            ctx->buffer.used = 0;
146        }
147    } else {
148        if (output_context->in.used) {
149            /* append input */
150            if (ctx->buffer.free < output_context->in.used) {
151                if (!(ctx->buffer.aptr = erealloc_recoverable(ctx->buffer.data, ctx->buffer.used + ctx->buffer.free + output_context->in.used))) {
152                    deflateEnd(&ctx->Z);
153                    return FAILURE;
154                }
155                ctx->buffer.data = ctx->buffer.aptr;
156                ctx->buffer.free += output_context->in.used;
157            }
158            memcpy(ctx->buffer.data + ctx->buffer.used, output_context->in.data, output_context->in.used);
159            ctx->buffer.free -= output_context->in.used;
160            ctx->buffer.used += output_context->in.used;
161        }
162        output_context->out.size = PHP_ZLIB_BUFFER_SIZE_GUESS(output_context->in.used);
163        output_context->out.data = emalloc(output_context->out.size);
164        output_context->out.free = 1;
165        output_context->out.used = 0;
166
167        ctx->Z.avail_in = ctx->buffer.used;
168        ctx->Z.next_in = (Bytef *) ctx->buffer.data;
169        ctx->Z.avail_out = output_context->out.size;
170        ctx->Z.next_out = (Bytef *) output_context->out.data;
171
172        if (output_context->op & PHP_OUTPUT_HANDLER_FINAL) {
173            flags = Z_FINISH;
174        } else if (output_context->op & PHP_OUTPUT_HANDLER_FLUSH) {
175            flags = Z_FULL_FLUSH;
176        }
177
178        switch (deflate(&ctx->Z, flags)) {
179            case Z_OK:
180                if (flags == Z_FINISH) {
181                    deflateEnd(&ctx->Z);
182                    return FAILURE;
183                }
184            case Z_STREAM_END:
185                if (ctx->Z.avail_in) {
186                    memmove(ctx->buffer.data, ctx->buffer.data + ctx->buffer.used - ctx->Z.avail_in, ctx->Z.avail_in);
187                }
188                ctx->buffer.free += ctx->buffer.used - ctx->Z.avail_in;
189                ctx->buffer.used = ctx->Z.avail_in;
190                output_context->out.used = output_context->out.size - ctx->Z.avail_out;
191                break;
192            default:
193                deflateEnd(&ctx->Z);
194                return FAILURE;
195        }
196
197        if (output_context->op & PHP_OUTPUT_HANDLER_FINAL) {
198            deflateEnd(&ctx->Z);
199        }
200    }
201
202    return SUCCESS;
203}
204/* }}} */
205
206/* {{{ php_zlib_output_handler() */
207static int php_zlib_output_handler(void **handler_context, php_output_context *output_context)
208{
209    php_zlib_context *ctx = *(php_zlib_context **) handler_context;
210    PHP_OUTPUT_TSRMLS(output_context);
211
212    if (!php_zlib_output_encoding()) {
213        /* "Vary: Accept-Encoding" header sent along uncompressed content breaks caching in MSIE,
214            so let's just send it with successfully compressed content or unless the complete
215            buffer gets discarded, see http://bugs.php.net/40325;
216
217            Test as follows:
218            +Vary: $ HTTP_ACCEPT_ENCODING=gzip ./sapi/cgi/php <<<'<?php ob_start("ob_gzhandler"); echo "foo\n";'
219            +Vary: $ HTTP_ACCEPT_ENCODING= ./sapi/cgi/php <<<'<?php ob_start("ob_gzhandler"); echo "foo\n";'
220            -Vary: $ HTTP_ACCEPT_ENCODING=gzip ./sapi/cgi/php <<<'<?php ob_start("ob_gzhandler"); echo "foo\n"; ob_end_clean();'
221            -Vary: $ HTTP_ACCEPT_ENCODING= ./sapi/cgi/php <<<'<?php ob_start("ob_gzhandler"); echo "foo\n"; ob_end_clean();'
222        */
223        if ((output_context->op & PHP_OUTPUT_HANDLER_START)
224        &&  (output_context->op != (PHP_OUTPUT_HANDLER_START|PHP_OUTPUT_HANDLER_CLEAN|PHP_OUTPUT_HANDLER_FINAL))
225        ) {
226            sapi_add_header_ex(ZEND_STRL("Vary: Accept-Encoding"), 1, 0);
227        }
228        return FAILURE;
229    }
230
231    if (SUCCESS != php_zlib_output_handler_ex(ctx, output_context)) {
232        return FAILURE;
233    }
234
235    if (!(output_context->op & PHP_OUTPUT_HANDLER_CLEAN)) {
236        int flags;
237
238        if (SUCCESS == php_output_handler_hook(PHP_OUTPUT_HANDLER_HOOK_GET_FLAGS, &flags)) {
239            /* only run this once */
240            if (!(flags & PHP_OUTPUT_HANDLER_STARTED)) {
241                if (SG(headers_sent) || !ZLIBG(output_compression)) {
242                    deflateEnd(&ctx->Z);
243                    return FAILURE;
244                }
245                switch (ZLIBG(compression_coding)) {
246                    case PHP_ZLIB_ENCODING_GZIP:
247                        sapi_add_header_ex(ZEND_STRL("Content-Encoding: gzip"), 1, 1);
248                        break;
249                    case PHP_ZLIB_ENCODING_DEFLATE:
250                        sapi_add_header_ex(ZEND_STRL("Content-Encoding: deflate"), 1, 1);
251                        break;
252                    default:
253                        deflateEnd(&ctx->Z);
254                        return FAILURE;
255                }
256                sapi_add_header_ex(ZEND_STRL("Vary: Accept-Encoding"), 1, 0);
257                php_output_handler_hook(PHP_OUTPUT_HANDLER_HOOK_IMMUTABLE, NULL);
258            }
259        }
260    }
261
262    return SUCCESS;
263}
264/* }}} */
265
266/* {{{ php_zlib_output_handler_context_init() */
267static php_zlib_context *php_zlib_output_handler_context_init(void)
268{
269    php_zlib_context *ctx = (php_zlib_context *) ecalloc(1, sizeof(php_zlib_context));
270    ctx->Z.zalloc = php_zlib_alloc;
271    ctx->Z.zfree = php_zlib_free;
272    return ctx;
273}
274/* }}} */
275
276/* {{{ php_zlib_output_handler_context_dtor() */
277static void php_zlib_output_handler_context_dtor(void *opaq)
278{
279    php_zlib_context *ctx = (php_zlib_context *) opaq;
280
281    if (ctx) {
282        if (ctx->buffer.data) {
283            efree(ctx->buffer.data);
284        }
285        efree(ctx);
286    }
287}
288/* }}} */
289
290/* {{{ php_zlib_output_handler_init() */
291static php_output_handler *php_zlib_output_handler_init(const char *handler_name, size_t handler_name_len, size_t chunk_size, int flags)
292{
293    php_output_handler *h = NULL;
294
295    if (!ZLIBG(output_compression)) {
296        ZLIBG(output_compression) = chunk_size ? chunk_size : PHP_OUTPUT_HANDLER_DEFAULT_SIZE;
297    }
298
299    ZLIBG(handler_registered) = 1;
300
301    if ((h = php_output_handler_create_internal(handler_name, handler_name_len, php_zlib_output_handler, chunk_size, flags))) {
302        php_output_handler_set_context(h, php_zlib_output_handler_context_init(), php_zlib_output_handler_context_dtor);
303    }
304
305    return h;
306}
307/* }}} */
308
309/* {{{ php_zlib_output_compression_start() */
310static void php_zlib_output_compression_start(void)
311{
312    zval zoh;
313    php_output_handler *h;
314
315    switch (ZLIBG(output_compression)) {
316        case 0:
317            break;
318        case 1:
319            ZLIBG(output_compression) = PHP_OUTPUT_HANDLER_DEFAULT_SIZE;
320            /* break omitted intentionally */
321        default:
322            if (    php_zlib_output_encoding() &&
323                    (h = php_zlib_output_handler_init(ZEND_STRL(PHP_ZLIB_OUTPUT_HANDLER_NAME), ZLIBG(output_compression), PHP_OUTPUT_HANDLER_STDFLAGS)) &&
324                    (SUCCESS == php_output_handler_start(h))) {
325                if (ZLIBG(output_handler) && *ZLIBG(output_handler)) {
326                    ZVAL_STRING(&zoh, ZLIBG(output_handler));
327                    php_output_start_user(&zoh, ZLIBG(output_compression), PHP_OUTPUT_HANDLER_STDFLAGS);
328                    zval_ptr_dtor(&zoh);
329                }
330            }
331            break;
332    }
333}
334/* }}} */
335
336/* {{{ php_zlib_encode() */
337static zend_string *php_zlib_encode(const char *in_buf, size_t in_len, int encoding, int level)
338{
339    int status;
340    z_stream Z;
341    zend_string *out;
342
343    memset(&Z, 0, sizeof(z_stream));
344    Z.zalloc = php_zlib_alloc;
345    Z.zfree = php_zlib_free;
346
347    if (Z_OK == (status = deflateInit2(&Z, level, Z_DEFLATED, encoding, MAX_MEM_LEVEL, Z_DEFAULT_STRATEGY))) {
348        out = zend_string_alloc(PHP_ZLIB_BUFFER_SIZE_GUESS(in_len), 0);
349
350        Z.next_in = (Bytef *) in_buf;
351        Z.next_out = (Bytef *) out->val;
352        Z.avail_in = in_len;
353        Z.avail_out = out->len;
354
355        status = deflate(&Z, Z_FINISH);
356        deflateEnd(&Z);
357
358        if (Z_STREAM_END == status) {
359            /* size buffer down to actual length */
360            out = zend_string_truncate(out, Z.total_out, 0);
361            out->val[out->len] = '\0';
362            return out;
363        } else {
364            zend_string_free(out);
365        }
366    }
367
368    php_error_docref(NULL, E_WARNING, "%s", zError(status));
369    return NULL;
370}
371/* }}} */
372
373/* {{{ php_zlib_inflate_rounds() */
374static inline int php_zlib_inflate_rounds(z_stream *Z, size_t max, char **buf, size_t *len)
375{
376    int status, round = 0;
377    php_zlib_buffer buffer = {NULL, NULL, 0, 0, 0};
378
379    *buf = NULL;
380    *len = 0;
381
382    buffer.size = (max && (max < Z->avail_in)) ? max : Z->avail_in;
383
384    do {
385        if ((max && (max <= buffer.used)) || !(buffer.aptr = erealloc_recoverable(buffer.data, buffer.size))) {
386            status = Z_MEM_ERROR;
387        } else {
388            buffer.data = buffer.aptr;
389            Z->avail_out = buffer.free = buffer.size - buffer.used;
390            Z->next_out = (Bytef *) buffer.data + buffer.used;
391#if 0
392            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);
393#endif
394            status = inflate(Z, Z_NO_FLUSH);
395
396            buffer.used += buffer.free - Z->avail_out;
397            buffer.free = Z->avail_out;
398#if 0
399            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);
400#endif
401            buffer.size += (buffer.size >> 3) + 1;
402        }
403    } while ((Z_BUF_ERROR == status || (Z_OK == status && Z->avail_in)) && ++round < 100);
404
405    if (status == Z_STREAM_END) {
406        buffer.data = erealloc(buffer.data, buffer.used + 1);
407        buffer.data[buffer.used] = '\0';
408        *buf = buffer.data;
409        *len = buffer.used;
410    } else {
411        if (buffer.data) {
412            efree(buffer.data);
413        }
414        /* HACK: See zlib/examples/zpipe.c inf() function for explanation. */
415        /* This works as long as this function is not used for streaming. Required to catch very short invalid data. */
416        status = (status == Z_OK) ? Z_DATA_ERROR : status;
417    }
418    return status;
419}
420/* }}} */
421
422/* {{{ php_zlib_decode() */
423static 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)
424{
425    int status = Z_DATA_ERROR;
426    z_stream Z;
427
428    memset(&Z, 0, sizeof(z_stream));
429    Z.zalloc = php_zlib_alloc;
430    Z.zfree = php_zlib_free;
431
432    if (in_len) {
433retry_raw_inflate:
434        status = inflateInit2(&Z, encoding);
435        if (Z_OK == status) {
436            Z.next_in = (Bytef *) in_buf;
437            Z.avail_in = in_len + 1; /* NOTE: data must be zero terminated */
438
439            switch (status = php_zlib_inflate_rounds(&Z, max_len, out_buf, out_len)) {
440                case Z_STREAM_END:
441                    inflateEnd(&Z);
442                    return SUCCESS;
443
444                case Z_DATA_ERROR:
445                    /* raw deflated data? */
446                    if (PHP_ZLIB_ENCODING_ANY == encoding) {
447                        inflateEnd(&Z);
448                        encoding = PHP_ZLIB_ENCODING_RAW;
449                        goto retry_raw_inflate;
450                    }
451            }
452            inflateEnd(&Z);
453        }
454    }
455
456    *out_buf = NULL;
457    *out_len = 0;
458
459    php_error_docref(NULL, E_WARNING, "%s", zError(status));
460    return FAILURE;
461}
462/* }}} */
463
464/* {{{ php_zlib_cleanup_ob_gzhandler_mess() */
465static void php_zlib_cleanup_ob_gzhandler_mess(void)
466{
467    if (ZLIBG(ob_gzhandler)) {
468        deflateEnd(&(ZLIBG(ob_gzhandler)->Z));
469        php_zlib_output_handler_context_dtor(ZLIBG(ob_gzhandler));
470        ZLIBG(ob_gzhandler) = NULL;
471    }
472}
473/* }}} */
474
475/* {{{ proto string ob_gzhandler(string data, int flags)
476   Legacy hack */
477static PHP_FUNCTION(ob_gzhandler)
478{
479    char *in_str;
480    size_t in_len;
481    zend_long flags = 0;
482    php_output_context ctx = {0};
483    int encoding, rv;
484
485    /*
486     * NOTE that the real ob_gzhandler is an alias to "zlib output compression".
487     * This is a really bad hack, because
488     * - we have to initialize a php_zlib_context on demand
489     * - we have to clean it up in RSHUTDOWN
490     * - OG(running) is not set or set to any other output handler
491     * - we have to mess around with php_output_context */
492
493    if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS(), "sl", &in_str, &in_len, &flags)) {
494        RETURN_FALSE;
495    }
496
497    if (!(encoding = php_zlib_output_encoding())) {
498        RETURN_FALSE;
499    }
500
501    if (flags & PHP_OUTPUT_HANDLER_START) {
502        switch (encoding) {
503            case PHP_ZLIB_ENCODING_GZIP:
504                sapi_add_header_ex(ZEND_STRL("Content-Encoding: gzip"), 1, 1);
505                break;
506            case PHP_ZLIB_ENCODING_DEFLATE:
507                sapi_add_header_ex(ZEND_STRL("Content-Encoding: deflate"), 1, 1);
508                break;
509        }
510        sapi_add_header_ex(ZEND_STRL("Vary: Accept-Encoding"), 1, 0);
511    }
512
513    if (!ZLIBG(ob_gzhandler)) {
514        ZLIBG(ob_gzhandler) = php_zlib_output_handler_context_init();
515    }
516
517    ctx.op = flags;
518    ctx.in.data = in_str;
519    ctx.in.used = in_len;
520
521    rv = php_zlib_output_handler_ex(ZLIBG(ob_gzhandler), &ctx);
522
523    if (SUCCESS != rv) {
524        if (ctx.out.data && ctx.out.free) {
525            efree(ctx.out.data);
526        }
527        php_zlib_cleanup_ob_gzhandler_mess();
528        RETURN_FALSE;
529    }
530
531    if (ctx.out.data) {
532        RETVAL_STRINGL(ctx.out.data, ctx.out.used);
533        if (ctx.out.free) {
534            efree(ctx.out.data);
535        }
536    } else {
537        RETVAL_EMPTY_STRING();
538    }
539}
540/* }}} */
541
542/* {{{ proto string zlib_get_coding_type(void)
543   Returns the coding type used for output compression */
544static PHP_FUNCTION(zlib_get_coding_type)
545{
546    if (zend_parse_parameters_none() == FAILURE) {
547        return;
548    }
549    switch (ZLIBG(compression_coding)) {
550        case PHP_ZLIB_ENCODING_GZIP:
551            RETURN_STRINGL("gzip", sizeof("gzip") - 1);
552        case PHP_ZLIB_ENCODING_DEFLATE:
553            RETURN_STRINGL("deflate", sizeof("deflate") - 1);
554        default:
555            RETURN_FALSE;
556    }
557}
558/* }}} */
559
560/* {{{ proto array gzfile(string filename [, int use_include_path])
561   Read and uncompress entire .gz-file into an array */
562static PHP_FUNCTION(gzfile)
563{
564    char *filename;
565    size_t filename_len;
566    int flags = REPORT_ERRORS;
567    char buf[8192] = {0};
568    register int i = 0;
569    zend_long use_include_path = 0;
570    php_stream *stream;
571
572    if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS(), "p|l", &filename, &filename_len, &use_include_path)) {
573        return;
574    }
575
576    if (use_include_path) {
577        flags |= USE_PATH;
578    }
579
580    /* using a stream here is a bit more efficient (resource wise) than php_gzopen_wrapper */
581    stream = php_stream_gzopen(NULL, filename, "rb", flags, NULL, NULL STREAMS_CC);
582
583    if (!stream) {
584        /* Error reporting is already done by stream code */
585        RETURN_FALSE;
586    }
587
588    /* Initialize return array */
589    array_init(return_value);
590
591    /* Now loop through the file and do the magic quotes thing if needed */
592    memset(buf, 0, sizeof(buf));
593
594    while (php_stream_gets(stream, buf, sizeof(buf) - 1) != NULL) {
595        add_index_string(return_value, i++, buf);
596    }
597    php_stream_close(stream);
598}
599/* }}} */
600
601/* {{{ proto resource gzopen(string filename, string mode [, int use_include_path])
602   Open a .gz-file and return a .gz-file pointer */
603static PHP_FUNCTION(gzopen)
604{
605    char *filename;
606    char *mode;
607    size_t filename_len, mode_len;
608    int flags = REPORT_ERRORS;
609    php_stream *stream;
610    zend_long use_include_path = 0;
611
612    if (zend_parse_parameters(ZEND_NUM_ARGS(), "ps|l", &filename, &filename_len, &mode, &mode_len, &use_include_path) == FAILURE) {
613        return;
614    }
615
616    if (use_include_path) {
617        flags |= USE_PATH;
618    }
619
620    stream = php_stream_gzopen(NULL, filename, mode, flags, NULL, NULL STREAMS_CC);
621
622    if (!stream) {
623        RETURN_FALSE;
624    }
625    php_stream_to_zval(stream, return_value);
626}
627/* }}} */
628
629/* {{{ proto int readgzfile(string filename [, int use_include_path])
630   Output a .gz-file */
631static PHP_FUNCTION(readgzfile)
632{
633    char *filename;
634    size_t filename_len;
635    int flags = REPORT_ERRORS;
636    php_stream *stream;
637    size_t size;
638    zend_long use_include_path = 0;
639
640    if (zend_parse_parameters(ZEND_NUM_ARGS(), "p|l", &filename, &filename_len, &use_include_path) == FAILURE) {
641        return;
642    }
643
644    if (use_include_path) {
645        flags |= USE_PATH;
646    }
647
648    stream = php_stream_gzopen(NULL, filename, "rb", flags, NULL, NULL STREAMS_CC);
649
650    if (!stream) {
651        RETURN_FALSE;
652    }
653    size = php_stream_passthru(stream);
654    php_stream_close(stream);
655    RETURN_LONG(size);
656}
657/* }}} */
658
659#define PHP_ZLIB_ENCODE_FUNC(name, default_encoding) \
660static PHP_FUNCTION(name) \
661{ \
662    zend_string *in, *out; \
663    zend_long level = -1; \
664    zend_long encoding = default_encoding; \
665    if (default_encoding) { \
666        if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS(), "S|ll", &in, &level, &encoding)) { \
667            return; \
668        } \
669    } else { \
670        if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS(), "Sl|l", &in, &encoding, &level)) { \
671            return; \
672        } \
673    } \
674    if (level < -1 || level > 9) { \
675        php_error_docref(NULL, E_WARNING, "compression level (%pd) must be within -1..9", level); \
676        RETURN_FALSE; \
677    } \
678    switch (encoding) { \
679        case PHP_ZLIB_ENCODING_RAW: \
680        case PHP_ZLIB_ENCODING_GZIP: \
681        case PHP_ZLIB_ENCODING_DEFLATE: \
682            break; \
683        default: \
684            php_error_docref(NULL, E_WARNING, "encoding mode must be either ZLIB_ENCODING_RAW, ZLIB_ENCODING_GZIP or ZLIB_ENCODING_DEFLATE"); \
685            RETURN_FALSE; \
686    } \
687    if ((out = php_zlib_encode(in->val, in->len, encoding, level)) == NULL) { \
688        RETURN_FALSE; \
689    } \
690    RETURN_STR(out); \
691}
692
693#define PHP_ZLIB_DECODE_FUNC(name, encoding) \
694static PHP_FUNCTION(name) \
695{ \
696    char *in_buf, *out_buf; \
697    size_t in_len; \
698    size_t out_len; \
699    zend_long max_len = 0; \
700    if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS(), "s|l", &in_buf, &in_len, &max_len)) { \
701        return; \
702    } \
703    if (max_len < 0) { \
704        php_error_docref(NULL, E_WARNING, "length (%pd) must be greater or equal zero", max_len); \
705        RETURN_FALSE; \
706    } \
707    if (SUCCESS != php_zlib_decode(in_buf, in_len, &out_buf, &out_len, encoding, max_len)) { \
708        RETURN_FALSE; \
709    } \
710    RETVAL_STRINGL(out_buf, out_len); \
711    efree(out_buf); \
712}
713
714/* {{{ proto binary zlib_encode(binary data, int encoding[, int level = -1])
715   Compress data with the specified encoding */
716PHP_ZLIB_ENCODE_FUNC(zlib_encode, 0);
717/* }}} */
718
719/* {{{ proto binary zlib_decode(binary data[, int max_decoded_len])
720   Uncompress any raw/gzip/zlib encoded data */
721PHP_ZLIB_DECODE_FUNC(zlib_decode, PHP_ZLIB_ENCODING_ANY);
722/* }}} */
723
724/* NOTE: The naming of these userland functions was quite unlucky */
725/* {{{ proto binary gzdeflate(binary data[, int level = -1[, int encoding = ZLIB_ENCODING_RAW])
726   Encode data with the raw deflate encoding */
727PHP_ZLIB_ENCODE_FUNC(gzdeflate, PHP_ZLIB_ENCODING_RAW);
728/* }}} */
729
730/* {{{ proto binary gzencode(binary data[, int level = -1[, int encoding = ZLIB_ENCODING_GZIP])
731   Encode data with the gzip encoding */
732PHP_ZLIB_ENCODE_FUNC(gzencode, PHP_ZLIB_ENCODING_GZIP);
733/* }}} */
734
735/* {{{ proto binary gzcompress(binary data[, int level = -1[, int encoding = ZLIB_ENCODING_DEFLATE])
736   Encode data with the zlib encoding */
737PHP_ZLIB_ENCODE_FUNC(gzcompress, PHP_ZLIB_ENCODING_DEFLATE);
738/* }}} */
739
740/* {{{ proto binary gzinflate(binary data[, int max_decoded_len])
741   Decode raw deflate encoded data */
742PHP_ZLIB_DECODE_FUNC(gzinflate, PHP_ZLIB_ENCODING_RAW);
743/* }}} */
744
745/* {{{ proto binary gzdecode(binary data[, int max_decoded_len])
746   Decode gzip encoded data */
747PHP_ZLIB_DECODE_FUNC(gzdecode, PHP_ZLIB_ENCODING_GZIP);
748/* }}} */
749
750/* {{{ proto binary gzuncompress(binary data[, int max_decoded_len])
751   Decode zlib encoded data */
752PHP_ZLIB_DECODE_FUNC(gzuncompress, PHP_ZLIB_ENCODING_DEFLATE);
753/* }}} */
754
755/* {{{ proto resource inflate_init(int encoding)
756   Initialize an incremental inflate context with the specified encoding */
757PHP_FUNCTION(inflate_init)
758{
759    z_stream *ctx;
760    zend_long encoding;
761
762    if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS(), "l", &encoding)) {
763        RETURN_FALSE;
764    }
765
766    switch (encoding) {
767        case PHP_ZLIB_ENCODING_RAW:
768        case PHP_ZLIB_ENCODING_GZIP:
769        case PHP_ZLIB_ENCODING_DEFLATE:
770            break;
771        default:
772            php_error_docref(NULL, E_WARNING,
773                "encoding mode must be ZLIB_ENCODING_RAW, ZLIB_ENCODING_GZIP or ZLIB_ENCODING_DEFLATE");
774            RETURN_FALSE;
775    }
776
777    ctx = ecalloc(1, sizeof(php_zlib_context));
778    ctx->zalloc = php_zlib_alloc;
779    ctx->zfree = php_zlib_free;
780
781    if (Z_OK == inflateInit2(ctx, encoding)) {
782        RETURN_RES(zend_register_resource(ctx, le_inflate));
783    } else {
784        efree(ctx);
785        php_error_docref(NULL, E_WARNING, "failed allocating zlib.inflate context");
786        RETURN_FALSE;
787    }
788}
789/* }}} */
790
791/* {{{ proto string inflate_add(resource context, string encoded_data[, int flush_mode = ZLIB_SYNC_FLUSH])
792   Incrementally inflate encoded data in the specified context */
793PHP_FUNCTION(inflate_add)
794{
795    zend_string *out;
796    char *in_buf;
797    size_t in_len, buffer_used = 0, CHUNK_SIZE = 8192;
798    zval *res;
799    z_stream *ctx;
800    zend_long flush_type = Z_SYNC_FLUSH;
801    int status;
802
803    if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS(), "rs|l", &res, &in_buf, &in_len, &flush_type)) {
804        RETURN_FALSE;
805    }
806
807    if (!(ctx = zend_fetch_resource_ex(res, NULL, le_inflate))) {
808        php_error_docref(NULL, E_WARNING, "Invalid zlib.inflate resource");
809        RETURN_FALSE;
810    }
811
812    switch (flush_type) {
813        case Z_NO_FLUSH:
814        case Z_PARTIAL_FLUSH:
815        case Z_SYNC_FLUSH:
816        case Z_FULL_FLUSH:
817        case Z_BLOCK:
818        case Z_FINISH:
819            break;
820
821        default:
822            php_error_docref(NULL, E_WARNING,
823                "flush mode must be ZLIB_NO_FLUSH, ZLIB_PARTIAL_FLUSH, ZLIB_SYNC_FLUSH, ZLIB_FULL_FLUSH, ZLIB_BLOCK or ZLIB_FINISH");
824            RETURN_FALSE;
825    }
826
827    if (in_len <= 0 && flush_type != Z_FINISH) {
828        RETURN_EMPTY_STRING();
829    }
830
831    out = zend_string_alloc((in_len > CHUNK_SIZE) ? in_len : CHUNK_SIZE, 0);
832    ctx->next_in = (Bytef *) in_buf;
833    ctx->next_out = (Bytef *) out->val;
834    ctx->avail_in = in_len;
835    ctx->avail_out = out->len;
836
837    do {
838        status = inflate(ctx, flush_type);
839        buffer_used = out->len - ctx->avail_out;
840
841        switch (status) {
842        case Z_OK:
843            if (ctx->avail_out == 0) {
844                /* more output buffer space needed; realloc and try again */
845                out = zend_string_realloc(out, out->len + CHUNK_SIZE, 0);
846                ctx->avail_out = CHUNK_SIZE;
847                ctx->next_out = (Bytef *) out->val + buffer_used;
848                break;
849            } else {
850                goto complete;
851            }
852        case Z_STREAM_END:
853            inflateReset(ctx);
854            goto complete;
855        case Z_BUF_ERROR:
856            if (flush_type == Z_FINISH && ctx->avail_out == 0) {
857                /* more output buffer space needed; realloc and try again */
858                out = zend_string_realloc(out, out->len + CHUNK_SIZE, 0);
859                ctx->avail_out = CHUNK_SIZE;
860                ctx->next_out = (Bytef *) out->val + buffer_used;
861                break;
862            } else {
863                /* No more input data; we're finished */
864                goto complete;
865            }
866        default:
867            zend_string_release(out);
868            php_error_docref(NULL, E_WARNING, "%s", zError(status));
869            RETURN_FALSE;
870        }
871    } while (1);
872
873    complete: {
874        out = zend_string_realloc(out, buffer_used, 0);
875        out->val[buffer_used] = 0;
876        RETURN_STR(out);
877    }
878}
879/* }}} */
880
881/* {{{ proto resource deflate_init(int encoding[, array options])
882   Initialize an incremental deflate context using the specified encoding */
883PHP_FUNCTION(deflate_init)
884{
885    z_stream *ctx;
886    zend_long encoding, level = -1, memory = 8;
887    HashTable *options = 0;
888    zval *option_buffer, cast_option_buffer;
889
890    if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS(), "l|H", &encoding, &options)) {
891        RETURN_FALSE;
892    }
893
894    if (options && (option_buffer = zend_symtable_str_find(options, "level", sizeof("level")-1)) != NULL) {
895        if (Z_TYPE_P(option_buffer) != IS_LONG) {
896            ZVAL_DUP(&cast_option_buffer, option_buffer);
897            convert_to_long(&cast_option_buffer);
898            level = Z_LVAL(cast_option_buffer);
899            zval_dtor(&cast_option_buffer);
900        } else {
901            level = Z_LVAL_P(option_buffer);
902        }
903    }
904    if (level < -1 || level > 9) {
905        php_error_docref(NULL, E_WARNING, "compression level (%pd) must be within -1..9", level);
906        RETURN_FALSE;
907    }
908
909    if (options && (option_buffer = zend_symtable_str_find(options, "memory", sizeof("memory")-1)) != NULL) {
910        if (Z_TYPE_P(option_buffer) != IS_LONG) {
911            ZVAL_DUP(&cast_option_buffer, option_buffer);
912            convert_to_long(&cast_option_buffer);
913            memory = Z_LVAL(cast_option_buffer);
914            zval_dtor(&cast_option_buffer);
915        } else {
916            memory = Z_LVAL_P(option_buffer);
917        }
918    }
919    if (memory < 1 || memory > 9) {
920        php_error_docref(NULL, E_WARNING, "compression memory level (%pd) must be within 1..9", memory);
921        RETURN_FALSE;
922    }
923
924    /* @TODO: in the future we may add "strategy" and "dictionary" options */
925
926    switch (encoding) {
927        case PHP_ZLIB_ENCODING_RAW:
928        case PHP_ZLIB_ENCODING_GZIP:
929        case PHP_ZLIB_ENCODING_DEFLATE:
930            break;
931        default:
932            php_error_docref(NULL, E_WARNING,
933                "encoding mode must be ZLIB_ENCODING_RAW, ZLIB_ENCODING_GZIP or ZLIB_ENCODING_DEFLATE");
934            RETURN_FALSE;
935    }
936
937    ctx = ecalloc(1, sizeof(php_zlib_context));
938    ctx->zalloc = php_zlib_alloc;
939    ctx->zfree = php_zlib_free;
940
941    if (Z_OK == deflateInit2(ctx, level, Z_DEFLATED, encoding, memory, Z_DEFAULT_STRATEGY)) {
942        RETURN_RES(zend_register_resource(ctx, le_deflate));
943    } else {
944        efree(ctx);
945        php_error_docref(NULL, E_WARNING, "failed allocating zlib.deflate context");
946        RETURN_FALSE;
947    }
948}
949/* }}} */
950
951/* {{{ proto string deflate_add(resource context, string data[, int flush_mode = ZLIB_SYNC_FLUSH])
952   Incrementally deflate data in the specified context */
953PHP_FUNCTION(deflate_add)
954{
955    zend_string *out;
956    char *in_buf;
957    size_t in_len, out_size;
958    zval *res;
959    z_stream *ctx;
960    zend_long flush_type = Z_SYNC_FLUSH;
961    int status;
962
963    if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS(), "rs|l", &res, &in_buf, &in_len, &flush_type)) {
964        return;
965    }
966
967    if (!(ctx = zend_fetch_resource_ex(res, NULL, le_deflate))) {
968        php_error_docref(NULL, E_WARNING, "Invalid deflate resource");
969        RETURN_FALSE;
970    }
971
972    switch (flush_type) {
973        case Z_NO_FLUSH:
974        case Z_PARTIAL_FLUSH:
975        case Z_SYNC_FLUSH:
976        case Z_FULL_FLUSH:
977        case Z_BLOCK:
978        case Z_FINISH:
979            break;
980
981        default:
982            php_error_docref(NULL, E_WARNING,
983                "flush mode must be ZLIB_NO_FLUSH, ZLIB_PARTIAL_FLUSH, ZLIB_SYNC_FLUSH, ZLIB_FULL_FLUSH, ZLIB_BLOCK or ZLIB_FINISH");
984            RETURN_FALSE;
985    }
986
987    if (in_len <= 0 && flush_type != Z_FINISH) {
988        RETURN_EMPTY_STRING();
989    }
990
991    out_size = PHP_ZLIB_BUFFER_SIZE_GUESS(ctx->total_in + in_len);
992    out_size = (ctx->total_out >= out_size) ? 16 : (out_size - ctx->total_out);
993    out_size = (out_size < 16) ? 16 : out_size;
994    out = zend_string_alloc(out_size, 0);
995
996    ctx->next_in = (Bytef *) in_buf;
997    ctx->next_out = (Bytef *) out->val;
998    ctx->avail_in = in_len;
999    ctx->avail_out = out->len;
1000
1001    status = deflate(ctx, flush_type);
1002    switch (status) {
1003        case Z_OK:
1004            out->len = (char *) ctx->next_out - out->val;
1005            out->val[out->len] = 0;
1006            RETURN_STR(out);
1007            break;
1008        case Z_STREAM_END:
1009            out->len = (char *) ctx->next_out - out->val;
1010            out->val[out->len] = 0;
1011            deflateReset(ctx);
1012            RETURN_STR(out);
1013            break;
1014        default:
1015            zend_string_release(out);
1016            php_error_docref(NULL, E_WARNING, "zlib error (%s)", zError(status));
1017            RETURN_FALSE;
1018    }
1019}
1020/* }}} */
1021
1022#ifdef COMPILE_DL_ZLIB
1023#ifdef ZTS
1024ZEND_TSRMLS_CACHE_DEFINE();
1025#endif
1026ZEND_GET_MODULE(php_zlib)
1027#endif
1028
1029/* {{{ arginfo */
1030ZEND_BEGIN_ARG_INFO_EX(arginfo_ob_gzhandler, 0, 0, 2)
1031    ZEND_ARG_INFO(0, data)
1032    ZEND_ARG_INFO(0, flags)
1033ZEND_END_ARG_INFO()
1034
1035ZEND_BEGIN_ARG_INFO(arginfo_zlib_get_coding_type, 0)
1036ZEND_END_ARG_INFO()
1037
1038ZEND_BEGIN_ARG_INFO_EX(arginfo_gzfile, 0, 0, 1)
1039    ZEND_ARG_INFO(0, filename)
1040    ZEND_ARG_INFO(0, use_include_path)
1041ZEND_END_ARG_INFO()
1042
1043ZEND_BEGIN_ARG_INFO_EX(arginfo_gzopen, 0, 0, 2)
1044    ZEND_ARG_INFO(0, filename)
1045    ZEND_ARG_INFO(0, mode)
1046    ZEND_ARG_INFO(0, use_include_path)
1047ZEND_END_ARG_INFO()
1048
1049ZEND_BEGIN_ARG_INFO_EX(arginfo_readgzfile, 0, 0, 1)
1050    ZEND_ARG_INFO(0, filename)
1051    ZEND_ARG_INFO(0, use_include_path)
1052ZEND_END_ARG_INFO()
1053
1054ZEND_BEGIN_ARG_INFO_EX(arginfo_zlib_encode, 0, 0, 2)
1055    ZEND_ARG_INFO(0, data)
1056    ZEND_ARG_INFO(0, encoding)
1057    ZEND_ARG_INFO(0, level)
1058ZEND_END_ARG_INFO()
1059
1060ZEND_BEGIN_ARG_INFO_EX(arginfo_zlib_decode, 0, 0, 1)
1061    ZEND_ARG_INFO(0, data)
1062    ZEND_ARG_INFO(0, max_decoded_len)
1063ZEND_END_ARG_INFO()
1064
1065ZEND_BEGIN_ARG_INFO_EX(arginfo_gzdeflate, 0, 0, 1)
1066    ZEND_ARG_INFO(0, data)
1067    ZEND_ARG_INFO(0, level)
1068    ZEND_ARG_INFO(0, encoding)
1069ZEND_END_ARG_INFO()
1070
1071ZEND_BEGIN_ARG_INFO_EX(arginfo_gzencode, 0, 0, 1)
1072    ZEND_ARG_INFO(0, data)
1073    ZEND_ARG_INFO(0, level)
1074    ZEND_ARG_INFO(0, encoding)
1075ZEND_END_ARG_INFO()
1076
1077ZEND_BEGIN_ARG_INFO_EX(arginfo_gzcompress, 0, 0, 1)
1078    ZEND_ARG_INFO(0, data)
1079    ZEND_ARG_INFO(0, level)
1080    ZEND_ARG_INFO(0, encoding)
1081ZEND_END_ARG_INFO()
1082
1083ZEND_BEGIN_ARG_INFO_EX(arginfo_gzinflate, 0, 0, 1)
1084    ZEND_ARG_INFO(0, data)
1085    ZEND_ARG_INFO(0, max_decoded_len)
1086ZEND_END_ARG_INFO()
1087
1088ZEND_BEGIN_ARG_INFO_EX(arginfo_gzdecode, 0, 0, 1)
1089    ZEND_ARG_INFO(0, data)
1090    ZEND_ARG_INFO(0, max_decoded_len)
1091ZEND_END_ARG_INFO()
1092
1093ZEND_BEGIN_ARG_INFO_EX(arginfo_gzuncompress, 0, 0, 1)
1094    ZEND_ARG_INFO(0, data)
1095    ZEND_ARG_INFO(0, max_decoded_len)
1096ZEND_END_ARG_INFO()
1097
1098ZEND_BEGIN_ARG_INFO_EX(arginfo_gzputs, 0, 0, 2)
1099    ZEND_ARG_INFO(0, fp)
1100    ZEND_ARG_INFO(0, str)
1101    ZEND_ARG_INFO(0, length)
1102ZEND_END_ARG_INFO()
1103
1104ZEND_BEGIN_ARG_INFO(arginfo_gzpassthru, 0)
1105    ZEND_ARG_INFO(0, fp)
1106ZEND_END_ARG_INFO()
1107
1108ZEND_BEGIN_ARG_INFO_EX(arginfo_gzseek, 0, 0, 2)
1109    ZEND_ARG_INFO(0, fp)
1110    ZEND_ARG_INFO(0, offset)
1111    ZEND_ARG_INFO(0, whence)
1112ZEND_END_ARG_INFO()
1113
1114ZEND_BEGIN_ARG_INFO(arginfo_gzread, 0)
1115    ZEND_ARG_INFO(0, fp)
1116    ZEND_ARG_INFO(0, length)
1117ZEND_END_ARG_INFO()
1118
1119ZEND_BEGIN_ARG_INFO_EX(arginfo_gzgetss, 0, 0, 1)
1120    ZEND_ARG_INFO(0, fp)
1121    ZEND_ARG_INFO(0, length)
1122    ZEND_ARG_INFO(0, allowable_tags)
1123ZEND_END_ARG_INFO()
1124
1125ZEND_BEGIN_ARG_INFO_EX(arginfo_gzgets, 0, 0, 1)
1126    ZEND_ARG_INFO(0, fp)
1127    ZEND_ARG_INFO(0, length)
1128ZEND_END_ARG_INFO()
1129
1130ZEND_BEGIN_ARG_INFO_EX(arginfo_deflate_init, 0, 0, 1)
1131    ZEND_ARG_INFO(0, encoding)
1132    ZEND_ARG_INFO(0, level)
1133ZEND_END_ARG_INFO()
1134
1135ZEND_BEGIN_ARG_INFO_EX(arginfo_deflate_add, 0, 0, 2)
1136    ZEND_ARG_INFO(0, resource)
1137    ZEND_ARG_INFO(0, add)
1138    ZEND_ARG_INFO(0, flush_behavior)
1139ZEND_END_ARG_INFO()
1140
1141ZEND_BEGIN_ARG_INFO_EX(arginfo_inflate_init, 0, 0, 1)
1142    ZEND_ARG_INFO(0, encoding)
1143ZEND_END_ARG_INFO()
1144
1145ZEND_BEGIN_ARG_INFO_EX(arginfo_inflate_add, 0, 0, 2)
1146    ZEND_ARG_INFO(0, resource)
1147    ZEND_ARG_INFO(0, flush_behavior)
1148ZEND_END_ARG_INFO()
1149
1150/* }}} */
1151
1152/* {{{ php_zlib_functions[] */
1153static const zend_function_entry php_zlib_functions[] = {
1154    PHP_FE(readgzfile,                      arginfo_readgzfile)
1155    PHP_FALIAS(gzrewind,    rewind,         arginfo_gzpassthru)
1156    PHP_FALIAS(gzclose,     fclose,         arginfo_gzpassthru)
1157    PHP_FALIAS(gzeof,       feof,           arginfo_gzpassthru)
1158    PHP_FALIAS(gzgetc,      fgetc,          arginfo_gzpassthru)
1159    PHP_FALIAS(gzgets,      fgets,          arginfo_gzgets)
1160    PHP_FALIAS(gzgetss,     fgetss,         arginfo_gzgetss)
1161    PHP_FALIAS(gzread,      fread,          arginfo_gzread)
1162    PHP_FE(gzopen,                          arginfo_gzopen)
1163    PHP_FALIAS(gzpassthru,  fpassthru,      arginfo_gzpassthru)
1164    PHP_FALIAS(gzseek,      fseek,          arginfo_gzseek)
1165    PHP_FALIAS(gztell,      ftell,          arginfo_gzpassthru)
1166    PHP_FALIAS(gzwrite,     fwrite,         arginfo_gzputs)
1167    PHP_FALIAS(gzputs,      fwrite,         arginfo_gzputs)
1168    PHP_FE(gzfile,                          arginfo_gzfile)
1169    PHP_FE(gzcompress,                      arginfo_gzcompress)
1170    PHP_FE(gzuncompress,                    arginfo_gzuncompress)
1171    PHP_FE(gzdeflate,                       arginfo_gzdeflate)
1172    PHP_FE(gzinflate,                       arginfo_gzinflate)
1173    PHP_FE(gzencode,                        arginfo_gzencode)
1174    PHP_FE(gzdecode,                        arginfo_gzdecode)
1175    PHP_FE(zlib_encode,                     arginfo_zlib_encode)
1176    PHP_FE(zlib_decode,                     arginfo_zlib_decode)
1177    PHP_FE(zlib_get_coding_type,            arginfo_zlib_get_coding_type)
1178    PHP_FE(deflate_init,                    arginfo_deflate_init)
1179    PHP_FE(deflate_add,                     arginfo_deflate_add)
1180    PHP_FE(inflate_init,                    arginfo_inflate_init)
1181    PHP_FE(inflate_add,                     arginfo_inflate_add)
1182    PHP_FE(ob_gzhandler,                    arginfo_ob_gzhandler)
1183    PHP_FE_END
1184};
1185/* }}} */
1186
1187/* {{{ OnUpdate_zlib_output_compression */
1188static PHP_INI_MH(OnUpdate_zlib_output_compression)
1189{
1190    int int_value;
1191    char *ini_value;
1192    zend_long *p;
1193#ifndef ZTS
1194    char *base = (char *) mh_arg2;
1195#else
1196    char *base;
1197
1198    base = (char *) ts_resource(*((int *) mh_arg2));
1199#endif
1200
1201    if (new_value == NULL) {
1202        return FAILURE;
1203    }
1204
1205    if (!strncasecmp(new_value->val, "off", sizeof("off"))) {
1206        int_value = 0;
1207    } else if (!strncasecmp(new_value->val, "on", sizeof("on"))) {
1208        int_value = 1;
1209    } else {
1210        int_value = zend_atoi(new_value->val, new_value->len);
1211    }
1212    ini_value = zend_ini_string("output_handler", sizeof("output_handler"), 0);
1213
1214    if (ini_value && *ini_value && int_value) {
1215        php_error_docref("ref.outcontrol", E_CORE_ERROR, "Cannot use both zlib.output_compression and output_handler together!!");
1216        return FAILURE;
1217    }
1218    if (stage == PHP_INI_STAGE_RUNTIME) {
1219        int status = php_output_get_status();
1220        if (status & PHP_OUTPUT_SENT) {
1221            php_error_docref("ref.outcontrol", E_WARNING, "Cannot change zlib.output_compression - headers already sent");
1222            return FAILURE;
1223        }
1224    }
1225
1226    p = (zend_long *) (base+(size_t) mh_arg1);
1227    *p = int_value;
1228
1229    ZLIBG(output_compression) = ZLIBG(output_compression_default);
1230    if (stage == PHP_INI_STAGE_RUNTIME && int_value) {
1231        if (!php_output_handler_started(ZEND_STRL(PHP_ZLIB_OUTPUT_HANDLER_NAME))) {
1232            php_zlib_output_compression_start();
1233        }
1234    }
1235
1236    return SUCCESS;
1237}
1238/* }}} */
1239
1240/* {{{ OnUpdate_zlib_output_handler */
1241static PHP_INI_MH(OnUpdate_zlib_output_handler)
1242{
1243    if (stage == PHP_INI_STAGE_RUNTIME && (php_output_get_status() & PHP_OUTPUT_SENT)) {
1244        php_error_docref("ref.outcontrol", E_WARNING, "Cannot change zlib.output_handler - headers already sent");
1245        return FAILURE;
1246    }
1247
1248    return OnUpdateString(entry, new_value, mh_arg1, mh_arg2, mh_arg3, stage);
1249}
1250/* }}} */
1251
1252/* {{{ INI */
1253PHP_INI_BEGIN()
1254    STD_PHP_INI_BOOLEAN("zlib.output_compression",      "0", PHP_INI_ALL, OnUpdate_zlib_output_compression,       output_compression_default,       zend_zlib_globals, zlib_globals)
1255    STD_PHP_INI_ENTRY("zlib.output_compression_level", "-1", PHP_INI_ALL, OnUpdateLong,                           output_compression_level, zend_zlib_globals, zlib_globals)
1256    STD_PHP_INI_ENTRY("zlib.output_handler",             "", PHP_INI_ALL, OnUpdate_zlib_output_handler,           output_handler,           zend_zlib_globals, zlib_globals)
1257PHP_INI_END()
1258
1259/* }}} */
1260
1261/* {{{ PHP_MINIT_FUNCTION */
1262static PHP_MINIT_FUNCTION(zlib)
1263{
1264    php_register_url_stream_wrapper("compress.zlib", &php_stream_gzip_wrapper);
1265    php_stream_filter_register_factory("zlib.*", &php_zlib_filter_factory);
1266
1267    php_output_handler_alias_register(ZEND_STRL("ob_gzhandler"), php_zlib_output_handler_init);
1268    php_output_handler_conflict_register(ZEND_STRL("ob_gzhandler"), php_zlib_output_conflict_check);
1269    php_output_handler_conflict_register(ZEND_STRL(PHP_ZLIB_OUTPUT_HANDLER_NAME), php_zlib_output_conflict_check);
1270
1271    le_deflate = zend_register_list_destructors_ex(deflate_rsrc_dtor, NULL, "zlib.deflate", module_number);
1272    le_inflate = zend_register_list_destructors_ex(inflate_rsrc_dtor, NULL, "zlib.inflate", module_number);
1273
1274    REGISTER_LONG_CONSTANT("FORCE_GZIP", PHP_ZLIB_ENCODING_GZIP, CONST_CS|CONST_PERSISTENT);
1275    REGISTER_LONG_CONSTANT("FORCE_DEFLATE", PHP_ZLIB_ENCODING_DEFLATE, CONST_CS|CONST_PERSISTENT);
1276
1277    REGISTER_LONG_CONSTANT("ZLIB_ENCODING_RAW", PHP_ZLIB_ENCODING_RAW, CONST_CS|CONST_PERSISTENT);
1278    REGISTER_LONG_CONSTANT("ZLIB_ENCODING_GZIP", PHP_ZLIB_ENCODING_GZIP, CONST_CS|CONST_PERSISTENT);
1279    REGISTER_LONG_CONSTANT("ZLIB_ENCODING_DEFLATE", PHP_ZLIB_ENCODING_DEFLATE, CONST_CS|CONST_PERSISTENT);
1280
1281    REGISTER_LONG_CONSTANT("ZLIB_NO_FLUSH", Z_NO_FLUSH, CONST_CS|CONST_PERSISTENT);
1282    REGISTER_LONG_CONSTANT("ZLIB_PARTIAL_FLUSH", Z_PARTIAL_FLUSH, CONST_CS|CONST_PERSISTENT);
1283    REGISTER_LONG_CONSTANT("ZLIB_SYNC_FLUSH", Z_SYNC_FLUSH, CONST_CS|CONST_PERSISTENT);
1284    REGISTER_LONG_CONSTANT("ZLIB_FULL_FLUSH", Z_FULL_FLUSH, CONST_CS|CONST_PERSISTENT);
1285    REGISTER_LONG_CONSTANT("ZLIB_BLOCK", Z_BLOCK, CONST_CS|CONST_PERSISTENT);
1286    REGISTER_LONG_CONSTANT("ZLIB_FINISH", Z_FINISH, CONST_CS|CONST_PERSISTENT);
1287    REGISTER_INI_ENTRIES();
1288    return SUCCESS;
1289}
1290/* }}} */
1291
1292/* {{{ PHP_MSHUTDOWN_FUNCTION */
1293static PHP_MSHUTDOWN_FUNCTION(zlib)
1294{
1295    php_unregister_url_stream_wrapper("zlib");
1296    php_stream_filter_unregister_factory("zlib.*");
1297
1298    UNREGISTER_INI_ENTRIES();
1299
1300    return SUCCESS;
1301}
1302/* }}} */
1303
1304/* {{{ PHP_RINIT_FUNCTION */
1305static PHP_RINIT_FUNCTION(zlib)
1306{
1307    ZLIBG(compression_coding) = 0;
1308    if (!ZLIBG(handler_registered)) {
1309        ZLIBG(output_compression) = ZLIBG(output_compression_default);
1310        php_zlib_output_compression_start();
1311    }
1312
1313    return SUCCESS;
1314}
1315/* }}} */
1316
1317/* {{{ PHP_RSHUTDOWN_FUNCTION */
1318static PHP_RSHUTDOWN_FUNCTION(zlib)
1319{
1320    php_zlib_cleanup_ob_gzhandler_mess();
1321    ZLIBG(handler_registered) = 0;
1322
1323    return SUCCESS;
1324}
1325/* }}} */
1326
1327/* {{{ PHP_MINFO_FUNCTION */
1328static PHP_MINFO_FUNCTION(zlib)
1329{
1330    php_info_print_table_start();
1331    php_info_print_table_header(2, "ZLib Support", "enabled");
1332    php_info_print_table_row(2, "Stream Wrapper", "compress.zlib://");
1333    php_info_print_table_row(2, "Stream Filter", "zlib.inflate, zlib.deflate");
1334    php_info_print_table_row(2, "Compiled Version", ZLIB_VERSION);
1335    php_info_print_table_row(2, "Linked Version", (char *) zlibVersion());
1336    php_info_print_table_end();
1337
1338    DISPLAY_INI_ENTRIES();
1339}
1340/* }}} */
1341
1342/* {{{ ZEND_MODULE_GLOBALS_CTOR */
1343static PHP_GINIT_FUNCTION(zlib)
1344{
1345#if defined(COMPILE_DL_ZLIB) && defined(ZTS)
1346    ZEND_TSRMLS_CACHE_UPDATE();
1347#endif
1348    zlib_globals->ob_gzhandler = NULL;
1349    zlib_globals->handler_registered = 0;
1350}
1351/* }}} */
1352
1353/* {{{ php_zlib_module_entry */
1354zend_module_entry php_zlib_module_entry = {
1355    STANDARD_MODULE_HEADER,
1356    "zlib",
1357    php_zlib_functions,
1358    PHP_MINIT(zlib),
1359    PHP_MSHUTDOWN(zlib),
1360    PHP_RINIT(zlib),
1361    PHP_RSHUTDOWN(zlib),
1362    PHP_MINFO(zlib),
1363    PHP_ZLIB_VERSION,
1364    PHP_MODULE_GLOBALS(zlib),
1365    PHP_GINIT(zlib),
1366    NULL,
1367    NULL,
1368    STANDARD_MODULE_PROPERTIES_EX
1369};
1370/* }}} */
1371
1372/*
1373 * Local variables:
1374 * tab-width: 4
1375 * c-basic-offset: 4
1376 * End:
1377 * vim600: sw=4 ts=4 fdm=marker
1378 * vim<600: sw=4 ts=4
1379 */
1380