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