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