1/* 2 +----------------------------------------------------------------------+ 3 | PHP Version 5 | 4 +----------------------------------------------------------------------+ 5 | Copyright (c) 1997-2013 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 +----------------------------------------------------------------------+ 20 */ 21 22/* $Id$ */ 23 24#ifdef HAVE_CONFIG_H 25#include "config.h" 26#endif 27 28#include "php.h" 29#include "SAPI.h" 30#include "php_ini.h" 31 32#include <stdlib.h> 33#include <errno.h> 34#include <sys/types.h> 35#include <sys/stat.h> 36#include <fcntl.h> 37 38#ifdef PHP_WIN32 39# define O_RDONLY _O_RDONLY 40# include "win32/param.h" 41#else 42# include <sys/param.h> 43/* #include <sys/uio.h> */ 44#endif 45 46#include "ext/standard/head.h" 47#include "safe_mode.h" 48#include "ext/standard/php_standard.h" 49#include "ext/standard/info.h" 50#include "php_zlib.h" 51#include "fopen_wrappers.h" 52 53#if HAVE_PWD_H 54# ifdef PHP_WIN32 55# include "win32/pwd.h" 56# else 57# include <pwd.h> 58# endif 59#endif 60 61#if defined(HAVE_UNISTD_H) && defined(PHP_WIN32) 62# undef HAVE_UNISTD_H 63#endif 64 65#ifdef COMPILE_DL_ZLIB 66# ifndef PUTS 67# define PUTS(a) php_printf("%s",a) 68# endif 69# ifndef PUTC 70# define PUTC(a) PUTS(a) 71# endif 72# ifndef PHPWRITE 73# define PHPWRITE(a,n) php_write((a),(n) TSRMLS_CC) 74# endif 75#endif 76 77/* Win32 needs some more memory */ 78#ifdef PHP_WIN32 79# define PHP_ZLIB_MODIFIER 100 80#else 81# define PHP_ZLIB_MODIFIER 1000 82#endif 83 84#define OS_CODE 0x03 /* FIXME */ 85#define GZIP_HEADER_LENGTH 10 86#define GZIP_FOOTER_LENGTH 8 87 88/* True globals, no need for thread safety */ 89static const int gz_magic[2] = {0x1f, 0x8b}; /* gzip magic header */ 90 91static int php_zlib_output_compression_start(TSRMLS_D); 92 93static PHP_MINIT_FUNCTION(zlib); 94static PHP_MSHUTDOWN_FUNCTION(zlib); 95static PHP_RINIT_FUNCTION(zlib); 96static PHP_MINFO_FUNCTION(zlib); 97static PHP_FUNCTION(gzopen); 98static PHP_FUNCTION(readgzfile); 99static PHP_FUNCTION(gzfile); 100static PHP_FUNCTION(gzcompress); 101static PHP_FUNCTION(gzuncompress); 102static PHP_FUNCTION(gzdeflate); 103static PHP_FUNCTION(gzinflate); 104static PHP_FUNCTION(gzencode); 105static PHP_FUNCTION(ob_gzhandler); 106static PHP_FUNCTION(zlib_get_coding_type); 107 108/* {{{ arginfo */ 109ZEND_BEGIN_ARG_INFO_EX(arginfo_gzfile, 0, 0, 1) 110 ZEND_ARG_INFO(0, filename) 111 ZEND_ARG_INFO(0, use_include_path) 112ZEND_END_ARG_INFO() 113 114ZEND_BEGIN_ARG_INFO_EX(arginfo_gzopen, 0, 0, 2) 115 ZEND_ARG_INFO(0, filename) 116 ZEND_ARG_INFO(0, mode) 117 ZEND_ARG_INFO(0, use_include_path) 118ZEND_END_ARG_INFO() 119 120ZEND_BEGIN_ARG_INFO_EX(arginfo_readgzfile, 0, 0, 1) 121 ZEND_ARG_INFO(0, filename) 122 ZEND_ARG_INFO(0, use_include_path) 123ZEND_END_ARG_INFO() 124 125ZEND_BEGIN_ARG_INFO_EX(arginfo_gzcompress, 0, 0, 1) 126 ZEND_ARG_INFO(0, data) 127 ZEND_ARG_INFO(0, level) 128ZEND_END_ARG_INFO() 129 130ZEND_BEGIN_ARG_INFO_EX(arginfo_gzuncompress, 0, 0, 1) 131 ZEND_ARG_INFO(0, data) 132 ZEND_ARG_INFO(0, length) 133ZEND_END_ARG_INFO() 134 135ZEND_BEGIN_ARG_INFO_EX(arginfo_gzdeflate, 0, 0, 1) 136 ZEND_ARG_INFO(0, data) 137 ZEND_ARG_INFO(0, level) 138ZEND_END_ARG_INFO() 139 140ZEND_BEGIN_ARG_INFO_EX(arginfo_gzinflate, 0, 0, 1) 141 ZEND_ARG_INFO(0, data) 142 ZEND_ARG_INFO(0, length) 143ZEND_END_ARG_INFO() 144 145ZEND_BEGIN_ARG_INFO(arginfo_zlib_get_coding_type, 0) 146ZEND_END_ARG_INFO() 147 148ZEND_BEGIN_ARG_INFO_EX(arginfo_gzencode, 0, 0, 1) 149 ZEND_ARG_INFO(0, data) 150 ZEND_ARG_INFO(0, level) 151 ZEND_ARG_INFO(0, encoding_mode) 152ZEND_END_ARG_INFO() 153 154ZEND_BEGIN_ARG_INFO_EX(arginfo_ob_gzhandler, 0, 0, 2) 155 ZEND_ARG_INFO(0, str) 156 ZEND_ARG_INFO(0, mode) 157ZEND_END_ARG_INFO() 158 159ZEND_BEGIN_ARG_INFO_EX(arginfo_gzputs, 0, 0, 2) 160 ZEND_ARG_INFO(0, fp) 161 ZEND_ARG_INFO(0, str) 162 ZEND_ARG_INFO(0, length) 163ZEND_END_ARG_INFO() 164 165ZEND_BEGIN_ARG_INFO(arginfo_gzpassthru, 0) 166 ZEND_ARG_INFO(0, fp) 167ZEND_END_ARG_INFO() 168 169ZEND_BEGIN_ARG_INFO_EX(arginfo_gzseek, 0, 0, 2) 170 ZEND_ARG_INFO(0, fp) 171 ZEND_ARG_INFO(0, offset) 172 ZEND_ARG_INFO(0, whence) 173ZEND_END_ARG_INFO() 174 175ZEND_BEGIN_ARG_INFO(arginfo_gzread, 0) 176 ZEND_ARG_INFO(0, fp) 177 ZEND_ARG_INFO(0, length) 178ZEND_END_ARG_INFO() 179 180ZEND_BEGIN_ARG_INFO_EX(arginfo_gzgetss, 0, 0, 1) 181 ZEND_ARG_INFO(0, fp) 182 ZEND_ARG_INFO(0, length) 183 ZEND_ARG_INFO(0, allowable_tags) 184ZEND_END_ARG_INFO() 185 186ZEND_BEGIN_ARG_INFO_EX(arginfo_gzgets, 0, 0, 1) 187 ZEND_ARG_INFO(0, fp) 188 ZEND_ARG_INFO(0, length) 189ZEND_END_ARG_INFO() 190/* }}} */ 191 192/* {{{ php_zlib_functions[] 193 */ 194static const zend_function_entry php_zlib_functions[] = { 195 PHP_FE(readgzfile, arginfo_readgzfile) 196 PHP_FALIAS(gzrewind, rewind, arginfo_gzpassthru) 197 PHP_FALIAS(gzclose, fclose, arginfo_gzpassthru) 198 PHP_FALIAS(gzeof, feof, arginfo_gzpassthru) 199 PHP_FALIAS(gzgetc, fgetc, arginfo_gzpassthru) 200 PHP_FALIAS(gzgets, fgets, arginfo_gzgets) 201 PHP_FALIAS(gzgetss, fgetss, arginfo_gzgetss) 202 PHP_FALIAS(gzread, fread, arginfo_gzread) 203 PHP_FE(gzopen, arginfo_gzopen) 204 PHP_FALIAS(gzpassthru, fpassthru, arginfo_gzpassthru) 205 PHP_FALIAS(gzseek, fseek, arginfo_gzseek) 206 PHP_FALIAS(gztell, ftell, arginfo_gzpassthru) 207 PHP_FALIAS(gzwrite, fwrite, arginfo_gzputs) 208 PHP_FALIAS(gzputs, fwrite, arginfo_gzputs) 209 PHP_FE(gzfile, arginfo_gzfile) 210 PHP_FE(gzcompress, arginfo_gzcompress) 211 PHP_FE(gzuncompress, arginfo_gzuncompress) 212 PHP_FE(gzdeflate, arginfo_gzdeflate) 213 PHP_FE(gzinflate, arginfo_gzinflate) 214 PHP_FE(gzencode, arginfo_gzencode) 215 PHP_FE(ob_gzhandler, arginfo_ob_gzhandler) 216 PHP_FE(zlib_get_coding_type, arginfo_zlib_get_coding_type) 217 PHP_FE_END 218}; 219/* }}} */ 220 221ZEND_DECLARE_MODULE_GLOBALS(zlib) 222 223/* {{{ php_zlib_module_entry 224 */ 225zend_module_entry php_zlib_module_entry = { 226 STANDARD_MODULE_HEADER, 227 "zlib", 228 php_zlib_functions, 229 PHP_MINIT(zlib), 230 PHP_MSHUTDOWN(zlib), 231 PHP_RINIT(zlib), 232 NULL, 233 PHP_MINFO(zlib), 234 "1.1", 235 PHP_MODULE_GLOBALS(zlib), 236 NULL, 237 NULL, 238 NULL, 239 STANDARD_MODULE_PROPERTIES_EX 240}; 241/* }}} */ 242 243#ifdef COMPILE_DL_ZLIB 244ZEND_GET_MODULE(php_zlib) 245#endif 246 247/* {{{ Memory management wrappers */ 248 249static voidpf php_zlib_alloc(voidpf opaque, uInt items, uInt size) 250{ 251 return (voidpf)safe_emalloc(items, size, 0); 252} 253 254static void php_zlib_free(voidpf opaque, voidpf address) 255{ 256 efree((void*)address); 257} 258/* }}} */ 259 260/* {{{ OnUpdate_zlib_output_compression */ 261static PHP_INI_MH(OnUpdate_zlib_output_compression) 262{ 263 int status, int_value; 264 char *ini_value; 265 266 if (new_value == NULL) { 267 return FAILURE; 268 } 269 270 if (!strncasecmp(new_value, "off", sizeof("off"))) { 271 new_value = "0"; 272 new_value_length = sizeof("0"); 273 } else if (!strncasecmp(new_value, "on", sizeof("on"))) { 274 new_value = "1"; 275 new_value_length = sizeof("1"); 276 } 277 278 int_value = zend_atoi(new_value, new_value_length); 279 ini_value = zend_ini_string("output_handler", sizeof("output_handler"), 0); 280 281 if (ini_value && *ini_value && int_value) { 282 php_error_docref("ref.outcontrol" TSRMLS_CC, E_CORE_ERROR, "Cannot use both zlib.output_compression and output_handler together!!"); 283 return FAILURE; 284 } 285 286 if (stage == PHP_INI_STAGE_RUNTIME && SG(headers_sent) && !SG(request_info).no_headers) { 287 php_error_docref("ref.outcontrol" TSRMLS_CC, E_WARNING, "Cannot change zlib.output_compression - headers already sent"); 288 return FAILURE; 289 } 290 291 status = OnUpdateLong(entry, new_value, new_value_length, mh_arg1, mh_arg2, mh_arg3, stage TSRMLS_CC); 292 293 if (stage == PHP_INI_STAGE_RUNTIME && int_value) { 294 status = php_zlib_output_compression_start(TSRMLS_C); 295 } 296 297 return status; 298} 299/* }}} */ 300 301/* {{{ OnUpdate_zlib_output_compression_level */ 302static PHP_INI_MH(OnUpdate_zlib_output_compression_level) 303{ 304 OnUpdateLong(entry, new_value, new_value_length, mh_arg1, mh_arg2, mh_arg3, stage TSRMLS_CC); 305 306 return SUCCESS; 307} 308/* }}} */ 309 310/* {{{ OnUpdate_zlib_output_handler */ 311static PHP_INI_MH(OnUpdate_zlib_output_handler) 312{ 313 if (stage == PHP_INI_STAGE_RUNTIME && SG(headers_sent) && !SG(request_info).no_headers) { 314 php_error_docref("ref.outcontrol" TSRMLS_CC, E_WARNING, "Cannot change zlib.output_handler - headers already sent"); 315 return FAILURE; 316 } 317 318 OnUpdateString(entry, new_value, new_value_length, mh_arg1, mh_arg2, mh_arg3, stage TSRMLS_CC); 319 320 return SUCCESS; 321} 322/* }}} */ 323 324 325PHP_INI_BEGIN() 326 STD_PHP_INI_BOOLEAN("zlib.output_compression", "0", PHP_INI_ALL, OnUpdate_zlib_output_compression, output_compression, zend_zlib_globals, zlib_globals) 327 STD_PHP_INI_ENTRY("zlib.output_compression_level", "-1", PHP_INI_ALL, OnUpdate_zlib_output_compression_level, output_compression_level, zend_zlib_globals, zlib_globals) 328 STD_PHP_INI_ENTRY("zlib.output_handler", "", PHP_INI_ALL, OnUpdate_zlib_output_handler, output_handler, zend_zlib_globals, zlib_globals) 329PHP_INI_END() 330 331/* {{{ PHP_MINIT_FUNCTION 332 */ 333static PHP_MINIT_FUNCTION(zlib) 334{ 335 php_register_url_stream_wrapper("compress.zlib", &php_stream_gzip_wrapper TSRMLS_CC); 336 php_stream_filter_register_factory("zlib.*", &php_zlib_filter_factory TSRMLS_CC); 337 338 REGISTER_LONG_CONSTANT("FORCE_GZIP", CODING_GZIP, CONST_CS | CONST_PERSISTENT); 339 REGISTER_LONG_CONSTANT("FORCE_DEFLATE", CODING_DEFLATE, CONST_CS | CONST_PERSISTENT); 340 341 REGISTER_INI_ENTRIES(); 342 343 return SUCCESS; 344} 345/* }}} */ 346 347/* {{{ PHP_RINIT_FUNCTION 348 */ 349static PHP_RINIT_FUNCTION(zlib) 350{ 351 ZLIBG(ob_gzhandler_status) = 0; 352 ZLIBG(compression_coding) = 0; 353 354 php_zlib_output_compression_start(TSRMLS_C); 355 356 return SUCCESS; 357} 358/* }}} */ 359 360/* {{{ PHP_MSHUTDOWN_FUNCTION 361 */ 362static PHP_MSHUTDOWN_FUNCTION(zlib) 363{ 364 php_unregister_url_stream_wrapper("zlib" TSRMLS_CC); 365 php_stream_filter_unregister_factory("zlib.*" TSRMLS_CC); 366 367 UNREGISTER_INI_ENTRIES(); 368 369 return SUCCESS; 370} 371/* }}} */ 372 373/* {{{ PHP_MINFO_FUNCTION 374 */ 375static PHP_MINFO_FUNCTION(zlib) 376{ 377 php_info_print_table_start(); 378 php_info_print_table_row(2, "ZLib Support", "enabled"); 379 php_info_print_table_row(2, "Stream Wrapper support", "compress.zlib://"); 380 php_info_print_table_row(2, "Stream Filter support", "zlib.inflate, zlib.deflate"); 381 php_info_print_table_row(2, "Compiled Version", ZLIB_VERSION); 382 php_info_print_table_row(2, "Linked Version", (char *) zlibVersion()); 383 php_info_print_table_end(); 384 385 DISPLAY_INI_ENTRIES(); 386} 387/* }}} */ 388 389/* {{{ proto array gzfile(string filename [, int use_include_path]) 390 Read und uncompress entire .gz-file into an array */ 391static PHP_FUNCTION(gzfile) 392{ 393 char *filename; 394 int filename_len; 395 long flags = 0; 396 char *slashed, buf[8192]; 397 register int i = 0; 398 int use_include_path = 0; 399 php_stream *stream; 400 401 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|l", &filename, &filename_len, &flags) == FAILURE) { 402 return; 403 } 404 405 use_include_path = flags ? USE_PATH : 0; 406 407 /* using a stream here is a bit more efficient (resource wise) than php_gzopen_wrapper */ 408 stream = php_stream_gzopen(NULL, filename, "rb", use_include_path | ENFORCE_SAFE_MODE | REPORT_ERRORS, NULL, NULL STREAMS_CC TSRMLS_CC); 409 if (stream == NULL) { 410 /* Error reporting is already done by stream code */ 411 RETURN_FALSE; 412 } 413 414 /* Initialize return array */ 415 array_init(return_value); 416 417 /* Now loop through the file and do the magic quotes thing if needed */ 418 memset(buf,0,sizeof(buf)); 419 420 while (php_stream_gets(stream, buf, sizeof(buf) - 1) != NULL) { 421 if (PG(magic_quotes_runtime)) { 422 int len; 423 424 slashed = php_addslashes(buf, 0, &len, 0 TSRMLS_CC); /* 0 = don't free source string */ 425 add_index_stringl(return_value, i++, slashed, len, 0); 426 } else { 427 add_index_string(return_value, i++, buf, 1); 428 } 429 } 430 php_stream_close(stream); 431} 432/* }}} */ 433 434/* {{{ proto resource gzopen(string filename, string mode [, int use_include_path]) 435 Open a .gz-file and return a .gz-file pointer */ 436static PHP_FUNCTION(gzopen) 437{ 438 char *filename, *mode; 439 int filename_len, mode_len; 440 long flags = 0; 441 php_stream *stream; 442 int use_include_path = 0; 443 444 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss|l", &filename, &filename_len, &mode, &mode_len, &flags) == FAILURE) { 445 return; 446 } 447 448 use_include_path = flags ? USE_PATH : 0; 449 450 stream = php_stream_gzopen(NULL, filename, mode, use_include_path | ENFORCE_SAFE_MODE | REPORT_ERRORS, NULL, NULL STREAMS_CC TSRMLS_CC); 451 452 if (!stream) { 453 RETURN_FALSE; 454 } 455 php_stream_to_zval(stream, return_value); 456} 457/* }}} */ 458 459/* 460 * Read a file and write the ouput to stdout 461 */ 462/* {{{ proto int readgzfile(string filename [, int use_include_path]) 463 Output a .gz-file */ 464static PHP_FUNCTION(readgzfile) 465{ 466 char *filename; 467 int filename_len; 468 long flags = 0; 469 php_stream *stream; 470 int size; 471 int use_include_path = 0; 472 473 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|l", &filename, &filename_len, &flags) == FAILURE) { 474 return; 475 } 476 477 use_include_path = flags ? USE_PATH : 0; 478 479 stream = php_stream_gzopen(NULL, filename, "rb", use_include_path | ENFORCE_SAFE_MODE, NULL, NULL STREAMS_CC TSRMLS_CC); 480 if (!stream) { 481 RETURN_FALSE; 482 } 483 size = php_stream_passthru(stream); 484 php_stream_close(stream); 485 RETURN_LONG(size); 486} 487/* }}} */ 488 489/* {{{ proto string gzcompress(string data [, int level]) 490 Gzip-compress a string */ 491static PHP_FUNCTION(gzcompress) 492{ 493 int data_len, status; 494 long level = Z_DEFAULT_COMPRESSION; 495 unsigned long l2; 496 char *data, *s2; 497 498 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|l", &data, &data_len, &level) == FAILURE) { 499 return; 500 } 501 502 if ((level < -1) || (level > 9)) { 503 php_error_docref(NULL TSRMLS_CC, E_WARNING, "compression level (%ld) must be within -1..9", level); 504 RETURN_FALSE; 505 } 506 507 l2 = data_len + (data_len / PHP_ZLIB_MODIFIER) + 15 + 1; /* room for \0 */ 508 s2 = (char *) emalloc(l2); 509 if (!s2) { 510 RETURN_FALSE; 511 } 512 513 if (level >= 0) { 514 status = compress2(s2, &l2, data, data_len, level); 515 } else { 516 status = compress(s2, &l2, data, data_len); 517 } 518 519 if (status == Z_OK) { 520 s2 = erealloc(s2, l2 + 1); 521 s2[l2] = '\0'; 522 RETURN_STRINGL(s2, l2, 0); 523 } else { 524 efree(s2); 525 php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", zError(status)); 526 RETURN_FALSE; 527 } 528} 529/* }}} */ 530 531/* {{{ proto string gzuncompress(string data [, int length]) 532 Unzip a gzip-compressed string */ 533static PHP_FUNCTION(gzuncompress) 534{ 535 int data_len, status; 536 unsigned int factor=1, maxfactor=16; 537 long limit = 0; 538 unsigned long plength=0, length; 539 char *data, *s1=NULL, *s2=NULL; 540 541 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|l", &data, &data_len, &limit) == FAILURE) { 542 return; 543 } 544 545 if (limit < 0) { 546 php_error_docref(NULL TSRMLS_CC, E_WARNING, "length (%ld) must be greater or equal zero", limit); 547 RETURN_FALSE; 548 } 549 plength = limit; 550 551 /* 552 zlib::uncompress() wants to know the output data length 553 if none was given as a parameter 554 we try from input length * 2 up to input length * 2^15 555 doubling it whenever it wasn't big enough 556 that should be eneugh for all real life cases 557 */ 558 do { 559 length = plength ? plength : (unsigned long)data_len * (1 << factor++); 560 s2 = (char *) erealloc(s1, length); 561 status = uncompress(s2, &length, data, data_len); 562 s1 = s2; 563 } while ((status == Z_BUF_ERROR) && (!plength) && (factor < maxfactor)); 564 565 if (status == Z_OK) { 566 s2 = erealloc(s2, length + 1); /* space for \0 */ 567 s2[ length ] = '\0'; 568 RETURN_STRINGL(s2, length, 0); 569 } else { 570 efree(s2); 571 php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", zError(status)); 572 RETURN_FALSE; 573 } 574} 575/* }}} */ 576 577/* {{{ proto string gzdeflate(string data [, int level]) 578 Gzip-compress a string */ 579static PHP_FUNCTION(gzdeflate) 580{ 581 int data_len,status; 582 long level = Z_DEFAULT_COMPRESSION; 583 z_stream stream; 584 char *data, *s2; 585 586 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|l", &data, &data_len, &level) == FAILURE) { 587 return; 588 } 589 590 if ((level < -1) || (level > 9)) { 591 php_error_docref(NULL TSRMLS_CC, E_WARNING, "compression level (%ld) must be within -1..9", level); 592 RETURN_FALSE; 593 } 594 595 stream.data_type = Z_ASCII; 596 stream.zalloc = php_zlib_alloc; 597 stream.zfree = php_zlib_free; 598 stream.opaque = (voidpf) Z_NULL; 599 600 stream.next_in = (Bytef *) data; 601 stream.avail_in = data_len; 602 603 stream.avail_out = stream.avail_in + (stream.avail_in / PHP_ZLIB_MODIFIER) + 15 + 1; /* room for \0 */ 604 605 s2 = (char *) emalloc(stream.avail_out); 606 if (!s2) { 607 RETURN_FALSE; 608 } 609 610 stream.next_out = s2; 611 612 /* init with -MAX_WBITS disables the zlib internal headers */ 613 status = deflateInit2(&stream, level, Z_DEFLATED, -MAX_WBITS, MAX_MEM_LEVEL, 0); 614 if (status == Z_OK) { 615 status = deflate(&stream, Z_FINISH); 616 if (status != Z_STREAM_END) { 617 deflateEnd(&stream); 618 if (status == Z_OK) { 619 status = Z_BUF_ERROR; 620 } 621 } else { 622 status = deflateEnd(&stream); 623 } 624 } 625 626 if (status == Z_OK) { 627 s2 = erealloc(s2,stream.total_out + 1); /* resize to buffer to the "right" size */ 628 s2[ stream.total_out ] = '\0'; 629 RETURN_STRINGL(s2, stream.total_out, 0); 630 } else { 631 efree(s2); 632 php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", zError(status)); 633 RETURN_FALSE; 634 } 635} 636/* }}} */ 637 638/* {{{ proto string gzinflate(string data [, int length]) 639 Unzip a gzip-compressed string */ 640static PHP_FUNCTION(gzinflate) 641{ 642 int data_len, status; 643 unsigned int factor=1, maxfactor=16; 644 long limit = 0; 645 unsigned long plength=0, length; 646 char *data, *s1=NULL, *s2=NULL; 647 z_stream stream; 648 649 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|l", &data, &data_len, &limit) == FAILURE) { 650 return; 651 } 652 653 if (!data_len) { 654 RETURN_FALSE; 655 } 656 657 if (limit < 0) { 658 php_error_docref(NULL TSRMLS_CC, E_WARNING, "length (%ld) must be greater or equal zero", limit); 659 RETURN_FALSE; 660 } 661 plength = limit; 662 663 stream.zalloc = php_zlib_alloc; 664 stream.zfree = php_zlib_free; 665 stream.opaque = Z_NULL; 666 stream.avail_in = data_len + 1; /* there is room for \0 */ 667 stream.next_in = (Bytef *) data; 668 stream.total_out = 0; 669 670 /* init with -MAX_WBITS disables the zlib internal headers */ 671 status = inflateInit2(&stream, -MAX_WBITS); 672 if (status != Z_OK) { 673 php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", zError(status)); 674 RETURN_FALSE; 675 } 676 677 /* 678 stream.avail_out wants to know the output data length 679 if none was given as a parameter 680 we try from input length * 2 up to input length * 2^15 681 doubling it whenever it wasn't big enough 682 that should be enaugh for all real life cases 683 */ 684 do { 685 length = plength ? plength : (unsigned long)data_len * (1 << factor++); 686 s2 = (char *) erealloc(s1, length); 687 688 if (!s2) { 689 if (s1) { 690 efree(s1); 691 } 692 inflateEnd(&stream); 693 RETURN_FALSE; 694 } 695 s1 = s2; 696 697 stream.next_out = (Bytef *) &s2[stream.total_out]; 698 stream.avail_out = length - stream.total_out; 699 status = inflate(&stream, Z_NO_FLUSH); 700 701 } while ((Z_BUF_ERROR == status || (Z_OK == status && stream.avail_in)) && !plength && factor < maxfactor); 702 703 inflateEnd(&stream); 704 705 if ((plength && Z_OK == status) || factor >= maxfactor) { 706 status = Z_MEM_ERROR; 707 } 708 709 if (Z_STREAM_END == status || Z_OK == status) { 710 s2 = erealloc(s2, stream.total_out + 1); /* room for \0 */ 711 s2[ stream.total_out ] = '\0'; 712 RETURN_STRINGL(s2, stream.total_out, 0); 713 } else { 714 efree(s2); 715 php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", zError(status)); 716 RETURN_FALSE; 717 } 718} 719/* }}} */ 720 721/* {{{ proto string zlib_get_coding_type(void) 722 Returns the coding type used for output compression */ 723static PHP_FUNCTION(zlib_get_coding_type) 724{ 725 switch (ZLIBG(compression_coding)) { 726 case CODING_GZIP: 727 RETURN_STRINGL("gzip", sizeof("gzip") - 1, 1); 728 729 case CODING_DEFLATE: 730 RETURN_STRINGL("deflate", sizeof("deflate") - 1, 1); 731 } 732 733 RETURN_FALSE; 734} 735 736/* {{{ php_do_deflate 737 */ 738static int php_do_deflate(uint str_length, Bytef **p_buffer, uint *p_buffer_len, zend_bool do_start, zend_bool do_end TSRMLS_DC) 739{ 740 Bytef *buffer; 741 uInt prev_outlen, outlen; 742 int err; 743 int start_offset = ((do_start && ZLIBG(compression_coding) == CODING_GZIP) ? 10 : 0); 744 int end_offset = (do_end ? 8 : 0); 745 746 outlen = (uint) (str_length + (str_length / PHP_ZLIB_MODIFIER) + 12 + 1); /* leave some room for a trailing \0 */ 747 if ((outlen + start_offset + end_offset) > *p_buffer_len) { 748 buffer = (Bytef *) emalloc(outlen + start_offset + end_offset); 749 } else { 750 buffer = *p_buffer; 751 } 752 753 ZLIBG(stream).next_out = buffer + start_offset; 754 ZLIBG(stream).avail_out = outlen; 755 756 err = deflate(&ZLIBG(stream), Z_SYNC_FLUSH); 757 while (err == Z_OK && !ZLIBG(stream).avail_out) { 758 prev_outlen = outlen; 759 outlen *= 3; 760 if ((outlen + start_offset + end_offset) > *p_buffer_len) { 761 buffer = erealloc(buffer, outlen + start_offset + end_offset); 762 } 763 764 ZLIBG(stream).next_out = buffer + start_offset + prev_outlen; 765 ZLIBG(stream).avail_out = prev_outlen * 2; 766 767 err = deflate(&ZLIBG(stream), Z_SYNC_FLUSH); 768 } 769 770 if (do_end) { 771 err = deflate(&ZLIBG(stream), Z_FINISH); 772 buffer[outlen + start_offset - ZLIBG(stream).avail_out] = '\0'; 773 } 774 775 *p_buffer = buffer; 776 *p_buffer_len = outlen - ZLIBG(stream).avail_out; 777 778 return err; 779} 780/* }}} */ 781 782/* {{{ php_deflate_string 783 */ 784static int php_deflate_string(const char *str, uint str_length, char **newstr, uint *new_length, zend_bool do_start, zend_bool do_end TSRMLS_DC) 785{ 786 int err; 787 788 if (do_start) { 789 ZLIBG(stream).zalloc = php_zlib_alloc; 790 ZLIBG(stream).zfree = php_zlib_free; 791 ZLIBG(stream).opaque = Z_NULL; 792 793 switch (ZLIBG(compression_coding)) { 794 case CODING_GZIP: 795 /* windowBits is passed < 0 to suppress zlib header & trailer */ 796 if (deflateInit2(&ZLIBG(stream), ZLIBG(output_compression_level), Z_DEFLATED, -MAX_WBITS, MAX_MEM_LEVEL, Z_DEFAULT_STRATEGY) != Z_OK) { 797 /* TODO: print out error */ 798 return FAILURE; 799 } 800 801 ZLIBG(crc) = crc32(0L, Z_NULL, 0); 802 break; 803 804 case CODING_DEFLATE: 805 if (deflateInit(&ZLIBG(stream), ZLIBG(output_compression_level)) != Z_OK) { 806 /* TODO: print out error */ 807 return FAILURE; 808 } 809 break; 810 } 811 } 812 813 ZLIBG(stream).next_in = (Bytef *) str; 814 ZLIBG(stream).avail_in = (uInt) str_length; 815 816 if (ZLIBG(compression_coding) == CODING_GZIP) { 817 ZLIBG(crc) = crc32(ZLIBG(crc), (const Bytef *) str, str_length); 818 } 819 820 err = php_do_deflate(str_length, (Bytef **) newstr, new_length, do_start, do_end TSRMLS_CC); 821 /* TODO: error handling (err may be Z_STREAM_ERROR, Z_BUF_ERROR, ?) */ 822 823 if (do_start && ZLIBG(compression_coding) == CODING_GZIP) { 824 /* Write a very simple .gz header: */ 825 (*newstr)[0] = gz_magic[0]; 826 (*newstr)[1] = gz_magic[1]; 827 (*newstr)[2] = Z_DEFLATED; 828 (*newstr)[3] = (*newstr)[4] = (*newstr)[5] = (*newstr)[6] = (*newstr)[7] = (*newstr)[8] = 0; 829 (*newstr)[9] = OS_CODE; 830 *new_length += 10; 831 } 832 if (do_end) { 833 if (ZLIBG(compression_coding) == CODING_GZIP) { 834 char *trailer = (*newstr) + (*new_length); 835 836 /* write crc & stream.total_in in LSB order */ 837 trailer[0] = (char) ZLIBG(crc) & 0xFF; 838 trailer[1] = (char) (ZLIBG(crc) >> 8) & 0xFF; 839 trailer[2] = (char) (ZLIBG(crc) >> 16) & 0xFF; 840 trailer[3] = (char) (ZLIBG(crc) >> 24) & 0xFF; 841 trailer[4] = (char) ZLIBG(stream).total_in & 0xFF; 842 trailer[5] = (char) (ZLIBG(stream).total_in >> 8) & 0xFF; 843 trailer[6] = (char) (ZLIBG(stream).total_in >> 16) & 0xFF; 844 trailer[7] = (char) (ZLIBG(stream).total_in >> 24) & 0xFF; 845 trailer[8] = '\0'; 846 *new_length += 8; 847 } 848 deflateEnd(&ZLIBG(stream)); 849 } 850 851 return SUCCESS; 852} 853/* }}} */ 854 855/* {{{ proto string gzencode(string data [, int level [, int encoding_mode]]) 856 GZ encode a string */ 857static PHP_FUNCTION(gzencode) 858{ 859 char *data, *s2; 860 int data_len; 861 long level = Z_DEFAULT_COMPRESSION, coding = CODING_GZIP; 862 int status; 863 z_stream stream; 864 865 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|ll", &data, &data_len, &level, &coding) == FAILURE) { 866 return; 867 } 868 869 if ((level < -1) || (level > 9)) { 870 php_error_docref(NULL TSRMLS_CC, E_WARNING, "compression level(%ld) must be within -1..9", level); 871 RETURN_FALSE; 872 } 873 874 if ((coding != CODING_GZIP) && (coding != CODING_DEFLATE)) { 875 php_error_docref(NULL TSRMLS_CC, E_WARNING, "encoding mode must be FORCE_GZIP or FORCE_DEFLATE"); 876 RETURN_FALSE; 877 } 878 879 stream.zalloc = php_zlib_alloc; 880 stream.zfree = php_zlib_free; 881 stream.opaque = Z_NULL; 882 883 stream.next_in = (Bytef *) data; 884 stream.avail_in = data_len; 885 886 stream.avail_out = stream.avail_in + (stream.avail_in / PHP_ZLIB_MODIFIER) + 15 + 1; /* room for \0 */ 887 s2 = (char *) emalloc(stream.avail_out + GZIP_HEADER_LENGTH + (coding == CODING_GZIP ? GZIP_FOOTER_LENGTH : 0)); 888 889 /* add gzip file header */ 890 s2[0] = gz_magic[0]; 891 s2[1] = gz_magic[1]; 892 s2[2] = Z_DEFLATED; 893 s2[3] = s2[4] = s2[5] = s2[6] = s2[7] = s2[8] = 0; /* time set to 0 */ 894 s2[9] = OS_CODE; 895 896 stream.next_out = &(s2[GZIP_HEADER_LENGTH]); 897 898 switch (coding) { 899 case CODING_GZIP: 900 /* windowBits is passed < 0 to suppress zlib header & trailer */ 901 if ((status = deflateInit2(&stream, level, Z_DEFLATED, -MAX_WBITS, MAX_MEM_LEVEL, Z_DEFAULT_STRATEGY)) != Z_OK) { 902 php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", zError(status)); 903 RETURN_FALSE; 904 } 905 906 break; 907 case CODING_DEFLATE: 908 if ((status = deflateInit(&stream, level)) != Z_OK) { 909 php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", zError(status)); 910 RETURN_FALSE; 911 } 912 break; 913 } 914 915 status = deflate(&stream, Z_FINISH); 916 if (status != Z_STREAM_END) { 917 deflateEnd(&stream); 918 if (status == Z_OK) { 919 status = Z_BUF_ERROR; 920 } 921 } else { 922 status = deflateEnd(&stream); 923 } 924 925 if (status == Z_OK) { 926 /* resize to buffer to the "right" size */ 927 s2 = erealloc(s2, stream.total_out + GZIP_HEADER_LENGTH + (coding == CODING_GZIP ? GZIP_FOOTER_LENGTH : 0) + 1); 928 929 if (coding == CODING_GZIP) { 930 char *trailer = s2 + (stream.total_out + GZIP_HEADER_LENGTH); 931 uLong crc = crc32(0L, Z_NULL, 0); 932 933 crc = crc32(crc, (const Bytef *) data, data_len); 934 935 /* write crc & stream.total_in in LSB order */ 936 trailer[0] = (char) crc & 0xFF; 937 trailer[1] = (char) (crc >> 8) & 0xFF; 938 trailer[2] = (char) (crc >> 16) & 0xFF; 939 trailer[3] = (char) (crc >> 24) & 0xFF; 940 trailer[4] = (char) stream.total_in & 0xFF; 941 trailer[5] = (char) (stream.total_in >> 8) & 0xFF; 942 trailer[6] = (char) (stream.total_in >> 16) & 0xFF; 943 trailer[7] = (char) (stream.total_in >> 24) & 0xFF; 944 trailer[8] = '\0'; 945 } else { 946 s2[stream.total_out + GZIP_HEADER_LENGTH + (coding == CODING_GZIP ? GZIP_FOOTER_LENGTH : 0)] = '\0'; 947 } 948 RETURN_STRINGL(s2, stream.total_out + GZIP_HEADER_LENGTH + (coding == CODING_GZIP ? GZIP_FOOTER_LENGTH : 0), 0); 949 } else { 950 efree(s2); 951 php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", zError(status)); 952 RETURN_FALSE; 953 } 954} 955/* }}} */ 956 957/* {{{ php_ob_gzhandler_check 958 */ 959int php_ob_gzhandler_check(TSRMLS_D) 960{ 961 /* check for wrong usages */ 962 if (OG(ob_nesting_level > 0)) { 963 if (php_ob_handler_used("ob_gzhandler" TSRMLS_CC)) { 964 php_error_docref("ref.outcontrol" TSRMLS_CC, E_WARNING, "output handler 'ob_gzhandler' cannot be used twice"); 965 return FAILURE; 966 } 967 if (php_ob_handler_used("mb_output_handler" TSRMLS_CC)) { 968 php_error_docref("ref.outcontrol" TSRMLS_CC, E_WARNING, "output handler 'ob_gzhandler' cannot be used after 'mb_output_handler'"); 969 return FAILURE; 970 } 971 if (php_ob_handler_used("URL-Rewriter" TSRMLS_CC)) { 972 php_error_docref("ref.outcontrol" TSRMLS_CC, E_WARNING, "output handler 'ob_gzhandler' cannot be used after 'URL-Rewriter'"); 973 return FAILURE; 974 } 975 if (php_ob_init_conflict("ob_gzhandler", "zlib output compression" TSRMLS_CC)) { 976 return FAILURE; 977 } 978 } 979 980 return SUCCESS; 981} 982 983/* }}} */ 984 985/* {{{ proto string ob_gzhandler(string str, int mode) 986 Encode str based on accept-encoding setting - designed to be called from ob_start() */ 987static PHP_FUNCTION(ob_gzhandler) 988{ 989 char *string; 990 int string_len; 991 long mode; 992 zval **a_encoding; 993 zend_bool return_original = 0; 994 zend_bool do_start, do_end; 995 996 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sl", &string, &string_len, &mode) == FAILURE) { 997 return; 998 } 999 1000 if (ZLIBG(ob_gzhandler_status) == -1) { 1001 RETURN_FALSE; 1002 } 1003 1004 zend_is_auto_global("_SERVER", sizeof("_SERVER")-1 TSRMLS_CC); 1005 1006 if (!PG(http_globals)[TRACK_VARS_SERVER] 1007 || zend_hash_find(PG(http_globals)[TRACK_VARS_SERVER]->value.ht, "HTTP_ACCEPT_ENCODING", sizeof("HTTP_ACCEPT_ENCODING"), (void **) &a_encoding) == FAILURE 1008 ) { 1009 ZLIBG(ob_gzhandler_status) = -1; 1010 RETURN_FALSE; 1011 } 1012 1013 convert_to_string_ex(a_encoding); 1014 if (php_memnstr(Z_STRVAL_PP(a_encoding), "gzip", 4, Z_STRVAL_PP(a_encoding) + Z_STRLEN_PP(a_encoding))) { 1015 ZLIBG(compression_coding) = CODING_GZIP; 1016 } else if (php_memnstr(Z_STRVAL_PP(a_encoding), "deflate", 7, Z_STRVAL_PP(a_encoding) + Z_STRLEN_PP(a_encoding))) { 1017 ZLIBG(compression_coding) = CODING_DEFLATE; 1018 } else { 1019 ZLIBG(ob_gzhandler_status) = -1; 1020 RETURN_FALSE; 1021 } 1022 1023 do_start = ((mode & PHP_OUTPUT_HANDLER_START) ? 1 : 0); 1024 do_end = ((mode & PHP_OUTPUT_HANDLER_END) ? 1 : 0); 1025 Z_STRVAL_P(return_value) = NULL; 1026 Z_STRLEN_P(return_value) = 0; 1027 1028 if (php_deflate_string(string, string_len, &Z_STRVAL_P(return_value), &Z_STRLEN_P(return_value), do_start, do_end TSRMLS_CC) == SUCCESS) { 1029 Z_TYPE_P(return_value) = IS_STRING; 1030 if (do_start) { 1031 switch (ZLIBG(compression_coding)) { 1032 case CODING_GZIP: 1033 if (sapi_add_header("Content-Encoding: gzip", sizeof("Content-Encoding: gzip") - 1, 1) == FAILURE) { 1034 return_original = 1; 1035 } 1036 if (sapi_add_header_ex("Vary: Accept-Encoding", sizeof("Vary: Accept-Encoding") - 1, 1, 0 TSRMLS_CC)==FAILURE) { 1037 return_original = 1; 1038 } 1039 break; 1040 case CODING_DEFLATE: 1041 if (sapi_add_header("Content-Encoding: deflate", sizeof("Content-Encoding: deflate") - 1, 1) == FAILURE) { 1042 return_original = 1; 1043 } 1044 if (sapi_add_header_ex("Vary: Accept-Encoding", sizeof("Vary: Accept-Encoding") - 1, 1, 0 TSRMLS_CC)==FAILURE) { 1045 return_original = 1; 1046 } 1047 break; 1048 default: 1049 return_original = 1; 1050 break; 1051 } 1052 } 1053 1054 if (return_original) { 1055 zval_dtor(return_value); 1056 } 1057 1058 } else { 1059 return_original = 1; 1060 } 1061 1062 if (return_original) { 1063 /* return the original string */ 1064 RETURN_STRINGL(string, string_len, 1); 1065 } 1066} 1067/* }}} */ 1068 1069/* {{{ php_gzip_output_handler 1070 */ 1071static void php_gzip_output_handler(char *output, uint output_len, char **handled_output, uint *handled_output_len, int mode TSRMLS_DC) 1072{ 1073 zend_bool do_start, do_end; 1074 1075 if (!ZLIBG(output_compression) || SG(sapi_headers).http_response_code == 204 || SG(sapi_headers).http_response_code == 304) { 1076 *handled_output = NULL; 1077 } else { 1078 do_start = (mode & PHP_OUTPUT_HANDLER_START ? 1 : 0); 1079 do_end = (mode & PHP_OUTPUT_HANDLER_END ? 1 : 0); 1080 1081 if (do_start) { 1082 if (!SG(headers_sent) && !SG(request_info).no_headers) { 1083 switch (ZLIBG(compression_coding)) { 1084 case CODING_GZIP: 1085 sapi_add_header_ex(ZEND_STRL("Content-Encoding: gzip"), 1, 1 TSRMLS_CC); 1086 break; 1087 case CODING_DEFLATE: 1088 sapi_add_header_ex(ZEND_STRL("Content-Encoding: deflate"), 1, 1 TSRMLS_CC); 1089 break; 1090 } 1091 sapi_add_header_ex(ZEND_STRL("Vary: Accept-Encoding"), 1, 0 TSRMLS_CC); 1092 } else { 1093 /* Disable compression if headers can not be set (Fix for bug #49816) */ 1094 ZLIBG(output_compression) = 0; 1095 *handled_output = NULL; 1096 return; 1097 } 1098 } 1099 1100 if (php_deflate_string(output, output_len, handled_output, handled_output_len, do_start, do_end TSRMLS_CC) != SUCCESS) { 1101 zend_error(E_ERROR, "Compression failed"); 1102 } 1103 } 1104} 1105/* }}} */ 1106 1107/* {{{ php_enable_output_compression 1108 */ 1109static int php_enable_output_compression(int buffer_size TSRMLS_DC) 1110{ 1111 zval **a_encoding; 1112 1113 zend_is_auto_global("_SERVER", sizeof("_SERVER")-1 TSRMLS_CC); 1114 1115 if (!PG(http_globals)[TRACK_VARS_SERVER] 1116 || zend_hash_find(PG(http_globals)[TRACK_VARS_SERVER]->value.ht, "HTTP_ACCEPT_ENCODING", sizeof("HTTP_ACCEPT_ENCODING"), (void **) &a_encoding) == FAILURE 1117 ) { 1118 return FAILURE; 1119 } 1120 1121 convert_to_string_ex(a_encoding); 1122 1123 if (php_memnstr(Z_STRVAL_PP(a_encoding), "gzip", 4, Z_STRVAL_PP(a_encoding) + Z_STRLEN_PP(a_encoding))) { 1124 ZLIBG(compression_coding) = CODING_GZIP; 1125 } else if (php_memnstr(Z_STRVAL_PP(a_encoding), "deflate", 7, Z_STRVAL_PP(a_encoding) + Z_STRLEN_PP(a_encoding))) { 1126 ZLIBG(compression_coding) = CODING_DEFLATE; 1127 } else { 1128 return FAILURE; 1129 } 1130 1131 php_ob_set_internal_handler(php_gzip_output_handler, (uint)buffer_size, "zlib output compression", 0 TSRMLS_CC); 1132 1133 if (ZLIBG(output_handler) && strlen(ZLIBG(output_handler))) { 1134 php_start_ob_buffer_named(ZLIBG(output_handler), 0, 1 TSRMLS_CC); 1135 } 1136 return SUCCESS; 1137} 1138/* }}} */ 1139 1140/* {{{ php_zlib_output_compression_start() */ 1141static int php_zlib_output_compression_start(TSRMLS_D) 1142{ 1143 switch (ZLIBG(output_compression)) { 1144 case 0: 1145 break; 1146 case 1: 1147 ZLIBG(output_compression) = 4096; 1148 /* break omitted intentionally */ 1149 default: 1150 /* ZLIBG(compression_coding) should be 0 when zlib compression hasn't been started yet.. */ 1151 if (ZLIBG(compression_coding) == 0) { 1152 return php_enable_output_compression(ZLIBG(output_compression) TSRMLS_CC); 1153 } 1154 } 1155 return SUCCESS; 1156} 1157/* }}} */ 1158 1159/* 1160 * Local variables: 1161 * tab-width: 4 1162 * c-basic-offset: 4 1163 * End: 1164 * vim600: sw=4 ts=4 fdm=marker 1165 * vim<600: sw=4 ts=4 1166 */ 1167