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