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
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_is_auto_global(ZEND_STRL("_SERVER") TSRMLS_CC);
86        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)) {
87            convert_to_string(*enc);
88            if (strstr(Z_STRVAL_PP(enc), "gzip")) {
89                ZLIBG(compression_coding) = PHP_ZLIB_ENCODING_GZIP;
90            } else if (strstr(Z_STRVAL_PP(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(TSRMLS_C)) {
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 TSRMLS_CC);
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 TSRMLS_CC)) {
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 TSRMLS_CC);
227                        break;
228                    case PHP_ZLIB_ENCODING_DEFLATE:
229                        sapi_add_header_ex(ZEND_STRL("Content-Encoding: deflate"), 1, 1 TSRMLS_CC);
230                        break;
231                    default:
232                        deflateEnd(&ctx->Z);
233                        return FAILURE;
234                }
235                sapi_add_header_ex(ZEND_STRL("Vary: Accept-Encoding"), 1, 0 TSRMLS_CC);
236                php_output_handler_hook(PHP_OUTPUT_HANDLER_HOOK_IMMUTABLE, NULL TSRMLS_CC);
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(TSRMLS_D)
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 TSRMLS_DC)
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 TSRMLS_DC)
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 TSRMLS_CC))) {
281        php_output_handler_set_context(h, php_zlib_output_handler_context_init(TSRMLS_C), php_zlib_output_handler_context_dtor TSRMLS_CC);
282    }
283
284    return h;
285}
286/* }}} */
287
288/* {{{ php_zlib_output_compression_start() */
289static void php_zlib_output_compression_start(TSRMLS_D)
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(TSRMLS_C) &&
302                    (h = php_zlib_output_handler_init(ZEND_STRL(PHP_ZLIB_OUTPUT_HANDLER_NAME), ZLIBG(output_compression), PHP_OUTPUT_HANDLER_STDFLAGS TSRMLS_CC)) &&
303                    (SUCCESS == php_output_handler_start(h TSRMLS_CC))) {
304                if (ZLIBG(output_handler) && *ZLIBG(output_handler)) {
305                    MAKE_STD_ZVAL(zoh);
306                    ZVAL_STRING(zoh, ZLIBG(output_handler), 1);
307                    php_output_start_user(zoh, ZLIBG(output_compression), PHP_OUTPUT_HANDLER_STDFLAGS TSRMLS_CC);
308                    zval_ptr_dtor(&zoh);
309                }
310            }
311            break;
312    }
313}
314/* }}} */
315
316/* {{{ php_zlib_encode() */
317static 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)
318{
319    int status;
320    z_stream Z;
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_len = PHP_ZLIB_BUFFER_SIZE_GUESS(in_len);
328        *out_buf = emalloc(*out_len);
329
330        Z.next_in = (Bytef *) in_buf;
331        Z.next_out = (Bytef *) *out_buf;
332        Z.avail_in = in_len;
333        Z.avail_out = *out_len;
334
335        status = deflate(&Z, Z_FINISH);
336        deflateEnd(&Z);
337
338        if (Z_STREAM_END == status) {
339            /* size buffer down to actual length */
340            *out_buf = erealloc(*out_buf, Z.total_out + 1);
341            (*out_buf)[*out_len = Z.total_out] = '\0';
342            return SUCCESS;
343        } else {
344            efree(*out_buf);
345        }
346    }
347
348    *out_buf = NULL;
349    *out_len = 0;
350
351    php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", zError(status));
352    return FAILURE;
353}
354/* }}} */
355
356/* {{{ php_zlib_inflate_rounds() */
357static inline int php_zlib_inflate_rounds(z_stream *Z, size_t max, char **buf, size_t *len)
358{
359    int status, round = 0;
360    php_zlib_buffer buffer = {NULL, NULL, 0, 0, 0};
361
362    *buf = NULL;
363    *len = 0;
364
365    buffer.size = (max && (max < Z->avail_in)) ? max : Z->avail_in;
366
367    do {
368        if ((max && (max <= buffer.used)) || !(buffer.aptr = erealloc_recoverable(buffer.data, buffer.size))) {
369            status = Z_MEM_ERROR;
370        } else {
371            buffer.data = buffer.aptr;
372            Z->avail_out = buffer.free = buffer.size - buffer.used;
373            Z->next_out = (Bytef *) buffer.data + buffer.used;
374#if 0
375            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);
376#endif
377            status = inflate(Z, Z_NO_FLUSH);
378
379            buffer.used += buffer.free - Z->avail_out;
380            buffer.free = Z->avail_out;
381#if 0
382            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);
383#endif
384            buffer.size += (buffer.size >> 3) + 1;
385        }
386    } while ((Z_BUF_ERROR == status || (Z_OK == status && Z->avail_in)) && ++round < 100);
387
388    if (status == Z_STREAM_END) {
389        buffer.data = erealloc(buffer.data, buffer.used + 1);
390        buffer.data[buffer.used] = '\0';
391        *buf = buffer.data;
392        *len = buffer.used;
393    } else {
394        if (buffer.data) {
395            efree(buffer.data);
396        }
397        /* HACK: See zlib/examples/zpipe.c inf() function for explanation. */
398        /* This works as long as this function is not used for streaming. Required to catch very short invalid data. */
399        status = (status == Z_OK) ? Z_DATA_ERROR : status;
400    }
401    return status;
402}
403/* }}} */
404
405/* {{{ php_zlib_decode() */
406static 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)
407{
408    int status = Z_DATA_ERROR;
409    z_stream Z;
410
411    memset(&Z, 0, sizeof(z_stream));
412    Z.zalloc = php_zlib_alloc;
413    Z.zfree = php_zlib_free;
414
415    if (in_len) {
416retry_raw_inflate:
417        status = inflateInit2(&Z, encoding);
418        if (Z_OK == status) {
419            Z.next_in = (Bytef *) in_buf;
420            Z.avail_in = in_len + 1; /* NOTE: data must be zero terminated */
421
422            switch (status = php_zlib_inflate_rounds(&Z, max_len, out_buf, out_len)) {
423                case Z_STREAM_END:
424                    inflateEnd(&Z);
425                    return SUCCESS;
426
427                case Z_DATA_ERROR:
428                    /* raw deflated data? */
429                    if (PHP_ZLIB_ENCODING_ANY == encoding) {
430                        inflateEnd(&Z);
431                        encoding = PHP_ZLIB_ENCODING_RAW;
432                        goto retry_raw_inflate;
433                    }
434            }
435            inflateEnd(&Z);
436        }
437    }
438
439    *out_buf = NULL;
440    *out_len = 0;
441
442    php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", zError(status));
443    return FAILURE;
444}
445/* }}} */
446
447/* {{{ php_zlib_cleanup_ob_gzhandler_mess() */
448static void php_zlib_cleanup_ob_gzhandler_mess(TSRMLS_D)
449{
450    if (ZLIBG(ob_gzhandler)) {
451        deflateEnd(&(ZLIBG(ob_gzhandler)->Z));
452        php_zlib_output_handler_context_dtor(ZLIBG(ob_gzhandler) TSRMLS_CC);
453        ZLIBG(ob_gzhandler) = NULL;
454    }
455}
456/* }}} */
457
458/* {{{ proto string ob_gzhandler(string data, int flags)
459   Legacy hack */
460static PHP_FUNCTION(ob_gzhandler)
461{
462    char *in_str;
463    int in_len;
464    long flags = 0;
465    php_output_context ctx = {0};
466    int encoding, rv;
467
468    /*
469     * NOTE that the real ob_gzhandler is an alias to "zlib output compression".
470     * This is a really bad hack, because
471     * - we have to initialize a php_zlib_context on demand
472     * - we have to clean it up in RSHUTDOWN
473     * - OG(running) is not set or set to any other output handler
474     * - we have to mess around with php_output_context */
475
476    if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sl", &in_str, &in_len, &flags)) {
477        RETURN_FALSE;
478    }
479
480    if (!(encoding = php_zlib_output_encoding(TSRMLS_C))) {
481        RETURN_FALSE;
482    }
483
484    if (flags & PHP_OUTPUT_HANDLER_START) {
485        switch (encoding) {
486            case PHP_ZLIB_ENCODING_GZIP:
487                sapi_add_header_ex(ZEND_STRL("Content-Encoding: gzip"), 1, 1 TSRMLS_CC);
488                break;
489            case PHP_ZLIB_ENCODING_DEFLATE:
490                sapi_add_header_ex(ZEND_STRL("Content-Encoding: deflate"), 1, 1 TSRMLS_CC);
491                break;
492        }
493        sapi_add_header_ex(ZEND_STRL("Vary: Accept-Encoding"), 1, 0 TSRMLS_CC);
494    }
495
496    if (!ZLIBG(ob_gzhandler)) {
497        ZLIBG(ob_gzhandler) = php_zlib_output_handler_context_init(TSRMLS_C);
498    }
499
500    TSRMLS_SET_CTX(ctx.tsrm_ls);
501    ctx.op = flags;
502    ctx.in.data = in_str;
503    ctx.in.used = in_len;
504
505    rv = php_zlib_output_handler_ex(ZLIBG(ob_gzhandler), &ctx);
506
507    if (SUCCESS != rv) {
508        if (ctx.out.data && ctx.out.free) {
509            efree(ctx.out.data);
510        }
511        php_zlib_cleanup_ob_gzhandler_mess(TSRMLS_C);
512        RETURN_FALSE;
513    }
514
515    if (ctx.out.data) {
516        RETVAL_STRINGL(ctx.out.data, ctx.out.used, 1);
517        if (ctx.out.free) {
518            efree(ctx.out.data);
519        }
520    } else {
521        RETVAL_EMPTY_STRING();
522    }
523}
524/* }}} */
525
526/* {{{ proto string zlib_get_coding_type(void)
527   Returns the coding type used for output compression */
528static PHP_FUNCTION(zlib_get_coding_type)
529{
530    if (zend_parse_parameters_none() == FAILURE) {
531        return;
532    }
533    switch (ZLIBG(compression_coding)) {
534        case PHP_ZLIB_ENCODING_GZIP:
535            RETURN_STRINGL("gzip", sizeof("gzip") - 1, 1);
536        case PHP_ZLIB_ENCODING_DEFLATE:
537            RETURN_STRINGL("deflate", sizeof("deflate") - 1, 1);
538        default:
539            RETURN_FALSE;
540    }
541}
542/* }}} */
543
544/* {{{ proto array gzfile(string filename [, int use_include_path])
545   Read and uncompress entire .gz-file into an array */
546static PHP_FUNCTION(gzfile)
547{
548    char *filename;
549    int filename_len;
550    int flags = REPORT_ERRORS;
551    char buf[8192] = {0};
552    register int i = 0;
553    long use_include_path = 0;
554    php_stream *stream;
555
556    if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "p|l", &filename, &filename_len, &use_include_path)) {
557        return;
558    }
559
560    if (use_include_path) {
561        flags |= USE_PATH;
562    }
563
564    /* using a stream here is a bit more efficient (resource wise) than php_gzopen_wrapper */
565    stream = php_stream_gzopen(NULL, filename, "rb", flags, NULL, NULL STREAMS_CC TSRMLS_CC);
566
567    if (!stream) {
568        /* Error reporting is already done by stream code */
569        RETURN_FALSE;
570    }
571
572    /* Initialize return array */
573    array_init(return_value);
574
575    /* Now loop through the file and do the magic quotes thing if needed */
576    memset(buf, 0, sizeof(buf));
577
578    while (php_stream_gets(stream, buf, sizeof(buf) - 1) != NULL) {
579        add_index_string(return_value, i++, buf, 1);
580    }
581    php_stream_close(stream);
582}
583/* }}} */
584
585/* {{{ proto resource gzopen(string filename, string mode [, int use_include_path])
586   Open a .gz-file and return a .gz-file pointer */
587static PHP_FUNCTION(gzopen)
588{
589    char *filename;
590    char *mode;
591    int filename_len, mode_len;
592    int flags = REPORT_ERRORS;
593    php_stream *stream;
594    long use_include_path = 0;
595
596    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss|l", &filename, &filename_len, &mode, &mode_len, &use_include_path) == FAILURE) {
597        return;
598    }
599
600    if (use_include_path) {
601        flags |= USE_PATH;
602    }
603
604    stream = php_stream_gzopen(NULL, filename, mode, flags, NULL, NULL STREAMS_CC TSRMLS_CC);
605
606    if (!stream) {
607        RETURN_FALSE;
608    }
609    php_stream_to_zval(stream, return_value);
610}
611/* }}} */
612
613/* {{{ proto int readgzfile(string filename [, int use_include_path])
614   Output a .gz-file */
615static PHP_FUNCTION(readgzfile)
616{
617    char *filename;
618    int filename_len;
619    int flags = REPORT_ERRORS;
620    php_stream *stream;
621    int size;
622    long use_include_path = 0;
623
624    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|l", &filename, &filename_len, &use_include_path) == FAILURE) {
625        return;
626    }
627
628    if (use_include_path) {
629        flags |= USE_PATH;
630    }
631
632    stream = php_stream_gzopen(NULL, filename, "rb", flags, NULL, NULL STREAMS_CC TSRMLS_CC);
633
634    if (!stream) {
635        RETURN_FALSE;
636    }
637    size = php_stream_passthru(stream);
638    php_stream_close(stream);
639    RETURN_LONG(size);
640}
641/* }}} */
642
643#define PHP_ZLIB_ENCODE_FUNC(name, default_encoding) \
644static PHP_FUNCTION(name) \
645{ \
646    char *in_buf, *out_buf; \
647    int in_len; \
648    size_t out_len; \
649    long level = -1; \
650    long encoding = default_encoding; \
651    if (default_encoding) { \
652        if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|ll", &in_buf, &in_len, &level, &encoding)) { \
653            return; \
654        } \
655    } else { \
656        if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sl|l", &in_buf, &in_len, &encoding, &level)) { \
657            return; \
658        } \
659    } \
660    if (level < -1 || level > 9) { \
661        php_error_docref(NULL TSRMLS_CC, E_WARNING, "compression level (%ld) must be within -1..9", level); \
662        RETURN_FALSE; \
663    } \
664    switch (encoding) { \
665        case PHP_ZLIB_ENCODING_RAW: \
666        case PHP_ZLIB_ENCODING_GZIP: \
667        case PHP_ZLIB_ENCODING_DEFLATE: \
668            break; \
669        default: \
670            php_error_docref(NULL TSRMLS_CC, E_WARNING, "encoding mode must be either ZLIB_ENCODING_RAW, ZLIB_ENCODING_GZIP or ZLIB_ENCODING_DEFLATE"); \
671            RETURN_FALSE; \
672    } \
673    if (SUCCESS != php_zlib_encode(in_buf, in_len, &out_buf, &out_len, encoding, level TSRMLS_CC)) { \
674        RETURN_FALSE; \
675    } \
676    RETURN_STRINGL(out_buf, out_len, 0); \
677}
678
679#define PHP_ZLIB_DECODE_FUNC(name, encoding) \
680static PHP_FUNCTION(name) \
681{ \
682    char *in_buf, *out_buf; \
683    int in_len; \
684    size_t out_len; \
685    long max_len = 0; \
686    if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|l", &in_buf, &in_len, &max_len)) { \
687        return; \
688    } \
689    if (max_len < 0) { \
690        php_error_docref(NULL TSRMLS_CC, E_WARNING, "length (%ld) must be greater or equal zero", max_len); \
691        RETURN_FALSE; \
692    } \
693    if (SUCCESS != php_zlib_decode(in_buf, in_len, &out_buf, &out_len, encoding, max_len TSRMLS_CC)) { \
694        RETURN_FALSE; \
695    } \
696    RETURN_STRINGL(out_buf, out_len, 0); \
697}
698
699/* {{{ proto binary zlib_encode(binary data, int encoding[, int level = -1])
700   Compress data with the specified encoding */
701PHP_ZLIB_ENCODE_FUNC(zlib_encode, 0);
702/* }}} */
703
704/* {{{ proto binary zlib_decode(binary data[, int max_decoded_len])
705   Uncompress any raw/gzip/zlib encoded data */
706PHP_ZLIB_DECODE_FUNC(zlib_decode, PHP_ZLIB_ENCODING_ANY);
707/* }}} */
708
709/* NOTE: The naming of these userland functions was quite unlucky */
710/* {{{ proto binary gzdeflate(binary data[, int level = -1[, int encoding = ZLIB_ENCODING_RAW])
711   Encode data with the raw deflate encoding */
712PHP_ZLIB_ENCODE_FUNC(gzdeflate, PHP_ZLIB_ENCODING_RAW);
713/* }}} */
714
715/* {{{ proto binary gzencode(binary data[, int level = -1[, int encoding = ZLIB_ENCODING_GZIP])
716   Encode data with the gzip encoding */
717PHP_ZLIB_ENCODE_FUNC(gzencode, PHP_ZLIB_ENCODING_GZIP);
718/* }}} */
719
720/* {{{ proto binary gzcompress(binary data[, int level = -1[, int encoding = ZLIB_ENCODING_DEFLATE])
721   Encode data with the zlib encoding */
722PHP_ZLIB_ENCODE_FUNC(gzcompress, PHP_ZLIB_ENCODING_DEFLATE);
723/* }}} */
724
725/* {{{ proto binary gzinflate(binary data[, int max_decoded_len])
726   Decode raw deflate encoded data */
727PHP_ZLIB_DECODE_FUNC(gzinflate, PHP_ZLIB_ENCODING_RAW);
728/* }}} */
729
730/* {{{ proto binary gzdecode(binary data[, int max_decoded_len])
731   Decode gzip encoded data */
732PHP_ZLIB_DECODE_FUNC(gzdecode, PHP_ZLIB_ENCODING_GZIP);
733/* }}} */
734
735/* {{{ proto binary gzuncompress(binary data[, int max_decoded_len])
736   Decode zlib encoded data */
737PHP_ZLIB_DECODE_FUNC(gzuncompress, PHP_ZLIB_ENCODING_DEFLATE);
738/* }}} */
739
740#ifdef COMPILE_DL_ZLIB
741ZEND_GET_MODULE(php_zlib)
742#endif
743
744/* {{{ arginfo */
745ZEND_BEGIN_ARG_INFO_EX(arginfo_ob_gzhandler, 0, 0, 2)
746    ZEND_ARG_INFO(0, data)
747    ZEND_ARG_INFO(0, flags)
748ZEND_END_ARG_INFO()
749
750ZEND_BEGIN_ARG_INFO(arginfo_zlib_get_coding_type, 0)
751ZEND_END_ARG_INFO()
752
753ZEND_BEGIN_ARG_INFO_EX(arginfo_gzfile, 0, 0, 1)
754    ZEND_ARG_INFO(0, filename)
755    ZEND_ARG_INFO(0, use_include_path)
756ZEND_END_ARG_INFO()
757
758ZEND_BEGIN_ARG_INFO_EX(arginfo_gzopen, 0, 0, 2)
759    ZEND_ARG_INFO(0, filename)
760    ZEND_ARG_INFO(0, mode)
761    ZEND_ARG_INFO(0, use_include_path)
762ZEND_END_ARG_INFO()
763
764ZEND_BEGIN_ARG_INFO_EX(arginfo_readgzfile, 0, 0, 1)
765    ZEND_ARG_INFO(0, filename)
766    ZEND_ARG_INFO(0, use_include_path)
767ZEND_END_ARG_INFO()
768
769ZEND_BEGIN_ARG_INFO_EX(arginfo_zlib_encode, 0, 0, 2)
770    ZEND_ARG_INFO(0, data)
771    ZEND_ARG_INFO(0, encoding)
772    ZEND_ARG_INFO(0, level)
773ZEND_END_ARG_INFO()
774
775ZEND_BEGIN_ARG_INFO_EX(arginfo_zlib_decode, 0, 0, 1)
776    ZEND_ARG_INFO(0, data)
777    ZEND_ARG_INFO(0, max_decoded_len)
778ZEND_END_ARG_INFO()
779
780ZEND_BEGIN_ARG_INFO_EX(arginfo_gzdeflate, 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_gzencode, 0, 0, 1)
787    ZEND_ARG_INFO(0, data)
788    ZEND_ARG_INFO(0, level)
789    ZEND_ARG_INFO(0, encoding)
790ZEND_END_ARG_INFO()
791
792ZEND_BEGIN_ARG_INFO_EX(arginfo_gzcompress, 0, 0, 1)
793    ZEND_ARG_INFO(0, data)
794    ZEND_ARG_INFO(0, level)
795    ZEND_ARG_INFO(0, encoding)
796ZEND_END_ARG_INFO()
797
798ZEND_BEGIN_ARG_INFO_EX(arginfo_gzinflate, 0, 0, 1)
799    ZEND_ARG_INFO(0, data)
800    ZEND_ARG_INFO(0, max_decoded_len)
801ZEND_END_ARG_INFO()
802
803ZEND_BEGIN_ARG_INFO_EX(arginfo_gzdecode, 0, 0, 1)
804    ZEND_ARG_INFO(0, data)
805    ZEND_ARG_INFO(0, max_decoded_len)
806ZEND_END_ARG_INFO()
807
808ZEND_BEGIN_ARG_INFO_EX(arginfo_gzuncompress, 0, 0, 1)
809    ZEND_ARG_INFO(0, data)
810    ZEND_ARG_INFO(0, max_decoded_len)
811ZEND_END_ARG_INFO()
812
813ZEND_BEGIN_ARG_INFO_EX(arginfo_gzputs, 0, 0, 2)
814    ZEND_ARG_INFO(0, fp)
815    ZEND_ARG_INFO(0, str)
816    ZEND_ARG_INFO(0, length)
817ZEND_END_ARG_INFO()
818
819ZEND_BEGIN_ARG_INFO(arginfo_gzpassthru, 0)
820    ZEND_ARG_INFO(0, fp)
821ZEND_END_ARG_INFO()
822
823ZEND_BEGIN_ARG_INFO_EX(arginfo_gzseek, 0, 0, 2)
824    ZEND_ARG_INFO(0, fp)
825    ZEND_ARG_INFO(0, offset)
826    ZEND_ARG_INFO(0, whence)
827ZEND_END_ARG_INFO()
828
829ZEND_BEGIN_ARG_INFO(arginfo_gzread, 0)
830    ZEND_ARG_INFO(0, fp)
831    ZEND_ARG_INFO(0, length)
832ZEND_END_ARG_INFO()
833
834ZEND_BEGIN_ARG_INFO_EX(arginfo_gzgetss, 0, 0, 1)
835    ZEND_ARG_INFO(0, fp)
836    ZEND_ARG_INFO(0, length)
837    ZEND_ARG_INFO(0, allowable_tags)
838ZEND_END_ARG_INFO()
839
840ZEND_BEGIN_ARG_INFO_EX(arginfo_gzgets, 0, 0, 1)
841    ZEND_ARG_INFO(0, fp)
842    ZEND_ARG_INFO(0, length)
843ZEND_END_ARG_INFO()
844/* }}} */
845
846/* {{{ php_zlib_functions[] */
847static const zend_function_entry php_zlib_functions[] = {
848    PHP_FE(readgzfile,                      arginfo_readgzfile)
849    PHP_FALIAS(gzrewind,    rewind,         arginfo_gzpassthru)
850    PHP_FALIAS(gzclose,     fclose,         arginfo_gzpassthru)
851    PHP_FALIAS(gzeof,       feof,           arginfo_gzpassthru)
852    PHP_FALIAS(gzgetc,      fgetc,          arginfo_gzpassthru)
853    PHP_FALIAS(gzgets,      fgets,          arginfo_gzgets)
854    PHP_FALIAS(gzgetss,     fgetss,         arginfo_gzgetss)
855    PHP_FALIAS(gzread,      fread,          arginfo_gzread)
856    PHP_FE(gzopen,                          arginfo_gzopen)
857    PHP_FALIAS(gzpassthru,  fpassthru,      arginfo_gzpassthru)
858    PHP_FALIAS(gzseek,      fseek,          arginfo_gzseek)
859    PHP_FALIAS(gztell,      ftell,          arginfo_gzpassthru)
860    PHP_FALIAS(gzwrite,     fwrite,         arginfo_gzputs)
861    PHP_FALIAS(gzputs,      fwrite,         arginfo_gzputs)
862    PHP_FE(gzfile,                          arginfo_gzfile)
863    PHP_FE(gzcompress,                      arginfo_gzcompress)
864    PHP_FE(gzuncompress,                    arginfo_gzuncompress)
865    PHP_FE(gzdeflate,                       arginfo_gzdeflate)
866    PHP_FE(gzinflate,                       arginfo_gzinflate)
867    PHP_FE(gzencode,                        arginfo_gzencode)
868    PHP_FE(gzdecode,                        arginfo_gzdecode)
869    PHP_FE(zlib_encode,                     arginfo_zlib_encode)
870    PHP_FE(zlib_decode,                     arginfo_zlib_decode)
871    PHP_FE(zlib_get_coding_type,            arginfo_zlib_get_coding_type)
872    PHP_FE(ob_gzhandler,                    arginfo_ob_gzhandler)
873    PHP_FE_END
874};
875/* }}} */
876
877/* {{{ OnUpdate_zlib_output_compression */
878static PHP_INI_MH(OnUpdate_zlib_output_compression)
879{
880    int status, int_value;
881    char *ini_value;
882
883    if (new_value == NULL) {
884        return FAILURE;
885    }
886
887    if (!strncasecmp(new_value, "off", sizeof("off"))) {
888        new_value = "0";
889        new_value_length = sizeof("0");
890    } else if (!strncasecmp(new_value, "on", sizeof("on"))) {
891        new_value = "1";
892        new_value_length = sizeof("1");
893    }
894
895    int_value = zend_atoi(new_value, new_value_length);
896    ini_value = zend_ini_string("output_handler", sizeof("output_handler"), 0);
897
898    if (ini_value && *ini_value && int_value) {
899        php_error_docref("ref.outcontrol" TSRMLS_CC, E_CORE_ERROR, "Cannot use both zlib.output_compression and output_handler together!!");
900        return FAILURE;
901    }
902    if (stage == PHP_INI_STAGE_RUNTIME) {
903        status = php_output_get_status(TSRMLS_C);
904        if (status & PHP_OUTPUT_SENT) {
905            php_error_docref("ref.outcontrol" TSRMLS_CC, E_WARNING, "Cannot change zlib.output_compression - headers already sent");
906            return FAILURE;
907        }
908    }
909
910    status = OnUpdateLong(entry, new_value, new_value_length, mh_arg1, mh_arg2, mh_arg3, stage TSRMLS_CC);
911
912    ZLIBG(output_compression) = ZLIBG(output_compression_default);
913    if (stage == PHP_INI_STAGE_RUNTIME && int_value) {
914        if (!php_output_handler_started(ZEND_STRL(PHP_ZLIB_OUTPUT_HANDLER_NAME) TSRMLS_CC)) {
915            php_zlib_output_compression_start(TSRMLS_C);
916        }
917    }
918
919    return status;
920}
921/* }}} */
922
923/* {{{ OnUpdate_zlib_output_handler */
924static PHP_INI_MH(OnUpdate_zlib_output_handler)
925{
926    if (stage == PHP_INI_STAGE_RUNTIME && (php_output_get_status(TSRMLS_C) & PHP_OUTPUT_SENT)) {
927        php_error_docref("ref.outcontrol" TSRMLS_CC, E_WARNING, "Cannot change zlib.output_handler - headers already sent");
928        return FAILURE;
929    }
930
931    return OnUpdateString(entry, new_value, new_value_length, mh_arg1, mh_arg2, mh_arg3, stage TSRMLS_CC);
932}
933/* }}} */
934
935/* {{{ INI */
936PHP_INI_BEGIN()
937    STD_PHP_INI_BOOLEAN("zlib.output_compression",      "0", PHP_INI_ALL, OnUpdate_zlib_output_compression,       output_compression_default,       zend_zlib_globals, zlib_globals)
938    STD_PHP_INI_ENTRY("zlib.output_compression_level", "-1", PHP_INI_ALL, OnUpdateLong,                           output_compression_level, zend_zlib_globals, zlib_globals)
939    STD_PHP_INI_ENTRY("zlib.output_handler",             "", PHP_INI_ALL, OnUpdate_zlib_output_handler,           output_handler,           zend_zlib_globals, zlib_globals)
940PHP_INI_END()
941
942/* }}} */
943
944/* {{{ PHP_MINIT_FUNCTION */
945static PHP_MINIT_FUNCTION(zlib)
946{
947    php_register_url_stream_wrapper("compress.zlib", &php_stream_gzip_wrapper TSRMLS_CC);
948    php_stream_filter_register_factory("zlib.*", &php_zlib_filter_factory TSRMLS_CC);
949
950    php_output_handler_alias_register(ZEND_STRL("ob_gzhandler"), php_zlib_output_handler_init TSRMLS_CC);
951    php_output_handler_conflict_register(ZEND_STRL("ob_gzhandler"), php_zlib_output_conflict_check TSRMLS_CC);
952    php_output_handler_conflict_register(ZEND_STRL(PHP_ZLIB_OUTPUT_HANDLER_NAME), php_zlib_output_conflict_check TSRMLS_CC);
953
954    REGISTER_LONG_CONSTANT("FORCE_GZIP", PHP_ZLIB_ENCODING_GZIP, CONST_CS|CONST_PERSISTENT);
955    REGISTER_LONG_CONSTANT("FORCE_DEFLATE", PHP_ZLIB_ENCODING_DEFLATE, CONST_CS|CONST_PERSISTENT);
956
957    REGISTER_LONG_CONSTANT("ZLIB_ENCODING_RAW", PHP_ZLIB_ENCODING_RAW, CONST_CS|CONST_PERSISTENT);
958    REGISTER_LONG_CONSTANT("ZLIB_ENCODING_GZIP", PHP_ZLIB_ENCODING_GZIP, CONST_CS|CONST_PERSISTENT);
959    REGISTER_LONG_CONSTANT("ZLIB_ENCODING_DEFLATE", PHP_ZLIB_ENCODING_DEFLATE, CONST_CS|CONST_PERSISTENT);
960    REGISTER_INI_ENTRIES();
961    return SUCCESS;
962}
963/* }}} */
964
965/* {{{ PHP_MSHUTDOWN_FUNCTION */
966static PHP_MSHUTDOWN_FUNCTION(zlib)
967{
968    php_unregister_url_stream_wrapper("zlib" TSRMLS_CC);
969    php_stream_filter_unregister_factory("zlib.*" TSRMLS_CC);
970
971    UNREGISTER_INI_ENTRIES();
972
973    return SUCCESS;
974}
975/* }}} */
976
977/* {{{ PHP_RINIT_FUNCTION */
978static PHP_RINIT_FUNCTION(zlib)
979{
980    ZLIBG(compression_coding) = 0;
981    if (!ZLIBG(handler_registered)) {
982        ZLIBG(output_compression) = ZLIBG(output_compression_default);
983        php_zlib_output_compression_start(TSRMLS_C);
984    }
985
986    return SUCCESS;
987}
988/* }}} */
989
990/* {{{ PHP_RSHUTDOWN_FUNCTION */
991static PHP_RSHUTDOWN_FUNCTION(zlib)
992{
993    php_zlib_cleanup_ob_gzhandler_mess(TSRMLS_C);
994    ZLIBG(handler_registered) = 0;
995
996    return SUCCESS;
997}
998/* }}} */
999
1000/* {{{ PHP_MINFO_FUNCTION */
1001static PHP_MINFO_FUNCTION(zlib)
1002{
1003    php_info_print_table_start();
1004    php_info_print_table_header(2, "ZLib Support", "enabled");
1005    php_info_print_table_row(2, "Stream Wrapper", "compress.zlib://");
1006    php_info_print_table_row(2, "Stream Filter", "zlib.inflate, zlib.deflate");
1007    php_info_print_table_row(2, "Compiled Version", ZLIB_VERSION);
1008    php_info_print_table_row(2, "Linked Version", (char *) zlibVersion());
1009    php_info_print_table_end();
1010
1011    DISPLAY_INI_ENTRIES();
1012}
1013/* }}} */
1014
1015/* {{{ ZEND_MODULE_GLOBALS_CTOR */
1016static ZEND_MODULE_GLOBALS_CTOR_D(zlib)
1017{
1018    zlib_globals->ob_gzhandler = NULL;
1019    zlib_globals->handler_registered = 0;
1020}
1021/* }}} */
1022
1023/* {{{ php_zlib_module_entry */
1024zend_module_entry php_zlib_module_entry = {
1025    STANDARD_MODULE_HEADER,
1026    "zlib",
1027    php_zlib_functions,
1028    PHP_MINIT(zlib),
1029    PHP_MSHUTDOWN(zlib),
1030    PHP_RINIT(zlib),
1031    PHP_RSHUTDOWN(zlib),
1032    PHP_MINFO(zlib),
1033    "2.0",
1034    PHP_MODULE_GLOBALS(zlib),
1035    ZEND_MODULE_GLOBALS_CTOR_N(zlib),
1036    NULL,
1037    NULL,
1038    STANDARD_MODULE_PROPERTIES_EX
1039};
1040/* }}} */
1041
1042/*
1043 * Local variables:
1044 * tab-width: 4
1045 * c-basic-offset: 4
1046 * End:
1047 * vim600: sw=4 ts=4 fdm=marker
1048 * vim<600: sw=4 ts=4
1049 */
1050