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