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