diff --git a/ext/dom/domimplementation.c b/ext/dom/domimplementation.c index 05e99af39b9f2..a518f44c08024 100644 --- a/ext/dom/domimplementation.c +++ b/ext/dom/domimplementation.c @@ -112,6 +112,11 @@ PHP_METHOD(domimplementation, createDocumentType) pch2 = (xmlChar *) systemid; } + if (strstr(name, "%00")) { + php_error_docref(NULL, E_WARNING, "URI must not contain percent-encoded NUL bytes"); + RETURN_FALSE; + } + uri = xmlParseURI(name); if (uri != NULL && uri->opaque != NULL) { localname = xmlStrdup((xmlChar *) uri->opaque); diff --git a/ext/dom/tests/bug79971_2.phpt b/ext/dom/tests/bug79971_2.phpt new file mode 100644 index 0000000000000..c4e6b1e4e0933 --- /dev/null +++ b/ext/dom/tests/bug79971_2.phpt @@ -0,0 +1,20 @@ +--TEST-- +Bug #79971 (special character is breaking the path in xml function) +--SKIPIF-- + +--FILE-- +createDocumentType("$uri%00foo")); +?> +--EXPECTF-- +Warning: DOMImplementation::createDocumentType(): URI must not contain percent-encoded NUL bytes in %s on line %d +bool(false) diff --git a/ext/gd/gd.c b/ext/gd/gd.c index 336a739692671..fde93bba496fd 100644 --- a/ext/gd/gd.c +++ b/ext/gd/gd.c @@ -1485,6 +1485,12 @@ PHP_FUNCTION(imageloadfont) font->w = FLIPWORD(font->w); font->h = FLIPWORD(font->h); font->nchars = FLIPWORD(font->nchars); + if (overflow2(font->nchars, font->h) || overflow2(font->nchars * font->h, font->w )) { + php_error_docref(NULL, E_WARNING, "Error reading font, invalid font header"); + efree(font); + php_stream_close(stream); + RETURN_FALSE; + } body_size = font->w * font->h * font->nchars; } @@ -1495,6 +1501,7 @@ PHP_FUNCTION(imageloadfont) RETURN_FALSE; } + ZEND_ASSERT(body_size > 0); font->data = emalloc(body_size); b = 0; while (b < body_size && (n = php_stream_read(stream, &font->data[b], body_size - b)) > 0) { diff --git a/ext/gd/gd.c.orig b/ext/gd/gd.c.orig new file mode 100644 index 0000000000000..336a739692671 --- /dev/null +++ b/ext/gd/gd.c.orig @@ -0,0 +1,5207 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 7 | + +----------------------------------------------------------------------+ + | Copyright (c) The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Rasmus Lerdorf | + | Stig Bakken | + | Jim Winstead | + +----------------------------------------------------------------------+ + */ + +/* gd 1.2 is copyright 1994, 1995, Quest Protein Database Center, + Cold Spring Harbor Labs. */ + +/* Note that there is no code from the gd package in this file */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "php.h" +#include "php_ini.h" +#include "ext/standard/head.h" +#include +#include "SAPI.h" +#include "php_gd.h" +#include "ext/standard/info.h" +#include "php_open_temporary_file.h" + + +#ifdef HAVE_SYS_WAIT_H +# include +#endif +#ifdef HAVE_UNISTD_H +# include +#endif +#ifdef PHP_WIN32 +# include +# include +# include +# include +# include +#endif + +#if defined(HAVE_GD_XPM) && defined(HAVE_GD_BUNDLED) +# include +#endif + +# include "gd_compat.h" + + +static int le_gd, le_gd_font; + +#ifdef HAVE_GD_BUNDLED +# include "libgd/gd.h" +# include "libgd/gd_errors.h" +# include "libgd/gdfontt.h" /* 1 Tiny font */ +# include "libgd/gdfonts.h" /* 2 Small font */ +# include "libgd/gdfontmb.h" /* 3 Medium bold font */ +# include "libgd/gdfontl.h" /* 4 Large font */ +# include "libgd/gdfontg.h" /* 5 Giant font */ +#else +# include +# include +# include /* 1 Tiny font */ +# include /* 2 Small font */ +# include /* 3 Medium bold font */ +# include /* 4 Large font */ +# include /* 5 Giant font */ +#endif + +#if defined(HAVE_GD_FREETYPE) && defined(HAVE_GD_BUNDLED) +# include +# include FT_FREETYPE_H +#endif + +#if defined(HAVE_GD_XPM) && defined(HAVE_GD_BUNDLED) +# include "X11/xpm.h" +#endif + +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif + +#ifdef HAVE_GD_FREETYPE +static void php_imagettftext_common(INTERNAL_FUNCTION_PARAMETERS, int, int); +#endif + +#include "gd_ctx.c" + +/* as it is not really public, duplicate declaration here to avoid + pointless warnings */ +int overflow2(int a, int b); + +/* Section Filters Declarations */ +/* IMPORTANT NOTE FOR NEW FILTER + * Do not forget to update: + * IMAGE_FILTER_MAX: define the last filter index + * IMAGE_FILTER_MAX_ARGS: define the biggest amount of arguments + * image_filter array in PHP_FUNCTION(imagefilter) + * */ +#define IMAGE_FILTER_NEGATE 0 +#define IMAGE_FILTER_GRAYSCALE 1 +#define IMAGE_FILTER_BRIGHTNESS 2 +#define IMAGE_FILTER_CONTRAST 3 +#define IMAGE_FILTER_COLORIZE 4 +#define IMAGE_FILTER_EDGEDETECT 5 +#define IMAGE_FILTER_EMBOSS 6 +#define IMAGE_FILTER_GAUSSIAN_BLUR 7 +#define IMAGE_FILTER_SELECTIVE_BLUR 8 +#define IMAGE_FILTER_MEAN_REMOVAL 9 +#define IMAGE_FILTER_SMOOTH 10 +#define IMAGE_FILTER_PIXELATE 11 +#define IMAGE_FILTER_SCATTER 12 +#define IMAGE_FILTER_MAX 12 +#define IMAGE_FILTER_MAX_ARGS 6 +static void php_image_filter_negate(INTERNAL_FUNCTION_PARAMETERS); +static void php_image_filter_grayscale(INTERNAL_FUNCTION_PARAMETERS); +static void php_image_filter_brightness(INTERNAL_FUNCTION_PARAMETERS); +static void php_image_filter_contrast(INTERNAL_FUNCTION_PARAMETERS); +static void php_image_filter_colorize(INTERNAL_FUNCTION_PARAMETERS); +static void php_image_filter_edgedetect(INTERNAL_FUNCTION_PARAMETERS); +static void php_image_filter_emboss(INTERNAL_FUNCTION_PARAMETERS); +static void php_image_filter_gaussian_blur(INTERNAL_FUNCTION_PARAMETERS); +static void php_image_filter_selective_blur(INTERNAL_FUNCTION_PARAMETERS); +static void php_image_filter_mean_removal(INTERNAL_FUNCTION_PARAMETERS); +static void php_image_filter_smooth(INTERNAL_FUNCTION_PARAMETERS); +static void php_image_filter_pixelate(INTERNAL_FUNCTION_PARAMETERS); +static void php_image_filter_scatter(INTERNAL_FUNCTION_PARAMETERS); + +/* End Section filters declarations */ +static gdImagePtr _php_image_create_from_string (zval *Data, char *tn, gdImagePtr (*ioctx_func_p)()); +static void _php_image_create_from(INTERNAL_FUNCTION_PARAMETERS, int image_type, char *tn, gdImagePtr (*func_p)(), gdImagePtr (*ioctx_func_p)()); +static void _php_image_output(INTERNAL_FUNCTION_PARAMETERS, int image_type, char *tn, void (*func_p)()); +static int _php_image_type(char data[12]); +static void _php_image_convert(INTERNAL_FUNCTION_PARAMETERS, int image_type); + +/* {{{ arginfo */ +ZEND_BEGIN_ARG_INFO(arginfo_gd_info, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO(arginfo_imageloadfont, 0) + ZEND_ARG_INFO(0, filename) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO(arginfo_imagesetstyle, 0) + ZEND_ARG_INFO(0, im) + ZEND_ARG_INFO(0, styles) /* ARRAY_INFO(0, styles, 0) */ +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO(arginfo_imagecreatetruecolor, 0) + ZEND_ARG_INFO(0, x_size) + ZEND_ARG_INFO(0, y_size) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO(arginfo_imageistruecolor, 0) + ZEND_ARG_INFO(0, im) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO(arginfo_imagetruecolortopalette, 0) + ZEND_ARG_INFO(0, im) + ZEND_ARG_INFO(0, ditherFlag) + ZEND_ARG_INFO(0, colorsWanted) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO(arginfo_imagepalettetotruecolor, 0) + ZEND_ARG_INFO(0, im) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO(arginfo_imagecolormatch, 0) + ZEND_ARG_INFO(0, im1) + ZEND_ARG_INFO(0, im2) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO(arginfo_imagesetthickness, 0) + ZEND_ARG_INFO(0, im) + ZEND_ARG_INFO(0, thickness) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO(arginfo_imagefilledellipse, 0) + ZEND_ARG_INFO(0, im) + ZEND_ARG_INFO(0, cx) + ZEND_ARG_INFO(0, cy) + ZEND_ARG_INFO(0, w) + ZEND_ARG_INFO(0, h) + ZEND_ARG_INFO(0, color) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO(arginfo_imagefilledarc, 0) + ZEND_ARG_INFO(0, im) + ZEND_ARG_INFO(0, cx) + ZEND_ARG_INFO(0, cy) + ZEND_ARG_INFO(0, w) + ZEND_ARG_INFO(0, h) + ZEND_ARG_INFO(0, s) + ZEND_ARG_INFO(0, e) + ZEND_ARG_INFO(0, col) + ZEND_ARG_INFO(0, style) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO(arginfo_imagealphablending, 0) + ZEND_ARG_INFO(0, im) + ZEND_ARG_INFO(0, blend) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO(arginfo_imagesavealpha, 0) + ZEND_ARG_INFO(0, im) + ZEND_ARG_INFO(0, save) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO(arginfo_imagelayereffect, 0) + ZEND_ARG_INFO(0, im) + ZEND_ARG_INFO(0, effect) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO(arginfo_imagecolorallocatealpha, 0) + ZEND_ARG_INFO(0, im) + ZEND_ARG_INFO(0, red) + ZEND_ARG_INFO(0, green) + ZEND_ARG_INFO(0, blue) + ZEND_ARG_INFO(0, alpha) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO(arginfo_imagecolorresolvealpha, 0) + ZEND_ARG_INFO(0, im) + ZEND_ARG_INFO(0, red) + ZEND_ARG_INFO(0, green) + ZEND_ARG_INFO(0, blue) + ZEND_ARG_INFO(0, alpha) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO(arginfo_imagecolorclosestalpha, 0) + ZEND_ARG_INFO(0, im) + ZEND_ARG_INFO(0, red) + ZEND_ARG_INFO(0, green) + ZEND_ARG_INFO(0, blue) + ZEND_ARG_INFO(0, alpha) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO(arginfo_imagecolorexactalpha, 0) + ZEND_ARG_INFO(0, im) + ZEND_ARG_INFO(0, red) + ZEND_ARG_INFO(0, green) + ZEND_ARG_INFO(0, blue) + ZEND_ARG_INFO(0, alpha) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO(arginfo_imagecopyresampled, 0) + ZEND_ARG_INFO(0, dst_im) + ZEND_ARG_INFO(0, src_im) + ZEND_ARG_INFO(0, dst_x) + ZEND_ARG_INFO(0, dst_y) + ZEND_ARG_INFO(0, src_x) + ZEND_ARG_INFO(0, src_y) + ZEND_ARG_INFO(0, dst_w) + ZEND_ARG_INFO(0, dst_h) + ZEND_ARG_INFO(0, src_w) + ZEND_ARG_INFO(0, src_h) +ZEND_END_ARG_INFO() + +#ifdef PHP_WIN32 +ZEND_BEGIN_ARG_INFO_EX(arginfo_imagegrabwindow, 0, 0, 1) + ZEND_ARG_INFO(0, handle) + ZEND_ARG_INFO(0, client_area) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO(arginfo_imagegrabscreen, 0) +ZEND_END_ARG_INFO() +#endif + +ZEND_BEGIN_ARG_INFO_EX(arginfo_imagerotate, 0, 0, 3) + ZEND_ARG_INFO(0, im) + ZEND_ARG_INFO(0, angle) + ZEND_ARG_INFO(0, bgdcolor) + ZEND_ARG_INFO(0, ignoretransparent) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO(arginfo_imagesettile, 0) + ZEND_ARG_INFO(0, im) + ZEND_ARG_INFO(0, tile) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO(arginfo_imagesetbrush, 0) + ZEND_ARG_INFO(0, im) + ZEND_ARG_INFO(0, brush) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO(arginfo_imagecreate, 0) + ZEND_ARG_INFO(0, x_size) + ZEND_ARG_INFO(0, y_size) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO(arginfo_imagetypes, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO(arginfo_imagecreatefromstring, 0) + ZEND_ARG_INFO(0, image) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO(arginfo_imagecreatefromgif, 0) + ZEND_ARG_INFO(0, filename) +ZEND_END_ARG_INFO() + +#ifdef HAVE_GD_JPG +ZEND_BEGIN_ARG_INFO(arginfo_imagecreatefromjpeg, 0) + ZEND_ARG_INFO(0, filename) +ZEND_END_ARG_INFO() +#endif + +#ifdef HAVE_GD_PNG +ZEND_BEGIN_ARG_INFO(arginfo_imagecreatefrompng, 0) + ZEND_ARG_INFO(0, filename) +ZEND_END_ARG_INFO() +#endif + +#ifdef HAVE_GD_WEBP +ZEND_BEGIN_ARG_INFO(arginfo_imagecreatefromwebp, 0) + ZEND_ARG_INFO(0, filename) +ZEND_END_ARG_INFO() +#endif + +ZEND_BEGIN_ARG_INFO(arginfo_imagecreatefromxbm, 0) + ZEND_ARG_INFO(0, filename) +ZEND_END_ARG_INFO() + +#if defined(HAVE_GD_XPM) +ZEND_BEGIN_ARG_INFO(arginfo_imagecreatefromxpm, 0) + ZEND_ARG_INFO(0, filename) +ZEND_END_ARG_INFO() +#endif + +ZEND_BEGIN_ARG_INFO(arginfo_imagecreatefromwbmp, 0) + ZEND_ARG_INFO(0, filename) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO(arginfo_imagecreatefromgd, 0) + ZEND_ARG_INFO(0, filename) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO(arginfo_imagecreatefromgd2, 0) + ZEND_ARG_INFO(0, filename) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO(arginfo_imagecreatefromgd2part, 0) + ZEND_ARG_INFO(0, filename) + ZEND_ARG_INFO(0, srcX) + ZEND_ARG_INFO(0, srcY) + ZEND_ARG_INFO(0, width) + ZEND_ARG_INFO(0, height) +ZEND_END_ARG_INFO() + +#if defined(HAVE_GD_BMP) +ZEND_BEGIN_ARG_INFO(arginfo_imagecreatefrombmp, 0) + ZEND_ARG_INFO(0, filename) +ZEND_END_ARG_INFO() +#endif + +#if defined(HAVE_GD_TGA) +ZEND_BEGIN_ARG_INFO(arginfo_imagecreatefromtga, 0) + ZEND_ARG_INFO(0, filename) +ZEND_END_ARG_INFO() +#endif + +ZEND_BEGIN_ARG_INFO_EX(arginfo_imagexbm, 0, 0, 2) + ZEND_ARG_INFO(0, im) + ZEND_ARG_INFO(0, filename) + ZEND_ARG_INFO(0, foreground) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_imagegif, 0, 0, 1) + ZEND_ARG_INFO(0, im) + ZEND_ARG_INFO(0, to) +ZEND_END_ARG_INFO() + +#ifdef HAVE_GD_PNG +ZEND_BEGIN_ARG_INFO_EX(arginfo_imagepng, 0, 0, 1) + ZEND_ARG_INFO(0, im) + ZEND_ARG_INFO(0, to) + ZEND_ARG_INFO(0, quality) + ZEND_ARG_INFO(0, filters) +ZEND_END_ARG_INFO() +#endif + +#ifdef HAVE_GD_WEBP +ZEND_BEGIN_ARG_INFO_EX(arginfo_imagewebp, 0, 0, 1) + ZEND_ARG_INFO(0, im) + ZEND_ARG_INFO(0, to) + ZEND_ARG_INFO(0, quality) +ZEND_END_ARG_INFO() +#endif + +#ifdef HAVE_GD_JPG +ZEND_BEGIN_ARG_INFO_EX(arginfo_imagejpeg, 0, 0, 1) + ZEND_ARG_INFO(0, im) + ZEND_ARG_INFO(0, to) + ZEND_ARG_INFO(0, quality) +ZEND_END_ARG_INFO() +#endif + +ZEND_BEGIN_ARG_INFO_EX(arginfo_imagewbmp, 0, 0, 1) + ZEND_ARG_INFO(0, im) + ZEND_ARG_INFO(0, to) + ZEND_ARG_INFO(0, foreground) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_imagegd, 0, 0, 1) + ZEND_ARG_INFO(0, im) + ZEND_ARG_INFO(0, to) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_imagegd2, 0, 0, 1) + ZEND_ARG_INFO(0, im) + ZEND_ARG_INFO(0, to) + ZEND_ARG_INFO(0, chunk_size) + ZEND_ARG_INFO(0, type) +ZEND_END_ARG_INFO() + +#if defined(HAVE_GD_BMP) +ZEND_BEGIN_ARG_INFO_EX(arginfo_imagebmp, 0, 0, 1) + ZEND_ARG_INFO(0, im) + ZEND_ARG_INFO(0, to) + ZEND_ARG_INFO(0, compressed) +ZEND_END_ARG_INFO() +#endif + +ZEND_BEGIN_ARG_INFO(arginfo_imagedestroy, 0) + ZEND_ARG_INFO(0, im) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO(arginfo_imagecolorallocate, 0) + ZEND_ARG_INFO(0, im) + ZEND_ARG_INFO(0, red) + ZEND_ARG_INFO(0, green) + ZEND_ARG_INFO(0, blue) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO(arginfo_imagepalettecopy, 0) + ZEND_ARG_INFO(0, dst) + ZEND_ARG_INFO(0, src) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO(arginfo_imagecolorat, 0) + ZEND_ARG_INFO(0, im) + ZEND_ARG_INFO(0, x) + ZEND_ARG_INFO(0, y) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO(arginfo_imagecolorclosest, 0) + ZEND_ARG_INFO(0, im) + ZEND_ARG_INFO(0, red) + ZEND_ARG_INFO(0, green) + ZEND_ARG_INFO(0, blue) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO(arginfo_imagecolorclosesthwb, 0) + ZEND_ARG_INFO(0, im) + ZEND_ARG_INFO(0, red) + ZEND_ARG_INFO(0, green) + ZEND_ARG_INFO(0, blue) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO(arginfo_imagecolordeallocate, 0) + ZEND_ARG_INFO(0, im) + ZEND_ARG_INFO(0, index) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO(arginfo_imagecolorresolve, 0) + ZEND_ARG_INFO(0, im) + ZEND_ARG_INFO(0, red) + ZEND_ARG_INFO(0, green) + ZEND_ARG_INFO(0, blue) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO(arginfo_imagecolorexact, 0) + ZEND_ARG_INFO(0, im) + ZEND_ARG_INFO(0, red) + ZEND_ARG_INFO(0, green) + ZEND_ARG_INFO(0, blue) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_imagecolorset, 0, 0, 5) + ZEND_ARG_INFO(0, im) + ZEND_ARG_INFO(0, color) + ZEND_ARG_INFO(0, red) + ZEND_ARG_INFO(0, green) + ZEND_ARG_INFO(0, blue) + ZEND_ARG_INFO(0, alpha) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO(arginfo_imagecolorsforindex, 0) + ZEND_ARG_INFO(0, im) + ZEND_ARG_INFO(0, index) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO(arginfo_imagegammacorrect, 0) + ZEND_ARG_INFO(0, im) + ZEND_ARG_INFO(0, inputgamma) + ZEND_ARG_INFO(0, outputgamma) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO(arginfo_imagesetpixel, 0) + ZEND_ARG_INFO(0, im) + ZEND_ARG_INFO(0, x) + ZEND_ARG_INFO(0, y) + ZEND_ARG_INFO(0, col) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO(arginfo_imageline, 0) + ZEND_ARG_INFO(0, im) + ZEND_ARG_INFO(0, x1) + ZEND_ARG_INFO(0, y1) + ZEND_ARG_INFO(0, x2) + ZEND_ARG_INFO(0, y2) + ZEND_ARG_INFO(0, col) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO(arginfo_imagedashedline, 0) + ZEND_ARG_INFO(0, im) + ZEND_ARG_INFO(0, x1) + ZEND_ARG_INFO(0, y1) + ZEND_ARG_INFO(0, x2) + ZEND_ARG_INFO(0, y2) + ZEND_ARG_INFO(0, col) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO(arginfo_imagerectangle, 0) + ZEND_ARG_INFO(0, im) + ZEND_ARG_INFO(0, x1) + ZEND_ARG_INFO(0, y1) + ZEND_ARG_INFO(0, x2) + ZEND_ARG_INFO(0, y2) + ZEND_ARG_INFO(0, col) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO(arginfo_imagefilledrectangle, 0) + ZEND_ARG_INFO(0, im) + ZEND_ARG_INFO(0, x1) + ZEND_ARG_INFO(0, y1) + ZEND_ARG_INFO(0, x2) + ZEND_ARG_INFO(0, y2) + ZEND_ARG_INFO(0, col) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO(arginfo_imagearc, 0) + ZEND_ARG_INFO(0, im) + ZEND_ARG_INFO(0, cx) + ZEND_ARG_INFO(0, cy) + ZEND_ARG_INFO(0, w) + ZEND_ARG_INFO(0, h) + ZEND_ARG_INFO(0, s) + ZEND_ARG_INFO(0, e) + ZEND_ARG_INFO(0, col) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO(arginfo_imageellipse, 0) + ZEND_ARG_INFO(0, im) + ZEND_ARG_INFO(0, cx) + ZEND_ARG_INFO(0, cy) + ZEND_ARG_INFO(0, w) + ZEND_ARG_INFO(0, h) + ZEND_ARG_INFO(0, color) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO(arginfo_imagefilltoborder, 0) + ZEND_ARG_INFO(0, im) + ZEND_ARG_INFO(0, x) + ZEND_ARG_INFO(0, y) + ZEND_ARG_INFO(0, border) + ZEND_ARG_INFO(0, col) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO(arginfo_imagefill, 0) + ZEND_ARG_INFO(0, im) + ZEND_ARG_INFO(0, x) + ZEND_ARG_INFO(0, y) + ZEND_ARG_INFO(0, col) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO(arginfo_imagecolorstotal, 0) + ZEND_ARG_INFO(0, im) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_imagecolortransparent, 0, 0, 1) + ZEND_ARG_INFO(0, im) + ZEND_ARG_INFO(0, col) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_imageinterlace, 0, 0, 1) + ZEND_ARG_INFO(0, im) + ZEND_ARG_INFO(0, interlace) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO(arginfo_imagepolygon, 0) + ZEND_ARG_INFO(0, im) + ZEND_ARG_INFO(0, points) /* ARRAY_INFO(0, points, 0) */ + ZEND_ARG_INFO(0, num_pos) + ZEND_ARG_INFO(0, col) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO(arginfo_imageopenpolygon, 0) + ZEND_ARG_INFO(0, im) + ZEND_ARG_INFO(0, points) /* ARRAY_INFO(0, points, 0) */ + ZEND_ARG_INFO(0, num_pos) + ZEND_ARG_INFO(0, col) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO(arginfo_imagefilledpolygon, 0) + ZEND_ARG_INFO(0, im) + ZEND_ARG_INFO(0, points) /* ARRAY_INFO(0, points, 0) */ + ZEND_ARG_INFO(0, num_pos) + ZEND_ARG_INFO(0, col) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO(arginfo_imagefontwidth, 0) + ZEND_ARG_INFO(0, font) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO(arginfo_imagefontheight, 0) + ZEND_ARG_INFO(0, font) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO(arginfo_imagechar, 0) + ZEND_ARG_INFO(0, im) + ZEND_ARG_INFO(0, font) + ZEND_ARG_INFO(0, x) + ZEND_ARG_INFO(0, y) + ZEND_ARG_INFO(0, c) + ZEND_ARG_INFO(0, col) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO(arginfo_imagecharup, 0) + ZEND_ARG_INFO(0, im) + ZEND_ARG_INFO(0, font) + ZEND_ARG_INFO(0, x) + ZEND_ARG_INFO(0, y) + ZEND_ARG_INFO(0, c) + ZEND_ARG_INFO(0, col) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO(arginfo_imagestring, 0) + ZEND_ARG_INFO(0, im) + ZEND_ARG_INFO(0, font) + ZEND_ARG_INFO(0, x) + ZEND_ARG_INFO(0, y) + ZEND_ARG_INFO(0, str) + ZEND_ARG_INFO(0, col) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO(arginfo_imagestringup, 0) + ZEND_ARG_INFO(0, im) + ZEND_ARG_INFO(0, font) + ZEND_ARG_INFO(0, x) + ZEND_ARG_INFO(0, y) + ZEND_ARG_INFO(0, str) + ZEND_ARG_INFO(0, col) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO(arginfo_imagecopy, 0) + ZEND_ARG_INFO(0, dst_im) + ZEND_ARG_INFO(0, src_im) + ZEND_ARG_INFO(0, dst_x) + ZEND_ARG_INFO(0, dst_y) + ZEND_ARG_INFO(0, src_x) + ZEND_ARG_INFO(0, src_y) + ZEND_ARG_INFO(0, src_w) + ZEND_ARG_INFO(0, src_h) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO(arginfo_imagecopymerge, 0) + ZEND_ARG_INFO(0, dst_im) + ZEND_ARG_INFO(0, src_im) + ZEND_ARG_INFO(0, dst_x) + ZEND_ARG_INFO(0, dst_y) + ZEND_ARG_INFO(0, src_x) + ZEND_ARG_INFO(0, src_y) + ZEND_ARG_INFO(0, src_w) + ZEND_ARG_INFO(0, src_h) + ZEND_ARG_INFO(0, pct) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO(arginfo_imagecopymergegray, 0) + ZEND_ARG_INFO(0, dst_im) + ZEND_ARG_INFO(0, src_im) + ZEND_ARG_INFO(0, dst_x) + ZEND_ARG_INFO(0, dst_y) + ZEND_ARG_INFO(0, src_x) + ZEND_ARG_INFO(0, src_y) + ZEND_ARG_INFO(0, src_w) + ZEND_ARG_INFO(0, src_h) + ZEND_ARG_INFO(0, pct) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO(arginfo_imagecopyresized, 0) + ZEND_ARG_INFO(0, dst_im) + ZEND_ARG_INFO(0, src_im) + ZEND_ARG_INFO(0, dst_x) + ZEND_ARG_INFO(0, dst_y) + ZEND_ARG_INFO(0, src_x) + ZEND_ARG_INFO(0, src_y) + ZEND_ARG_INFO(0, dst_w) + ZEND_ARG_INFO(0, dst_h) + ZEND_ARG_INFO(0, src_w) + ZEND_ARG_INFO(0, src_h) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO(arginfo_imagesx, 0) + ZEND_ARG_INFO(0, im) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO(arginfo_imagesy, 0) + ZEND_ARG_INFO(0, im) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO(arginfo_imagesetclip, 0) + ZEND_ARG_INFO(0, im) + ZEND_ARG_INFO(0, x1) + ZEND_ARG_INFO(0, y1) + ZEND_ARG_INFO(0, x2) + ZEND_ARG_INFO(0, y2) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO(arginfo_imagegetclip, 0) + ZEND_ARG_INFO(0, im) +ZEND_END_ARG_INFO() + +#ifdef HAVE_GD_FREETYPE +ZEND_BEGIN_ARG_INFO_EX(arginfo_imageftbbox, 0, 0, 4) + ZEND_ARG_INFO(0, size) + ZEND_ARG_INFO(0, angle) + ZEND_ARG_INFO(0, font_file) + ZEND_ARG_INFO(0, text) + ZEND_ARG_INFO(0, extrainfo) /* ARRAY_INFO(0, extrainfo, 0) */ +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_imagefttext, 0, 0, 8) + ZEND_ARG_INFO(0, im) + ZEND_ARG_INFO(0, size) + ZEND_ARG_INFO(0, angle) + ZEND_ARG_INFO(0, x) + ZEND_ARG_INFO(0, y) + ZEND_ARG_INFO(0, col) + ZEND_ARG_INFO(0, font_file) + ZEND_ARG_INFO(0, text) + ZEND_ARG_INFO(0, extrainfo) /* ARRAY_INFO(0, extrainfo, 0) */ +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO(arginfo_imagettfbbox, 0) + ZEND_ARG_INFO(0, size) + ZEND_ARG_INFO(0, angle) + ZEND_ARG_INFO(0, font_file) + ZEND_ARG_INFO(0, text) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO(arginfo_imagettftext, 0) + ZEND_ARG_INFO(0, im) + ZEND_ARG_INFO(0, size) + ZEND_ARG_INFO(0, angle) + ZEND_ARG_INFO(0, x) + ZEND_ARG_INFO(0, y) + ZEND_ARG_INFO(0, col) + ZEND_ARG_INFO(0, font_file) + ZEND_ARG_INFO(0, text) +ZEND_END_ARG_INFO() +#endif + +ZEND_BEGIN_ARG_INFO_EX(arginfo_image2wbmp, 0, 0, 1) + ZEND_ARG_INFO(0, im) + ZEND_ARG_INFO(0, filename) + ZEND_ARG_INFO(0, foreground) +ZEND_END_ARG_INFO() + +#if defined(HAVE_GD_JPG) +ZEND_BEGIN_ARG_INFO(arginfo_jpeg2wbmp, 0) + ZEND_ARG_INFO(0, f_org) + ZEND_ARG_INFO(0, f_dest) + ZEND_ARG_INFO(0, d_height) + ZEND_ARG_INFO(0, d_width) + ZEND_ARG_INFO(0, d_threshold) +ZEND_END_ARG_INFO() +#endif + +#if defined(HAVE_GD_PNG) +ZEND_BEGIN_ARG_INFO(arginfo_png2wbmp, 0) + ZEND_ARG_INFO(0, f_org) + ZEND_ARG_INFO(0, f_dest) + ZEND_ARG_INFO(0, d_height) + ZEND_ARG_INFO(0, d_width) + ZEND_ARG_INFO(0, d_threshold) +ZEND_END_ARG_INFO() +#endif + +ZEND_BEGIN_ARG_INFO_EX(arginfo_imagefilter, 0, 0, 2) + ZEND_ARG_INFO(0, im) + ZEND_ARG_INFO(0, filtertype) + ZEND_ARG_INFO(0, arg1) + ZEND_ARG_INFO(0, arg2) + ZEND_ARG_INFO(0, arg3) + ZEND_ARG_INFO(0, arg4) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO(arginfo_imageconvolution, 0) + ZEND_ARG_INFO(0, im) + ZEND_ARG_INFO(0, matrix3x3) /* ARRAY_INFO(0, matrix3x3, 0) */ + ZEND_ARG_INFO(0, div) + ZEND_ARG_INFO(0, offset) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO(arginfo_imageflip, 0) + ZEND_ARG_INFO(0, im) + ZEND_ARG_INFO(0, mode) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO(arginfo_imageantialias, 0) + ZEND_ARG_INFO(0, im) + ZEND_ARG_INFO(0, on) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO(arginfo_imagecrop, 0) + ZEND_ARG_INFO(0, im) + ZEND_ARG_INFO(0, rect) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_imagecropauto, 0, 0, 1) + ZEND_ARG_INFO(0, im) + ZEND_ARG_INFO(0, mode) + ZEND_ARG_INFO(0, threshold) + ZEND_ARG_INFO(0, color) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_imagescale, 0, 0, 2) + ZEND_ARG_INFO(0, im) + ZEND_ARG_INFO(0, new_width) + ZEND_ARG_INFO(0, new_height) + ZEND_ARG_INFO(0, mode) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_imageaffine, 0, 0, 2) + ZEND_ARG_INFO(0, im) + ZEND_ARG_INFO(0, affine) + ZEND_ARG_INFO(0, clip) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_imageaffinematrixget, 0, 0, 1) + ZEND_ARG_INFO(0, type) + ZEND_ARG_INFO(0, options) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO(arginfo_imageaffinematrixconcat, 0) + ZEND_ARG_INFO(0, m1) + ZEND_ARG_INFO(0, m2) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_imagesetinterpolation, 0, 0, 1) + ZEND_ARG_INFO(0, im) + ZEND_ARG_INFO(0, method) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_imageresolution, 0, 0, 1) + ZEND_ARG_INFO(0, im) + ZEND_ARG_INFO(0, res_x) + ZEND_ARG_INFO(0, res_y) +ZEND_END_ARG_INFO() + +/* }}} */ + +/* {{{ gd_functions[] + */ +static const zend_function_entry gd_functions[] = { + PHP_FE(gd_info, arginfo_gd_info) + PHP_FE(imagearc, arginfo_imagearc) + PHP_FE(imageellipse, arginfo_imageellipse) + PHP_FE(imagechar, arginfo_imagechar) + PHP_FE(imagecharup, arginfo_imagecharup) + PHP_FE(imagecolorat, arginfo_imagecolorat) + PHP_FE(imagecolorallocate, arginfo_imagecolorallocate) + PHP_FE(imagepalettecopy, arginfo_imagepalettecopy) + PHP_FE(imagecreatefromstring, arginfo_imagecreatefromstring) + PHP_FE(imagecolorclosest, arginfo_imagecolorclosest) + PHP_FE(imagecolorclosesthwb, arginfo_imagecolorclosesthwb) + PHP_FE(imagecolordeallocate, arginfo_imagecolordeallocate) + PHP_FE(imagecolorresolve, arginfo_imagecolorresolve) + PHP_FE(imagecolorexact, arginfo_imagecolorexact) + PHP_FE(imagecolorset, arginfo_imagecolorset) + PHP_FE(imagecolortransparent, arginfo_imagecolortransparent) + PHP_FE(imagecolorstotal, arginfo_imagecolorstotal) + PHP_FE(imagecolorsforindex, arginfo_imagecolorsforindex) + PHP_FE(imagecopy, arginfo_imagecopy) + PHP_FE(imagecopymerge, arginfo_imagecopymerge) + PHP_FE(imagecopymergegray, arginfo_imagecopymergegray) + PHP_FE(imagecopyresized, arginfo_imagecopyresized) + PHP_FE(imagecreate, arginfo_imagecreate) + PHP_FE(imagecreatetruecolor, arginfo_imagecreatetruecolor) + PHP_FE(imageistruecolor, arginfo_imageistruecolor) + PHP_FE(imagetruecolortopalette, arginfo_imagetruecolortopalette) + PHP_FE(imagepalettetotruecolor, arginfo_imagepalettetotruecolor) + PHP_FE(imagesetthickness, arginfo_imagesetthickness) + PHP_FE(imagefilledarc, arginfo_imagefilledarc) + PHP_FE(imagefilledellipse, arginfo_imagefilledellipse) + PHP_FE(imagealphablending, arginfo_imagealphablending) + PHP_FE(imagesavealpha, arginfo_imagesavealpha) + PHP_FE(imagecolorallocatealpha, arginfo_imagecolorallocatealpha) + PHP_FE(imagecolorresolvealpha, arginfo_imagecolorresolvealpha) + PHP_FE(imagecolorclosestalpha, arginfo_imagecolorclosestalpha) + PHP_FE(imagecolorexactalpha, arginfo_imagecolorexactalpha) + PHP_FE(imagecopyresampled, arginfo_imagecopyresampled) + +#ifdef PHP_WIN32 + PHP_FE(imagegrabwindow, arginfo_imagegrabwindow) + PHP_FE(imagegrabscreen, arginfo_imagegrabscreen) +#endif + + PHP_FE(imagerotate, arginfo_imagerotate) + PHP_FE(imageflip, arginfo_imageflip) + + PHP_FE(imageantialias, arginfo_imageantialias) + PHP_FE(imagecrop, arginfo_imagecrop) + PHP_FE(imagecropauto, arginfo_imagecropauto) + PHP_FE(imagescale, arginfo_imagescale) + PHP_FE(imageaffine, arginfo_imageaffine) + PHP_FE(imageaffinematrixconcat, arginfo_imageaffinematrixconcat) + PHP_FE(imageaffinematrixget, arginfo_imageaffinematrixget) + PHP_FE(imagesetinterpolation, arginfo_imagesetinterpolation) + PHP_FE(imagesettile, arginfo_imagesettile) + PHP_FE(imagesetbrush, arginfo_imagesetbrush) + PHP_FE(imagesetstyle, arginfo_imagesetstyle) + +#ifdef HAVE_GD_PNG + PHP_FE(imagecreatefrompng, arginfo_imagecreatefrompng) +#endif +#ifdef HAVE_GD_WEBP + PHP_FE(imagecreatefromwebp, arginfo_imagecreatefromwebp) +#endif + PHP_FE(imagecreatefromgif, arginfo_imagecreatefromgif) +#ifdef HAVE_GD_JPG + PHP_FE(imagecreatefromjpeg, arginfo_imagecreatefromjpeg) +#endif + PHP_FE(imagecreatefromwbmp, arginfo_imagecreatefromwbmp) + PHP_FE(imagecreatefromxbm, arginfo_imagecreatefromxbm) +#if defined(HAVE_GD_XPM) + PHP_FE(imagecreatefromxpm, arginfo_imagecreatefromxpm) +#endif + PHP_FE(imagecreatefromgd, arginfo_imagecreatefromgd) + PHP_FE(imagecreatefromgd2, arginfo_imagecreatefromgd2) + PHP_FE(imagecreatefromgd2part, arginfo_imagecreatefromgd2part) +#ifdef HAVE_GD_BMP + PHP_FE(imagecreatefrombmp, arginfo_imagecreatefrombmp) +#endif +#ifdef HAVE_GD_TGA + PHP_FE(imagecreatefromtga, arginfo_imagecreatefromtga) +#endif +#ifdef HAVE_GD_PNG + PHP_FE(imagepng, arginfo_imagepng) +#endif +#ifdef HAVE_GD_WEBP + PHP_FE(imagewebp, arginfo_imagewebp) +#endif + PHP_FE(imagegif, arginfo_imagegif) +#ifdef HAVE_GD_JPG + PHP_FE(imagejpeg, arginfo_imagejpeg) +#endif + PHP_FE(imagewbmp, arginfo_imagewbmp) + PHP_FE(imagegd, arginfo_imagegd) + PHP_FE(imagegd2, arginfo_imagegd2) +#ifdef HAVE_GD_BMP + PHP_FE(imagebmp, arginfo_imagebmp) +#endif + + PHP_FE(imagedestroy, arginfo_imagedestroy) + PHP_FE(imagegammacorrect, arginfo_imagegammacorrect) + PHP_FE(imagefill, arginfo_imagefill) + PHP_FE(imagefilledpolygon, arginfo_imagefilledpolygon) + PHP_FE(imagefilledrectangle, arginfo_imagefilledrectangle) + PHP_FE(imagefilltoborder, arginfo_imagefilltoborder) + PHP_FE(imagefontwidth, arginfo_imagefontwidth) + PHP_FE(imagefontheight, arginfo_imagefontheight) + PHP_FE(imageinterlace, arginfo_imageinterlace) + PHP_FE(imageline, arginfo_imageline) + PHP_FE(imageloadfont, arginfo_imageloadfont) + PHP_FE(imagepolygon, arginfo_imagepolygon) + PHP_FE(imageopenpolygon, arginfo_imageopenpolygon) + PHP_FE(imagerectangle, arginfo_imagerectangle) + PHP_FE(imagesetpixel, arginfo_imagesetpixel) + PHP_FE(imagestring, arginfo_imagestring) + PHP_FE(imagestringup, arginfo_imagestringup) + PHP_FE(imagesx, arginfo_imagesx) + PHP_FE(imagesy, arginfo_imagesy) + PHP_FE(imagesetclip, arginfo_imagesetclip) + PHP_FE(imagegetclip, arginfo_imagegetclip) + PHP_FE(imagedashedline, arginfo_imagedashedline) + +#ifdef HAVE_GD_FREETYPE + PHP_FE(imagettfbbox, arginfo_imagettfbbox) + PHP_FE(imagettftext, arginfo_imagettftext) + PHP_FE(imageftbbox, arginfo_imageftbbox) + PHP_FE(imagefttext, arginfo_imagefttext) +#endif + + PHP_FE(imagetypes, arginfo_imagetypes) + +#if defined(HAVE_GD_JPG) + PHP_DEP_FE(jpeg2wbmp, arginfo_jpeg2wbmp) +#endif +#if defined(HAVE_GD_PNG) + PHP_DEP_FE(png2wbmp, arginfo_png2wbmp) +#endif + PHP_DEP_FE(image2wbmp, arginfo_image2wbmp) + PHP_FE(imagelayereffect, arginfo_imagelayereffect) + PHP_FE(imagexbm, arginfo_imagexbm) + + PHP_FE(imagecolormatch, arginfo_imagecolormatch) + +/* gd filters */ + PHP_FE(imagefilter, arginfo_imagefilter) + PHP_FE(imageconvolution, arginfo_imageconvolution) + + PHP_FE(imageresolution, arginfo_imageresolution) + + PHP_FE_END +}; +/* }}} */ + +zend_module_entry gd_module_entry = { + STANDARD_MODULE_HEADER, + "gd", + gd_functions, + PHP_MINIT(gd), + PHP_MSHUTDOWN(gd), + NULL, + PHP_RSHUTDOWN(gd), + PHP_MINFO(gd), + PHP_GD_VERSION, + STANDARD_MODULE_PROPERTIES +}; + +#ifdef COMPILE_DL_GD +ZEND_GET_MODULE(gd) +#endif + +/* {{{ PHP_INI_BEGIN */ +PHP_INI_BEGIN() + PHP_INI_ENTRY("gd.jpeg_ignore_warning", "1", PHP_INI_ALL, NULL) +PHP_INI_END() +/* }}} */ + +/* {{{ php_free_gd_image + */ +static void php_free_gd_image(zend_resource *rsrc) +{ + gdImageDestroy((gdImagePtr) rsrc->ptr); +} +/* }}} */ + +/* {{{ php_free_gd_font + */ +static void php_free_gd_font(zend_resource *rsrc) +{ + gdFontPtr fp = (gdFontPtr) rsrc->ptr; + + if (fp->data) { + efree(fp->data); + } + + efree(fp); +} +/* }}} */ + +/* {{{ php_gd_error_method + */ +void php_gd_error_method(int type, const char *format, va_list args) +{ + + switch (type) { +#ifndef PHP_WIN32 + case GD_DEBUG: + case GD_INFO: +#endif + case GD_NOTICE: + type = E_NOTICE; + break; + case GD_WARNING: + type = E_WARNING; + break; + default: + type = E_ERROR; + } + php_verror(NULL, "", type, format, args); +} +/* }}} */ + +/* {{{ PHP_MINIT_FUNCTION + */ +PHP_MINIT_FUNCTION(gd) +{ + le_gd = zend_register_list_destructors_ex(php_free_gd_image, NULL, "gd", module_number); + le_gd_font = zend_register_list_destructors_ex(php_free_gd_font, NULL, "gd font", module_number); + +#if defined(HAVE_GD_FREETYPE) && defined(HAVE_GD_BUNDLED) + gdFontCacheMutexSetup(); +#endif + gdSetErrorMethod(php_gd_error_method); + + REGISTER_INI_ENTRIES(); + + REGISTER_LONG_CONSTANT("IMG_GIF", PHP_IMG_GIF, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IMG_JPG", PHP_IMG_JPG, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IMG_JPEG", PHP_IMG_JPEG, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IMG_PNG", PHP_IMG_PNG, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IMG_WBMP", PHP_IMG_WBMP, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IMG_XPM", PHP_IMG_XPM, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IMG_WEBP", PHP_IMG_WEBP, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IMG_BMP", PHP_IMG_BMP, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IMG_TGA", PHP_IMG_TGA, CONST_CS | CONST_PERSISTENT); + + /* special colours for gd */ + REGISTER_LONG_CONSTANT("IMG_COLOR_TILED", gdTiled, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IMG_COLOR_STYLED", gdStyled, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IMG_COLOR_BRUSHED", gdBrushed, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IMG_COLOR_STYLEDBRUSHED", gdStyledBrushed, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IMG_COLOR_TRANSPARENT", gdTransparent, CONST_CS | CONST_PERSISTENT); + + /* for imagefilledarc */ + REGISTER_LONG_CONSTANT("IMG_ARC_ROUNDED", gdArc, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IMG_ARC_PIE", gdPie, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IMG_ARC_CHORD", gdChord, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IMG_ARC_NOFILL", gdNoFill, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IMG_ARC_EDGED", gdEdged, CONST_CS | CONST_PERSISTENT); + + /* GD2 image format types */ + REGISTER_LONG_CONSTANT("IMG_GD2_RAW", GD2_FMT_RAW, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IMG_GD2_COMPRESSED", GD2_FMT_COMPRESSED, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IMG_FLIP_HORIZONTAL", GD_FLIP_HORINZONTAL, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IMG_FLIP_VERTICAL", GD_FLIP_VERTICAL, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IMG_FLIP_BOTH", GD_FLIP_BOTH, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IMG_EFFECT_REPLACE", gdEffectReplace, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IMG_EFFECT_ALPHABLEND", gdEffectAlphaBlend, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IMG_EFFECT_NORMAL", gdEffectNormal, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IMG_EFFECT_OVERLAY", gdEffectOverlay, CONST_CS | CONST_PERSISTENT); +#ifdef gdEffectMultiply + REGISTER_LONG_CONSTANT("IMG_EFFECT_MULTIPLY", gdEffectMultiply, CONST_CS | CONST_PERSISTENT); +#endif + + REGISTER_LONG_CONSTANT("IMG_CROP_DEFAULT", GD_CROP_DEFAULT, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IMG_CROP_TRANSPARENT", GD_CROP_TRANSPARENT, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IMG_CROP_BLACK", GD_CROP_BLACK, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IMG_CROP_WHITE", GD_CROP_WHITE, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IMG_CROP_SIDES", GD_CROP_SIDES, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IMG_CROP_THRESHOLD", GD_CROP_THRESHOLD, CONST_CS | CONST_PERSISTENT); + + + REGISTER_LONG_CONSTANT("IMG_BELL", GD_BELL, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IMG_BESSEL", GD_BESSEL, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IMG_BILINEAR_FIXED", GD_BILINEAR_FIXED, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IMG_BICUBIC", GD_BICUBIC, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IMG_BICUBIC_FIXED", GD_BICUBIC_FIXED, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IMG_BLACKMAN", GD_BLACKMAN, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IMG_BOX", GD_BOX, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IMG_BSPLINE", GD_BSPLINE, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IMG_CATMULLROM", GD_CATMULLROM, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IMG_GAUSSIAN", GD_GAUSSIAN, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IMG_GENERALIZED_CUBIC", GD_GENERALIZED_CUBIC, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IMG_HERMITE", GD_HERMITE, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IMG_HAMMING", GD_HAMMING, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IMG_HANNING", GD_HANNING, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IMG_MITCHELL", GD_MITCHELL, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IMG_POWER", GD_POWER, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IMG_QUADRATIC", GD_QUADRATIC, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IMG_SINC", GD_SINC, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IMG_NEAREST_NEIGHBOUR", GD_NEAREST_NEIGHBOUR, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IMG_WEIGHTED4", GD_WEIGHTED4, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IMG_TRIANGLE", GD_TRIANGLE, CONST_CS | CONST_PERSISTENT); + + REGISTER_LONG_CONSTANT("IMG_AFFINE_TRANSLATE", GD_AFFINE_TRANSLATE, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IMG_AFFINE_SCALE", GD_AFFINE_SCALE, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IMG_AFFINE_ROTATE", GD_AFFINE_ROTATE, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IMG_AFFINE_SHEAR_HORIZONTAL", GD_AFFINE_SHEAR_HORIZONTAL, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IMG_AFFINE_SHEAR_VERTICAL", GD_AFFINE_SHEAR_VERTICAL, CONST_CS | CONST_PERSISTENT); + +#if defined(HAVE_GD_BUNDLED) + REGISTER_LONG_CONSTANT("GD_BUNDLED", 1, CONST_CS | CONST_PERSISTENT); +#else + REGISTER_LONG_CONSTANT("GD_BUNDLED", 0, CONST_CS | CONST_PERSISTENT); +#endif + + /* Section Filters */ + REGISTER_LONG_CONSTANT("IMG_FILTER_NEGATE", IMAGE_FILTER_NEGATE, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IMG_FILTER_GRAYSCALE", IMAGE_FILTER_GRAYSCALE, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IMG_FILTER_BRIGHTNESS", IMAGE_FILTER_BRIGHTNESS, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IMG_FILTER_CONTRAST", IMAGE_FILTER_CONTRAST, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IMG_FILTER_COLORIZE", IMAGE_FILTER_COLORIZE, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IMG_FILTER_EDGEDETECT", IMAGE_FILTER_EDGEDETECT, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IMG_FILTER_GAUSSIAN_BLUR", IMAGE_FILTER_GAUSSIAN_BLUR, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IMG_FILTER_SELECTIVE_BLUR", IMAGE_FILTER_SELECTIVE_BLUR, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IMG_FILTER_EMBOSS", IMAGE_FILTER_EMBOSS, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IMG_FILTER_MEAN_REMOVAL", IMAGE_FILTER_MEAN_REMOVAL, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IMG_FILTER_SMOOTH", IMAGE_FILTER_SMOOTH, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IMG_FILTER_PIXELATE", IMAGE_FILTER_PIXELATE, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IMG_FILTER_SCATTER", IMAGE_FILTER_SCATTER, CONST_CS | CONST_PERSISTENT); + /* End Section Filters */ + +#ifdef GD_VERSION_STRING + REGISTER_STRING_CONSTANT("GD_VERSION", GD_VERSION_STRING, CONST_CS | CONST_PERSISTENT); +#endif + +#if defined(GD_MAJOR_VERSION) && defined(GD_MINOR_VERSION) && defined(GD_RELEASE_VERSION) && defined(GD_EXTRA_VERSION) + REGISTER_LONG_CONSTANT("GD_MAJOR_VERSION", GD_MAJOR_VERSION, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("GD_MINOR_VERSION", GD_MINOR_VERSION, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("GD_RELEASE_VERSION", GD_RELEASE_VERSION, CONST_CS | CONST_PERSISTENT); + REGISTER_STRING_CONSTANT("GD_EXTRA_VERSION", GD_EXTRA_VERSION, CONST_CS | CONST_PERSISTENT); +#endif + + +#ifdef HAVE_GD_PNG + + /* + * cannot include #include "png.h" + * /usr/include/pngconf.h:310:2: error: #error png.h already includes setjmp.h with some additional fixup. + * as error, use the values for now... + */ + REGISTER_LONG_CONSTANT("PNG_NO_FILTER", 0x00, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("PNG_FILTER_NONE", 0x08, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("PNG_FILTER_SUB", 0x10, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("PNG_FILTER_UP", 0x20, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("PNG_FILTER_AVG", 0x40, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("PNG_FILTER_PAETH", 0x80, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("PNG_ALL_FILTERS", 0x08 | 0x10 | 0x20 | 0x40 | 0x80, CONST_CS | CONST_PERSISTENT); +#endif + + return SUCCESS; +} +/* }}} */ + +/* {{{ PHP_MSHUTDOWN_FUNCTION + */ +PHP_MSHUTDOWN_FUNCTION(gd) +{ +#if defined(HAVE_GD_FREETYPE) && defined(HAVE_GD_BUNDLED) + gdFontCacheMutexShutdown(); +#endif + UNREGISTER_INI_ENTRIES(); + return SUCCESS; +} +/* }}} */ + +/* {{{ PHP_RSHUTDOWN_FUNCTION + */ +PHP_RSHUTDOWN_FUNCTION(gd) +{ +#ifdef HAVE_GD_FREETYPE + gdFontCacheShutdown(); +#endif + return SUCCESS; +} +/* }}} */ + +#if defined(HAVE_GD_BUNDLED) +#define PHP_GD_VERSION_STRING "bundled (2.1.0 compatible)" +#else +# define PHP_GD_VERSION_STRING GD_VERSION_STRING +#endif + +/* {{{ PHP_MINFO_FUNCTION + */ +PHP_MINFO_FUNCTION(gd) +{ + php_info_print_table_start(); + php_info_print_table_row(2, "GD Support", "enabled"); + + /* need to use a PHPAPI function here because it is external module in windows */ + +#if defined(HAVE_GD_BUNDLED) + php_info_print_table_row(2, "GD Version", PHP_GD_VERSION_STRING); +#else + php_info_print_table_row(2, "GD headers Version", PHP_GD_VERSION_STRING); +#if defined(HAVE_GD_LIBVERSION) + php_info_print_table_row(2, "GD library Version", gdVersionString()); +#endif +#endif + +#ifdef HAVE_GD_FREETYPE + php_info_print_table_row(2, "FreeType Support", "enabled"); + php_info_print_table_row(2, "FreeType Linkage", "with freetype"); +#ifdef HAVE_GD_BUNDLED + { + char tmp[256]; + +#ifdef FREETYPE_PATCH + snprintf(tmp, sizeof(tmp), "%d.%d.%d", FREETYPE_MAJOR, FREETYPE_MINOR, FREETYPE_PATCH); +#elif defined(FREETYPE_MAJOR) + snprintf(tmp, sizeof(tmp), "%d.%d", FREETYPE_MAJOR, FREETYPE_MINOR); +#else + snprintf(tmp, sizeof(tmp), "1.x"); +#endif + php_info_print_table_row(2, "FreeType Version", tmp); + } +#endif +#endif + + php_info_print_table_row(2, "GIF Read Support", "enabled"); + php_info_print_table_row(2, "GIF Create Support", "enabled"); + +#ifdef HAVE_GD_JPG + { + php_info_print_table_row(2, "JPEG Support", "enabled"); +#if defined(HAVE_GD_BUNDLED) + php_info_print_table_row(2, "libJPEG Version", gdJpegGetVersionString()); +#endif + } +#endif + +#ifdef HAVE_GD_PNG + php_info_print_table_row(2, "PNG Support", "enabled"); +#if defined(HAVE_GD_BUNDLED) + php_info_print_table_row(2, "libPNG Version", gdPngGetVersionString()); +#endif +#endif + php_info_print_table_row(2, "WBMP Support", "enabled"); +#if defined(HAVE_GD_XPM) + php_info_print_table_row(2, "XPM Support", "enabled"); +#if defined(HAVE_GD_BUNDLED) + { + char tmp[12]; + snprintf(tmp, sizeof(tmp), "%d", XpmLibraryVersion()); + php_info_print_table_row(2, "libXpm Version", tmp); + } +#endif +#endif + php_info_print_table_row(2, "XBM Support", "enabled"); +#if defined(USE_GD_JISX0208) + php_info_print_table_row(2, "JIS-mapped Japanese Font Support", "enabled"); +#endif +#ifdef HAVE_GD_WEBP + php_info_print_table_row(2, "WebP Support", "enabled"); +#endif +#ifdef HAVE_GD_BMP + php_info_print_table_row(2, "BMP Support", "enabled"); +#endif +#ifdef HAVE_GD_TGA + php_info_print_table_row(2, "TGA Read Support", "enabled"); +#endif + php_info_print_table_end(); + DISPLAY_INI_ENTRIES(); +} +/* }}} */ + +/* {{{ proto array gd_info() + */ +PHP_FUNCTION(gd_info) +{ + if (zend_parse_parameters_none() == FAILURE) { + return; + } + + array_init(return_value); + + add_assoc_string(return_value, "GD Version", PHP_GD_VERSION_STRING); + +#ifdef HAVE_GD_FREETYPE + add_assoc_bool(return_value, "FreeType Support", 1); + add_assoc_string(return_value, "FreeType Linkage", "with freetype"); +#else + add_assoc_bool(return_value, "FreeType Support", 0); +#endif + add_assoc_bool(return_value, "GIF Read Support", 1); + add_assoc_bool(return_value, "GIF Create Support", 1); +#ifdef HAVE_GD_JPG + add_assoc_bool(return_value, "JPEG Support", 1); +#else + add_assoc_bool(return_value, "JPEG Support", 0); +#endif +#ifdef HAVE_GD_PNG + add_assoc_bool(return_value, "PNG Support", 1); +#else + add_assoc_bool(return_value, "PNG Support", 0); +#endif + add_assoc_bool(return_value, "WBMP Support", 1); +#if defined(HAVE_GD_XPM) + add_assoc_bool(return_value, "XPM Support", 1); +#else + add_assoc_bool(return_value, "XPM Support", 0); +#endif + add_assoc_bool(return_value, "XBM Support", 1); +#ifdef HAVE_GD_WEBP + add_assoc_bool(return_value, "WebP Support", 1); +#else + add_assoc_bool(return_value, "WebP Support", 0); +#endif +#ifdef HAVE_GD_BMP + add_assoc_bool(return_value, "BMP Support", 1); +#else + add_assoc_bool(return_value, "BMP Support", 0); +#endif +#ifdef HAVE_GD_TGA + add_assoc_bool(return_value, "TGA Read Support", 1); +#else + add_assoc_bool(return_value, "TGA Read Support", 0); +#endif +#if defined(USE_GD_JISX0208) + add_assoc_bool(return_value, "JIS-mapped Japanese Font Support", 1); +#else + add_assoc_bool(return_value, "JIS-mapped Japanese Font Support", 0); +#endif +} +/* }}} */ + +/* Need this for cpdf. See also comment in file.c php3i_get_le_fp() */ +PHP_GD_API int phpi_get_le_gd(void) +{ + return le_gd; +} +/* }}} */ + +#define FLIPWORD(a) (((a & 0xff000000) >> 24) | ((a & 0x00ff0000) >> 8) | ((a & 0x0000ff00) << 8) | ((a & 0x000000ff) << 24)) + +/* {{{ proto int imageloadfont(string filename) + Load a new font */ +PHP_FUNCTION(imageloadfont) +{ + zval *ind; + zend_string *file; + int hdr_size = sizeof(gdFont) - sizeof(char *); + int body_size, n = 0, b, i, body_size_check; + gdFontPtr font; + php_stream *stream; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "P", &file) == FAILURE) { + return; + } + + stream = php_stream_open_wrapper(ZSTR_VAL(file), "rb", IGNORE_PATH | IGNORE_URL_WIN | REPORT_ERRORS, NULL); + if (stream == NULL) { + RETURN_FALSE; + } + + /* Only supports a architecture-dependent binary dump format + * at the moment. + * The file format is like this on machines with 32-byte integers: + * + * byte 0-3: (int) number of characters in the font + * byte 4-7: (int) value of first character in the font (often 32, space) + * byte 8-11: (int) pixel width of each character + * byte 12-15: (int) pixel height of each character + * bytes 16-: (char) array with character data, one byte per pixel + * in each character, for a total of + * (nchars*width*height) bytes. + */ + font = (gdFontPtr) emalloc(sizeof(gdFont)); + b = 0; + while (b < hdr_size && (n = php_stream_read(stream, (char*)&font[b], hdr_size - b)) > 0) { + b += n; + } + + if (n <= 0) { + efree(font); + if (php_stream_eof(stream)) { + php_error_docref(NULL, E_WARNING, "End of file while reading header"); + } else { + php_error_docref(NULL, E_WARNING, "Error while reading header"); + } + php_stream_close(stream); + RETURN_FALSE; + } + i = php_stream_tell(stream); + php_stream_seek(stream, 0, SEEK_END); + body_size_check = php_stream_tell(stream) - hdr_size; + php_stream_seek(stream, i, SEEK_SET); + + if (overflow2(font->nchars, font->h) || overflow2(font->nchars * font->h, font->w )) { + php_error_docref(NULL, E_WARNING, "Error reading font, invalid font header"); + efree(font); + php_stream_close(stream); + RETURN_FALSE; + } + + body_size = font->w * font->h * font->nchars; + if (body_size != body_size_check) { + font->w = FLIPWORD(font->w); + font->h = FLIPWORD(font->h); + font->nchars = FLIPWORD(font->nchars); + body_size = font->w * font->h * font->nchars; + } + + if (body_size != body_size_check) { + php_error_docref(NULL, E_WARNING, "Error reading font"); + efree(font); + php_stream_close(stream); + RETURN_FALSE; + } + + font->data = emalloc(body_size); + b = 0; + while (b < body_size && (n = php_stream_read(stream, &font->data[b], body_size - b)) > 0) { + b += n; + } + + if (n <= 0) { + efree(font->data); + efree(font); + if (php_stream_eof(stream)) { + php_error_docref(NULL, E_WARNING, "End of file while reading body"); + } else { + php_error_docref(NULL, E_WARNING, "Error while reading body"); + } + php_stream_close(stream); + RETURN_FALSE; + } + php_stream_close(stream); + + ind = zend_list_insert(font, le_gd_font); + + /* Adding 5 to the font index so we will never have font indices + * that overlap with the old fonts (with indices 1-5). The first + * list index given out is always 1. + */ + RETURN_LONG(Z_RES_HANDLE_P(ind) + 5); +} +/* }}} */ + +/* {{{ proto bool imagesetstyle(resource im, array styles) + Set the line drawing styles for use with imageline and IMG_COLOR_STYLED. */ +PHP_FUNCTION(imagesetstyle) +{ + zval *IM, *styles, *item; + gdImagePtr im; + int *stylearr; + int index = 0; + uint32_t num_styles; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "ra", &IM, &styles) == FAILURE) { + return; + } + + if ((im = (gdImagePtr)zend_fetch_resource(Z_RES_P(IM), "Image", le_gd)) == NULL) { + RETURN_FALSE; + } + + num_styles = zend_hash_num_elements(Z_ARRVAL_P(styles)); + if (num_styles == 0) { + php_error_docref(NULL, E_WARNING, "styles array must not be empty"); + RETURN_FALSE; + } + + /* copy the style values in the stylearr */ + stylearr = safe_emalloc(sizeof(int), num_styles, 0); + + ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(styles), item) { + stylearr[index++] = zval_get_long(item); + } ZEND_HASH_FOREACH_END(); + + gdImageSetStyle(im, stylearr, index); + + efree(stylearr); + + RETURN_TRUE; +} +/* }}} */ + +/* {{{ proto resource imagecreatetruecolor(int x_size, int y_size) + Create a new true color image */ +PHP_FUNCTION(imagecreatetruecolor) +{ + zend_long x_size, y_size; + gdImagePtr im; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "ll", &x_size, &y_size) == FAILURE) { + return; + } + + if (x_size <= 0 || y_size <= 0 || x_size >= INT_MAX || y_size >= INT_MAX) { + php_error_docref(NULL, E_WARNING, "Invalid image dimensions"); + RETURN_FALSE; + } + + im = gdImageCreateTrueColor(x_size, y_size); + + if (!im) { + RETURN_FALSE; + } + + RETURN_RES(zend_register_resource(im, le_gd)); +} +/* }}} */ + +/* {{{ proto bool imageistruecolor(resource im) + return true if the image uses truecolor */ +PHP_FUNCTION(imageistruecolor) +{ + zval *IM; + gdImagePtr im; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &IM) == FAILURE) { + return; + } + + if ((im = (gdImagePtr)zend_fetch_resource(Z_RES_P(IM), "Image", le_gd)) == NULL) { + RETURN_FALSE; + } + + RETURN_BOOL(im->trueColor); +} +/* }}} */ + +/* {{{ proto void imagetruecolortopalette(resource im, bool ditherFlag, int colorsWanted) + Convert a true color image to a palette based image with a number of colors, optionally using dithering. */ +PHP_FUNCTION(imagetruecolortopalette) +{ + zval *IM; + zend_bool dither; + zend_long ncolors; + gdImagePtr im; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "rbl", &IM, &dither, &ncolors) == FAILURE) { + return; + } + + if ((im = (gdImagePtr)zend_fetch_resource(Z_RES_P(IM), "Image", le_gd)) == NULL) { + RETURN_FALSE; + } + + if (ncolors <= 0 || ZEND_LONG_INT_OVFL(ncolors)) { + php_error_docref(NULL, E_WARNING, "Number of colors has to be greater than zero and no more than %d", INT_MAX); + RETURN_FALSE; + } + if (gdImageTrueColorToPalette(im, dither, (int)ncolors)) { + RETURN_TRUE; + } else { + php_error_docref(NULL, E_WARNING, "Couldn't convert to palette"); + RETURN_FALSE; + } +} +/* }}} */ + +/* {{{ proto void imagepalettetotruecolor(resource im) + Convert a palette based image to a true color image. */ +PHP_FUNCTION(imagepalettetotruecolor) +{ + zval *IM; + gdImagePtr im; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &IM) == FAILURE) { + return; + } + + if ((im = (gdImagePtr)zend_fetch_resource(Z_RES_P(IM), "Image", le_gd)) == NULL) { + RETURN_FALSE; + } + + if (gdImagePaletteToTrueColor(im) == 0) { + RETURN_FALSE; + } + + RETURN_TRUE; +} +/* }}} */ + +/* {{{ proto bool imagecolormatch(resource im1, resource im2) + Makes the colors of the palette version of an image more closely match the true color version */ +PHP_FUNCTION(imagecolormatch) +{ + zval *IM1, *IM2; + gdImagePtr im1, im2; + int result; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "rr", &IM1, &IM2) == FAILURE) { + return; + } + + if ((im1 = (gdImagePtr)zend_fetch_resource(Z_RES_P(IM1), "Image", le_gd)) == NULL) { + RETURN_FALSE; + } + if ((im2 = (gdImagePtr)zend_fetch_resource(Z_RES_P(IM2), "Image", le_gd)) == NULL) { + RETURN_FALSE; + } + + result = gdImageColorMatch(im1, im2); + switch (result) { + case -1: + php_error_docref(NULL, E_WARNING, "Image1 must be TrueColor" ); + RETURN_FALSE; + break; + case -2: + php_error_docref(NULL, E_WARNING, "Image2 must be Palette" ); + RETURN_FALSE; + break; + case -3: + php_error_docref(NULL, E_WARNING, "Image1 and Image2 must be the same size" ); + RETURN_FALSE; + break; + case -4: + php_error_docref(NULL, E_WARNING, "Image2 must have at least one color" ); + RETURN_FALSE; + break; + } + + RETURN_TRUE; +} +/* }}} */ + +/* {{{ proto bool imagesetthickness(resource im, int thickness) + Set line thickness for drawing lines, ellipses, rectangles, polygons etc. */ +PHP_FUNCTION(imagesetthickness) +{ + zval *IM; + zend_long thick; + gdImagePtr im; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "rl", &IM, &thick) == FAILURE) { + return; + } + + if ((im = (gdImagePtr)zend_fetch_resource(Z_RES_P(IM), "Image", le_gd)) == NULL) { + RETURN_FALSE; + } + + gdImageSetThickness(im, thick); + + RETURN_TRUE; +} +/* }}} */ + +/* {{{ proto bool imagefilledellipse(resource im, int cx, int cy, int w, int h, int color) + Draw an ellipse */ +PHP_FUNCTION(imagefilledellipse) +{ + zval *IM; + zend_long cx, cy, w, h, color; + gdImagePtr im; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "rlllll", &IM, &cx, &cy, &w, &h, &color) == FAILURE) { + return; + } + + if ((im = (gdImagePtr)zend_fetch_resource(Z_RES_P(IM), "Image", le_gd)) == NULL) { + RETURN_FALSE; + } + + gdImageFilledEllipse(im, cx, cy, w, h, color); + + RETURN_TRUE; +} +/* }}} */ + +/* {{{ proto bool imagefilledarc(resource im, int cx, int cy, int w, int h, int s, int e, int col, int style) + Draw a filled partial ellipse */ +PHP_FUNCTION(imagefilledarc) +{ + zval *IM; + zend_long cx, cy, w, h, ST, E, col, style; + gdImagePtr im; + int e, st; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "rllllllll", &IM, &cx, &cy, &w, &h, &ST, &E, &col, &style) == FAILURE) { + return; + } + + if ((im = (gdImagePtr)zend_fetch_resource(Z_RES_P(IM), "Image", le_gd)) == NULL) { + RETURN_FALSE; + } + + e = E; + if (e < 0) { + e %= 360; + } + + st = ST; + if (st < 0) { + st %= 360; + } + + gdImageFilledArc(im, cx, cy, w, h, st, e, col, style); + + RETURN_TRUE; +} +/* }}} */ + +/* {{{ proto bool imagealphablending(resource im, bool on) + Turn alpha blending mode on or off for the given image */ +PHP_FUNCTION(imagealphablending) +{ + zval *IM; + zend_bool blend; + gdImagePtr im; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "rb", &IM, &blend) == FAILURE) { + return; + } + + if ((im = (gdImagePtr)zend_fetch_resource(Z_RES_P(IM), "Image", le_gd)) == NULL) { + RETURN_FALSE; + } + + gdImageAlphaBlending(im, blend); + + RETURN_TRUE; +} +/* }}} */ + +/* {{{ proto bool imagesavealpha(resource im, bool on) + Include alpha channel to a saved image */ +PHP_FUNCTION(imagesavealpha) +{ + zval *IM; + zend_bool save; + gdImagePtr im; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "rb", &IM, &save) == FAILURE) { + return; + } + + if ((im = (gdImagePtr)zend_fetch_resource(Z_RES_P(IM), "Image", le_gd)) == NULL) { + RETURN_FALSE; + } + + gdImageSaveAlpha(im, save); + + RETURN_TRUE; +} +/* }}} */ + +/* {{{ proto bool imagelayereffect(resource im, int effect) + Set the alpha blending flag to use the bundled libgd layering effects */ +PHP_FUNCTION(imagelayereffect) +{ + zval *IM; + zend_long effect; + gdImagePtr im; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "rl", &IM, &effect) == FAILURE) { + return; + } + + if ((im = (gdImagePtr)zend_fetch_resource(Z_RES_P(IM), "Image", le_gd)) == NULL) { + RETURN_FALSE; + } + + gdImageAlphaBlending(im, effect); + + RETURN_TRUE; +} +/* }}} */ + +#define CHECK_RGBA_RANGE(component, name) \ + if (component < 0 || component > gd##name##Max) { \ + php_error_docref(NULL, E_WARNING, #name " component is out of range"); \ + RETURN_FALSE; \ + } + +/* {{{ proto int imagecolorallocatealpha(resource im, int red, int green, int blue, int alpha) + Allocate a color with an alpha level. Works for true color and palette based images */ +PHP_FUNCTION(imagecolorallocatealpha) +{ + zval *IM; + zend_long red, green, blue, alpha; + gdImagePtr im; + int ct = (-1); + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "rllll", &IM, &red, &green, &blue, &alpha) == FAILURE) { + RETURN_FALSE; + } + + if ((im = (gdImagePtr)zend_fetch_resource(Z_RES_P(IM), "Image", le_gd)) == NULL) { + RETURN_FALSE; + } + + CHECK_RGBA_RANGE(red, Red); + CHECK_RGBA_RANGE(green, Green); + CHECK_RGBA_RANGE(blue, Blue); + CHECK_RGBA_RANGE(alpha, Alpha); + + ct = gdImageColorAllocateAlpha(im, red, green, blue, alpha); + if (ct < 0) { + RETURN_FALSE; + } + RETURN_LONG((zend_long)ct); +} +/* }}} */ + +/* {{{ proto int imagecolorresolvealpha(resource im, int red, int green, int blue, int alpha) + Resolve/Allocate a colour with an alpha level. Works for true colour and palette based images */ +PHP_FUNCTION(imagecolorresolvealpha) +{ + zval *IM; + zend_long red, green, blue, alpha; + gdImagePtr im; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "rllll", &IM, &red, &green, &blue, &alpha) == FAILURE) { + return; + } + + if ((im = (gdImagePtr)zend_fetch_resource(Z_RES_P(IM), "Image", le_gd)) == NULL) { + RETURN_FALSE; + } + + CHECK_RGBA_RANGE(red, Red); + CHECK_RGBA_RANGE(green, Green); + CHECK_RGBA_RANGE(blue, Blue); + CHECK_RGBA_RANGE(alpha, Alpha); + + RETURN_LONG(gdImageColorResolveAlpha(im, red, green, blue, alpha)); +} +/* }}} */ + +/* {{{ proto int imagecolorclosestalpha(resource im, int red, int green, int blue, int alpha) + Find the closest matching colour with alpha transparency */ +PHP_FUNCTION(imagecolorclosestalpha) +{ + zval *IM; + zend_long red, green, blue, alpha; + gdImagePtr im; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "rllll", &IM, &red, &green, &blue, &alpha) == FAILURE) { + return; + } + + if ((im = (gdImagePtr)zend_fetch_resource(Z_RES_P(IM), "Image", le_gd)) == NULL) { + RETURN_FALSE; + } + + CHECK_RGBA_RANGE(red, Red); + CHECK_RGBA_RANGE(green, Green); + CHECK_RGBA_RANGE(blue, Blue); + CHECK_RGBA_RANGE(alpha, Alpha); + + RETURN_LONG(gdImageColorClosestAlpha(im, red, green, blue, alpha)); +} +/* }}} */ + +/* {{{ proto int imagecolorexactalpha(resource im, int red, int green, int blue, int alpha) + Find exact match for colour with transparency */ +PHP_FUNCTION(imagecolorexactalpha) +{ + zval *IM; + zend_long red, green, blue, alpha; + gdImagePtr im; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "rllll", &IM, &red, &green, &blue, &alpha) == FAILURE) { + return; + } + + if ((im = (gdImagePtr)zend_fetch_resource(Z_RES_P(IM), "Image", le_gd)) == NULL) { + RETURN_FALSE; + } + + CHECK_RGBA_RANGE(red, Red); + CHECK_RGBA_RANGE(green, Green); + CHECK_RGBA_RANGE(blue, Blue); + CHECK_RGBA_RANGE(alpha, Alpha); + + RETURN_LONG(gdImageColorExactAlpha(im, red, green, blue, alpha)); +} +/* }}} */ + +/* {{{ proto bool imagecopyresampled(resource dst_im, resource src_im, int dst_x, int dst_y, int src_x, int src_y, int dst_w, int dst_h, int src_w, int src_h) + Copy and resize part of an image using resampling to help ensure clarity */ +PHP_FUNCTION(imagecopyresampled) +{ + zval *SIM, *DIM; + zend_long SX, SY, SW, SH, DX, DY, DW, DH; + gdImagePtr im_dst, im_src; + int srcH, srcW, dstH, dstW, srcY, srcX, dstY, dstX; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "rrllllllll", &DIM, &SIM, &DX, &DY, &SX, &SY, &DW, &DH, &SW, &SH) == FAILURE) { + return; + } + + if ((im_dst = (gdImagePtr)zend_fetch_resource(Z_RES_P(DIM), "Image", le_gd)) == NULL) { + RETURN_FALSE; + } + + if ((im_src = (gdImagePtr)zend_fetch_resource(Z_RES_P(SIM), "Image", le_gd)) == NULL) { + RETURN_FALSE; + } + + srcX = SX; + srcY = SY; + srcH = SH; + srcW = SW; + dstX = DX; + dstY = DY; + dstH = DH; + dstW = DW; + + gdImageCopyResampled(im_dst, im_src, dstX, dstY, srcX, srcY, dstW, dstH, srcW, srcH); + + RETURN_TRUE; +} +/* }}} */ + +#ifdef PHP_WIN32 +/* {{{ proto resource imagegrabwindow(int window_handle [, int client_area]) + Grab a window or its client area using a windows handle (HWND property in COM instance) */ +PHP_FUNCTION(imagegrabwindow) +{ + HWND window; + zend_long client_area = 0; + RECT rc = {0}; + int Width, Height; + HDC hdc; + HDC memDC; + HBITMAP memBM; + HBITMAP hOld; + zend_long lwindow_handle; + gdImagePtr im = NULL; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "l|l", &lwindow_handle, &client_area) == FAILURE) { + RETURN_FALSE; + } + + window = (HWND) lwindow_handle; + + if (!IsWindow(window)) { + php_error_docref(NULL, E_NOTICE, "Invalid window handle"); + RETURN_FALSE; + } + + hdc = GetDC(0); + + if (client_area) { + GetClientRect(window, &rc); + Width = rc.right; + Height = rc.bottom; + } else { + GetWindowRect(window, &rc); + Width = rc.right - rc.left; + Height = rc.bottom - rc.top; + } + + Width = (Width/4)*4; + + memDC = CreateCompatibleDC(hdc); + memBM = CreateCompatibleBitmap(hdc, Width, Height); + hOld = (HBITMAP) SelectObject (memDC, memBM); + + PrintWindow(window, memDC, (UINT) client_area); + + im = gdImageCreateTrueColor(Width, Height); + if (im) { + int x,y; + for (y=0; y <= Height; y++) { + for (x=0; x <= Width; x++) { + int c = GetPixel(memDC, x,y); + gdImageSetPixel(im, x, y, gdTrueColor(GetRValue(c), GetGValue(c), GetBValue(c))); + } + } + } + + SelectObject(memDC,hOld); + DeleteObject(memBM); + DeleteDC(memDC); + ReleaseDC( 0, hdc ); + + if (!im) { + RETURN_FALSE; + } else { + RETURN_RES(zend_register_resource(im, le_gd)); + } +} +/* }}} */ + +/* {{{ proto resource imagegrabscreen() + Grab a screenshot */ +PHP_FUNCTION(imagegrabscreen) +{ + HWND window = GetDesktopWindow(); + RECT rc = {0}; + int Width, Height; + HDC hdc; + HDC memDC; + HBITMAP memBM; + HBITMAP hOld; + gdImagePtr im; + hdc = GetDC(0); + + if (zend_parse_parameters_none() == FAILURE) { + return; + } + + if (!hdc) { + RETURN_FALSE; + } + + GetWindowRect(window, &rc); + Width = rc.right - rc.left; + Height = rc.bottom - rc.top; + + Width = (Width/4)*4; + + memDC = CreateCompatibleDC(hdc); + memBM = CreateCompatibleBitmap(hdc, Width, Height); + hOld = (HBITMAP) SelectObject (memDC, memBM); + BitBlt( memDC, 0, 0, Width, Height , hdc, rc.left, rc.top , SRCCOPY ); + + im = gdImageCreateTrueColor(Width, Height); + if (im) { + int x,y; + for (y=0; y <= Height; y++) { + for (x=0; x <= Width; x++) { + int c = GetPixel(memDC, x,y); + gdImageSetPixel(im, x, y, gdTrueColor(GetRValue(c), GetGValue(c), GetBValue(c))); + } + } + } + + SelectObject(memDC,hOld); + DeleteObject(memBM); + DeleteDC(memDC); + ReleaseDC( 0, hdc ); + + if (!im) { + RETURN_FALSE; + } else { + RETURN_RES(zend_register_resource(im, le_gd)); + } +} +/* }}} */ +#endif /* PHP_WIN32 */ + +/* {{{ proto resource imagerotate(resource src_im, float angle, int bgdcolor [, int ignoretransparent]) + Rotate an image using a custom angle */ +PHP_FUNCTION(imagerotate) +{ + zval *SIM; + gdImagePtr im_dst, im_src; + double degrees; + zend_long color; + zend_long ignoretransparent = 0; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "rdl|l", &SIM, °rees, &color, &ignoretransparent) == FAILURE) { + RETURN_FALSE; + } + + if ((im_src = (gdImagePtr)zend_fetch_resource(Z_RES_P(SIM), "Image", le_gd)) == NULL) { + RETURN_FALSE; + } + + im_dst = gdImageRotateInterpolated(im_src, (const float)degrees, color); + + if (im_dst != NULL) { + RETURN_RES(zend_register_resource(im_dst, le_gd)); + } else { + RETURN_FALSE; + } +} +/* }}} */ + +/* {{{ proto bool imagesettile(resource image, resource tile) + Set the tile image to $tile when filling $image with the "IMG_COLOR_TILED" color */ +PHP_FUNCTION(imagesettile) +{ + zval *IM, *TILE; + gdImagePtr im, tile; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "rr", &IM, &TILE) == FAILURE) { + return; + } + + if ((im = (gdImagePtr)zend_fetch_resource(Z_RES_P(IM), "Image", le_gd)) == NULL) { + RETURN_FALSE; + } + + if ((tile = (gdImagePtr)zend_fetch_resource(Z_RES_P(TILE), "Image", le_gd)) == NULL) { + RETURN_FALSE; + } + + gdImageSetTile(im, tile); + + RETURN_TRUE; +} +/* }}} */ + +/* {{{ proto bool imagesetbrush(resource image, resource brush) + Set the brush image to $brush when filling $image with the "IMG_COLOR_BRUSHED" color */ +PHP_FUNCTION(imagesetbrush) +{ + zval *IM, *TILE; + gdImagePtr im, tile; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "rr", &IM, &TILE) == FAILURE) { + return; + } + + if ((im = (gdImagePtr)zend_fetch_resource(Z_RES_P(IM), "Image", le_gd)) == NULL) { + RETURN_FALSE; + } + + if ((tile = (gdImagePtr)zend_fetch_resource(Z_RES_P(TILE), "Image", le_gd)) == NULL) { + RETURN_FALSE; + } + + gdImageSetBrush(im, tile); + + RETURN_TRUE; +} +/* }}} */ + +/* {{{ proto resource imagecreate(int x_size, int y_size) + Create a new image */ +PHP_FUNCTION(imagecreate) +{ + zend_long x_size, y_size; + gdImagePtr im; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "ll", &x_size, &y_size) == FAILURE) { + return; + } + + if (x_size <= 0 || y_size <= 0 || x_size >= INT_MAX || y_size >= INT_MAX) { + php_error_docref(NULL, E_WARNING, "Invalid image dimensions"); + RETURN_FALSE; + } + + im = gdImageCreate(x_size, y_size); + + if (!im) { + RETURN_FALSE; + } + + RETURN_RES(zend_register_resource(im, le_gd)); +} +/* }}} */ + +/* {{{ proto int imagetypes(void) + Return the types of images supported in a bitfield - 1=GIF, 2=JPEG, 4=PNG, 8=WBMP, 16=XPM */ +PHP_FUNCTION(imagetypes) +{ + int ret = 0; + ret = PHP_IMG_GIF; +#ifdef HAVE_GD_JPG + ret |= PHP_IMG_JPG; +#endif +#ifdef HAVE_GD_PNG + ret |= PHP_IMG_PNG; +#endif + ret |= PHP_IMG_WBMP; +#if defined(HAVE_GD_XPM) + ret |= PHP_IMG_XPM; +#endif +#ifdef HAVE_GD_WEBP + ret |= PHP_IMG_WEBP; +#endif +#ifdef HAVE_GD_BMP + ret |= PHP_IMG_BMP; +#endif +#ifdef HAVE_GD_TGA + ret |= PHP_IMG_TGA; +#endif + + if (zend_parse_parameters_none() == FAILURE) { + return; + } + + RETURN_LONG(ret); +} +/* }}} */ + +/* {{{ _php_ctx_getmbi + */ + +static int _php_ctx_getmbi(gdIOCtx *ctx) +{ + int i, mbi = 0; + + do { + i = (ctx->getC)(ctx); + if (i < 0) { + return -1; + } + mbi = (mbi << 7) | (i & 0x7f); + } while (i & 0x80); + + return mbi; +} +/* }}} */ + +/* {{{ _php_image_type + */ +static const char php_sig_gd2[3] = {'g', 'd', '2'}; + +static int _php_image_type (char data[12]) +{ + /* Based on ext/standard/image.c */ + + if (data == NULL) { + return -1; + } + + if (!memcmp(data, php_sig_gd2, sizeof(php_sig_gd2))) { + return PHP_GDIMG_TYPE_GD2; + } else if (!memcmp(data, php_sig_jpg, sizeof(php_sig_jpg))) { + return PHP_GDIMG_TYPE_JPG; + } else if (!memcmp(data, php_sig_png, sizeof(php_sig_png))) { + return PHP_GDIMG_TYPE_PNG; + } else if (!memcmp(data, php_sig_gif, sizeof(php_sig_gif))) { + return PHP_GDIMG_TYPE_GIF; + } else if (!memcmp(data, php_sig_bmp, sizeof(php_sig_bmp))) { + return PHP_GDIMG_TYPE_BMP; + } else if(!memcmp(data, php_sig_riff, sizeof(php_sig_riff)) && !memcmp(data + sizeof(php_sig_riff) + sizeof(uint32_t), php_sig_webp, sizeof(php_sig_webp))) { + return PHP_GDIMG_TYPE_WEBP; + } + else { + gdIOCtx *io_ctx; + io_ctx = gdNewDynamicCtxEx(8, data, 0); + if (io_ctx) { + if (_php_ctx_getmbi(io_ctx) == 0 && _php_ctx_getmbi(io_ctx) >= 0) { + io_ctx->gd_free(io_ctx); + return PHP_GDIMG_TYPE_WBM; + } else { + io_ctx->gd_free(io_ctx); + } + } + } + return -1; +} +/* }}} */ + +/* {{{ _php_image_create_from_string + */ +gdImagePtr _php_image_create_from_string(zval *data, char *tn, gdImagePtr (*ioctx_func_p)()) +{ + gdImagePtr im; + gdIOCtx *io_ctx; + + io_ctx = gdNewDynamicCtxEx(Z_STRLEN_P(data), Z_STRVAL_P(data), 0); + + if (!io_ctx) { + return NULL; + } + + im = (*ioctx_func_p)(io_ctx); + if (!im) { + php_error_docref(NULL, E_WARNING, "Passed data is not in '%s' format", tn); + io_ctx->gd_free(io_ctx); + return NULL; + } + + io_ctx->gd_free(io_ctx); + + return im; +} +/* }}} */ + +/* {{{ proto resource imagecreatefromstring(string image) + Create a new image from the image stream in the string */ +PHP_FUNCTION(imagecreatefromstring) +{ + zval *data; + gdImagePtr im; + int imtype; + char sig[12]; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &data) == FAILURE) { + return; + } + + if (!try_convert_to_string(data)) { + return; + } + + if (Z_STRLEN_P(data) < sizeof(sig)) { + php_error_docref(NULL, E_WARNING, "Empty string or invalid image"); + RETURN_FALSE; + } + + memcpy(sig, Z_STRVAL_P(data), sizeof(sig)); + + imtype = _php_image_type(sig); + + switch (imtype) { + case PHP_GDIMG_TYPE_JPG: +#ifdef HAVE_GD_JPG + im = _php_image_create_from_string(data, "JPEG", gdImageCreateFromJpegCtx); +#else + php_error_docref(NULL, E_WARNING, "No JPEG support in this PHP build"); + RETURN_FALSE; +#endif + break; + + case PHP_GDIMG_TYPE_PNG: +#ifdef HAVE_GD_PNG + im = _php_image_create_from_string(data, "PNG", gdImageCreateFromPngCtx); +#else + php_error_docref(NULL, E_WARNING, "No PNG support in this PHP build"); + RETURN_FALSE; +#endif + break; + + case PHP_GDIMG_TYPE_GIF: + im = _php_image_create_from_string(data, "GIF", gdImageCreateFromGifCtx); + break; + + case PHP_GDIMG_TYPE_WBM: + im = _php_image_create_from_string(data, "WBMP", gdImageCreateFromWBMPCtx); + break; + + case PHP_GDIMG_TYPE_GD2: + im = _php_image_create_from_string(data, "GD2", gdImageCreateFromGd2Ctx); + break; + + case PHP_GDIMG_TYPE_BMP: + im = _php_image_create_from_string(data, "BMP", gdImageCreateFromBmpCtx); + break; + + case PHP_GDIMG_TYPE_WEBP: +#ifdef HAVE_GD_WEBP + im = _php_image_create_from_string(data, "WEBP", gdImageCreateFromWebpCtx); + break; +#else + php_error_docref(NULL, E_WARNING, "No WEBP support in this PHP build"); + RETURN_FALSE; +#endif + + default: + php_error_docref(NULL, E_WARNING, "Data is not in a recognized format"); + RETURN_FALSE; + } + + if (!im) { + php_error_docref(NULL, E_WARNING, "Couldn't create GD Image Stream out of Data"); + RETURN_FALSE; + } + + RETURN_RES(zend_register_resource(im, le_gd)); +} +/* }}} */ + +/* {{{ _php_image_create_from + */ +static void _php_image_create_from(INTERNAL_FUNCTION_PARAMETERS, int image_type, char *tn, gdImagePtr (*func_p)(), gdImagePtr (*ioctx_func_p)()) +{ + char *file; + size_t file_len; + zend_long srcx, srcy, width, height; + gdImagePtr im = NULL; + php_stream *stream; + FILE * fp = NULL; +#ifdef HAVE_GD_JPG + long ignore_warning; +#endif + + if (image_type == PHP_GDIMG_TYPE_GD2PART) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "pllll", &file, &file_len, &srcx, &srcy, &width, &height) == FAILURE) { + return; + } + if (width < 1 || height < 1) { + php_error_docref(NULL, E_WARNING, "Zero width or height not allowed"); + RETURN_FALSE; + } + } else { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "p", &file, &file_len) == FAILURE) { + return; + } + } + + + stream = php_stream_open_wrapper(file, "rb", REPORT_ERRORS|IGNORE_PATH|IGNORE_URL_WIN, NULL); + if (stream == NULL) { + RETURN_FALSE; + } + + /* try and avoid allocating a FILE* if the stream is not naturally a FILE* */ + if (php_stream_is(stream, PHP_STREAM_IS_STDIO)) { + if (FAILURE == php_stream_cast(stream, PHP_STREAM_AS_STDIO, (void**)&fp, REPORT_ERRORS)) { + goto out_err; + } + } else if (ioctx_func_p) { + /* we can create an io context */ + gdIOCtx* io_ctx; + zend_string *buff; + char *pstr; + + buff = php_stream_copy_to_mem(stream, PHP_STREAM_COPY_ALL, 0); + + if (!buff) { + php_error_docref(NULL, E_WARNING,"Cannot read image data"); + goto out_err; + } + + /* needs to be malloc (persistent) - GD will free() it later */ + pstr = pestrndup(ZSTR_VAL(buff), ZSTR_LEN(buff), 1); + io_ctx = gdNewDynamicCtxEx(ZSTR_LEN(buff), pstr, 0); + if (!io_ctx) { + pefree(pstr, 1); + zend_string_release_ex(buff, 0); + php_error_docref(NULL, E_WARNING,"Cannot allocate GD IO context"); + goto out_err; + } + + if (image_type == PHP_GDIMG_TYPE_GD2PART) { + im = (*ioctx_func_p)(io_ctx, srcx, srcy, width, height); + } else { + im = (*ioctx_func_p)(io_ctx); + } + io_ctx->gd_free(io_ctx); + pefree(pstr, 1); + zend_string_release_ex(buff, 0); + } + else if (php_stream_can_cast(stream, PHP_STREAM_AS_STDIO)) { + /* try and force the stream to be FILE* */ + if (FAILURE == php_stream_cast(stream, PHP_STREAM_AS_STDIO | PHP_STREAM_CAST_TRY_HARD, (void **) &fp, REPORT_ERRORS)) { + goto out_err; + } + } + + if (!im && fp) { + switch (image_type) { + case PHP_GDIMG_TYPE_GD2PART: + im = (*func_p)(fp, srcx, srcy, width, height); + break; +#if defined(HAVE_GD_XPM) + case PHP_GDIMG_TYPE_XPM: + im = gdImageCreateFromXpm(file); + break; +#endif + +#ifdef HAVE_GD_JPG + case PHP_GDIMG_TYPE_JPG: + ignore_warning = INI_INT("gd.jpeg_ignore_warning"); + im = gdImageCreateFromJpegEx(fp, ignore_warning); + break; +#endif + + default: + im = (*func_p)(fp); + break; + } + + fflush(fp); + } + +/* register_im: */ + if (im) { + RETVAL_RES(zend_register_resource(im, le_gd)); + php_stream_close(stream); + return; + } + + php_error_docref(NULL, E_WARNING, "'%s' is not a valid %s file", file, tn); +out_err: + php_stream_close(stream); + RETURN_FALSE; + +} +/* }}} */ + +/* {{{ proto resource imagecreatefromgif(string filename) + Create a new image from GIF file or URL */ +PHP_FUNCTION(imagecreatefromgif) +{ + _php_image_create_from(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_GDIMG_TYPE_GIF, "GIF", gdImageCreateFromGif, gdImageCreateFromGifCtx); +} +/* }}} */ + +#ifdef HAVE_GD_JPG +/* {{{ proto resource imagecreatefromjpeg(string filename) + Create a new image from JPEG file or URL */ +PHP_FUNCTION(imagecreatefromjpeg) +{ + _php_image_create_from(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_GDIMG_TYPE_JPG, "JPEG", gdImageCreateFromJpeg, gdImageCreateFromJpegCtx); +} +/* }}} */ +#endif /* HAVE_GD_JPG */ + +#ifdef HAVE_GD_PNG +/* {{{ proto resource imagecreatefrompng(string filename) + Create a new image from PNG file or URL */ +PHP_FUNCTION(imagecreatefrompng) +{ + _php_image_create_from(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_GDIMG_TYPE_PNG, "PNG", gdImageCreateFromPng, gdImageCreateFromPngCtx); +} +/* }}} */ +#endif /* HAVE_GD_PNG */ + +#ifdef HAVE_GD_WEBP +/* {{{ proto resource imagecreatefromwebp(string filename) + Create a new image from WEBP file or URL */ +PHP_FUNCTION(imagecreatefromwebp) +{ + _php_image_create_from(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_GDIMG_TYPE_WEBP, "WEBP", gdImageCreateFromWebp, gdImageCreateFromWebpCtx); +} +/* }}} */ +#endif /* HAVE_GD_WEBP */ + +/* {{{ proto resource imagecreatefromxbm(string filename) + Create a new image from XBM file or URL */ +PHP_FUNCTION(imagecreatefromxbm) +{ + _php_image_create_from(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_GDIMG_TYPE_XBM, "XBM", gdImageCreateFromXbm, NULL); +} +/* }}} */ + +#if defined(HAVE_GD_XPM) +/* {{{ proto resource imagecreatefromxpm(string filename) + Create a new image from XPM file or URL */ +PHP_FUNCTION(imagecreatefromxpm) +{ + _php_image_create_from(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_GDIMG_TYPE_XPM, "XPM", gdImageCreateFromXpm, NULL); +} +/* }}} */ +#endif + +/* {{{ proto resource imagecreatefromwbmp(string filename) + Create a new image from WBMP file or URL */ +PHP_FUNCTION(imagecreatefromwbmp) +{ + _php_image_create_from(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_GDIMG_TYPE_WBM, "WBMP", gdImageCreateFromWBMP, gdImageCreateFromWBMPCtx); +} +/* }}} */ + +/* {{{ proto resource imagecreatefromgd(string filename) + Create a new image from GD file or URL */ +PHP_FUNCTION(imagecreatefromgd) +{ + _php_image_create_from(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_GDIMG_TYPE_GD, "GD", gdImageCreateFromGd, gdImageCreateFromGdCtx); +} +/* }}} */ + +/* {{{ proto resource imagecreatefromgd2(string filename) + Create a new image from GD2 file or URL */ +PHP_FUNCTION(imagecreatefromgd2) +{ + _php_image_create_from(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_GDIMG_TYPE_GD2, "GD2", gdImageCreateFromGd2, gdImageCreateFromGd2Ctx); +} +/* }}} */ + +/* {{{ proto resource imagecreatefromgd2part(string filename, int srcX, int srcY, int width, int height) + Create a new image from a given part of GD2 file or URL */ +PHP_FUNCTION(imagecreatefromgd2part) +{ + _php_image_create_from(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_GDIMG_TYPE_GD2PART, "GD2", gdImageCreateFromGd2Part, gdImageCreateFromGd2PartCtx); +} +/* }}} */ + +#if defined(HAVE_GD_BMP) +/* {{{ proto resource imagecreatefrombmp(string filename) + Create a new image from BMP file or URL */ +PHP_FUNCTION(imagecreatefrombmp) +{ + _php_image_create_from(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_GDIMG_TYPE_BMP, "BMP", gdImageCreateFromBmp, gdImageCreateFromBmpCtx); +} +/* }}} */ +#endif + +#if defined(HAVE_GD_TGA) +/* {{{ proto resource imagecreatefromtga(string filename) + Create a new image from TGA file or URL */ +PHP_FUNCTION(imagecreatefromtga) +{ + _php_image_create_from(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_GDIMG_TYPE_TGA, "TGA", gdImageCreateFromTga, gdImageCreateFromTgaCtx); +} +/* }}} */ +#endif + +/* {{{ _php_image_output + */ +static void _php_image_output(INTERNAL_FUNCTION_PARAMETERS, int image_type, char *tn, void (*func_p)()) +{ + zval *imgind; + char *file = NULL; + zend_long quality = 0, type = 0; + gdImagePtr im; + char *fn = NULL; + FILE *fp; + size_t file_len = 0; + int argc = ZEND_NUM_ARGS(); + int q = -1, t = 1; + + /* The quality parameter for Wbmp stands for the foreground when called from image2wbmp() */ + /* The quality parameter for gd2 stands for chunk size */ + + if (zend_parse_parameters(argc, "r|pll", &imgind, &file, &file_len, &quality, &type) == FAILURE) { + return; + } + + if ((im = (gdImagePtr)zend_fetch_resource(Z_RES_P(imgind), "Image", le_gd)) == NULL) { + RETURN_FALSE; + } + + if (argc > 1) { + fn = file; + if (argc >= 3) { + q = quality; + if (argc == 4) { + t = type; + } + } + } + + if (argc >= 2 && file_len) { + PHP_GD_CHECK_OPEN_BASEDIR(fn, "Invalid filename"); + + fp = VCWD_FOPEN(fn, "wb"); + if (!fp) { + php_error_docref(NULL, E_WARNING, "Unable to open '%s' for writing", fn); + RETURN_FALSE; + } + + switch (image_type) { + case PHP_GDIMG_CONVERT_WBM: + if (q == -1) { + q = 0; + } else if (q < 0 || q > 255) { + php_error_docref(NULL, E_WARNING, "Invalid threshold value '%d'. It must be between 0 and 255", q); + q = 0; + } + gdImageWBMP(im, q, fp); + break; + case PHP_GDIMG_TYPE_GD: + (*func_p)(im, fp); + break; + case PHP_GDIMG_TYPE_GD2: + if (q == -1) { + q = 128; + } + (*func_p)(im, fp, q, t); + break; + default: + ZEND_ASSERT(0); + } + fflush(fp); + fclose(fp); + } else { + int b; + FILE *tmp; + char buf[4096]; + zend_string *path; + + tmp = php_open_temporary_file(NULL, NULL, &path); + if (tmp == NULL) { + php_error_docref(NULL, E_WARNING, "Unable to open temporary file"); + RETURN_FALSE; + } + + switch (image_type) { + case PHP_GDIMG_CONVERT_WBM: + if (q == -1) { + q = 0; + } else if (q < 0 || q > 255) { + php_error_docref(NULL, E_WARNING, "Invalid threshold value '%d'. It must be between 0 and 255", q); + q = 0; + } + gdImageWBMP(im, q, tmp); + break; + case PHP_GDIMG_TYPE_GD: + (*func_p)(im, tmp); + break; + case PHP_GDIMG_TYPE_GD2: + if (q == -1) { + q = 128; + } + (*func_p)(im, tmp, q, t); + break; + default: + ZEND_ASSERT(0); + } + + fseek(tmp, 0, SEEK_SET); + + while ((b = fread(buf, 1, sizeof(buf), tmp)) > 0) { + php_write(buf, b); + } + + fclose(tmp); + VCWD_UNLINK((const char *)ZSTR_VAL(path)); /* make sure that the temporary file is removed */ + zend_string_release_ex(path, 0); + } + RETURN_TRUE; +} +/* }}} */ + +/* {{{ proto int imagexbm(int im, string filename [, int foreground]) + Output XBM image to browser or file */ +PHP_FUNCTION(imagexbm) +{ + _php_image_output_ctx(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_GDIMG_TYPE_XBM, "XBM", gdImageXbmCtx); +} +/* }}} */ + +/* {{{ proto bool imagegif(resource im [, mixed to]) + Output GIF image to browser or file */ +PHP_FUNCTION(imagegif) +{ + _php_image_output_ctx(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_GDIMG_TYPE_GIF, "GIF", gdImageGifCtx); +} +/* }}} */ + +#ifdef HAVE_GD_PNG +/* {{{ proto bool imagepng(resource im [, mixed to]) + Output PNG image to browser or file */ +PHP_FUNCTION(imagepng) +{ + _php_image_output_ctx(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_GDIMG_TYPE_PNG, "PNG", gdImagePngCtxEx); +} +/* }}} */ +#endif /* HAVE_GD_PNG */ + + +#ifdef HAVE_GD_WEBP +/* {{{ proto bool imagewebp(resource im [, mixed to[, int quality]] ) + Output WEBP image to browser or file */ +PHP_FUNCTION(imagewebp) +{ + _php_image_output_ctx(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_GDIMG_TYPE_WEBP, "WEBP", gdImageWebpCtx); +} +/* }}} */ +#endif /* HAVE_GD_WEBP */ + + +#ifdef HAVE_GD_JPG +/* {{{ proto bool imagejpeg(resource im [, mixed to [, int quality]]) + Output JPEG image to browser or file */ +PHP_FUNCTION(imagejpeg) +{ + _php_image_output_ctx(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_GDIMG_TYPE_JPG, "JPEG", gdImageJpegCtx); +} +/* }}} */ +#endif /* HAVE_GD_JPG */ + +/* {{{ proto bool imagewbmp(resource im [, mixed to [, int foreground]]) + Output WBMP image to browser or file */ +PHP_FUNCTION(imagewbmp) +{ + _php_image_output_ctx(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_GDIMG_TYPE_WBM, "WBMP", gdImageWBMPCtx); +} +/* }}} */ + +/* {{{ proto bool imagegd(resource im [, mixed to]) + Output GD image to browser or file */ +PHP_FUNCTION(imagegd) +{ + _php_image_output(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_GDIMG_TYPE_GD, "GD", gdImageGd); +} +/* }}} */ + +/* {{{ proto bool imagegd2(resource im [, mixed to [, int chunk_size [, int type]]]) + Output GD2 image to browser or file */ +PHP_FUNCTION(imagegd2) +{ + _php_image_output(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_GDIMG_TYPE_GD2, "GD2", gdImageGd2); +} +/* }}} */ + +#ifdef HAVE_GD_BMP +/* {{{ proto bool imagebmp(resource im [, mixed to [, bool compressed]]) + Output BMP image to browser or file */ +PHP_FUNCTION(imagebmp) +{ + _php_image_output_ctx(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_GDIMG_TYPE_BMP, "BMP", gdImageBmpCtx); +} +/* }}} */ +#endif + +/* {{{ proto bool imagedestroy(resource im) + Destroy an image */ +PHP_FUNCTION(imagedestroy) +{ + zval *IM; + gdImagePtr im; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &IM) == FAILURE) { + return; + } + + if ((im = (gdImagePtr)zend_fetch_resource(Z_RES_P(IM), "Image", le_gd)) == NULL) { + RETURN_FALSE; + } + + zend_list_close(Z_RES_P(IM)); + + RETURN_TRUE; +} +/* }}} */ + +/* {{{ proto int imagecolorallocate(resource im, int red, int green, int blue) + Allocate a color for an image */ +PHP_FUNCTION(imagecolorallocate) +{ + zval *IM; + zend_long red, green, blue; + gdImagePtr im; + int ct = (-1); + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "rlll", &IM, &red, &green, &blue) == FAILURE) { + return; + } + + if ((im = (gdImagePtr)zend_fetch_resource(Z_RES_P(IM), "Image", le_gd)) == NULL) { + RETURN_FALSE; + } + + CHECK_RGBA_RANGE(red, Red); + CHECK_RGBA_RANGE(green, Green); + CHECK_RGBA_RANGE(blue, Blue); + + ct = gdImageColorAllocate(im, red, green, blue); + if (ct < 0) { + RETURN_FALSE; + } + RETURN_LONG(ct); +} +/* }}} */ + +/* {{{ proto void imagepalettecopy(resource dst, resource src) + Copy the palette from the src image onto the dst image */ +PHP_FUNCTION(imagepalettecopy) +{ + zval *dstim, *srcim; + gdImagePtr dst, src; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "rr", &dstim, &srcim) == FAILURE) { + return; + } + + if ((dst = (gdImagePtr)zend_fetch_resource(Z_RES_P(dstim), "Image", le_gd)) == NULL) { + RETURN_FALSE; + } + + if ((src = (gdImagePtr)zend_fetch_resource(Z_RES_P(srcim), "Image", le_gd)) == NULL) { + RETURN_FALSE; + } + + gdImagePaletteCopy(dst, src); +} +/* }}} */ + +/* {{{ proto int imagecolorat(resource im, int x, int y) + Get the index of the color of a pixel */ +PHP_FUNCTION(imagecolorat) +{ + zval *IM; + zend_long x, y; + gdImagePtr im; + + ZEND_PARSE_PARAMETERS_START(3, 3) + Z_PARAM_RESOURCE(IM) + Z_PARAM_LONG(x) + Z_PARAM_LONG(y) + ZEND_PARSE_PARAMETERS_END(); + + if ((im = (gdImagePtr)zend_fetch_resource(Z_RES_P(IM), "Image", le_gd)) == NULL) { + RETURN_FALSE; + } + + if (gdImageTrueColor(im)) { + if (im->tpixels && gdImageBoundsSafe(im, x, y)) { + RETURN_LONG(gdImageTrueColorPixel(im, x, y)); + } else { + php_error_docref(NULL, E_NOTICE, "" ZEND_LONG_FMT "," ZEND_LONG_FMT " is out of bounds", x, y); + RETURN_FALSE; + } + } else { + if (im->pixels && gdImageBoundsSafe(im, x, y)) { + RETURN_LONG(im->pixels[y][x]); + } else { + php_error_docref(NULL, E_NOTICE, "" ZEND_LONG_FMT "," ZEND_LONG_FMT " is out of bounds", x, y); + RETURN_FALSE; + } + } +} +/* }}} */ + +/* {{{ proto int imagecolorclosest(resource im, int red, int green, int blue) + Get the index of the closest color to the specified color */ +PHP_FUNCTION(imagecolorclosest) +{ + zval *IM; + zend_long red, green, blue; + gdImagePtr im; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "rlll", &IM, &red, &green, &blue) == FAILURE) { + return; + } + + if ((im = (gdImagePtr)zend_fetch_resource(Z_RES_P(IM), "Image", le_gd)) == NULL) { + RETURN_FALSE; + } + + CHECK_RGBA_RANGE(red, Red); + CHECK_RGBA_RANGE(green, Green); + CHECK_RGBA_RANGE(blue, Blue); + + RETURN_LONG(gdImageColorClosest(im, red, green, blue)); +} +/* }}} */ + +/* {{{ proto int imagecolorclosesthwb(resource im, int red, int green, int blue) + Get the index of the color which has the hue, white and blackness nearest to the given color */ +PHP_FUNCTION(imagecolorclosesthwb) +{ + zval *IM; + zend_long red, green, blue; + gdImagePtr im; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "rlll", &IM, &red, &green, &blue) == FAILURE) { + return; + } + + if ((im = (gdImagePtr)zend_fetch_resource(Z_RES_P(IM), "Image", le_gd)) == NULL) { + RETURN_FALSE; + } + + CHECK_RGBA_RANGE(red, Red); + CHECK_RGBA_RANGE(green, Green); + CHECK_RGBA_RANGE(blue, Blue); + + RETURN_LONG(gdImageColorClosestHWB(im, red, green, blue)); +} +/* }}} */ + +/* {{{ proto bool imagecolordeallocate(resource im, int index) + De-allocate a color for an image */ +PHP_FUNCTION(imagecolordeallocate) +{ + zval *IM; + zend_long index; + int col; + gdImagePtr im; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "rl", &IM, &index) == FAILURE) { + return; + } + + if ((im = (gdImagePtr)zend_fetch_resource(Z_RES_P(IM), "Image", le_gd)) == NULL) { + RETURN_FALSE; + } + + /* We can return right away for a truecolor image as deallocating colours is meaningless here */ + if (gdImageTrueColor(im)) { + RETURN_TRUE; + } + + col = index; + + if (col >= 0 && col < gdImageColorsTotal(im)) { + gdImageColorDeallocate(im, col); + RETURN_TRUE; + } else { + php_error_docref(NULL, E_WARNING, "Color index %d out of range", col); + RETURN_FALSE; + } +} +/* }}} */ + +/* {{{ proto int imagecolorresolve(resource im, int red, int green, int blue) + Get the index of the specified color or its closest possible alternative */ +PHP_FUNCTION(imagecolorresolve) +{ + zval *IM; + zend_long red, green, blue; + gdImagePtr im; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "rlll", &IM, &red, &green, &blue) == FAILURE) { + return; + } + + if ((im = (gdImagePtr)zend_fetch_resource(Z_RES_P(IM), "Image", le_gd)) == NULL) { + RETURN_FALSE; + } + + CHECK_RGBA_RANGE(red, Red); + CHECK_RGBA_RANGE(green, Green); + CHECK_RGBA_RANGE(blue, Blue); + + RETURN_LONG(gdImageColorResolve(im, red, green, blue)); +} +/* }}} */ + +/* {{{ proto int imagecolorexact(resource im, int red, int green, int blue) + Get the index of the specified color */ +PHP_FUNCTION(imagecolorexact) +{ + zval *IM; + zend_long red, green, blue; + gdImagePtr im; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "rlll", &IM, &red, &green, &blue) == FAILURE) { + return; + } + + if ((im = (gdImagePtr)zend_fetch_resource(Z_RES_P(IM), "Image", le_gd)) == NULL) { + RETURN_FALSE; + } + + CHECK_RGBA_RANGE(red, Red); + CHECK_RGBA_RANGE(green, Green); + CHECK_RGBA_RANGE(blue, Blue); + + RETURN_LONG(gdImageColorExact(im, red, green, blue)); +} +/* }}} */ + +/* {{{ proto bool imagecolorset(resource im, int col, int red, int green, int blue) + Set the color for the specified palette index */ +PHP_FUNCTION(imagecolorset) +{ + zval *IM; + zend_long color, red, green, blue, alpha = 0; + int col; + gdImagePtr im; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "rllll|l", &IM, &color, &red, &green, &blue, &alpha) == FAILURE) { + return; + } + + if ((im = (gdImagePtr)zend_fetch_resource(Z_RES_P(IM), "Image", le_gd)) == NULL) { + RETURN_FALSE; + } + + CHECK_RGBA_RANGE(red, Red); + CHECK_RGBA_RANGE(green, Green); + CHECK_RGBA_RANGE(blue, Blue); + CHECK_RGBA_RANGE(alpha, Alpha); + + col = color; + + if (col >= 0 && col < gdImageColorsTotal(im)) { + im->red[col] = red; + im->green[col] = green; + im->blue[col] = blue; + im->alpha[col] = alpha; + } else { + RETURN_FALSE; + } +} +/* }}} */ + +/* {{{ proto array imagecolorsforindex(resource im, int col) + Get the colors for an index */ +PHP_FUNCTION(imagecolorsforindex) +{ + zval *IM; + zend_long index; + int col; + gdImagePtr im; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "rl", &IM, &index) == FAILURE) { + return; + } + + if ((im = (gdImagePtr)zend_fetch_resource(Z_RES_P(IM), "Image", le_gd)) == NULL) { + RETURN_FALSE; + } + + col = index; + + if ((col >= 0 && gdImageTrueColor(im)) || (!gdImageTrueColor(im) && col >= 0 && col < gdImageColorsTotal(im))) { + array_init(return_value); + + add_assoc_long(return_value,"red", gdImageRed(im,col)); + add_assoc_long(return_value,"green", gdImageGreen(im,col)); + add_assoc_long(return_value,"blue", gdImageBlue(im,col)); + add_assoc_long(return_value,"alpha", gdImageAlpha(im,col)); + } else { + php_error_docref(NULL, E_WARNING, "Color index %d out of range", col); + RETURN_FALSE; + } +} +/* }}} */ + +/* {{{ proto bool imagegammacorrect(resource im, float inputgamma, float outputgamma) + Apply a gamma correction to a GD image */ +PHP_FUNCTION(imagegammacorrect) +{ + zval *IM; + gdImagePtr im; + int i; + double input, output, gamma; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "rdd", &IM, &input, &output) == FAILURE) { + return; + } + + if ( input <= 0.0 || output <= 0.0 ) { + php_error_docref(NULL, E_WARNING, "Gamma values should be positive"); + RETURN_FALSE; + } + + gamma = input / output; + + if ((im = (gdImagePtr)zend_fetch_resource(Z_RES_P(IM), "Image", le_gd)) == NULL) { + RETURN_FALSE; + } + + if (gdImageTrueColor(im)) { + int x, y, c; + + for (y = 0; y < gdImageSY(im); y++) { + for (x = 0; x < gdImageSX(im); x++) { + c = gdImageGetPixel(im, x, y); + gdImageSetPixel(im, x, y, + gdTrueColorAlpha( + (int) ((pow((gdTrueColorGetRed(c) / 255.0), gamma) * 255) + .5), + (int) ((pow((gdTrueColorGetGreen(c) / 255.0), gamma) * 255) + .5), + (int) ((pow((gdTrueColorGetBlue(c) / 255.0), gamma) * 255) + .5), + gdTrueColorGetAlpha(c) + ) + ); + } + } + RETURN_TRUE; + } + + for (i = 0; i < gdImageColorsTotal(im); i++) { + im->red[i] = (int)((pow((im->red[i] / 255.0), gamma) * 255) + .5); + im->green[i] = (int)((pow((im->green[i] / 255.0), gamma) * 255) + .5); + im->blue[i] = (int)((pow((im->blue[i] / 255.0), gamma) * 255) + .5); + } + + RETURN_TRUE; +} +/* }}} */ + +/* {{{ proto bool imagesetpixel(resource im, int x, int y, int col) + Set a single pixel */ +PHP_FUNCTION(imagesetpixel) +{ + zval *IM; + zend_long x, y, col; + gdImagePtr im; + + ZEND_PARSE_PARAMETERS_START(4, 4) + Z_PARAM_RESOURCE(IM) + Z_PARAM_LONG(x) + Z_PARAM_LONG(y) + Z_PARAM_LONG(col) + ZEND_PARSE_PARAMETERS_END(); + + if ((im = (gdImagePtr)zend_fetch_resource(Z_RES_P(IM), "Image", le_gd)) == NULL) { + RETURN_FALSE; + } + + gdImageSetPixel(im, x, y, col); + RETURN_TRUE; +} +/* }}} */ + +/* {{{ proto bool imageline(resource im, int x1, int y1, int x2, int y2, int col) + Draw a line */ +PHP_FUNCTION(imageline) +{ + zval *IM; + zend_long x1, y1, x2, y2, col; + gdImagePtr im; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "rlllll", &IM, &x1, &y1, &x2, &y2, &col) == FAILURE) { + return; + } + + if ((im = (gdImagePtr)zend_fetch_resource(Z_RES_P(IM), "Image", le_gd)) == NULL) { + RETURN_FALSE; + } + + if (im->AA) { + gdImageSetAntiAliased(im, col); + col = gdAntiAliased; + } + gdImageLine(im, x1, y1, x2, y2, col); + RETURN_TRUE; +} +/* }}} */ + +/* {{{ proto bool imagedashedline(resource im, int x1, int y1, int x2, int y2, int col) + Draw a dashed line */ +PHP_FUNCTION(imagedashedline) +{ + zval *IM; + zend_long x1, y1, x2, y2, col; + gdImagePtr im; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "rlllll", &IM, &x1, &y1, &x2, &y2, &col) == FAILURE) { + return; + } + + if ((im = (gdImagePtr)zend_fetch_resource(Z_RES_P(IM), "Image", le_gd)) == NULL) { + RETURN_FALSE; + } + + gdImageDashedLine(im, x1, y1, x2, y2, col); + RETURN_TRUE; +} +/* }}} */ + +/* {{{ proto bool imagerectangle(resource im, int x1, int y1, int x2, int y2, int col) + Draw a rectangle */ +PHP_FUNCTION(imagerectangle) +{ + zval *IM; + zend_long x1, y1, x2, y2, col; + gdImagePtr im; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "rlllll", &IM, &x1, &y1, &x2, &y2, &col) == FAILURE) { + return; + } + + if ((im = (gdImagePtr)zend_fetch_resource(Z_RES_P(IM), "Image", le_gd)) == NULL) { + RETURN_FALSE; + } + + gdImageRectangle(im, x1, y1, x2, y2, col); + RETURN_TRUE; +} +/* }}} */ + +/* {{{ proto bool imagefilledrectangle(resource im, int x1, int y1, int x2, int y2, int col) + Draw a filled rectangle */ +PHP_FUNCTION(imagefilledrectangle) +{ + zval *IM; + zend_long x1, y1, x2, y2, col; + gdImagePtr im; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "rlllll", &IM, &x1, &y1, &x2, &y2, &col) == FAILURE) { + return; + } + + if ((im = (gdImagePtr)zend_fetch_resource(Z_RES_P(IM), "Image", le_gd)) == NULL) { + RETURN_FALSE; + } + gdImageFilledRectangle(im, x1, y1, x2, y2, col); + RETURN_TRUE; +} +/* }}} */ + +/* {{{ proto bool imagearc(resource im, int cx, int cy, int w, int h, int s, int e, int col) + Draw a partial ellipse */ +PHP_FUNCTION(imagearc) +{ + zval *IM; + zend_long cx, cy, w, h, ST, E, col; + gdImagePtr im; + int e, st; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "rlllllll", &IM, &cx, &cy, &w, &h, &ST, &E, &col) == FAILURE) { + return; + } + + if ((im = (gdImagePtr)zend_fetch_resource(Z_RES_P(IM), "Image", le_gd)) == NULL) { + RETURN_FALSE; + } + + e = E; + if (e < 0) { + e %= 360; + } + + st = ST; + if (st < 0) { + st %= 360; + } + + gdImageArc(im, cx, cy, w, h, st, e, col); + RETURN_TRUE; +} +/* }}} */ + +/* {{{ proto bool imageellipse(resource im, int cx, int cy, int w, int h, int color) + Draw an ellipse */ +PHP_FUNCTION(imageellipse) +{ + zval *IM; + zend_long cx, cy, w, h, color; + gdImagePtr im; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "rlllll", &IM, &cx, &cy, &w, &h, &color) == FAILURE) { + return; + } + + if ((im = (gdImagePtr)zend_fetch_resource(Z_RES_P(IM), "Image", le_gd)) == NULL) { + RETURN_FALSE; + } + + gdImageEllipse(im, cx, cy, w, h, color); + RETURN_TRUE; +} +/* }}} */ + +/* {{{ proto bool imagefilltoborder(resource im, int x, int y, int border, int col) + Flood fill to specific color */ +PHP_FUNCTION(imagefilltoborder) +{ + zval *IM; + zend_long x, y, border, col; + gdImagePtr im; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "rllll", &IM, &x, &y, &border, &col) == FAILURE) { + return; + } + + if ((im = (gdImagePtr)zend_fetch_resource(Z_RES_P(IM), "Image", le_gd)) == NULL) { + RETURN_FALSE; + } + + gdImageFillToBorder(im, x, y, border, col); + RETURN_TRUE; +} +/* }}} */ + +/* {{{ proto bool imagefill(resource im, int x, int y, int col) + Flood fill */ +PHP_FUNCTION(imagefill) +{ + zval *IM; + zend_long x, y, col; + gdImagePtr im; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "rlll", &IM, &x, &y, &col) == FAILURE) { + return; + } + + if ((im = (gdImagePtr)zend_fetch_resource(Z_RES_P(IM), "Image", le_gd)) == NULL) { + RETURN_FALSE; + } + + gdImageFill(im, x, y, col); + RETURN_TRUE; +} +/* }}} */ + +/* {{{ proto int imagecolorstotal(resource im) + Find out the number of colors in an image's palette */ +PHP_FUNCTION(imagecolorstotal) +{ + zval *IM; + gdImagePtr im; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &IM) == FAILURE) { + return; + } + + if ((im = (gdImagePtr)zend_fetch_resource(Z_RES_P(IM), "Image", le_gd)) == NULL) { + RETURN_FALSE; + } + + RETURN_LONG(gdImageColorsTotal(im)); +} +/* }}} */ + +/* {{{ proto int imagecolortransparent(resource im [, int col]) + Define a color as transparent */ +PHP_FUNCTION(imagecolortransparent) +{ + zval *IM; + zend_long COL = 0; + gdImagePtr im; + int argc = ZEND_NUM_ARGS(); + + if (zend_parse_parameters(argc, "r|l", &IM, &COL) == FAILURE) { + return; + } + + if ((im = (gdImagePtr)zend_fetch_resource(Z_RES_P(IM), "Image", le_gd)) == NULL) { + RETURN_FALSE; + } + + if (argc > 1) { + gdImageColorTransparent(im, COL); + } + + RETURN_LONG(gdImageGetTransparent(im)); +} +/* }}} */ + +/* {{{ proto int imageinterlace(resource im [, int interlace]) + Enable or disable interlace */ +PHP_FUNCTION(imageinterlace) +{ + zval *IM; + int argc = ZEND_NUM_ARGS(); + zend_long INT = 0; + gdImagePtr im; + + if (zend_parse_parameters(argc, "r|l", &IM, &INT) == FAILURE) { + return; + } + + if ((im = (gdImagePtr)zend_fetch_resource(Z_RES_P(IM), "Image", le_gd)) == NULL) { + RETURN_FALSE; + } + + if (argc > 1) { + gdImageInterlace(im, INT); + } + + RETURN_LONG(gdImageGetInterlaced(im)); +} +/* }}} */ + +/* {{{ php_imagepolygon + arg = -1 open polygon + arg = 0 normal polygon + arg = 1 filled polygon */ +/* im, points, num_points, col */ +static void php_imagepolygon(INTERNAL_FUNCTION_PARAMETERS, int filled) +{ + zval *IM, *POINTS; + zend_long NPOINTS, COL; + zval *var = NULL; + gdImagePtr im; + gdPointPtr points; + int npoints, col, nelem, i; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "rall", &IM, &POINTS, &NPOINTS, &COL) == FAILURE) { + return; + } + + if ((im = (gdImagePtr)zend_fetch_resource(Z_RES_P(IM), "Image", le_gd)) == NULL) { + RETURN_FALSE; + } + + npoints = NPOINTS; + col = COL; + + nelem = zend_hash_num_elements(Z_ARRVAL_P(POINTS)); + if (nelem < 6) { + php_error_docref(NULL, E_WARNING, "You must have at least 3 points in your array"); + RETURN_FALSE; + } + if (npoints <= 0) { + php_error_docref(NULL, E_WARNING, "You must give a positive number of points"); + RETURN_FALSE; + } + if (nelem < npoints * 2) { + php_error_docref(NULL, E_WARNING, "Trying to use %d points in array with only %d points", npoints, nelem/2); + RETURN_FALSE; + } + + points = (gdPointPtr) safe_emalloc(npoints, sizeof(gdPoint), 0); + + for (i = 0; i < npoints; i++) { + if ((var = zend_hash_index_find(Z_ARRVAL_P(POINTS), (i * 2))) != NULL) { + points[i].x = zval_get_long(var); + } + if ((var = zend_hash_index_find(Z_ARRVAL_P(POINTS), (i * 2) + 1)) != NULL) { + points[i].y = zval_get_long(var); + } + } + + if (im->AA) { + gdImageSetAntiAliased(im, col); + col = gdAntiAliased; + } + switch (filled) { + case -1: + gdImageOpenPolygon(im, points, npoints, col); + break; + case 0: + gdImagePolygon(im, points, npoints, col); + break; + case 1: + gdImageFilledPolygon(im, points, npoints, col); + break; + } + + efree(points); + RETURN_TRUE; +} +/* }}} */ + +/* {{{ proto bool imagepolygon(resource im, array point, int num_points, int col) + Draw a polygon */ +PHP_FUNCTION(imagepolygon) +{ + php_imagepolygon(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0); +} +/* }}} */ + +/* {{{ proto bool imageopenpolygon(resource im, array point, int num_points, int col) + Draw a polygon */ +PHP_FUNCTION(imageopenpolygon) +{ + php_imagepolygon(INTERNAL_FUNCTION_PARAM_PASSTHRU, -1); +} +/* }}} */ + +/* {{{ proto bool imagefilledpolygon(resource im, array point, int num_points, int col) + Draw a filled polygon */ +PHP_FUNCTION(imagefilledpolygon) +{ + php_imagepolygon(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1); +} +/* }}} */ + +/* {{{ php_find_gd_font + */ +static gdFontPtr php_find_gd_font(int size) +{ + gdFontPtr font; + + switch (size) { + case 1: + font = gdFontTiny; + break; + case 2: + font = gdFontSmall; + break; + case 3: + font = gdFontMediumBold; + break; + case 4: + font = gdFontLarge; + break; + case 5: + font = gdFontGiant; + break; + default: { + zval *zv = zend_hash_index_find(&EG(regular_list), size - 5); + if (!zv || (Z_RES_P(zv))->type != le_gd_font) { + if (size < 1) { + font = gdFontTiny; + } else { + font = gdFontGiant; + } + } else { + font = (gdFontPtr)Z_RES_P(zv)->ptr; + } + } + break; + } + + return font; +} +/* }}} */ + +/* {{{ php_imagefontsize + * arg = 0 ImageFontWidth + * arg = 1 ImageFontHeight + */ +static void php_imagefontsize(INTERNAL_FUNCTION_PARAMETERS, int arg) +{ + zend_long SIZE; + gdFontPtr font; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &SIZE) == FAILURE) { + return; + } + + font = php_find_gd_font(SIZE); + RETURN_LONG(arg ? font->h : font->w); +} +/* }}} */ + +/* {{{ proto int imagefontwidth(int font) + Get font width */ +PHP_FUNCTION(imagefontwidth) +{ + php_imagefontsize(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0); +} +/* }}} */ + +/* {{{ proto int imagefontheight(int font) + Get font height */ +PHP_FUNCTION(imagefontheight) +{ + php_imagefontsize(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1); +} +/* }}} */ + +/* {{{ php_gdimagecharup + * workaround for a bug in gd 1.2 */ +static void php_gdimagecharup(gdImagePtr im, gdFontPtr f, int x, int y, int c, int color) +{ + int cx, cy, px, py, fline; + cx = 0; + cy = 0; + + if ((c < f->offset) || (c >= (f->offset + f->nchars))) { + return; + } + + fline = (c - f->offset) * f->h * f->w; + for (py = y; (py > (y - f->w)); py--) { + for (px = x; (px < (x + f->h)); px++) { + if (f->data[fline + cy * f->w + cx]) { + gdImageSetPixel(im, px, py, color); + } + cy++; + } + cy = 0; + cx++; + } +} +/* }}} */ + +/* {{{ php_imagechar + * arg = 0 ImageChar + * arg = 1 ImageCharUp + * arg = 2 ImageString + * arg = 3 ImageStringUp + */ +static void php_imagechar(INTERNAL_FUNCTION_PARAMETERS, int mode) +{ + zval *IM; + zend_long SIZE, X, Y, COL; + char *C; + size_t C_len; + gdImagePtr im; + int ch = 0, col, x, y, size, i, l = 0; + unsigned char *str = NULL; + gdFontPtr font; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "rlllsl", &IM, &SIZE, &X, &Y, &C, &C_len, &COL) == FAILURE) { + return; + } + + if ((im = (gdImagePtr)zend_fetch_resource(Z_RES_P(IM), "Image", le_gd)) == NULL) { + RETURN_FALSE; + } + + col = COL; + + if (mode < 2) { + ch = (int)((unsigned char)*C); + } else { + str = (unsigned char *) estrndup(C, C_len); + l = strlen((char *)str); + } + + y = Y; + x = X; + size = SIZE; + + font = php_find_gd_font(size); + + switch (mode) { + case 0: + gdImageChar(im, font, x, y, ch, col); + break; + case 1: + php_gdimagecharup(im, font, x, y, ch, col); + break; + case 2: + for (i = 0; (i < l); i++) { + gdImageChar(im, font, x, y, (int) ((unsigned char) str[i]), col); + x += font->w; + } + break; + case 3: { + for (i = 0; (i < l); i++) { + /* php_gdimagecharup(im, font, x, y, (int) str[i], col); */ + gdImageCharUp(im, font, x, y, (int) str[i], col); + y -= font->w; + } + break; + } + } + if (str) { + efree(str); + } + RETURN_TRUE; +} +/* }}} */ + +/* {{{ proto bool imagechar(resource im, int font, int x, int y, string c, int col) + Draw a character */ +PHP_FUNCTION(imagechar) +{ + php_imagechar(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0); +} +/* }}} */ + +/* {{{ proto bool imagecharup(resource im, int font, int x, int y, string c, int col) + Draw a character rotated 90 degrees counter-clockwise */ +PHP_FUNCTION(imagecharup) +{ + php_imagechar(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1); +} +/* }}} */ + +/* {{{ proto bool imagestring(resource im, int font, int x, int y, string str, int col) + Draw a string horizontally */ +PHP_FUNCTION(imagestring) +{ + php_imagechar(INTERNAL_FUNCTION_PARAM_PASSTHRU, 2); +} +/* }}} */ + +/* {{{ proto bool imagestringup(resource im, int font, int x, int y, string str, int col) + Draw a string vertically - rotated 90 degrees counter-clockwise */ +PHP_FUNCTION(imagestringup) +{ + php_imagechar(INTERNAL_FUNCTION_PARAM_PASSTHRU, 3); +} +/* }}} */ + +/* {{{ proto bool imagecopy(resource dst_im, resource src_im, int dst_x, int dst_y, int src_x, int src_y, int src_w, int src_h) + Copy part of an image */ +PHP_FUNCTION(imagecopy) +{ + zval *SIM, *DIM; + zend_long SX, SY, SW, SH, DX, DY; + gdImagePtr im_dst, im_src; + int srcH, srcW, srcY, srcX, dstY, dstX; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "rrllllll", &DIM, &SIM, &DX, &DY, &SX, &SY, &SW, &SH) == FAILURE) { + return; + } + + if ((im_dst = (gdImagePtr)zend_fetch_resource(Z_RES_P(DIM), "Image", le_gd)) == NULL) { + RETURN_FALSE; + } + + if ((im_src = (gdImagePtr)zend_fetch_resource(Z_RES_P(SIM), "Image", le_gd)) == NULL) { + RETURN_FALSE; + } + + srcX = SX; + srcY = SY; + srcH = SH; + srcW = SW; + dstX = DX; + dstY = DY; + + gdImageCopy(im_dst, im_src, dstX, dstY, srcX, srcY, srcW, srcH); + RETURN_TRUE; +} +/* }}} */ + +/* {{{ proto bool imagecopymerge(resource dst_im, resource src_im, int dst_x, int dst_y, int src_x, int src_y, int src_w, int src_h, int pct) + Merge one part of an image with another */ +PHP_FUNCTION(imagecopymerge) +{ + zval *SIM, *DIM; + zend_long SX, SY, SW, SH, DX, DY, PCT; + gdImagePtr im_dst, im_src; + int srcH, srcW, srcY, srcX, dstY, dstX, pct; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "rrlllllll", &DIM, &SIM, &DX, &DY, &SX, &SY, &SW, &SH, &PCT) == FAILURE) { + return; + } + + if ((im_dst = (gdImagePtr)zend_fetch_resource(Z_RES_P(DIM), "Image", le_gd)) == NULL) { + RETURN_FALSE; + } + + if ((im_src = (gdImagePtr)zend_fetch_resource(Z_RES_P(SIM), "Image", le_gd)) == NULL) { + RETURN_FALSE; + } + + srcX = SX; + srcY = SY; + srcH = SH; + srcW = SW; + dstX = DX; + dstY = DY; + pct = PCT; + + gdImageCopyMerge(im_dst, im_src, dstX, dstY, srcX, srcY, srcW, srcH, pct); + RETURN_TRUE; +} +/* }}} */ + +/* {{{ proto bool imagecopymergegray(resource dst_im, resource src_im, int dst_x, int dst_y, int src_x, int src_y, int src_w, int src_h, int pct) + Merge one part of an image with another */ +PHP_FUNCTION(imagecopymergegray) +{ + zval *SIM, *DIM; + zend_long SX, SY, SW, SH, DX, DY, PCT; + gdImagePtr im_dst, im_src; + int srcH, srcW, srcY, srcX, dstY, dstX, pct; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "rrlllllll", &DIM, &SIM, &DX, &DY, &SX, &SY, &SW, &SH, &PCT) == FAILURE) { + return; + } + + if ((im_dst = (gdImagePtr)zend_fetch_resource(Z_RES_P(DIM), "Image", le_gd)) == NULL) { + RETURN_FALSE; + } + + if ((im_src = (gdImagePtr)zend_fetch_resource(Z_RES_P(SIM), "Image", le_gd)) == NULL) { + RETURN_FALSE; + } + + srcX = SX; + srcY = SY; + srcH = SH; + srcW = SW; + dstX = DX; + dstY = DY; + pct = PCT; + + gdImageCopyMergeGray(im_dst, im_src, dstX, dstY, srcX, srcY, srcW, srcH, pct); + RETURN_TRUE; +} +/* }}} */ + +/* {{{ proto bool imagecopyresized(resource dst_im, resource src_im, int dst_x, int dst_y, int src_x, int src_y, int dst_w, int dst_h, int src_w, int src_h) + Copy and resize part of an image */ +PHP_FUNCTION(imagecopyresized) +{ + zval *SIM, *DIM; + zend_long SX, SY, SW, SH, DX, DY, DW, DH; + gdImagePtr im_dst, im_src; + int srcH, srcW, dstH, dstW, srcY, srcX, dstY, dstX; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "rrllllllll", &DIM, &SIM, &DX, &DY, &SX, &SY, &DW, &DH, &SW, &SH) == FAILURE) { + return; + } + + if ((im_dst = (gdImagePtr)zend_fetch_resource(Z_RES_P(DIM), "Image", le_gd)) == NULL) { + RETURN_FALSE; + } + + if ((im_src = (gdImagePtr)zend_fetch_resource(Z_RES_P(SIM), "Image", le_gd)) == NULL) { + RETURN_FALSE; + } + + srcX = SX; + srcY = SY; + srcH = SH; + srcW = SW; + dstX = DX; + dstY = DY; + dstH = DH; + dstW = DW; + + if (dstW <= 0 || dstH <= 0 || srcW <= 0 || srcH <= 0) { + php_error_docref(NULL, E_WARNING, "Invalid image dimensions"); + RETURN_FALSE; + } + + gdImageCopyResized(im_dst, im_src, dstX, dstY, srcX, srcY, dstW, dstH, srcW, srcH); + RETURN_TRUE; +} +/* }}} */ + +/* {{{ proto int imagesx(resource im) + Get image width */ +PHP_FUNCTION(imagesx) +{ + zval *IM; + gdImagePtr im; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &IM) == FAILURE) { + return; + } + + if ((im = (gdImagePtr)zend_fetch_resource(Z_RES_P(IM), "Image", le_gd)) == NULL) { + RETURN_FALSE; + } + + RETURN_LONG(gdImageSX(im)); +} +/* }}} */ + +/* {{{ proto int imagesy(resource im) + Get image height */ +PHP_FUNCTION(imagesy) +{ + zval *IM; + gdImagePtr im; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &IM) == FAILURE) { + return; + } + + if ((im = (gdImagePtr)zend_fetch_resource(Z_RES_P(IM), "Image", le_gd)) == NULL) { + RETURN_FALSE; + } + + RETURN_LONG(gdImageSY(im)); +} +/* }}} */ + +/* {{{ proto bool imagesetclip(resource im, int x1, int y1, int x2, int y2) + Set the clipping rectangle. */ +PHP_FUNCTION(imagesetclip) +{ + zval *im_zval; + gdImagePtr im; + zend_long x1, y1, x2, y2; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "rllll", &im_zval, &x1, &y1, &x2, &y2) == FAILURE) { + return; + } + + if ((im = (gdImagePtr)zend_fetch_resource(Z_RES_P(im_zval), "Image", le_gd)) == NULL) { + RETURN_FALSE; + } + + gdImageSetClip(im, x1, y1, x2, y2); + RETURN_TRUE; +} +/* }}} */ + +/* {{{ proto array imagegetclip(resource im) + Get the clipping rectangle. */ +PHP_FUNCTION(imagegetclip) +{ + zval *im_zval; + gdImagePtr im; + int x1, y1, x2, y2; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &im_zval) == FAILURE) { + return; + } + + if ((im = (gdImagePtr)zend_fetch_resource(Z_RES_P(im_zval), "Image", le_gd)) == NULL) { + RETURN_FALSE; + } + + gdImageGetClip(im, &x1, &y1, &x2, &y2); + + array_init(return_value); + add_next_index_long(return_value, x1); + add_next_index_long(return_value, y1); + add_next_index_long(return_value, x2); + add_next_index_long(return_value, y2); +} +/* }}} */ + +#define TTFTEXT_DRAW 0 +#define TTFTEXT_BBOX 1 + +#ifdef HAVE_GD_FREETYPE +/* {{{ proto array imageftbbox(float size, float angle, string font_file, string text [, array extrainfo]) + Give the bounding box of a text using fonts via freetype2 */ +PHP_FUNCTION(imageftbbox) +{ + php_imagettftext_common(INTERNAL_FUNCTION_PARAM_PASSTHRU, TTFTEXT_BBOX, 1); +} +/* }}} */ + +/* {{{ proto array imagefttext(resource im, float size, float angle, int x, int y, int col, string font_file, string text [, array extrainfo]) + Write text to the image using fonts via freetype2 */ +PHP_FUNCTION(imagefttext) +{ + php_imagettftext_common(INTERNAL_FUNCTION_PARAM_PASSTHRU, TTFTEXT_DRAW, 1); +} +/* }}} */ + +/* {{{ proto array imagettfbbox(float size, float angle, string font_file, string text) + Give the bounding box of a text using TrueType fonts */ +PHP_FUNCTION(imagettfbbox) +{ + php_imagettftext_common(INTERNAL_FUNCTION_PARAM_PASSTHRU, TTFTEXT_BBOX, 0); +} +/* }}} */ + +/* {{{ proto array imagettftext(resource im, float size, float angle, int x, int y, int col, string font_file, string text) + Write text to the image using a TrueType font */ +PHP_FUNCTION(imagettftext) +{ + php_imagettftext_common(INTERNAL_FUNCTION_PARAM_PASSTHRU, TTFTEXT_DRAW, 0); +} +/* }}} */ + +/* {{{ php_imagettftext_common + */ +static void php_imagettftext_common(INTERNAL_FUNCTION_PARAMETERS, int mode, int extended) +{ + zval *IM, *EXT = NULL; + gdImagePtr im=NULL; + zend_long col = -1, x = 0, y = 0; + size_t str_len, fontname_len; + int i, brect[8]; + double ptsize, angle; + char *str = NULL, *fontname = NULL; + char *error = NULL; + int argc = ZEND_NUM_ARGS(); + gdFTStringExtra strex = {0}; + + if (mode == TTFTEXT_BBOX) { + if (argc < 4 || argc > ((extended) ? 5 : 4)) { + ZEND_WRONG_PARAM_COUNT(); + } else if (zend_parse_parameters(argc, "ddss|a", &ptsize, &angle, &fontname, &fontname_len, &str, &str_len, &EXT) == FAILURE) { + RETURN_FALSE; + } + } else { + if (argc < 8 || argc > ((extended) ? 9 : 8)) { + ZEND_WRONG_PARAM_COUNT(); + } else if (zend_parse_parameters(argc, "rddlllss|a", &IM, &ptsize, &angle, &x, &y, &col, &fontname, &fontname_len, &str, &str_len, &EXT) == FAILURE) { + RETURN_FALSE; + } + if ((im = (gdImagePtr)zend_fetch_resource(Z_RES_P(IM), "Image", le_gd)) == NULL) { + RETURN_FALSE; + } + } + + /* convert angle to radians */ + angle = angle * (M_PI/180); + + if (extended && EXT) { /* parse extended info */ + zval *item; + zend_string *key; + + /* walk the assoc array */ + ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(EXT), key, item) { + if (key == NULL) { + continue; + } + if (strcmp("linespacing", ZSTR_VAL(key)) == 0) { + strex.flags |= gdFTEX_LINESPACE; + strex.linespacing = zval_get_double(item); + } + } ZEND_HASH_FOREACH_END(); + } + +#ifdef VIRTUAL_DIR + { + char tmp_font_path[MAXPATHLEN]; + + if (!VCWD_REALPATH(fontname, tmp_font_path)) { + fontname = NULL; + } + } +#endif /* VIRTUAL_DIR */ + + PHP_GD_CHECK_OPEN_BASEDIR(fontname, "Invalid font filename"); + + if (extended) { + error = gdImageStringFTEx(im, brect, col, fontname, ptsize, angle, x, y, str, &strex); + } else { + error = gdImageStringFT(im, brect, col, fontname, ptsize, angle, x, y, str); + } + + if (error) { + php_error_docref(NULL, E_WARNING, "%s", error); + RETURN_FALSE; + } + + array_init(return_value); + + /* return array with the text's bounding box */ + for (i = 0; i < 8; i++) { + add_next_index_long(return_value, brect[i]); + } +} +/* }}} */ +#endif /* HAVE_GD_FREETYPE */ + +/* {{{ proto bool image2wbmp(resource im [, string filename [, int foreground]]) + Output WBMP image to browser or file */ +PHP_FUNCTION(image2wbmp) +{ + _php_image_output(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_GDIMG_CONVERT_WBM, "WBMP", NULL); +} +/* }}} */ + +#if defined(HAVE_GD_JPG) +/* {{{ proto bool jpeg2wbmp(string f_org, string f_dest, int d_height, int d_width, int threshold) + Convert JPEG image to WBMP image */ +PHP_FUNCTION(jpeg2wbmp) +{ + _php_image_convert(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_GDIMG_TYPE_JPG); +} +/* }}} */ +#endif + +#if defined(HAVE_GD_PNG) +/* {{{ proto bool png2wbmp(string f_org, string f_dest, int d_height, int d_width, int threshold) + Convert PNG image to WBMP image */ +PHP_FUNCTION(png2wbmp) +{ + _php_image_convert(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_GDIMG_TYPE_PNG); +} +/* }}} */ +#endif + +/* {{{ _php_image_convert + * _php_image_convert converts jpeg/png images to wbmp and resizes them as needed */ +static void _php_image_convert(INTERNAL_FUNCTION_PARAMETERS, int image_type ) +{ + char *f_org, *f_dest; + size_t f_org_len, f_dest_len; + zend_long height, width, threshold; + gdImagePtr im_org, im_dest, im_tmp; + char *fn_org = NULL; + char *fn_dest = NULL; + FILE *org, *dest; + int dest_height = -1; + int dest_width = -1; + int org_height, org_width; + int white, black; + int color, color_org, median; + int int_threshold; + int x, y; + float x_ratio, y_ratio; +#ifdef HAVE_GD_JPG + zend_long ignore_warning; +#endif + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "pplll", &f_org, &f_org_len, &f_dest, &f_dest_len, &height, &width, &threshold) == FAILURE) { + return; + } + + fn_org = f_org; + fn_dest = f_dest; + dest_height = height; + dest_width = width; + int_threshold = threshold; + + /* Check threshold value */ + if (int_threshold < 0 || int_threshold > 8) { + php_error_docref(NULL, E_WARNING, "Invalid threshold value '%d'", int_threshold); + RETURN_FALSE; + } + + /* Check origin file */ + PHP_GD_CHECK_OPEN_BASEDIR(fn_org, "Invalid origin filename"); + + /* Check destination file */ + PHP_GD_CHECK_OPEN_BASEDIR(fn_dest, "Invalid destination filename"); + + /* Open origin file */ + org = VCWD_FOPEN(fn_org, "rb"); + if (!org) { + php_error_docref(NULL, E_WARNING, "Unable to open '%s' for reading", fn_org); + RETURN_FALSE; + } + + /* Open destination file */ + dest = VCWD_FOPEN(fn_dest, "wb"); + if (!dest) { + php_error_docref(NULL, E_WARNING, "Unable to open '%s' for writing", fn_dest); + fclose(org); + RETURN_FALSE; + } + + switch (image_type) { + +#ifdef HAVE_GD_JPG + case PHP_GDIMG_TYPE_JPG: + ignore_warning = INI_INT("gd.jpeg_ignore_warning"); + im_org = gdImageCreateFromJpegEx(org, ignore_warning); + if (im_org == NULL) { + php_error_docref(NULL, E_WARNING, "Unable to open '%s' Not a valid JPEG file", fn_dest); + fclose(org); + fclose(dest); + RETURN_FALSE; + } + break; +#endif /* HAVE_GD_JPG */ + +#ifdef HAVE_GD_PNG + case PHP_GDIMG_TYPE_PNG: + im_org = gdImageCreateFromPng(org); + if (im_org == NULL) { + php_error_docref(NULL, E_WARNING, "Unable to open '%s' Not a valid PNG file", fn_dest); + fclose(org); + fclose(dest); + RETURN_FALSE; + } + break; +#endif /* HAVE_GD_PNG */ + + default: + php_error_docref(NULL, E_WARNING, "Format not supported"); + fclose(org); + fclose(dest); + RETURN_FALSE; + break; + } + + fclose(org); + + org_width = gdImageSX (im_org); + org_height = gdImageSY (im_org); + + x_ratio = (float) org_width / (float) dest_width; + y_ratio = (float) org_height / (float) dest_height; + + if (x_ratio > 1 && y_ratio > 1) { + if (y_ratio > x_ratio) { + x_ratio = y_ratio; + } else { + y_ratio = x_ratio; + } + dest_width = (int) (org_width / x_ratio); + dest_height = (int) (org_height / y_ratio); + } else { + x_ratio = (float) dest_width / (float) org_width; + y_ratio = (float) dest_height / (float) org_height; + + if (y_ratio < x_ratio) { + x_ratio = y_ratio; + } else { + y_ratio = x_ratio; + } + dest_width = (int) (org_width * x_ratio); + dest_height = (int) (org_height * y_ratio); + } + + im_tmp = gdImageCreate (dest_width, dest_height); + if (im_tmp == NULL ) { + php_error_docref(NULL, E_WARNING, "Unable to allocate temporary buffer"); + fclose(dest); + gdImageDestroy(im_org); + RETURN_FALSE; + } + + gdImageCopyResized (im_tmp, im_org, 0, 0, 0, 0, dest_width, dest_height, org_width, org_height); + + gdImageDestroy(im_org); + + im_dest = gdImageCreate(dest_width, dest_height); + if (im_dest == NULL) { + php_error_docref(NULL, E_WARNING, "Unable to allocate destination buffer"); + fclose(dest); + gdImageDestroy(im_tmp); + RETURN_FALSE; + } + + white = gdImageColorAllocate(im_dest, 255, 255, 255); + if (white == -1) { + php_error_docref(NULL, E_WARNING, "Unable to allocate the colors for the destination buffer"); + fclose(dest); + gdImageDestroy(im_tmp); + gdImageDestroy(im_dest); + RETURN_FALSE; + } + + black = gdImageColorAllocate(im_dest, 0, 0, 0); + if (black == -1) { + php_error_docref(NULL, E_WARNING, "Unable to allocate the colors for the destination buffer"); + fclose(dest); + gdImageDestroy(im_tmp); + gdImageDestroy(im_dest); + RETURN_FALSE; + } + + int_threshold = int_threshold * 32; + + for (y = 0; y < dest_height; y++) { + for (x = 0; x < dest_width; x++) { + color_org = gdImageGetPixel (im_tmp, x, y); + median = (im_tmp->red[color_org] + im_tmp->green[color_org] + im_tmp->blue[color_org]) / 3; + if (median < int_threshold) { + color = black; + } else { + color = white; + } + gdImageSetPixel (im_dest, x, y, color); + } + } + + gdImageDestroy (im_tmp ); + + gdImageWBMP(im_dest, black , dest); + + fflush(dest); + fclose(dest); + + gdImageDestroy(im_dest); + + RETURN_TRUE; +} +/* }}} */ + +/* Section Filters */ +#define PHP_GD_SINGLE_RES \ + zval *SIM; \ + gdImagePtr im_src; \ + if (zend_parse_parameters(1, "r", &SIM) == FAILURE) { \ + RETURN_FALSE; \ + } \ + if ((im_src = (gdImagePtr)zend_fetch_resource(Z_RES_P(SIM), "Image", le_gd)) == NULL) { \ + RETURN_FALSE; \ + } + +static void php_image_filter_negate(INTERNAL_FUNCTION_PARAMETERS) +{ + PHP_GD_SINGLE_RES + + if (gdImageNegate(im_src) == 1) { + RETURN_TRUE; + } + + RETURN_FALSE; +} + +static void php_image_filter_grayscale(INTERNAL_FUNCTION_PARAMETERS) +{ + PHP_GD_SINGLE_RES + + if (gdImageGrayScale(im_src) == 1) { + RETURN_TRUE; + } + + RETURN_FALSE; +} + +static void php_image_filter_brightness(INTERNAL_FUNCTION_PARAMETERS) +{ + zval *SIM; + gdImagePtr im_src; + zend_long brightness, tmp; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "zll", &SIM, &tmp, &brightness) == FAILURE) { + RETURN_FALSE; + } + + if ((im_src = (gdImagePtr)zend_fetch_resource(Z_RES_P(SIM), "Image", le_gd)) == NULL) { + RETURN_FALSE; + } + + if (gdImageBrightness(im_src, (int)brightness) == 1) { + RETURN_TRUE; + } + + RETURN_FALSE; +} + +static void php_image_filter_contrast(INTERNAL_FUNCTION_PARAMETERS) +{ + zval *SIM; + gdImagePtr im_src; + zend_long contrast, tmp; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "rll", &SIM, &tmp, &contrast) == FAILURE) { + RETURN_FALSE; + } + + if ((im_src = (gdImagePtr)zend_fetch_resource(Z_RES_P(SIM), "Image", le_gd)) == NULL) { + RETURN_FALSE; + } + + if (gdImageContrast(im_src, (int)contrast) == 1) { + RETURN_TRUE; + } + + RETURN_FALSE; +} + +static void php_image_filter_colorize(INTERNAL_FUNCTION_PARAMETERS) +{ + zval *SIM; + gdImagePtr im_src; + zend_long r,g,b,tmp; + zend_long a = 0; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "rllll|l", &SIM, &tmp, &r, &g, &b, &a) == FAILURE) { + RETURN_FALSE; + } + + if ((im_src = (gdImagePtr)zend_fetch_resource(Z_RES_P(SIM), "Image", le_gd)) == NULL) { + RETURN_FALSE; + } + + if (gdImageColor(im_src, (int) r, (int) g, (int) b, (int) a) == 1) { + RETURN_TRUE; + } + + RETURN_FALSE; +} + +static void php_image_filter_edgedetect(INTERNAL_FUNCTION_PARAMETERS) +{ + PHP_GD_SINGLE_RES + + if (gdImageEdgeDetectQuick(im_src) == 1) { + RETURN_TRUE; + } + + RETURN_FALSE; +} + +static void php_image_filter_emboss(INTERNAL_FUNCTION_PARAMETERS) +{ + PHP_GD_SINGLE_RES + + if (gdImageEmboss(im_src) == 1) { + RETURN_TRUE; + } + + RETURN_FALSE; +} + +static void php_image_filter_gaussian_blur(INTERNAL_FUNCTION_PARAMETERS) +{ + PHP_GD_SINGLE_RES + + if (gdImageGaussianBlur(im_src) == 1) { + RETURN_TRUE; + } + + RETURN_FALSE; +} + +static void php_image_filter_selective_blur(INTERNAL_FUNCTION_PARAMETERS) +{ + PHP_GD_SINGLE_RES + + if (gdImageSelectiveBlur(im_src) == 1) { + RETURN_TRUE; + } + + RETURN_FALSE; +} + +static void php_image_filter_mean_removal(INTERNAL_FUNCTION_PARAMETERS) +{ + PHP_GD_SINGLE_RES + + if (gdImageMeanRemoval(im_src) == 1) { + RETURN_TRUE; + } + + RETURN_FALSE; +} + +static void php_image_filter_smooth(INTERNAL_FUNCTION_PARAMETERS) +{ + zval *SIM; + zend_long tmp; + gdImagePtr im_src; + double weight; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "rld", &SIM, &tmp, &weight) == FAILURE) { + RETURN_FALSE; + } + + if ((im_src = (gdImagePtr)zend_fetch_resource(Z_RES_P(SIM), "Image", le_gd)) == NULL) { + RETURN_FALSE; + } + + if (gdImageSmooth(im_src, (float)weight)==1) { + RETURN_TRUE; + } + + RETURN_FALSE; +} + +static void php_image_filter_pixelate(INTERNAL_FUNCTION_PARAMETERS) +{ + zval *IM; + gdImagePtr im; + zend_long tmp, blocksize; + zend_bool mode = 0; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "rll|b", &IM, &tmp, &blocksize, &mode) == FAILURE) { + RETURN_FALSE; + } + + if ((im = (gdImagePtr)zend_fetch_resource(Z_RES_P(IM), "Image", le_gd)) == NULL) { + RETURN_FALSE; + } + + if (gdImagePixelate(im, (int) blocksize, (const unsigned int) mode)) { + RETURN_TRUE; + } + + RETURN_FALSE; +} + +static void php_image_filter_scatter(INTERNAL_FUNCTION_PARAMETERS) +{ + zval *IM; + zval *hash_colors = NULL; + gdImagePtr im; + zend_long tmp; + zend_long scatter_sub, scatter_plus; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "rlll|a", &IM, &tmp, &scatter_sub, &scatter_plus, &hash_colors) == FAILURE) { + RETURN_FALSE; + } + + if ((im = (gdImagePtr)zend_fetch_resource(Z_RES_P(IM), "Image", le_gd)) == NULL) { + RETURN_FALSE; + } + + if (hash_colors) { + uint32_t i = 0; + uint32_t num_colors = zend_hash_num_elements(Z_ARRVAL_P(hash_colors)); + zval *color; + int *colors; + + if (num_colors == 0) { + RETURN_BOOL(gdImageScatter(im, (int)scatter_sub, (int)scatter_plus)); + } + + colors = emalloc(num_colors * sizeof(int)); + + ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(hash_colors), color) { + *(colors + i++) = (int) zval_get_long(color); + } ZEND_HASH_FOREACH_END(); + + RETVAL_BOOL(gdImageScatterColor(im, (int)scatter_sub, (int)scatter_plus, colors, num_colors)); + + efree(colors); + } else { + RETURN_BOOL(gdImageScatter(im, (int) scatter_sub, (int) scatter_plus)) + } +} + +/* {{{ proto bool imagefilter(resource src_im, int filtertype[, int arg1 [, int arg2 [, int arg3 [, int arg4 ]]]] ) + Applies Filter an image using a custom angle */ +PHP_FUNCTION(imagefilter) +{ + zval *tmp; + + typedef void (*image_filter)(INTERNAL_FUNCTION_PARAMETERS); + zend_long filtertype; + image_filter filters[] = + { + php_image_filter_negate , + php_image_filter_grayscale, + php_image_filter_brightness, + php_image_filter_contrast, + php_image_filter_colorize, + php_image_filter_edgedetect, + php_image_filter_emboss, + php_image_filter_gaussian_blur, + php_image_filter_selective_blur, + php_image_filter_mean_removal, + php_image_filter_smooth, + php_image_filter_pixelate, + php_image_filter_scatter + }; + + if (ZEND_NUM_ARGS() < 2 || ZEND_NUM_ARGS() > IMAGE_FILTER_MAX_ARGS) { + WRONG_PARAM_COUNT; + } else if (zend_parse_parameters(2, "rl", &tmp, &filtertype) == FAILURE) { + return; + } + + if (filtertype >= 0 && filtertype <= IMAGE_FILTER_MAX) { + filters[filtertype](INTERNAL_FUNCTION_PARAM_PASSTHRU); + } +} +/* }}} */ + +/* {{{ proto resource imageconvolution(resource src_im, array matrix3x3, double div, double offset) + Apply a 3x3 convolution matrix, using coefficient div and offset */ +PHP_FUNCTION(imageconvolution) +{ + zval *SIM, *hash_matrix; + zval *var = NULL, *var2 = NULL; + gdImagePtr im_src = NULL; + double div, offset; + int nelem, i, j, res; + float matrix[3][3] = {{0,0,0}, {0,0,0}, {0,0,0}}; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "radd", &SIM, &hash_matrix, &div, &offset) == FAILURE) { + RETURN_FALSE; + } + + if ((im_src = (gdImagePtr)zend_fetch_resource(Z_RES_P(SIM), "Image", le_gd)) == NULL) { + RETURN_FALSE; + } + + nelem = zend_hash_num_elements(Z_ARRVAL_P(hash_matrix)); + if (nelem != 3) { + php_error_docref(NULL, E_WARNING, "You must have 3x3 array"); + RETURN_FALSE; + } + + for (i=0; i<3; i++) { + if ((var = zend_hash_index_find(Z_ARRVAL_P(hash_matrix), (i))) != NULL && Z_TYPE_P(var) == IS_ARRAY) { + if (zend_hash_num_elements(Z_ARRVAL_P(var)) != 3 ) { + php_error_docref(NULL, E_WARNING, "You must have 3x3 array"); + RETURN_FALSE; + } + + for (j=0; j<3; j++) { + if ((var2 = zend_hash_index_find(Z_ARRVAL_P(var), j)) != NULL) { + matrix[i][j] = (float) zval_get_double(var2); + } else { + php_error_docref(NULL, E_WARNING, "You must have a 3x3 matrix"); + RETURN_FALSE; + } + } + } + } + res = gdImageConvolution(im_src, matrix, (float)div, (float)offset); + + if (res) { + RETURN_TRUE; + } else { + RETURN_FALSE; + } +} +/* }}} */ +/* End section: Filters */ + +/* {{{ proto bool imageflip(resource im, int mode) + Flip an image (in place) horizontally, vertically or both directions. */ +PHP_FUNCTION(imageflip) +{ + zval *IM; + zend_long mode; + gdImagePtr im; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "rl", &IM, &mode) == FAILURE) { + return; + } + + if ((im = (gdImagePtr)zend_fetch_resource(Z_RES_P(IM), "Image", le_gd)) == NULL) { + RETURN_FALSE; + } + + switch (mode) { + case GD_FLIP_VERTICAL: + gdImageFlipVertical(im); + break; + + case GD_FLIP_HORINZONTAL: + gdImageFlipHorizontal(im); + break; + + case GD_FLIP_BOTH: + gdImageFlipBoth(im); + break; + + default: + php_error_docref(NULL, E_WARNING, "Unknown flip mode"); + RETURN_FALSE; + } + + RETURN_TRUE; +} +/* }}} */ + +/* {{{ proto bool imageantialias(resource im, bool on) + Should antialiased functions used or not*/ +PHP_FUNCTION(imageantialias) +{ + zval *IM; + zend_bool alias; + gdImagePtr im; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "rb", &IM, &alias) == FAILURE) { + return; + } + + if ((im = (gdImagePtr)zend_fetch_resource(Z_RES_P(IM), "Image", le_gd)) == NULL) { + RETURN_FALSE; + } + + if (im->trueColor) { + im->AA = alias; + } + + RETURN_TRUE; +} +/* }}} */ + +/* {{{ proto resource imagecrop(resource im, array rect) + Crop an image using the given coordinates and size, x, y, width and height. */ +PHP_FUNCTION(imagecrop) +{ + zval *IM; + gdImagePtr im; + gdImagePtr im_crop; + gdRect rect; + zval *z_rect; + zval *tmp; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "ra", &IM, &z_rect) == FAILURE) { + return; + } + + if ((im = (gdImagePtr)zend_fetch_resource(Z_RES_P(IM), "Image", le_gd)) == NULL) { + RETURN_FALSE; + } + + if ((tmp = zend_hash_str_find(Z_ARRVAL_P(z_rect), "x", sizeof("x") -1)) != NULL) { + rect.x = zval_get_long(tmp); + } else { + php_error_docref(NULL, E_WARNING, "Missing x position"); + RETURN_FALSE; + } + + if ((tmp = zend_hash_str_find(Z_ARRVAL_P(z_rect), "y", sizeof("y") - 1)) != NULL) { + rect.y = zval_get_long(tmp); + } else { + php_error_docref(NULL, E_WARNING, "Missing y position"); + RETURN_FALSE; + } + + if ((tmp = zend_hash_str_find(Z_ARRVAL_P(z_rect), "width", sizeof("width") - 1)) != NULL) { + rect.width = zval_get_long(tmp); + } else { + php_error_docref(NULL, E_WARNING, "Missing width"); + RETURN_FALSE; + } + + if ((tmp = zend_hash_str_find(Z_ARRVAL_P(z_rect), "height", sizeof("height") - 1)) != NULL) { + rect.height = zval_get_long(tmp); + } else { + php_error_docref(NULL, E_WARNING, "Missing height"); + RETURN_FALSE; + } + + im_crop = gdImageCrop(im, &rect); + + if (im_crop == NULL) { + RETURN_FALSE; + } else { + RETURN_RES(zend_register_resource(im_crop, le_gd)); + } +} +/* }}} */ + +/* {{{ proto resource imagecropauto(resource im [, int mode = GD_CROP_DEFAULT [, float threshold [, int color]]]) + Crop an image automatically using one of the available modes. */ +PHP_FUNCTION(imagecropauto) +{ + zval *IM; + zend_long mode = GD_CROP_DEFAULT; + zend_long color = -1; + double threshold = 0.5f; + gdImagePtr im; + gdImagePtr im_crop; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "r|ldl", &IM, &mode, &threshold, &color) == FAILURE) { + return; + } + + if ((im = (gdImagePtr)zend_fetch_resource(Z_RES_P(IM), "Image", le_gd)) == NULL) { + RETURN_FALSE; + } + + switch (mode) { + case -1: + php_error_docref(NULL, E_DEPRECATED, "Crop mode -1 is deprecated. Use IMG_CROP_DEFAULT instead."); + mode = GD_CROP_DEFAULT; + /* FALLTHRU */ + case GD_CROP_DEFAULT: + case GD_CROP_TRANSPARENT: + case GD_CROP_BLACK: + case GD_CROP_WHITE: + case GD_CROP_SIDES: + im_crop = gdImageCropAuto(im, mode); + break; + + case GD_CROP_THRESHOLD: + if (color < 0 || (!gdImageTrueColor(im) && color >= gdImageColorsTotal(im))) { + php_error_docref(NULL, E_WARNING, "Color argument missing with threshold mode"); + RETURN_FALSE; + } + im_crop = gdImageCropThreshold(im, color, (float) threshold); + break; + + default: + php_error_docref(NULL, E_WARNING, "Unknown crop mode"); + RETURN_FALSE; + } + if (im_crop == NULL) { + RETURN_FALSE; + } else { + RETURN_RES(zend_register_resource(im_crop, le_gd)); + } +} +/* }}} */ + +/* {{{ proto resource imagescale(resource im, int new_width[, int new_height[, int method]]) + Scale an image using the given new width and height. */ +PHP_FUNCTION(imagescale) +{ + zval *IM; + gdImagePtr im; + gdImagePtr im_scaled = NULL; + int new_width, new_height; + zend_long tmp_w, tmp_h=-1, tmp_m = GD_BILINEAR_FIXED; + gdInterpolationMethod method, old_method; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "rl|ll", &IM, &tmp_w, &tmp_h, &tmp_m) == FAILURE) { + return; + } + method = tmp_m; + + if ((im = (gdImagePtr)zend_fetch_resource(Z_RES_P(IM), "Image", le_gd)) == NULL) { + RETURN_FALSE; + } + + if (tmp_h < 0 || tmp_w < 0) { + /* preserve ratio */ + long src_x, src_y; + + src_x = gdImageSX(im); + src_y = gdImageSY(im); + + if (src_x && tmp_h < 0) { + tmp_h = tmp_w * src_y / src_x; + } + if (src_y && tmp_w < 0) { + tmp_w = tmp_h * src_x / src_y; + } + } + + if (tmp_h <= 0 || tmp_h > INT_MAX || tmp_w <= 0 || tmp_w > INT_MAX) { + RETURN_FALSE; + } + + new_width = tmp_w; + new_height = tmp_h; + + /* gdImageGetInterpolationMethod() is only available as of GD 2.1.1 */ + old_method = im->interpolation_id; + if (gdImageSetInterpolationMethod(im, method)) { + im_scaled = gdImageScale(im, new_width, new_height); + } + gdImageSetInterpolationMethod(im, old_method); + + if (im_scaled == NULL) { + RETURN_FALSE; + } else { + RETURN_RES(zend_register_resource(im_scaled, le_gd)); + } +} +/* }}} */ + +/* {{{ proto resource imageaffine(resource src, array affine[, array clip]) + Return an image containing the affine tramsformed src image, using an optional clipping area */ +PHP_FUNCTION(imageaffine) +{ + zval *IM; + gdImagePtr src; + gdImagePtr dst; + gdRect rect; + gdRectPtr pRect = NULL; + zval *z_rect = NULL; + zval *z_affine; + zval *tmp; + double affine[6]; + int i, nelems; + zval *zval_affine_elem = NULL; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "ra|a", &IM, &z_affine, &z_rect) == FAILURE) { + return; + } + + if ((src = (gdImagePtr)zend_fetch_resource(Z_RES_P(IM), "Image", le_gd)) == NULL) { + RETURN_FALSE; + } + + if ((nelems = zend_hash_num_elements(Z_ARRVAL_P(z_affine))) != 6) { + php_error_docref(NULL, E_WARNING, "Affine array must have six elements"); + RETURN_FALSE; + } + + for (i = 0; i < nelems; i++) { + if ((zval_affine_elem = zend_hash_index_find(Z_ARRVAL_P(z_affine), i)) != NULL) { + switch (Z_TYPE_P(zval_affine_elem)) { + case IS_LONG: + affine[i] = Z_LVAL_P(zval_affine_elem); + break; + case IS_DOUBLE: + affine[i] = Z_DVAL_P(zval_affine_elem); + break; + case IS_STRING: + affine[i] = zval_get_double(zval_affine_elem); + break; + default: + php_error_docref(NULL, E_WARNING, "Invalid type for element %i", i); + RETURN_FALSE; + } + } + } + + if (z_rect != NULL) { + if ((tmp = zend_hash_str_find(Z_ARRVAL_P(z_rect), "x", sizeof("x") - 1)) != NULL) { + rect.x = zval_get_long(tmp); + } else { + php_error_docref(NULL, E_WARNING, "Missing x position"); + RETURN_FALSE; + } + + if ((tmp = zend_hash_str_find(Z_ARRVAL_P(z_rect), "y", sizeof("y") - 1)) != NULL) { + rect.y = zval_get_long(tmp); + } else { + php_error_docref(NULL, E_WARNING, "Missing y position"); + RETURN_FALSE; + } + + if ((tmp = zend_hash_str_find(Z_ARRVAL_P(z_rect), "width", sizeof("width") - 1)) != NULL) { + rect.width = zval_get_long(tmp); + } else { + php_error_docref(NULL, E_WARNING, "Missing width"); + RETURN_FALSE; + } + + if ((tmp = zend_hash_str_find(Z_ARRVAL_P(z_rect), "height", sizeof("height") - 1)) != NULL) { + rect.height = zval_get_long(tmp); + } else { + php_error_docref(NULL, E_WARNING, "Missing height"); + RETURN_FALSE; + } + pRect = ▭ + } else { + rect.x = -1; + rect.y = -1; + rect.width = gdImageSX(src); + rect.height = gdImageSY(src); + pRect = NULL; + } + + if (gdTransformAffineGetImage(&dst, src, pRect, affine) != GD_TRUE) { + RETURN_FALSE; + } + + if (dst == NULL) { + RETURN_FALSE; + } else { + RETURN_RES(zend_register_resource(dst, le_gd)); + } +} +/* }}} */ + +/* {{{ proto array imageaffinematrixget(int type[, array options]) + Return an image containing the affine tramsformed src image, using an optional clipping area */ +PHP_FUNCTION(imageaffinematrixget) +{ + double affine[6]; + zend_long type; + zval *options = NULL; + zval *tmp; + int res = GD_FALSE, i; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "l|z", &type, &options) == FAILURE) { + return; + } + + switch((gdAffineStandardMatrix)type) { + case GD_AFFINE_TRANSLATE: + case GD_AFFINE_SCALE: { + double x, y; + if (!options || Z_TYPE_P(options) != IS_ARRAY) { + php_error_docref(NULL, E_WARNING, "Array expected as options"); + RETURN_FALSE; + } + if ((tmp = zend_hash_str_find(Z_ARRVAL_P(options), "x", sizeof("x") - 1)) != NULL) { + x = zval_get_double(tmp); + } else { + php_error_docref(NULL, E_WARNING, "Missing x position"); + RETURN_FALSE; + } + + if ((tmp = zend_hash_str_find(Z_ARRVAL_P(options), "y", sizeof("y") - 1)) != NULL) { + y = zval_get_double(tmp); + } else { + php_error_docref(NULL, E_WARNING, "Missing y position"); + RETURN_FALSE; + } + + if (type == GD_AFFINE_TRANSLATE) { + res = gdAffineTranslate(affine, x, y); + } else { + res = gdAffineScale(affine, x, y); + } + break; + } + + case GD_AFFINE_ROTATE: + case GD_AFFINE_SHEAR_HORIZONTAL: + case GD_AFFINE_SHEAR_VERTICAL: { + double angle; + + if (!options) { + php_error_docref(NULL, E_WARNING, "Number is expected as option"); + RETURN_FALSE; + } + + angle = zval_get_double(options); + + if (type == GD_AFFINE_SHEAR_HORIZONTAL) { + res = gdAffineShearHorizontal(affine, angle); + } else if (type == GD_AFFINE_SHEAR_VERTICAL) { + res = gdAffineShearVertical(affine, angle); + } else { + res = gdAffineRotate(affine, angle); + } + break; + } + + default: + php_error_docref(NULL, E_WARNING, "Invalid type for element " ZEND_LONG_FMT, type); + RETURN_FALSE; + } + + if (res == GD_FALSE) { + RETURN_FALSE; + } else { + array_init(return_value); + for (i = 0; i < 6; i++) { + add_index_double(return_value, i, affine[i]); + } + } +} /* }}} */ + +/* {{{ proto array imageaffineconcat(array m1, array m2) + Concat two matrices (as in doing many ops in one go) */ +PHP_FUNCTION(imageaffinematrixconcat) +{ + double m1[6]; + double m2[6]; + double mr[6]; + + zval *tmp; + zval *z_m1; + zval *z_m2; + int i, nelems; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "aa", &z_m1, &z_m2) == FAILURE) { + return; + } + + if (((nelems = zend_hash_num_elements(Z_ARRVAL_P(z_m1))) != 6) || (nelems = zend_hash_num_elements(Z_ARRVAL_P(z_m2))) != 6) { + php_error_docref(NULL, E_WARNING, "Affine arrays must have six elements"); + RETURN_FALSE; + } + + for (i = 0; i < 6; i++) { + if ((tmp = zend_hash_index_find(Z_ARRVAL_P(z_m1), i)) != NULL) { + switch (Z_TYPE_P(tmp)) { + case IS_LONG: + m1[i] = Z_LVAL_P(tmp); + break; + case IS_DOUBLE: + m1[i] = Z_DVAL_P(tmp); + break; + case IS_STRING: + m1[i] = zval_get_double(tmp); + break; + default: + php_error_docref(NULL, E_WARNING, "Invalid type for element %i", i); + RETURN_FALSE; + } + } + if ((tmp = zend_hash_index_find(Z_ARRVAL_P(z_m2), i)) != NULL) { + switch (Z_TYPE_P(tmp)) { + case IS_LONG: + m2[i] = Z_LVAL_P(tmp); + break; + case IS_DOUBLE: + m2[i] = Z_DVAL_P(tmp); + break; + case IS_STRING: + m2[i] = zval_get_double(tmp); + break; + default: + php_error_docref(NULL, E_WARNING, "Invalid type for element %i", i); + RETURN_FALSE; + } + } + } + + if (gdAffineConcat (mr, m1, m2) != GD_TRUE) { + RETURN_FALSE; + } + + array_init(return_value); + for (i = 0; i < 6; i++) { + add_index_double(return_value, i, mr[i]); + } +} /* }}} */ + +/* {{{ proto resource imagesetinterpolation(resource im [, int method]]) + Set the default interpolation method, passing -1 or 0 sets it to the libgd default (bilinear). */ +PHP_FUNCTION(imagesetinterpolation) +{ + zval *IM; + gdImagePtr im; + zend_long method = GD_BILINEAR_FIXED; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "r|l", &IM, &method) == FAILURE) { + return; + } + + if ((im = (gdImagePtr)zend_fetch_resource(Z_RES_P(IM), "Image", le_gd)) == NULL) { + RETURN_FALSE; + } + + if (method == -1) { + method = GD_BILINEAR_FIXED; + } + RETURN_BOOL(gdImageSetInterpolationMethod(im, (gdInterpolationMethod) method)); +} +/* }}} */ + +/* {{{ proto array imageresolution(resource im [, res_x, [res_y]]) + Get or set the resolution of the image in DPI. */ +PHP_FUNCTION(imageresolution) +{ + zval *IM; + gdImagePtr im; + zend_long res_x = GD_RESOLUTION, res_y = GD_RESOLUTION; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "r|ll", &IM, &res_x, &res_y) == FAILURE) { + return; + } + + if ((im = (gdImagePtr)zend_fetch_resource(Z_RES_P(IM), "Image", le_gd)) == NULL) { + RETURN_FALSE; + } + + switch (ZEND_NUM_ARGS()) { + case 3: + gdImageSetResolution(im, res_x, res_y); + RETURN_TRUE; + case 2: + gdImageSetResolution(im, res_x, res_x); + RETURN_TRUE; + default: + array_init(return_value); + add_next_index_long(return_value, gdImageResolutionX(im)); + add_next_index_long(return_value, gdImageResolutionY(im)); + } +} +/* }}} */ diff --git a/ext/gd/tests/bug81739.phpt b/ext/gd/tests/bug81739.phpt new file mode 100644 index 0000000000000..cc2a90381bab4 --- /dev/null +++ b/ext/gd/tests/bug81739.phpt @@ -0,0 +1,24 @@ +--TEST-- +Bug #81739 (OOB read due to insufficient validation in imageloadfont()) +--SKIPIF-- + +--FILE-- + +--CLEAN-- + +--EXPECTF-- +Warning: imageloadfont(): %croduct of memory allocation multiplication would exceed INT_MAX, failing operation gracefully + in %s on line %d + +Warning: imageloadfont(): Error reading font, invalid font header in %s on line %d +bool(false) \ No newline at end of file diff --git a/ext/libxml/libxml.c b/ext/libxml/libxml.c index fc194770e1013..d343135b98d37 100644 --- a/ext/libxml/libxml.c +++ b/ext/libxml/libxml.c @@ -303,6 +303,10 @@ static void *php_libxml_streams_IO_open_wrapper(const char *filename, const char int isescaped=0; xmlURI *uri; + if (strstr(filename, "%00")) { + php_error_docref(NULL, E_WARNING, "URI must not contain percent-encoded NUL bytes"); + return NULL; + } uri = xmlParseURI(filename); if (uri && (uri->scheme == NULL || @@ -482,6 +486,11 @@ php_libxml_output_buffer_create_filename(const char *URI, if (URI == NULL) return(NULL); + if (strstr(URI, "%00")) { + php_error_docref(NULL, E_WARNING, "URI must not contain percent-encoded NUL bytes"); + return NULL; + } + puri = xmlParseURI(URI); if (puri != NULL) { if (puri->scheme != NULL) diff --git a/ext/libxml/libxml.c.orig b/ext/libxml/libxml.c.orig new file mode 100644 index 0000000000000..fc194770e1013 --- /dev/null +++ b/ext/libxml/libxml.c.orig @@ -0,0 +1,1401 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 7 | + +----------------------------------------------------------------------+ + | Copyright (c) The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Shane Caraveo | + | Wez Furlong | + +----------------------------------------------------------------------+ + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "php.h" +#include "SAPI.h" + +#include "zend_variables.h" +#include "ext/standard/php_string.h" +#include "ext/standard/info.h" +#include "ext/standard/file.h" + +#if HAVE_LIBXML + +#include +#include +#include +#include +#include +#include +#ifdef LIBXML_SCHEMAS_ENABLED +#include +#include +#endif + +#include "php_libxml.h" + +#define PHP_LIBXML_ERROR 0 +#define PHP_LIBXML_CTX_ERROR 1 +#define PHP_LIBXML_CTX_WARNING 2 + +/* a true global for initialization */ +static int _php_libxml_initialized = 0; +static int _php_libxml_per_request_initialization = 1; +static xmlExternalEntityLoader _php_libxml_default_entity_loader; + +typedef struct _php_libxml_func_handler { + php_libxml_export_node export_func; +} php_libxml_func_handler; + +static HashTable php_libxml_exports; + +static ZEND_DECLARE_MODULE_GLOBALS(libxml) +static PHP_GINIT_FUNCTION(libxml); + +static PHP_FUNCTION(libxml_set_streams_context); +static PHP_FUNCTION(libxml_use_internal_errors); +static PHP_FUNCTION(libxml_get_last_error); +static PHP_FUNCTION(libxml_clear_errors); +static PHP_FUNCTION(libxml_get_errors); +static PHP_FUNCTION(libxml_set_external_entity_loader); +static PHP_FUNCTION(libxml_disable_entity_loader); + +static zend_class_entry *libxmlerror_class_entry; + +/* {{{ dynamically loadable module stuff */ +#ifdef COMPILE_DL_LIBXML +#ifdef ZTS +ZEND_TSRMLS_CACHE_DEFINE() +#endif +ZEND_GET_MODULE(libxml) +#endif /* COMPILE_DL_LIBXML */ +/* }}} */ + +/* {{{ function prototypes */ +static PHP_MINIT_FUNCTION(libxml); +static PHP_RINIT_FUNCTION(libxml); +static PHP_RSHUTDOWN_FUNCTION(libxml); +static PHP_MSHUTDOWN_FUNCTION(libxml); +static PHP_MINFO_FUNCTION(libxml); +static int php_libxml_post_deactivate(void); + +/* }}} */ + +/* {{{ arginfo */ +ZEND_BEGIN_ARG_INFO(arginfo_libxml_set_streams_context, 0) + ZEND_ARG_INFO(0, context) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_libxml_use_internal_errors, 0, 0, 0) + ZEND_ARG_INFO(0, use_errors) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO(arginfo_libxml_get_last_error, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO(arginfo_libxml_get_errors, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO(arginfo_libxml_clear_errors, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_libxml_disable_entity_loader, 0, 0, 0) + ZEND_ARG_INFO(0, disable) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_libxml_set_external_entity_loader, 0, 0, 1) + ZEND_ARG_INFO(0, resolver_function) +ZEND_END_ARG_INFO() +/* }}} */ + +/* {{{ extension definition structures */ +static const zend_function_entry libxml_functions[] = { + PHP_FE(libxml_set_streams_context, arginfo_libxml_set_streams_context) + PHP_FE(libxml_use_internal_errors, arginfo_libxml_use_internal_errors) + PHP_FE(libxml_get_last_error, arginfo_libxml_get_last_error) + PHP_FE(libxml_clear_errors, arginfo_libxml_clear_errors) + PHP_FE(libxml_get_errors, arginfo_libxml_get_errors) + PHP_FE(libxml_disable_entity_loader, arginfo_libxml_disable_entity_loader) + PHP_FE(libxml_set_external_entity_loader, arginfo_libxml_set_external_entity_loader) + PHP_FE_END +}; + +zend_module_entry libxml_module_entry = { + STANDARD_MODULE_HEADER, + "libxml", /* extension name */ + libxml_functions, /* extension function list */ + PHP_MINIT(libxml), /* extension-wide startup function */ + PHP_MSHUTDOWN(libxml), /* extension-wide shutdown function */ + PHP_RINIT(libxml), /* per-request startup function */ + PHP_RSHUTDOWN(libxml), /* per-request shutdown function */ + PHP_MINFO(libxml), /* information function */ + PHP_LIBXML_VERSION, + PHP_MODULE_GLOBALS(libxml), /* globals descriptor */ + PHP_GINIT(libxml), /* globals ctor */ + NULL, /* globals dtor */ + php_libxml_post_deactivate, /* post deactivate */ + STANDARD_MODULE_PROPERTIES_EX +}; + +/* }}} */ + +/* {{{ internal functions for interoperability */ +static int php_libxml_clear_object(php_libxml_node_object *object) +{ + if (object->properties) { + object->properties = NULL; + } + php_libxml_decrement_node_ptr(object); + return php_libxml_decrement_doc_ref(object); +} + +static int php_libxml_unregister_node(xmlNodePtr nodep) +{ + php_libxml_node_object *wrapper; + + php_libxml_node_ptr *nodeptr = nodep->_private; + + if (nodeptr != NULL) { + wrapper = nodeptr->_private; + if (wrapper) { + php_libxml_clear_object(wrapper); + } else { + if (nodeptr->node != NULL && nodeptr->node->type != XML_DOCUMENT_NODE) { + nodeptr->node->_private = NULL; + } + nodeptr->node = NULL; + } + } + + return -1; +} + +static void php_libxml_node_free(xmlNodePtr node) +{ + if(node) { + if (node->_private != NULL) { + ((php_libxml_node_ptr *) node->_private)->node = NULL; + } + switch (node->type) { + case XML_ATTRIBUTE_NODE: + xmlFreeProp((xmlAttrPtr) node); + break; + case XML_ENTITY_DECL: + case XML_ELEMENT_DECL: + case XML_ATTRIBUTE_DECL: + break; + case XML_NOTATION_NODE: + /* These require special handling */ + if (node->name != NULL) { + xmlFree((char *) node->name); + } + if (((xmlEntityPtr) node)->ExternalID != NULL) { + xmlFree((char *) ((xmlEntityPtr) node)->ExternalID); + } + if (((xmlEntityPtr) node)->SystemID != NULL) { + xmlFree((char *) ((xmlEntityPtr) node)->SystemID); + } + xmlFree(node); + break; + case XML_NAMESPACE_DECL: + if (node->ns) { + xmlFreeNs(node->ns); + node->ns = NULL; + } + node->type = XML_ELEMENT_NODE; + default: + xmlFreeNode(node); + } + } +} + +PHP_LIBXML_API void php_libxml_node_free_list(xmlNodePtr node) +{ + xmlNodePtr curnode; + + if (node != NULL) { + curnode = node; + while (curnode != NULL) { + node = curnode; + switch (node->type) { + /* Skip property freeing for the following types */ + case XML_NOTATION_NODE: + case XML_ENTITY_DECL: + break; + case XML_ENTITY_REF_NODE: + php_libxml_node_free_list((xmlNodePtr) node->properties); + break; + case XML_ATTRIBUTE_NODE: + if ((node->doc != NULL) && (((xmlAttrPtr) node)->atype == XML_ATTRIBUTE_ID)) { + xmlRemoveID(node->doc, (xmlAttrPtr) node); + } + case XML_ATTRIBUTE_DECL: + case XML_DTD_NODE: + case XML_DOCUMENT_TYPE_NODE: + case XML_NAMESPACE_DECL: + case XML_TEXT_NODE: + php_libxml_node_free_list(node->children); + break; + default: + php_libxml_node_free_list(node->children); + php_libxml_node_free_list((xmlNodePtr) node->properties); + } + + curnode = node->next; + xmlUnlinkNode(node); + if (php_libxml_unregister_node(node) == 0) { + node->doc = NULL; + } + php_libxml_node_free(node); + } + } +} + +/* }}} */ + +/* {{{ startup, shutdown and info functions */ +static PHP_GINIT_FUNCTION(libxml) +{ +#if defined(COMPILE_DL_LIBXML) && defined(ZTS) + ZEND_TSRMLS_CACHE_UPDATE(); +#endif + ZVAL_UNDEF(&libxml_globals->stream_context); + libxml_globals->error_buffer.s = NULL; + libxml_globals->error_list = NULL; + ZVAL_UNDEF(&libxml_globals->entity_loader.object); + libxml_globals->entity_loader.fci.size = 0; + libxml_globals->entity_loader_disabled = 0; +} + +static void _php_libxml_destroy_fci(zend_fcall_info *fci, zval *object) +{ + if (fci->size > 0) { + zval_ptr_dtor(&fci->function_name); + fci->size = 0; + } + if (!Z_ISUNDEF_P(object)) { + zval_ptr_dtor(object); + ZVAL_UNDEF(object); + } +} + +/* Channel libxml file io layer through the PHP streams subsystem. + * This allows use of ftps:// and https:// urls */ + +static void *php_libxml_streams_IO_open_wrapper(const char *filename, const char *mode, const int read_only) +{ + php_stream_statbuf ssbuf; + php_stream_context *context = NULL; + php_stream_wrapper *wrapper = NULL; + char *resolved_path; + const char *path_to_open = NULL; + void *ret_val = NULL; + int isescaped=0; + xmlURI *uri; + + + uri = xmlParseURI(filename); + if (uri && (uri->scheme == NULL || + (xmlStrncmp(BAD_CAST uri->scheme, BAD_CAST "file", 4) == 0))) { + resolved_path = xmlURIUnescapeString(filename, 0, NULL); + isescaped = 1; +#if LIBXML_VERSION >= 20902 && defined(PHP_WIN32) + /* Libxml 2.9.2 prefixes local paths with file:/ instead of file://, + thus the php stream wrapper will fail on a valid case. For this + reason the prefix is rather better cut off. */ + { + size_t pre_len = sizeof("file:/") - 1; + + if (strncasecmp(resolved_path, "file:/", pre_len) == 0 + && '/' != resolved_path[pre_len]) { + xmlChar *tmp = xmlStrdup(resolved_path + pre_len); + xmlFree(resolved_path); + resolved_path = tmp; + } + } +#endif + } else { + resolved_path = (char *)filename; + } + + if (uri) { + xmlFreeURI(uri); + } + + if (resolved_path == NULL) { + return NULL; + } + + /* logic copied from _php_stream_stat, but we only want to fail + if the wrapper supports stat, otherwise, figure it out from + the open. This logic is only to support hiding warnings + that the streams layer puts out at times, but for libxml we + may try to open files that don't exist, but it is not a failure + in xml processing (eg. DTD files) */ + wrapper = php_stream_locate_url_wrapper(resolved_path, &path_to_open, 0); + if (wrapper && read_only && wrapper->wops->url_stat) { + if (wrapper->wops->url_stat(wrapper, path_to_open, PHP_STREAM_URL_STAT_QUIET, &ssbuf, NULL) == -1) { + if (isescaped) { + xmlFree(resolved_path); + } + return NULL; + } + } + + context = php_stream_context_from_zval(Z_ISUNDEF(LIBXML(stream_context))? NULL : &LIBXML(stream_context), 0); + + ret_val = php_stream_open_wrapper_ex(path_to_open, (char *)mode, REPORT_ERRORS, NULL, context); + if (ret_val) { + /* Prevent from closing this by fclose() */ + ((php_stream*)ret_val)->flags |= PHP_STREAM_FLAG_NO_FCLOSE; + } + if (isescaped) { + xmlFree(resolved_path); + } + return ret_val; +} + +static void *php_libxml_streams_IO_open_read_wrapper(const char *filename) +{ + return php_libxml_streams_IO_open_wrapper(filename, "rb", 1); +} + +static void *php_libxml_streams_IO_open_write_wrapper(const char *filename) +{ + return php_libxml_streams_IO_open_wrapper(filename, "wb", 0); +} + +static int php_libxml_streams_IO_read(void *context, char *buffer, int len) +{ + return php_stream_read((php_stream*)context, buffer, len); +} + +static int php_libxml_streams_IO_write(void *context, const char *buffer, int len) +{ + return php_stream_write((php_stream*)context, buffer, len); +} + +static int php_libxml_streams_IO_close(void *context) +{ + return php_stream_close((php_stream*)context); +} + +static xmlParserInputBufferPtr +php_libxml_input_buffer_create_filename(const char *URI, xmlCharEncoding enc) +{ + xmlParserInputBufferPtr ret; + void *context = NULL; + + if (LIBXML(entity_loader_disabled)) { + return NULL; + } + + if (URI == NULL) + return(NULL); + + context = php_libxml_streams_IO_open_read_wrapper(URI); + + if (context == NULL) { + return(NULL); + } + + /* Check if there's been an external transport protocol with an encoding information */ + if (enc == XML_CHAR_ENCODING_NONE) { + php_stream *s = (php_stream *) context; + + if (Z_TYPE(s->wrapperdata) == IS_ARRAY) { + zval *header; + + ZEND_HASH_FOREACH_VAL_IND(Z_ARRVAL(s->wrapperdata), header) { + const char buf[] = "Content-Type:"; + if (Z_TYPE_P(header) == IS_STRING && + !zend_binary_strncasecmp(Z_STRVAL_P(header), Z_STRLEN_P(header), buf, sizeof(buf)-1, sizeof(buf)-1)) { + char *needle = estrdup("charset="); + char *haystack = estrndup(Z_STRVAL_P(header), Z_STRLEN_P(header)); + char *encoding = php_stristr(haystack, needle, Z_STRLEN_P(header), sizeof("charset=")-1); + + if (encoding) { + char *end; + + encoding += sizeof("charset=")-1; + if (*encoding == '"') { + encoding++; + } + end = strchr(encoding, ';'); + if (end == NULL) { + end = encoding + strlen(encoding); + } + end--; /* end == encoding-1 isn't a buffer underrun */ + while (*end == ' ' || *end == '\t') { + end--; + } + if (*end == '"') { + end--; + } + if (encoding >= end) continue; + *(end+1) = '\0'; + enc = xmlParseCharEncoding(encoding); + if (enc <= XML_CHAR_ENCODING_NONE) { + enc = XML_CHAR_ENCODING_NONE; + } + } + efree(haystack); + efree(needle); + break; /* found content-type */ + } + } ZEND_HASH_FOREACH_END(); + } + } + + /* Allocate the Input buffer front-end. */ + ret = xmlAllocParserInputBuffer(enc); + if (ret != NULL) { + ret->context = context; + ret->readcallback = php_libxml_streams_IO_read; + ret->closecallback = php_libxml_streams_IO_close; + } else + php_libxml_streams_IO_close(context); + + return(ret); +} + +static xmlOutputBufferPtr +php_libxml_output_buffer_create_filename(const char *URI, + xmlCharEncodingHandlerPtr encoder, + int compression ATTRIBUTE_UNUSED) +{ + xmlOutputBufferPtr ret; + xmlURIPtr puri; + void *context = NULL; + char *unescaped = NULL; + + if (URI == NULL) + return(NULL); + + puri = xmlParseURI(URI); + if (puri != NULL) { + if (puri->scheme != NULL) + unescaped = xmlURIUnescapeString(URI, 0, NULL); + xmlFreeURI(puri); + } + + if (unescaped != NULL) { + context = php_libxml_streams_IO_open_write_wrapper(unescaped); + xmlFree(unescaped); + } + + /* try with a non-escaped URI this may be a strange filename */ + if (context == NULL) { + context = php_libxml_streams_IO_open_write_wrapper(URI); + } + + if (context == NULL) { + return(NULL); + } + + /* Allocate the Output buffer front-end. */ + ret = xmlAllocOutputBuffer(encoder); + if (ret != NULL) { + ret->context = context; + ret->writecallback = php_libxml_streams_IO_write; + ret->closecallback = php_libxml_streams_IO_close; + } + + return(ret); +} + +static int _php_libxml_free_error(xmlErrorPtr error) +{ + /* This will free the libxml alloc'd memory */ + xmlResetError(error); + return 1; +} + +static void _php_list_set_error_structure(xmlErrorPtr error, const char *msg) +{ + xmlError error_copy; + int ret; + + + memset(&error_copy, 0, sizeof(xmlError)); + + if (error) { + ret = xmlCopyError(error, &error_copy); + } else { + error_copy.domain = 0; + error_copy.code = XML_ERR_INTERNAL_ERROR; + error_copy.level = XML_ERR_ERROR; + error_copy.line = 0; + error_copy.node = NULL; + error_copy.int1 = 0; + error_copy.int2 = 0; + error_copy.ctxt = NULL; + error_copy.message = (char*)xmlStrdup((xmlChar*)msg); + error_copy.file = NULL; + error_copy.str1 = NULL; + error_copy.str2 = NULL; + error_copy.str3 = NULL; + ret = 0; + } + + if (ret == 0) { + zend_llist_add_element(LIBXML(error_list), &error_copy); + } +} + +static void php_libxml_ctx_error_level(int level, void *ctx, const char *msg) +{ + xmlParserCtxtPtr parser; + + parser = (xmlParserCtxtPtr) ctx; + + if (parser != NULL && parser->input != NULL) { + if (parser->input->filename) { + php_error_docref(NULL, level, "%s in %s, line: %d", msg, parser->input->filename, parser->input->line); + } else { + php_error_docref(NULL, level, "%s in Entity, line: %d", msg, parser->input->line); + } + } +} + +void php_libxml_issue_error(int level, const char *msg) +{ + if (LIBXML(error_list)) { + _php_list_set_error_structure(NULL, msg); + } else { + php_error_docref(NULL, level, "%s", msg); + } +} + +static void php_libxml_internal_error_handler(int error_type, void *ctx, const char **msg, va_list ap) +{ + char *buf; + int len, len_iter, output = 0; + + + len = vspprintf(&buf, 0, *msg, ap); + len_iter = len; + + /* remove any trailing \n */ + while (len_iter && buf[--len_iter] == '\n') { + buf[len_iter] = '\0'; + output = 1; + } + + smart_str_appendl(&LIBXML(error_buffer), buf, len); + + efree(buf); + + if (output == 1) { + if (LIBXML(error_list)) { + _php_list_set_error_structure(NULL, ZSTR_VAL(LIBXML(error_buffer).s)); + } else { + switch (error_type) { + case PHP_LIBXML_CTX_ERROR: + php_libxml_ctx_error_level(E_WARNING, ctx, ZSTR_VAL(LIBXML(error_buffer).s)); + break; + case PHP_LIBXML_CTX_WARNING: + php_libxml_ctx_error_level(E_NOTICE, ctx, ZSTR_VAL(LIBXML(error_buffer).s)); + break; + default: + php_error_docref(NULL, E_WARNING, "%s", ZSTR_VAL(LIBXML(error_buffer).s)); + } + } + smart_str_free(&LIBXML(error_buffer)); + } +} + +static xmlParserInputPtr _php_libxml_external_entity_loader(const char *URL, + const char *ID, xmlParserCtxtPtr context) +{ + xmlParserInputPtr ret = NULL; + const char *resource = NULL; + zval *ctxzv, retval; + zval params[3]; + int status; + zend_fcall_info *fci; + + fci = &LIBXML(entity_loader).fci; + + if (fci->size == 0) { + /* no custom user-land callback set up; delegate to original loader */ + return _php_libxml_default_entity_loader(URL, ID, context); + } + + if (ID != NULL) { + ZVAL_STRING(¶ms[0], ID); + } else { + ZVAL_NULL(¶ms[0]); + } + if (URL != NULL) { + ZVAL_STRING(¶ms[1], URL); + } else { + ZVAL_NULL(¶ms[1]); + } + ctxzv = ¶ms[2]; + array_init_size(ctxzv, 4); + +#define ADD_NULL_OR_STRING_KEY(memb) \ + if (context->memb == NULL) { \ + add_assoc_null_ex(ctxzv, #memb, sizeof(#memb) - 1); \ + } else { \ + add_assoc_string_ex(ctxzv, #memb, sizeof(#memb) - 1, \ + (char *)context->memb); \ + } + + ADD_NULL_OR_STRING_KEY(directory) + ADD_NULL_OR_STRING_KEY(intSubName) + ADD_NULL_OR_STRING_KEY(extSubURI) + ADD_NULL_OR_STRING_KEY(extSubSystem) + +#undef ADD_NULL_OR_STRING_KEY + + fci->retval = &retval; + fci->params = params; + fci->param_count = sizeof(params)/sizeof(*params); + fci->no_separation = 1; + + status = zend_call_function(fci, &LIBXML(entity_loader).fcc); + if (status != SUCCESS || Z_ISUNDEF(retval)) { + php_libxml_ctx_error(context, + "Call to user entity loader callback '%s' has failed", + Z_STRVAL(fci->function_name)); + } else { + /* + retval_ptr = *fci->retval_ptr_ptr; + if (retval_ptr == NULL) { + php_libxml_ctx_error(context, + "Call to user entity loader callback '%s' has failed; " + "probably it has thrown an exception", + fci->function_name); + } else */ if (Z_TYPE(retval) == IS_STRING) { +is_string: + resource = Z_STRVAL(retval); + } else if (Z_TYPE(retval) == IS_RESOURCE) { + php_stream *stream; + php_stream_from_zval_no_verify(stream, &retval); + if (stream == NULL) { + php_libxml_ctx_error(context, + "The user entity loader callback '%s' has returned a " + "resource, but it is not a stream", + Z_STRVAL(fci->function_name)); + } else { + /* TODO: allow storing the encoding in the stream context? */ + xmlCharEncoding enc = XML_CHAR_ENCODING_NONE; + xmlParserInputBufferPtr pib = xmlAllocParserInputBuffer(enc); + if (pib == NULL) { + php_libxml_ctx_error(context, "Could not allocate parser " + "input buffer"); + } else { + /* make stream not being closed when the zval is freed */ + GC_ADDREF(stream->res); + pib->context = stream; + pib->readcallback = php_libxml_streams_IO_read; + pib->closecallback = php_libxml_streams_IO_close; + + ret = xmlNewIOInputStream(context, pib, enc); + if (ret == NULL) { + xmlFreeParserInputBuffer(pib); + } + } + } + } else if (Z_TYPE(retval) != IS_NULL) { + /* retval not string nor resource nor null; convert to string */ + if (try_convert_to_string(&retval)) { + goto is_string; + } + } /* else is null; don't try anything */ + } + + if (ret == NULL) { + if (resource == NULL) { + if (ID == NULL) { + ID = "NULL"; + } + php_libxml_ctx_error(context, + "Failed to load external entity \"%s\"\n", ID); + } else { + /* we got the resource in the form of a string; open it */ + ret = xmlNewInputFromFile(context, resource); + } + } + + zval_ptr_dtor(¶ms[0]); + zval_ptr_dtor(¶ms[1]); + zval_ptr_dtor(¶ms[2]); + zval_ptr_dtor(&retval); + return ret; +} + +static xmlParserInputPtr _php_libxml_pre_ext_ent_loader(const char *URL, + const char *ID, xmlParserCtxtPtr context) +{ + + /* Check whether we're running in a PHP context, since the entity loader + * we've defined is an application level (true global) setting. + * If we are, we also want to check whether we've finished activating + * the modules (RINIT phase). Using our external entity loader during a + * RINIT should not be problem per se (though during MINIT it is, because + * we don't even have a resource list by then), but then whether one + * extension would be using the custom external entity loader or not + * could depend on extension loading order + * (if _php_libxml_per_request_initialization */ + if (xmlGenericError == php_libxml_error_handler && PG(modules_activated)) { + return _php_libxml_external_entity_loader(URL, ID, context); + } else { + return _php_libxml_default_entity_loader(URL, ID, context); + } +} + +PHP_LIBXML_API void php_libxml_ctx_error(void *ctx, const char *msg, ...) +{ + va_list args; + va_start(args, msg); + php_libxml_internal_error_handler(PHP_LIBXML_CTX_ERROR, ctx, &msg, args); + va_end(args); +} + +PHP_LIBXML_API void php_libxml_ctx_warning(void *ctx, const char *msg, ...) +{ + va_list args; + va_start(args, msg); + php_libxml_internal_error_handler(PHP_LIBXML_CTX_WARNING, ctx, &msg, args); + va_end(args); +} + +PHP_LIBXML_API void php_libxml_structured_error_handler(void *userData, xmlErrorPtr error) +{ + _php_list_set_error_structure(error, NULL); + + return; +} + +PHP_LIBXML_API void php_libxml_error_handler(void *ctx, const char *msg, ...) +{ + va_list args; + va_start(args, msg); + php_libxml_internal_error_handler(PHP_LIBXML_ERROR, ctx, &msg, args); + va_end(args); +} + +static void php_libxml_exports_dtor(zval *zv) +{ + free(Z_PTR_P(zv)); +} + +PHP_LIBXML_API void php_libxml_initialize(void) +{ + if (!_php_libxml_initialized) { + /* we should be the only one's to ever init!! */ + ZEND_IGNORE_LEAKS_BEGIN(); + xmlInitParser(); + ZEND_IGNORE_LEAKS_END(); + + _php_libxml_default_entity_loader = xmlGetExternalEntityLoader(); + xmlSetExternalEntityLoader(_php_libxml_pre_ext_ent_loader); + + zend_hash_init(&php_libxml_exports, 0, NULL, php_libxml_exports_dtor, 1); + + _php_libxml_initialized = 1; + } +} + +PHP_LIBXML_API void php_libxml_shutdown(void) +{ + if (_php_libxml_initialized) { +#if defined(LIBXML_SCHEMAS_ENABLED) + xmlRelaxNGCleanupTypes(); +#endif + /* xmlCleanupParser(); */ + zend_hash_destroy(&php_libxml_exports); + + xmlSetExternalEntityLoader(_php_libxml_default_entity_loader); + _php_libxml_initialized = 0; + } +} + +PHP_LIBXML_API void php_libxml_switch_context(zval *context, zval *oldcontext) +{ + if (oldcontext) { + ZVAL_COPY_VALUE(oldcontext, &LIBXML(stream_context)); + } + if (context) { + ZVAL_COPY_VALUE(&LIBXML(stream_context), context); + } +} + +static PHP_MINIT_FUNCTION(libxml) +{ + zend_class_entry ce; + + php_libxml_initialize(); + + REGISTER_LONG_CONSTANT("LIBXML_VERSION", LIBXML_VERSION, CONST_CS | CONST_PERSISTENT); + REGISTER_STRING_CONSTANT("LIBXML_DOTTED_VERSION", LIBXML_DOTTED_VERSION, CONST_CS | CONST_PERSISTENT); + REGISTER_STRING_CONSTANT("LIBXML_LOADED_VERSION", (char *)xmlParserVersion, CONST_CS | CONST_PERSISTENT); + + /* For use with loading xml */ + REGISTER_LONG_CONSTANT("LIBXML_NOENT", XML_PARSE_NOENT, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("LIBXML_DTDLOAD", XML_PARSE_DTDLOAD, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("LIBXML_DTDATTR", XML_PARSE_DTDATTR, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("LIBXML_DTDVALID", XML_PARSE_DTDVALID, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("LIBXML_NOERROR", XML_PARSE_NOERROR, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("LIBXML_NOWARNING", XML_PARSE_NOWARNING, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("LIBXML_NOBLANKS", XML_PARSE_NOBLANKS, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("LIBXML_XINCLUDE", XML_PARSE_XINCLUDE, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("LIBXML_NSCLEAN", XML_PARSE_NSCLEAN, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("LIBXML_NOCDATA", XML_PARSE_NOCDATA, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("LIBXML_NONET", XML_PARSE_NONET, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("LIBXML_PEDANTIC", XML_PARSE_PEDANTIC, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("LIBXML_COMPACT", XML_PARSE_COMPACT, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("LIBXML_NOXMLDECL", XML_SAVE_NO_DECL, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("LIBXML_PARSEHUGE", XML_PARSE_HUGE, CONST_CS | CONST_PERSISTENT); +#if LIBXML_VERSION >= 20900 + REGISTER_LONG_CONSTANT("LIBXML_BIGLINES", XML_PARSE_BIG_LINES, CONST_CS | CONST_PERSISTENT); +#endif + REGISTER_LONG_CONSTANT("LIBXML_NOEMPTYTAG", LIBXML_SAVE_NOEMPTYTAG, CONST_CS | CONST_PERSISTENT); + + /* Schema validation options */ +#if defined(LIBXML_SCHEMAS_ENABLED) + REGISTER_LONG_CONSTANT("LIBXML_SCHEMA_CREATE", XML_SCHEMA_VAL_VC_I_CREATE, CONST_CS | CONST_PERSISTENT); +#endif + + /* Additional constants for use with loading html */ +#if LIBXML_VERSION >= 20707 + REGISTER_LONG_CONSTANT("LIBXML_HTML_NOIMPLIED", HTML_PARSE_NOIMPLIED, CONST_CS | CONST_PERSISTENT); +#endif + +#if LIBXML_VERSION >= 20708 + REGISTER_LONG_CONSTANT("LIBXML_HTML_NODEFDTD", HTML_PARSE_NODEFDTD, CONST_CS | CONST_PERSISTENT); +#endif + + /* Error levels */ + REGISTER_LONG_CONSTANT("LIBXML_ERR_NONE", XML_ERR_NONE, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("LIBXML_ERR_WARNING", XML_ERR_WARNING, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("LIBXML_ERR_ERROR", XML_ERR_ERROR, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("LIBXML_ERR_FATAL", XML_ERR_FATAL, CONST_CS | CONST_PERSISTENT); + + INIT_CLASS_ENTRY(ce, "LibXMLError", NULL); + libxmlerror_class_entry = zend_register_internal_class(&ce); + + if (sapi_module.name) { + static const char * const supported_sapis[] = { + "cgi-fcgi", + "litespeed", + NULL + }; + const char * const *sapi_name; + + for (sapi_name = supported_sapis; *sapi_name; sapi_name++) { + if (strcmp(sapi_module.name, *sapi_name) == 0) { + _php_libxml_per_request_initialization = 0; + break; + } + } + } + + if (!_php_libxml_per_request_initialization) { + /* report errors via handler rather than stderr */ + xmlSetGenericErrorFunc(NULL, php_libxml_error_handler); + xmlParserInputBufferCreateFilenameDefault(php_libxml_input_buffer_create_filename); + xmlOutputBufferCreateFilenameDefault(php_libxml_output_buffer_create_filename); + } + + return SUCCESS; +} + + +static PHP_RINIT_FUNCTION(libxml) +{ + if (_php_libxml_per_request_initialization) { + /* report errors via handler rather than stderr */ + xmlSetGenericErrorFunc(NULL, php_libxml_error_handler); + xmlParserInputBufferCreateFilenameDefault(php_libxml_input_buffer_create_filename); + xmlOutputBufferCreateFilenameDefault(php_libxml_output_buffer_create_filename); + } + + /* Enable the entity loader by default. This ensures that + * other threads/requests that might have disabled the loader + * do not affect the current request. + */ + LIBXML(entity_loader_disabled) = 0; + + return SUCCESS; +} + +static PHP_RSHUTDOWN_FUNCTION(libxml) +{ + _php_libxml_destroy_fci(&LIBXML(entity_loader).fci, &LIBXML(entity_loader).object); + + return SUCCESS; +} + +static PHP_MSHUTDOWN_FUNCTION(libxml) +{ + if (!_php_libxml_per_request_initialization) { + xmlSetGenericErrorFunc(NULL, NULL); + + xmlParserInputBufferCreateFilenameDefault(NULL); + xmlOutputBufferCreateFilenameDefault(NULL); + } + php_libxml_shutdown(); + + return SUCCESS; +} + +static int php_libxml_post_deactivate(void) +{ + /* reset libxml generic error handling */ + if (_php_libxml_per_request_initialization) { + xmlSetGenericErrorFunc(NULL, NULL); + + xmlParserInputBufferCreateFilenameDefault(NULL); + xmlOutputBufferCreateFilenameDefault(NULL); + } + xmlSetStructuredErrorFunc(NULL, NULL); + + /* the steam_context resource will be released by resource list destructor */ + ZVAL_UNDEF(&LIBXML(stream_context)); + smart_str_free(&LIBXML(error_buffer)); + if (LIBXML(error_list)) { + zend_llist_destroy(LIBXML(error_list)); + efree(LIBXML(error_list)); + LIBXML(error_list) = NULL; + } + xmlResetLastError(); + + return SUCCESS; +} + + +static PHP_MINFO_FUNCTION(libxml) +{ + php_info_print_table_start(); + php_info_print_table_row(2, "libXML support", "active"); + php_info_print_table_row(2, "libXML Compiled Version", LIBXML_DOTTED_VERSION); + php_info_print_table_row(2, "libXML Loaded Version", (char *)xmlParserVersion); + php_info_print_table_row(2, "libXML streams", "enabled"); + php_info_print_table_end(); +} +/* }}} */ + +/* {{{ proto void libxml_set_streams_context(resource streams_context) + Set the streams context for the next libxml document load or write */ +static PHP_FUNCTION(libxml_set_streams_context) +{ + zval *arg; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_RESOURCE(arg) + ZEND_PARSE_PARAMETERS_END(); + + if (!Z_ISUNDEF(LIBXML(stream_context))) { + zval_ptr_dtor(&LIBXML(stream_context)); + ZVAL_UNDEF(&LIBXML(stream_context)); + } + ZVAL_COPY(&LIBXML(stream_context), arg); +} +/* }}} */ + +/* {{{ proto bool libxml_use_internal_errors([boolean use_errors]) + Disable libxml errors and allow user to fetch error information as needed */ +static PHP_FUNCTION(libxml_use_internal_errors) +{ + xmlStructuredErrorFunc current_handler; + zend_bool use_errors=0, retval; + + ZEND_PARSE_PARAMETERS_START(0, 1) + Z_PARAM_OPTIONAL + Z_PARAM_BOOL(use_errors) + ZEND_PARSE_PARAMETERS_END(); + + current_handler = xmlStructuredError; + if (current_handler && current_handler == php_libxml_structured_error_handler) { + retval = 1; + } else { + retval = 0; + } + + if (ZEND_NUM_ARGS() == 0) { + RETURN_BOOL(retval); + } + + if (use_errors == 0) { + xmlSetStructuredErrorFunc(NULL, NULL); + if (LIBXML(error_list)) { + zend_llist_destroy(LIBXML(error_list)); + efree(LIBXML(error_list)); + LIBXML(error_list) = NULL; + } + } else { + xmlSetStructuredErrorFunc(NULL, php_libxml_structured_error_handler); + if (LIBXML(error_list) == NULL) { + LIBXML(error_list) = (zend_llist *) emalloc(sizeof(zend_llist)); + zend_llist_init(LIBXML(error_list), sizeof(xmlError), (llist_dtor_func_t) _php_libxml_free_error, 0); + } + } + RETURN_BOOL(retval); +} +/* }}} */ + +/* {{{ proto object libxml_get_last_error() + Retrieve last error from libxml */ +static PHP_FUNCTION(libxml_get_last_error) +{ + xmlErrorPtr error; + + error = xmlGetLastError(); + + if (error) { + object_init_ex(return_value, libxmlerror_class_entry); + add_property_long(return_value, "level", error->level); + add_property_long(return_value, "code", error->code); + add_property_long(return_value, "column", error->int2); + if (error->message) { + add_property_string(return_value, "message", error->message); + } else { + add_property_stringl(return_value, "message", "", 0); + } + if (error->file) { + add_property_string(return_value, "file", error->file); + } else { + add_property_stringl(return_value, "file", "", 0); + } + add_property_long(return_value, "line", error->line); + } else { + RETURN_FALSE; + } +} +/* }}} */ + +/* {{{ proto object libxml_get_errors() + Retrieve array of errors */ +static PHP_FUNCTION(libxml_get_errors) +{ + + xmlErrorPtr error; + + if (LIBXML(error_list)) { + + array_init(return_value); + error = zend_llist_get_first(LIBXML(error_list)); + + while (error != NULL) { + zval z_error; + + object_init_ex(&z_error, libxmlerror_class_entry); + add_property_long_ex(&z_error, "level", sizeof("level") - 1, error->level); + add_property_long_ex(&z_error, "code", sizeof("code") - 1, error->code); + add_property_long_ex(&z_error, "column", sizeof("column") - 1, error->int2 ); + if (error->message) { + add_property_string_ex(&z_error, "message", sizeof("message") - 1, error->message); + } else { + add_property_stringl_ex(&z_error, "message", sizeof("message") - 1, "", 0); + } + if (error->file) { + add_property_string_ex(&z_error, "file", sizeof("file") - 1, error->file); + } else { + add_property_stringl_ex(&z_error, "file", sizeof("file") - 1, "", 0); + } + add_property_long_ex(&z_error, "line", sizeof("line") - 1, error->line); + add_next_index_zval(return_value, &z_error); + + error = zend_llist_get_next(LIBXML(error_list)); + } + } else { + RETURN_EMPTY_ARRAY(); + } +} +/* }}} */ + +/* {{{ proto void libxml_clear_errors() + Clear last error from libxml */ +static PHP_FUNCTION(libxml_clear_errors) +{ + xmlResetLastError(); + if (LIBXML(error_list)) { + zend_llist_clean(LIBXML(error_list)); + } +} +/* }}} */ + +PHP_LIBXML_API zend_bool php_libxml_disable_entity_loader(zend_bool disable) /* {{{ */ +{ + zend_bool old = LIBXML(entity_loader_disabled); + + LIBXML(entity_loader_disabled) = disable; + return old; +} /* }}} */ + +/* {{{ proto bool libxml_disable_entity_loader([boolean disable]) + Disable/Enable ability to load external entities */ +static PHP_FUNCTION(libxml_disable_entity_loader) +{ + zend_bool disable = 1; + + ZEND_PARSE_PARAMETERS_START(0, 1) + Z_PARAM_OPTIONAL + Z_PARAM_BOOL(disable) + ZEND_PARSE_PARAMETERS_END(); + + RETURN_BOOL(php_libxml_disable_entity_loader(disable)); +} +/* }}} */ + +/* {{{ proto void libxml_set_external_entity_loader(callback resolver_function) + Changes the default external entity loader */ +static PHP_FUNCTION(libxml_set_external_entity_loader) +{ + zend_fcall_info fci; + zend_fcall_info_cache fcc; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_FUNC_EX(fci, fcc, 1, 0) + ZEND_PARSE_PARAMETERS_END(); + + _php_libxml_destroy_fci(&LIBXML(entity_loader).fci, &LIBXML(entity_loader).object); + + if (fci.size > 0) { /* argument not null */ + LIBXML(entity_loader).fci = fci; + Z_ADDREF(fci.function_name); + if (fci.object != NULL) { + ZVAL_OBJ(&LIBXML(entity_loader).object, fci.object); + Z_ADDREF(LIBXML(entity_loader).object); + } + LIBXML(entity_loader).fcc = fcc; + } + + RETURN_TRUE; +} +/* }}} */ + +/* {{{ Common functions shared by extensions */ +int php_libxml_xmlCheckUTF8(const unsigned char *s) +{ + size_t i; + unsigned char c; + + for (i = 0; (c = s[i++]);) { + if ((c & 0x80) == 0) { + } else if ((c & 0xe0) == 0xc0) { + if ((s[i++] & 0xc0) != 0x80) { + return 0; + } + } else if ((c & 0xf0) == 0xe0) { + if ((s[i++] & 0xc0) != 0x80 || (s[i++] & 0xc0) != 0x80) { + return 0; + } + } else if ((c & 0xf8) == 0xf0) { + if ((s[i++] & 0xc0) != 0x80 || (s[i++] & 0xc0) != 0x80 || (s[i++] & 0xc0) != 0x80) { + return 0; + } + } else { + return 0; + } + } + return 1; +} + +zval *php_libxml_register_export(zend_class_entry *ce, php_libxml_export_node export_function) +{ + php_libxml_func_handler export_hnd; + + /* Initialize in case this module hasn't been loaded yet */ + php_libxml_initialize(); + export_hnd.export_func = export_function; + + return zend_hash_add_mem(&php_libxml_exports, ce->name, &export_hnd, sizeof(export_hnd)); +} + +PHP_LIBXML_API xmlNodePtr php_libxml_import_node(zval *object) +{ + zend_class_entry *ce = NULL; + xmlNodePtr node = NULL; + php_libxml_func_handler *export_hnd; + + if (Z_TYPE_P(object) == IS_OBJECT) { + ce = Z_OBJCE_P(object); + while (ce->parent != NULL) { + ce = ce->parent; + } + if ((export_hnd = zend_hash_find_ptr(&php_libxml_exports, ce->name))) { + node = export_hnd->export_func(object); + } + } + return node; +} + +PHP_LIBXML_API int php_libxml_increment_node_ptr(php_libxml_node_object *object, xmlNodePtr node, void *private_data) +{ + int ret_refcount = -1; + + if (object != NULL && node != NULL) { + if (object->node != NULL) { + if (object->node->node == node) { + return object->node->refcount; + } else { + php_libxml_decrement_node_ptr(object); + } + } + if (node->_private != NULL) { + object->node = node->_private; + ret_refcount = ++object->node->refcount; + /* Only dom uses _private */ + if (object->node->_private == NULL) { + object->node->_private = private_data; + } + } else { + ret_refcount = 1; + object->node = emalloc(sizeof(php_libxml_node_ptr)); + object->node->node = node; + object->node->refcount = 1; + object->node->_private = private_data; + node->_private = object->node; + } + } + + return ret_refcount; +} + +PHP_LIBXML_API int php_libxml_decrement_node_ptr(php_libxml_node_object *object) +{ + int ret_refcount = -1; + php_libxml_node_ptr *obj_node; + + if (object != NULL && object->node != NULL) { + obj_node = (php_libxml_node_ptr *) object->node; + ret_refcount = --obj_node->refcount; + if (ret_refcount == 0) { + if (obj_node->node != NULL) { + obj_node->node->_private = NULL; + } + efree(obj_node); + } + object->node = NULL; + } + + return ret_refcount; +} + +PHP_LIBXML_API int php_libxml_increment_doc_ref(php_libxml_node_object *object, xmlDocPtr docp) +{ + int ret_refcount = -1; + + if (object->document != NULL) { + object->document->refcount++; + ret_refcount = object->document->refcount; + } else if (docp != NULL) { + ret_refcount = 1; + object->document = emalloc(sizeof(php_libxml_ref_obj)); + object->document->ptr = docp; + object->document->refcount = ret_refcount; + object->document->doc_props = NULL; + } + + return ret_refcount; +} + +PHP_LIBXML_API int php_libxml_decrement_doc_ref(php_libxml_node_object *object) +{ + int ret_refcount = -1; + + if (object != NULL && object->document != NULL) { + ret_refcount = --object->document->refcount; + if (ret_refcount == 0) { + if (object->document->ptr != NULL) { + xmlFreeDoc((xmlDoc *) object->document->ptr); + } + if (object->document->doc_props != NULL) { + if (object->document->doc_props->classmap) { + zend_hash_destroy(object->document->doc_props->classmap); + FREE_HASHTABLE(object->document->doc_props->classmap); + } + efree(object->document->doc_props); + } + efree(object->document); + } + object->document = NULL; + } + + return ret_refcount; +} + +PHP_LIBXML_API void php_libxml_node_free_resource(xmlNodePtr node) +{ + if (!node) { + return; + } + + switch (node->type) { + case XML_DOCUMENT_NODE: + case XML_HTML_DOCUMENT_NODE: + break; + default: + if (node->parent == NULL || node->type == XML_NAMESPACE_DECL) { + php_libxml_node_free_list((xmlNodePtr) node->children); + switch (node->type) { + /* Skip property freeing for the following types */ + case XML_ATTRIBUTE_DECL: + case XML_DTD_NODE: + case XML_DOCUMENT_TYPE_NODE: + case XML_ENTITY_DECL: + case XML_ATTRIBUTE_NODE: + case XML_NAMESPACE_DECL: + case XML_TEXT_NODE: + break; + default: + php_libxml_node_free_list((xmlNodePtr) node->properties); + } + if (php_libxml_unregister_node(node) == 0) { + node->doc = NULL; + } + php_libxml_node_free(node); + } else { + php_libxml_unregister_node(node); + } + } +} + +PHP_LIBXML_API void php_libxml_node_decrement_resource(php_libxml_node_object *object) +{ + int ret_refcount = -1; + xmlNodePtr nodep; + php_libxml_node_ptr *obj_node; + + if (object != NULL && object->node != NULL) { + obj_node = (php_libxml_node_ptr *) object->node; + nodep = object->node->node; + ret_refcount = php_libxml_decrement_node_ptr(object); + if (ret_refcount == 0) { + php_libxml_node_free_resource(nodep); + } else { + if (obj_node && object == obj_node->_private) { + obj_node->_private = NULL; + } + } + } + if (object != NULL && object->document != NULL) { + /* Safe to call as if the resource were freed then doc pointer is NULL */ + php_libxml_decrement_doc_ref(object); + } +} +/* }}} */ + +#if defined(PHP_WIN32) && defined(COMPILE_DL_LIBXML) +PHP_LIBXML_API BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) +{ + return xmlDllMain(hinstDLL, fdwReason, lpvReserved); +} +#endif + +#endif diff --git a/ext/mysqlnd/mysqlnd_wireprotocol.c b/ext/mysqlnd/mysqlnd_wireprotocol.c index 87b2e7c31331e..e4a298adaea4f 100644 --- a/ext/mysqlnd/mysqlnd_wireprotocol.c +++ b/ext/mysqlnd/mysqlnd_wireprotocol.c @@ -771,7 +771,8 @@ php_mysqlnd_change_auth_response_write(MYSQLND_CONN_DATA * conn, void * _packet) MYSQLND_VIO * vio = conn->vio; MYSQLND_STATS * stats = conn->stats; MYSQLND_CONNECTION_STATE * connection_state = &conn->state; - zend_uchar * const buffer = pfc->cmd_buffer.length >= packet->auth_data_len? pfc->cmd_buffer.buffer : mnd_emalloc(packet->auth_data_len); + size_t total_packet_size = packet->auth_data_len + MYSQLND_HEADER_SIZE; + zend_uchar * const buffer = pfc->cmd_buffer.length >= total_packet_size? pfc->cmd_buffer.buffer : mnd_emalloc(total_packet_size); zend_uchar * p = buffer + MYSQLND_HEADER_SIZE; /* start after the header */ DBG_ENTER("php_mysqlnd_change_auth_response_write"); diff --git a/ext/pgsql/pgsql.c b/ext/pgsql/pgsql.c index f52ff884d83cd..7dcd56cf1441b 100644 --- a/ext/pgsql/pgsql.c +++ b/ext/pgsql/pgsql.c @@ -1994,7 +1994,7 @@ PHP_FUNCTION(pg_query_params) if (Z_TYPE(tmp_val) != IS_STRING) { php_error_docref(NULL, E_WARNING,"Error converting parameter"); zval_ptr_dtor(&tmp_val); - _php_pgsql_free_params(params, num_params); + _php_pgsql_free_params(params, i); RETURN_FALSE; } params[i] = estrndup(Z_STRVAL(tmp_val), Z_STRLEN(tmp_val)); @@ -5175,8 +5175,8 @@ PHP_FUNCTION(pg_send_execute) params[i] = NULL; } else { zend_string *tmp_str = zval_try_get_string(tmp); - if (UNEXPECTED(!tmp)) { - _php_pgsql_free_params(params, num_params); + if (UNEXPECTED(!tmp_str)) { + _php_pgsql_free_params(params, i); return; } params[i] = estrndup(ZSTR_VAL(tmp_str), ZSTR_LEN(tmp_str)); diff --git a/ext/pgsql/tests/bug81720.phpt b/ext/pgsql/tests/bug81720.phpt new file mode 100644 index 0000000000000..d79f1fcdd6128 --- /dev/null +++ b/ext/pgsql/tests/bug81720.phpt @@ -0,0 +1,27 @@ +--TEST-- +Bug #81720 (Uninitialized array in pg_query_params() leading to RCE) +--SKIPIF-- + +--FILE-- +getMessage(), PHP_EOL; +} + +try { + pg_send_prepare($conn, "my_query", 'SELECT $1, $2'); + pg_get_result($conn); + pg_send_execute($conn, "my_query", [1, new stdClass()]); +} catch (Throwable $ex) { + echo $ex->getMessage(), PHP_EOL; +} +?> +--EXPECT-- +Object of class stdClass could not be converted to string +Object of class stdClass could not be converted to string diff --git a/ext/simplexml/tests/bug79971_1.phpt b/ext/simplexml/tests/bug79971_1.phpt new file mode 100644 index 0000000000000..197776d82d38d --- /dev/null +++ b/ext/simplexml/tests/bug79971_1.phpt @@ -0,0 +1,27 @@ +--TEST-- +Bug #79971 (special character is breaking the path in xml function) +--SKIPIF-- + +--FILE-- +asXML("$uri.out%00foo")); +?> +--EXPECTF-- +Warning: simplexml_load_file(): URI must not contain percent-encoded NUL bytes in %s on line %d + +Warning: simplexml_load_file(): I/O warning : failed to load external entity "%s/bug79971_1.xml%00foo" in %s on line %d +bool(false) + +Warning: SimpleXMLElement::asXML(): URI must not contain percent-encoded NUL bytes in %s on line %d +bool(false) diff --git a/ext/simplexml/tests/bug79971_1.xml b/ext/simplexml/tests/bug79971_1.xml new file mode 100644 index 0000000000000..912bb76d9d7e2 --- /dev/null +++ b/ext/simplexml/tests/bug79971_1.xml @@ -0,0 +1,2 @@ + + diff --git a/ext/standard/crypt.c b/ext/standard/crypt.c index 92430b69f7726..04487f3fe5a72 100644 --- a/ext/standard/crypt.c +++ b/ext/standard/crypt.c @@ -151,6 +151,7 @@ PHPAPI zend_string *php_crypt(const char *password, const int pass_len, const ch } else if ( salt[0] == '$' && salt[1] == '2' && + salt[2] != 0 && salt[3] == '$') { char output[PHP_MAX_SALT_LEN + 1]; diff --git a/ext/standard/crypt.c.orig b/ext/standard/crypt.c.orig new file mode 100644 index 0000000000000..92430b69f7726 --- /dev/null +++ b/ext/standard/crypt.c.orig @@ -0,0 +1,280 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 7 | + +----------------------------------------------------------------------+ + | Copyright (c) The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Stig Bakken | + | Zeev Suraski | + | Rasmus Lerdorf | + | Pierre Joye | + +----------------------------------------------------------------------+ +*/ + +#include + +#include "php.h" + +#if HAVE_UNISTD_H +#include +#endif +#if PHP_USE_PHP_CRYPT_R +# include "php_crypt_r.h" +# include "crypt_freesec.h" +#else +# if HAVE_CRYPT_H +# if defined(CRYPT_R_GNU_SOURCE) && !defined(_GNU_SOURCE) +# define _GNU_SOURCE +# endif +# include +# endif +#endif +#include +#include + +#ifdef PHP_WIN32 +#include +#endif + +#include "php_crypt.h" +#include "php_random.h" + +/* sha512 crypt has the maximal salt length of 123 characters */ +#define PHP_MAX_SALT_LEN 123 + +/* Used to check DES salts to ensure that they contain only valid characters */ +#define IS_VALID_SALT_CHARACTER(c) (((c) >= '.' && (c) <= '9') || ((c) >= 'A' && (c) <= 'Z') || ((c) >= 'a' && (c) <= 'z')) + +#define DES_INVALID_SALT_ERROR "Supplied salt is not valid for DES. Possible bug in provided salt format." + + +PHP_MINIT_FUNCTION(crypt) /* {{{ */ +{ + REGISTER_LONG_CONSTANT("CRYPT_SALT_LENGTH", PHP_MAX_SALT_LEN, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CRYPT_STD_DES", 1, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CRYPT_EXT_DES", 1, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CRYPT_MD5", 1, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CRYPT_BLOWFISH", 1, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CRYPT_SHA256", 1, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("CRYPT_SHA512", 1, CONST_CS | CONST_PERSISTENT); + +#if PHP_USE_PHP_CRYPT_R + php_init_crypt_r(); +#endif + + return SUCCESS; +} +/* }}} */ + +PHP_MSHUTDOWN_FUNCTION(crypt) /* {{{ */ +{ +#if PHP_USE_PHP_CRYPT_R + php_shutdown_crypt_r(); +#endif + + return SUCCESS; +} +/* }}} */ + +static unsigned char itoa64[] = "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + +static void php_to64(char *s, int n) /* {{{ */ +{ + while (--n >= 0) { + *s = itoa64[*s & 0x3f]; + s++; + } +} +/* }}} */ + +PHPAPI zend_string *php_crypt(const char *password, const int pass_len, const char *salt, int salt_len, zend_bool quiet) +{ + char *crypt_res; + zend_string *result; + + if (salt[0] == '*' && (salt[1] == '0' || salt[1] == '1')) { + return NULL; + } + +/* Windows (win32/crypt) has a stripped down version of libxcrypt and + a CryptoApi md5_crypt implementation */ +#if PHP_USE_PHP_CRYPT_R + { + struct php_crypt_extended_data buffer; + + if (salt[0]=='$' && salt[1]=='1' && salt[2]=='$') { + char output[MD5_HASH_MAX_LEN], *out; + + out = php_md5_crypt_r(password, salt, output); + if (out) { + return zend_string_init(out, strlen(out), 0); + } + return NULL; + } else if (salt[0]=='$' && salt[1]=='6' && salt[2]=='$') { + char *output; + output = emalloc(PHP_MAX_SALT_LEN); + + crypt_res = php_sha512_crypt_r(password, salt, output, PHP_MAX_SALT_LEN); + if (!crypt_res) { + ZEND_SECURE_ZERO(output, PHP_MAX_SALT_LEN); + efree(output); + return NULL; + } else { + result = zend_string_init(output, strlen(output), 0); + ZEND_SECURE_ZERO(output, PHP_MAX_SALT_LEN); + efree(output); + return result; + } + } else if (salt[0]=='$' && salt[1]=='5' && salt[2]=='$') { + char *output; + output = emalloc(PHP_MAX_SALT_LEN); + + crypt_res = php_sha256_crypt_r(password, salt, output, PHP_MAX_SALT_LEN); + if (!crypt_res) { + ZEND_SECURE_ZERO(output, PHP_MAX_SALT_LEN); + efree(output); + return NULL; + } else { + result = zend_string_init(output, strlen(output), 0); + ZEND_SECURE_ZERO(output, PHP_MAX_SALT_LEN); + efree(output); + return result; + } + } else if ( + salt[0] == '$' && + salt[1] == '2' && + salt[3] == '$') { + char output[PHP_MAX_SALT_LEN + 1]; + + memset(output, 0, PHP_MAX_SALT_LEN + 1); + + crypt_res = php_crypt_blowfish_rn(password, salt, output, sizeof(output)); + if (!crypt_res) { + ZEND_SECURE_ZERO(output, PHP_MAX_SALT_LEN + 1); + return NULL; + } else { + result = zend_string_init(output, strlen(output), 0); + ZEND_SECURE_ZERO(output, PHP_MAX_SALT_LEN + 1); + return result; + } + } else { + /* DES Fallback */ + + /* Only check the salt if it's not EXT_DES */ + if (salt[0] != '_') { + /* DES style hashes */ + if (!IS_VALID_SALT_CHARACTER(salt[0]) || !IS_VALID_SALT_CHARACTER(salt[1])) { + if (!quiet) { + /* error consistently about invalid DES fallbacks */ + php_error_docref(NULL, E_DEPRECATED, DES_INVALID_SALT_ERROR); + } + } + } + + memset(&buffer, 0, sizeof(buffer)); + _crypt_extended_init_r(); + + crypt_res = _crypt_extended_r((const unsigned char *) password, salt, &buffer); + if (!crypt_res || (salt[0] == '*' && salt[1] == '0')) { + return NULL; + } else { + result = zend_string_init(crypt_res, strlen(crypt_res), 0); + return result; + } + } + } +#else + + if (salt[0] != '$' && salt[0] != '_' && (!IS_VALID_SALT_CHARACTER(salt[0]) || !IS_VALID_SALT_CHARACTER(salt[1]))) { + if (!quiet) { + /* error consistently about invalid DES fallbacks */ + php_error_docref(NULL, E_DEPRECATED, DES_INVALID_SALT_ERROR); + } + } + +# if defined(HAVE_CRYPT_R) && (defined(_REENTRANT) || defined(_THREAD_SAFE)) + { +# if defined(CRYPT_R_STRUCT_CRYPT_DATA) + struct crypt_data buffer; + memset(&buffer, 0, sizeof(buffer)); +# elif defined(CRYPT_R_CRYPTD) + CRYPTD buffer; +# else +# error Data struct used by crypt_r() is unknown. Please report. +# endif + crypt_res = crypt_r(password, salt, &buffer); + } +# elif defined(HAVE_CRYPT) + crypt_res = crypt(password, salt); +# else +# error No crypt() implementation +# endif +#endif + + if (!crypt_res || (salt[0] == '*' && salt[1] == '0')) { + return NULL; + } else { + result = zend_string_init(crypt_res, strlen(crypt_res), 0); + return result; + } +} +/* }}} */ + + +/* {{{ proto string crypt(string str [, string salt]) + Hash a string */ +PHP_FUNCTION(crypt) +{ + char salt[PHP_MAX_SALT_LEN + 1]; + char *str, *salt_in = NULL; + size_t str_len, salt_in_len = 0; + zend_string *result; + + ZEND_PARSE_PARAMETERS_START(1, 2) + Z_PARAM_STRING(str, str_len) + Z_PARAM_OPTIONAL + Z_PARAM_STRING(salt_in, salt_in_len) + ZEND_PARSE_PARAMETERS_END(); + + salt[0] = salt[PHP_MAX_SALT_LEN] = '\0'; + + /* This will produce suitable results if people depend on DES-encryption + * available (passing always 2-character salt). At least for glibc6.1 */ + memset(&salt[1], '$', PHP_MAX_SALT_LEN - 1); + + if (salt_in) { + memcpy(salt, salt_in, MIN(PHP_MAX_SALT_LEN, salt_in_len)); + } else { + php_error_docref(NULL, E_NOTICE, "No salt parameter was specified. You must use a randomly generated salt and a strong hash function to produce a secure hash."); + } + + /* The automatic salt generation covers standard DES, md5-crypt and Blowfish (simple) */ + if (!*salt) { + memcpy(salt, "$1$", 3); + php_random_bytes_throw(&salt[3], 8); + php_to64(&salt[3], 8); + strncpy(&salt[11], "$", PHP_MAX_SALT_LEN - 11); + salt_in_len = strlen(salt); + } else { + salt_in_len = MIN(PHP_MAX_SALT_LEN, salt_in_len); + } + salt[salt_in_len] = '\0'; + + if ((result = php_crypt(str, (int)str_len, salt, (int)salt_in_len, 0)) == NULL) { + if (salt[0] == '*' && salt[1] == '0') { + RETURN_STRING("*1"); + } else { + RETURN_STRING("*0"); + } + } + RETURN_STR(result); +} +/* }}} */ diff --git a/ext/standard/crypt_blowfish.c b/ext/standard/crypt_blowfish.c index c1f945f29edd4..aa7e1bc2e6894 100644 --- a/ext/standard/crypt_blowfish.c +++ b/ext/standard/crypt_blowfish.c @@ -376,7 +376,6 @@ static unsigned char BF_atoi64[0x60] = { #define BF_safe_atoi64(dst, src) \ { \ tmp = (unsigned char)(src); \ - if (tmp == '$') break; /* PHP hack */ \ if ((unsigned int)(tmp -= 0x20) >= 0x60) return -1; \ tmp = BF_atoi64[tmp]; \ if (tmp > 63) return -1; \ @@ -404,13 +403,6 @@ static int BF_decode(BF_word *dst, const char *src, int size) *dptr++ = ((c3 & 0x03) << 6) | c4; } while (dptr < end); - if (end - dptr == size) { - return -1; - } - - while (dptr < end) /* PHP hack */ - *dptr++ = 0; - return 0; } diff --git a/ext/standard/crypt_blowfish.c.orig b/ext/standard/crypt_blowfish.c.orig new file mode 100644 index 0000000000000..c1f945f29edd4 --- /dev/null +++ b/ext/standard/crypt_blowfish.c.orig @@ -0,0 +1,917 @@ +/* + * The crypt_blowfish homepage is: + * + * http://www.openwall.com/crypt/ + * + * This code comes from John the Ripper password cracker, with reentrant + * and crypt(3) interfaces added, but optimizations specific to password + * cracking removed. + * + * Written by Solar Designer in 1998-2015. + * No copyright is claimed, and the software is hereby placed in the public + * domain. In case this attempt to disclaim copyright and place the software + * in the public domain is deemed null and void, then the software is + * Copyright (c) 1998-2015 Solar Designer and it is hereby released to the + * general public under the following terms: + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted. + * + * There's ABSOLUTELY NO WARRANTY, express or implied. + * + * It is my intent that you should be able to use this on your system, + * as part of a software package, or anywhere else to improve security, + * ensure compatibility, or for any other purpose. I would appreciate + * it if you give credit where it is due and keep your modifications in + * the public domain as well, but I don't require that in order to let + * you place this code and any modifications you make under a license + * of your choice. + * + * This implementation is fully compatible with OpenBSD's bcrypt.c for prefix + * "$2b$", originally by Niels Provos , and it uses + * some of his ideas. The password hashing algorithm was designed by David + * Mazieres . For information on the level of + * compatibility for bcrypt hash prefixes other than "$2b$", please refer to + * the comments in BF_set_key() below and to the included crypt(3) man page. + * + * There's a paper on the algorithm that explains its design decisions: + * + * http://www.usenix.org/events/usenix99/provos.html + * + * Some of the tricks in BF_ROUND might be inspired by Eric Young's + * Blowfish library (I can't be sure if I would think of something if I + * hadn't seen his code). + */ + +#include + +#include +#ifndef __set_errno +#define __set_errno(val) errno = (val) +#endif + +/* Just to make sure the prototypes match the actual definitions */ +#include "crypt_blowfish.h" + +#ifdef __i386__ +#define BF_ASM 0 +#define BF_SCALE 1 +#elif defined(__x86_64__) || defined(__alpha__) || defined(__hppa__) +#define BF_ASM 0 +#define BF_SCALE 1 +#else +#define BF_ASM 0 +#define BF_SCALE 0 +#endif + +typedef unsigned int BF_word; +typedef signed int BF_word_signed; + +/* Number of Blowfish rounds, this is also hardcoded into a few places */ +#define BF_N 16 + +typedef BF_word BF_key[BF_N + 2]; + +typedef struct { + BF_word S[4][0x100]; + BF_key P; +} BF_ctx; + +/* + * Magic IV for 64 Blowfish encryptions that we do at the end. + * The string is "OrpheanBeholderScryDoubt" on big-endian. + */ +static BF_word BF_magic_w[6] = { + 0x4F727068, 0x65616E42, 0x65686F6C, + 0x64657253, 0x63727944, 0x6F756274 +}; + +/* + * P-box and S-box tables initialized with digits of Pi. + */ +static BF_ctx BF_init_state = { + { + { + 0xd1310ba6, 0x98dfb5ac, 0x2ffd72db, 0xd01adfb7, + 0xb8e1afed, 0x6a267e96, 0xba7c9045, 0xf12c7f99, + 0x24a19947, 0xb3916cf7, 0x0801f2e2, 0x858efc16, + 0x636920d8, 0x71574e69, 0xa458fea3, 0xf4933d7e, + 0x0d95748f, 0x728eb658, 0x718bcd58, 0x82154aee, + 0x7b54a41d, 0xc25a59b5, 0x9c30d539, 0x2af26013, + 0xc5d1b023, 0x286085f0, 0xca417918, 0xb8db38ef, + 0x8e79dcb0, 0x603a180e, 0x6c9e0e8b, 0xb01e8a3e, + 0xd71577c1, 0xbd314b27, 0x78af2fda, 0x55605c60, + 0xe65525f3, 0xaa55ab94, 0x57489862, 0x63e81440, + 0x55ca396a, 0x2aab10b6, 0xb4cc5c34, 0x1141e8ce, + 0xa15486af, 0x7c72e993, 0xb3ee1411, 0x636fbc2a, + 0x2ba9c55d, 0x741831f6, 0xce5c3e16, 0x9b87931e, + 0xafd6ba33, 0x6c24cf5c, 0x7a325381, 0x28958677, + 0x3b8f4898, 0x6b4bb9af, 0xc4bfe81b, 0x66282193, + 0x61d809cc, 0xfb21a991, 0x487cac60, 0x5dec8032, + 0xef845d5d, 0xe98575b1, 0xdc262302, 0xeb651b88, + 0x23893e81, 0xd396acc5, 0x0f6d6ff3, 0x83f44239, + 0x2e0b4482, 0xa4842004, 0x69c8f04a, 0x9e1f9b5e, + 0x21c66842, 0xf6e96c9a, 0x670c9c61, 0xabd388f0, + 0x6a51a0d2, 0xd8542f68, 0x960fa728, 0xab5133a3, + 0x6eef0b6c, 0x137a3be4, 0xba3bf050, 0x7efb2a98, + 0xa1f1651d, 0x39af0176, 0x66ca593e, 0x82430e88, + 0x8cee8619, 0x456f9fb4, 0x7d84a5c3, 0x3b8b5ebe, + 0xe06f75d8, 0x85c12073, 0x401a449f, 0x56c16aa6, + 0x4ed3aa62, 0x363f7706, 0x1bfedf72, 0x429b023d, + 0x37d0d724, 0xd00a1248, 0xdb0fead3, 0x49f1c09b, + 0x075372c9, 0x80991b7b, 0x25d479d8, 0xf6e8def7, + 0xe3fe501a, 0xb6794c3b, 0x976ce0bd, 0x04c006ba, + 0xc1a94fb6, 0x409f60c4, 0x5e5c9ec2, 0x196a2463, + 0x68fb6faf, 0x3e6c53b5, 0x1339b2eb, 0x3b52ec6f, + 0x6dfc511f, 0x9b30952c, 0xcc814544, 0xaf5ebd09, + 0xbee3d004, 0xde334afd, 0x660f2807, 0x192e4bb3, + 0xc0cba857, 0x45c8740f, 0xd20b5f39, 0xb9d3fbdb, + 0x5579c0bd, 0x1a60320a, 0xd6a100c6, 0x402c7279, + 0x679f25fe, 0xfb1fa3cc, 0x8ea5e9f8, 0xdb3222f8, + 0x3c7516df, 0xfd616b15, 0x2f501ec8, 0xad0552ab, + 0x323db5fa, 0xfd238760, 0x53317b48, 0x3e00df82, + 0x9e5c57bb, 0xca6f8ca0, 0x1a87562e, 0xdf1769db, + 0xd542a8f6, 0x287effc3, 0xac6732c6, 0x8c4f5573, + 0x695b27b0, 0xbbca58c8, 0xe1ffa35d, 0xb8f011a0, + 0x10fa3d98, 0xfd2183b8, 0x4afcb56c, 0x2dd1d35b, + 0x9a53e479, 0xb6f84565, 0xd28e49bc, 0x4bfb9790, + 0xe1ddf2da, 0xa4cb7e33, 0x62fb1341, 0xcee4c6e8, + 0xef20cada, 0x36774c01, 0xd07e9efe, 0x2bf11fb4, + 0x95dbda4d, 0xae909198, 0xeaad8e71, 0x6b93d5a0, + 0xd08ed1d0, 0xafc725e0, 0x8e3c5b2f, 0x8e7594b7, + 0x8ff6e2fb, 0xf2122b64, 0x8888b812, 0x900df01c, + 0x4fad5ea0, 0x688fc31c, 0xd1cff191, 0xb3a8c1ad, + 0x2f2f2218, 0xbe0e1777, 0xea752dfe, 0x8b021fa1, + 0xe5a0cc0f, 0xb56f74e8, 0x18acf3d6, 0xce89e299, + 0xb4a84fe0, 0xfd13e0b7, 0x7cc43b81, 0xd2ada8d9, + 0x165fa266, 0x80957705, 0x93cc7314, 0x211a1477, + 0xe6ad2065, 0x77b5fa86, 0xc75442f5, 0xfb9d35cf, + 0xebcdaf0c, 0x7b3e89a0, 0xd6411bd3, 0xae1e7e49, + 0x00250e2d, 0x2071b35e, 0x226800bb, 0x57b8e0af, + 0x2464369b, 0xf009b91e, 0x5563911d, 0x59dfa6aa, + 0x78c14389, 0xd95a537f, 0x207d5ba2, 0x02e5b9c5, + 0x83260376, 0x6295cfa9, 0x11c81968, 0x4e734a41, + 0xb3472dca, 0x7b14a94a, 0x1b510052, 0x9a532915, + 0xd60f573f, 0xbc9bc6e4, 0x2b60a476, 0x81e67400, + 0x08ba6fb5, 0x571be91f, 0xf296ec6b, 0x2a0dd915, + 0xb6636521, 0xe7b9f9b6, 0xff34052e, 0xc5855664, + 0x53b02d5d, 0xa99f8fa1, 0x08ba4799, 0x6e85076a + }, { + 0x4b7a70e9, 0xb5b32944, 0xdb75092e, 0xc4192623, + 0xad6ea6b0, 0x49a7df7d, 0x9cee60b8, 0x8fedb266, + 0xecaa8c71, 0x699a17ff, 0x5664526c, 0xc2b19ee1, + 0x193602a5, 0x75094c29, 0xa0591340, 0xe4183a3e, + 0x3f54989a, 0x5b429d65, 0x6b8fe4d6, 0x99f73fd6, + 0xa1d29c07, 0xefe830f5, 0x4d2d38e6, 0xf0255dc1, + 0x4cdd2086, 0x8470eb26, 0x6382e9c6, 0x021ecc5e, + 0x09686b3f, 0x3ebaefc9, 0x3c971814, 0x6b6a70a1, + 0x687f3584, 0x52a0e286, 0xb79c5305, 0xaa500737, + 0x3e07841c, 0x7fdeae5c, 0x8e7d44ec, 0x5716f2b8, + 0xb03ada37, 0xf0500c0d, 0xf01c1f04, 0x0200b3ff, + 0xae0cf51a, 0x3cb574b2, 0x25837a58, 0xdc0921bd, + 0xd19113f9, 0x7ca92ff6, 0x94324773, 0x22f54701, + 0x3ae5e581, 0x37c2dadc, 0xc8b57634, 0x9af3dda7, + 0xa9446146, 0x0fd0030e, 0xecc8c73e, 0xa4751e41, + 0xe238cd99, 0x3bea0e2f, 0x3280bba1, 0x183eb331, + 0x4e548b38, 0x4f6db908, 0x6f420d03, 0xf60a04bf, + 0x2cb81290, 0x24977c79, 0x5679b072, 0xbcaf89af, + 0xde9a771f, 0xd9930810, 0xb38bae12, 0xdccf3f2e, + 0x5512721f, 0x2e6b7124, 0x501adde6, 0x9f84cd87, + 0x7a584718, 0x7408da17, 0xbc9f9abc, 0xe94b7d8c, + 0xec7aec3a, 0xdb851dfa, 0x63094366, 0xc464c3d2, + 0xef1c1847, 0x3215d908, 0xdd433b37, 0x24c2ba16, + 0x12a14d43, 0x2a65c451, 0x50940002, 0x133ae4dd, + 0x71dff89e, 0x10314e55, 0x81ac77d6, 0x5f11199b, + 0x043556f1, 0xd7a3c76b, 0x3c11183b, 0x5924a509, + 0xf28fe6ed, 0x97f1fbfa, 0x9ebabf2c, 0x1e153c6e, + 0x86e34570, 0xeae96fb1, 0x860e5e0a, 0x5a3e2ab3, + 0x771fe71c, 0x4e3d06fa, 0x2965dcb9, 0x99e71d0f, + 0x803e89d6, 0x5266c825, 0x2e4cc978, 0x9c10b36a, + 0xc6150eba, 0x94e2ea78, 0xa5fc3c53, 0x1e0a2df4, + 0xf2f74ea7, 0x361d2b3d, 0x1939260f, 0x19c27960, + 0x5223a708, 0xf71312b6, 0xebadfe6e, 0xeac31f66, + 0xe3bc4595, 0xa67bc883, 0xb17f37d1, 0x018cff28, + 0xc332ddef, 0xbe6c5aa5, 0x65582185, 0x68ab9802, + 0xeecea50f, 0xdb2f953b, 0x2aef7dad, 0x5b6e2f84, + 0x1521b628, 0x29076170, 0xecdd4775, 0x619f1510, + 0x13cca830, 0xeb61bd96, 0x0334fe1e, 0xaa0363cf, + 0xb5735c90, 0x4c70a239, 0xd59e9e0b, 0xcbaade14, + 0xeecc86bc, 0x60622ca7, 0x9cab5cab, 0xb2f3846e, + 0x648b1eaf, 0x19bdf0ca, 0xa02369b9, 0x655abb50, + 0x40685a32, 0x3c2ab4b3, 0x319ee9d5, 0xc021b8f7, + 0x9b540b19, 0x875fa099, 0x95f7997e, 0x623d7da8, + 0xf837889a, 0x97e32d77, 0x11ed935f, 0x16681281, + 0x0e358829, 0xc7e61fd6, 0x96dedfa1, 0x7858ba99, + 0x57f584a5, 0x1b227263, 0x9b83c3ff, 0x1ac24696, + 0xcdb30aeb, 0x532e3054, 0x8fd948e4, 0x6dbc3128, + 0x58ebf2ef, 0x34c6ffea, 0xfe28ed61, 0xee7c3c73, + 0x5d4a14d9, 0xe864b7e3, 0x42105d14, 0x203e13e0, + 0x45eee2b6, 0xa3aaabea, 0xdb6c4f15, 0xfacb4fd0, + 0xc742f442, 0xef6abbb5, 0x654f3b1d, 0x41cd2105, + 0xd81e799e, 0x86854dc7, 0xe44b476a, 0x3d816250, + 0xcf62a1f2, 0x5b8d2646, 0xfc8883a0, 0xc1c7b6a3, + 0x7f1524c3, 0x69cb7492, 0x47848a0b, 0x5692b285, + 0x095bbf00, 0xad19489d, 0x1462b174, 0x23820e00, + 0x58428d2a, 0x0c55f5ea, 0x1dadf43e, 0x233f7061, + 0x3372f092, 0x8d937e41, 0xd65fecf1, 0x6c223bdb, + 0x7cde3759, 0xcbee7460, 0x4085f2a7, 0xce77326e, + 0xa6078084, 0x19f8509e, 0xe8efd855, 0x61d99735, + 0xa969a7aa, 0xc50c06c2, 0x5a04abfc, 0x800bcadc, + 0x9e447a2e, 0xc3453484, 0xfdd56705, 0x0e1e9ec9, + 0xdb73dbd3, 0x105588cd, 0x675fda79, 0xe3674340, + 0xc5c43465, 0x713e38d8, 0x3d28f89e, 0xf16dff20, + 0x153e21e7, 0x8fb03d4a, 0xe6e39f2b, 0xdb83adf7 + }, { + 0xe93d5a68, 0x948140f7, 0xf64c261c, 0x94692934, + 0x411520f7, 0x7602d4f7, 0xbcf46b2e, 0xd4a20068, + 0xd4082471, 0x3320f46a, 0x43b7d4b7, 0x500061af, + 0x1e39f62e, 0x97244546, 0x14214f74, 0xbf8b8840, + 0x4d95fc1d, 0x96b591af, 0x70f4ddd3, 0x66a02f45, + 0xbfbc09ec, 0x03bd9785, 0x7fac6dd0, 0x31cb8504, + 0x96eb27b3, 0x55fd3941, 0xda2547e6, 0xabca0a9a, + 0x28507825, 0x530429f4, 0x0a2c86da, 0xe9b66dfb, + 0x68dc1462, 0xd7486900, 0x680ec0a4, 0x27a18dee, + 0x4f3ffea2, 0xe887ad8c, 0xb58ce006, 0x7af4d6b6, + 0xaace1e7c, 0xd3375fec, 0xce78a399, 0x406b2a42, + 0x20fe9e35, 0xd9f385b9, 0xee39d7ab, 0x3b124e8b, + 0x1dc9faf7, 0x4b6d1856, 0x26a36631, 0xeae397b2, + 0x3a6efa74, 0xdd5b4332, 0x6841e7f7, 0xca7820fb, + 0xfb0af54e, 0xd8feb397, 0x454056ac, 0xba489527, + 0x55533a3a, 0x20838d87, 0xfe6ba9b7, 0xd096954b, + 0x55a867bc, 0xa1159a58, 0xcca92963, 0x99e1db33, + 0xa62a4a56, 0x3f3125f9, 0x5ef47e1c, 0x9029317c, + 0xfdf8e802, 0x04272f70, 0x80bb155c, 0x05282ce3, + 0x95c11548, 0xe4c66d22, 0x48c1133f, 0xc70f86dc, + 0x07f9c9ee, 0x41041f0f, 0x404779a4, 0x5d886e17, + 0x325f51eb, 0xd59bc0d1, 0xf2bcc18f, 0x41113564, + 0x257b7834, 0x602a9c60, 0xdff8e8a3, 0x1f636c1b, + 0x0e12b4c2, 0x02e1329e, 0xaf664fd1, 0xcad18115, + 0x6b2395e0, 0x333e92e1, 0x3b240b62, 0xeebeb922, + 0x85b2a20e, 0xe6ba0d99, 0xde720c8c, 0x2da2f728, + 0xd0127845, 0x95b794fd, 0x647d0862, 0xe7ccf5f0, + 0x5449a36f, 0x877d48fa, 0xc39dfd27, 0xf33e8d1e, + 0x0a476341, 0x992eff74, 0x3a6f6eab, 0xf4f8fd37, + 0xa812dc60, 0xa1ebddf8, 0x991be14c, 0xdb6e6b0d, + 0xc67b5510, 0x6d672c37, 0x2765d43b, 0xdcd0e804, + 0xf1290dc7, 0xcc00ffa3, 0xb5390f92, 0x690fed0b, + 0x667b9ffb, 0xcedb7d9c, 0xa091cf0b, 0xd9155ea3, + 0xbb132f88, 0x515bad24, 0x7b9479bf, 0x763bd6eb, + 0x37392eb3, 0xcc115979, 0x8026e297, 0xf42e312d, + 0x6842ada7, 0xc66a2b3b, 0x12754ccc, 0x782ef11c, + 0x6a124237, 0xb79251e7, 0x06a1bbe6, 0x4bfb6350, + 0x1a6b1018, 0x11caedfa, 0x3d25bdd8, 0xe2e1c3c9, + 0x44421659, 0x0a121386, 0xd90cec6e, 0xd5abea2a, + 0x64af674e, 0xda86a85f, 0xbebfe988, 0x64e4c3fe, + 0x9dbc8057, 0xf0f7c086, 0x60787bf8, 0x6003604d, + 0xd1fd8346, 0xf6381fb0, 0x7745ae04, 0xd736fccc, + 0x83426b33, 0xf01eab71, 0xb0804187, 0x3c005e5f, + 0x77a057be, 0xbde8ae24, 0x55464299, 0xbf582e61, + 0x4e58f48f, 0xf2ddfda2, 0xf474ef38, 0x8789bdc2, + 0x5366f9c3, 0xc8b38e74, 0xb475f255, 0x46fcd9b9, + 0x7aeb2661, 0x8b1ddf84, 0x846a0e79, 0x915f95e2, + 0x466e598e, 0x20b45770, 0x8cd55591, 0xc902de4c, + 0xb90bace1, 0xbb8205d0, 0x11a86248, 0x7574a99e, + 0xb77f19b6, 0xe0a9dc09, 0x662d09a1, 0xc4324633, + 0xe85a1f02, 0x09f0be8c, 0x4a99a025, 0x1d6efe10, + 0x1ab93d1d, 0x0ba5a4df, 0xa186f20f, 0x2868f169, + 0xdcb7da83, 0x573906fe, 0xa1e2ce9b, 0x4fcd7f52, + 0x50115e01, 0xa70683fa, 0xa002b5c4, 0x0de6d027, + 0x9af88c27, 0x773f8641, 0xc3604c06, 0x61a806b5, + 0xf0177a28, 0xc0f586e0, 0x006058aa, 0x30dc7d62, + 0x11e69ed7, 0x2338ea63, 0x53c2dd94, 0xc2c21634, + 0xbbcbee56, 0x90bcb6de, 0xebfc7da1, 0xce591d76, + 0x6f05e409, 0x4b7c0188, 0x39720a3d, 0x7c927c24, + 0x86e3725f, 0x724d9db9, 0x1ac15bb4, 0xd39eb8fc, + 0xed545578, 0x08fca5b5, 0xd83d7cd3, 0x4dad0fc4, + 0x1e50ef5e, 0xb161e6f8, 0xa28514d9, 0x6c51133c, + 0x6fd5c7e7, 0x56e14ec4, 0x362abfce, 0xddc6c837, + 0xd79a3234, 0x92638212, 0x670efa8e, 0x406000e0 + }, { + 0x3a39ce37, 0xd3faf5cf, 0xabc27737, 0x5ac52d1b, + 0x5cb0679e, 0x4fa33742, 0xd3822740, 0x99bc9bbe, + 0xd5118e9d, 0xbf0f7315, 0xd62d1c7e, 0xc700c47b, + 0xb78c1b6b, 0x21a19045, 0xb26eb1be, 0x6a366eb4, + 0x5748ab2f, 0xbc946e79, 0xc6a376d2, 0x6549c2c8, + 0x530ff8ee, 0x468dde7d, 0xd5730a1d, 0x4cd04dc6, + 0x2939bbdb, 0xa9ba4650, 0xac9526e8, 0xbe5ee304, + 0xa1fad5f0, 0x6a2d519a, 0x63ef8ce2, 0x9a86ee22, + 0xc089c2b8, 0x43242ef6, 0xa51e03aa, 0x9cf2d0a4, + 0x83c061ba, 0x9be96a4d, 0x8fe51550, 0xba645bd6, + 0x2826a2f9, 0xa73a3ae1, 0x4ba99586, 0xef5562e9, + 0xc72fefd3, 0xf752f7da, 0x3f046f69, 0x77fa0a59, + 0x80e4a915, 0x87b08601, 0x9b09e6ad, 0x3b3ee593, + 0xe990fd5a, 0x9e34d797, 0x2cf0b7d9, 0x022b8b51, + 0x96d5ac3a, 0x017da67d, 0xd1cf3ed6, 0x7c7d2d28, + 0x1f9f25cf, 0xadf2b89b, 0x5ad6b472, 0x5a88f54c, + 0xe029ac71, 0xe019a5e6, 0x47b0acfd, 0xed93fa9b, + 0xe8d3c48d, 0x283b57cc, 0xf8d56629, 0x79132e28, + 0x785f0191, 0xed756055, 0xf7960e44, 0xe3d35e8c, + 0x15056dd4, 0x88f46dba, 0x03a16125, 0x0564f0bd, + 0xc3eb9e15, 0x3c9057a2, 0x97271aec, 0xa93a072a, + 0x1b3f6d9b, 0x1e6321f5, 0xf59c66fb, 0x26dcf319, + 0x7533d928, 0xb155fdf5, 0x03563482, 0x8aba3cbb, + 0x28517711, 0xc20ad9f8, 0xabcc5167, 0xccad925f, + 0x4de81751, 0x3830dc8e, 0x379d5862, 0x9320f991, + 0xea7a90c2, 0xfb3e7bce, 0x5121ce64, 0x774fbe32, + 0xa8b6e37e, 0xc3293d46, 0x48de5369, 0x6413e680, + 0xa2ae0810, 0xdd6db224, 0x69852dfd, 0x09072166, + 0xb39a460a, 0x6445c0dd, 0x586cdecf, 0x1c20c8ae, + 0x5bbef7dd, 0x1b588d40, 0xccd2017f, 0x6bb4e3bb, + 0xdda26a7e, 0x3a59ff45, 0x3e350a44, 0xbcb4cdd5, + 0x72eacea8, 0xfa6484bb, 0x8d6612ae, 0xbf3c6f47, + 0xd29be463, 0x542f5d9e, 0xaec2771b, 0xf64e6370, + 0x740e0d8d, 0xe75b1357, 0xf8721671, 0xaf537d5d, + 0x4040cb08, 0x4eb4e2cc, 0x34d2466a, 0x0115af84, + 0xe1b00428, 0x95983a1d, 0x06b89fb4, 0xce6ea048, + 0x6f3f3b82, 0x3520ab82, 0x011a1d4b, 0x277227f8, + 0x611560b1, 0xe7933fdc, 0xbb3a792b, 0x344525bd, + 0xa08839e1, 0x51ce794b, 0x2f32c9b7, 0xa01fbac9, + 0xe01cc87e, 0xbcc7d1f6, 0xcf0111c3, 0xa1e8aac7, + 0x1a908749, 0xd44fbd9a, 0xd0dadecb, 0xd50ada38, + 0x0339c32a, 0xc6913667, 0x8df9317c, 0xe0b12b4f, + 0xf79e59b7, 0x43f5bb3a, 0xf2d519ff, 0x27d9459c, + 0xbf97222c, 0x15e6fc2a, 0x0f91fc71, 0x9b941525, + 0xfae59361, 0xceb69ceb, 0xc2a86459, 0x12baa8d1, + 0xb6c1075e, 0xe3056a0c, 0x10d25065, 0xcb03a442, + 0xe0ec6e0e, 0x1698db3b, 0x4c98a0be, 0x3278e964, + 0x9f1f9532, 0xe0d392df, 0xd3a0342b, 0x8971f21e, + 0x1b0a7441, 0x4ba3348c, 0xc5be7120, 0xc37632d8, + 0xdf359f8d, 0x9b992f2e, 0xe60b6f47, 0x0fe3f11d, + 0xe54cda54, 0x1edad891, 0xce6279cf, 0xcd3e7e6f, + 0x1618b166, 0xfd2c1d05, 0x848fd2c5, 0xf6fb2299, + 0xf523f357, 0xa6327623, 0x93a83531, 0x56cccd02, + 0xacf08162, 0x5a75ebb5, 0x6e163697, 0x88d273cc, + 0xde966292, 0x81b949d0, 0x4c50901b, 0x71c65614, + 0xe6c6c7bd, 0x327a140a, 0x45e1d006, 0xc3f27b9a, + 0xc9aa53fd, 0x62a80f00, 0xbb25bfe2, 0x35bdd2f6, + 0x71126905, 0xb2040222, 0xb6cbcf7c, 0xcd769c2b, + 0x53113ec0, 0x1640e3d3, 0x38abbd60, 0x2547adf0, + 0xba38209c, 0xf746ce76, 0x77afa1c5, 0x20756060, + 0x85cbfe4e, 0x8ae88dd8, 0x7aaaf9b0, 0x4cf9aa7e, + 0x1948c25c, 0x02fb8a8c, 0x01c36ae4, 0xd6ebe1f9, + 0x90d4f869, 0xa65cdea0, 0x3f09252d, 0xc208e69f, + 0xb74e6132, 0xce77e25b, 0x578fdfe3, 0x3ac372e6 + } + }, { + 0x243f6a88, 0x85a308d3, 0x13198a2e, 0x03707344, + 0xa4093822, 0x299f31d0, 0x082efa98, 0xec4e6c89, + 0x452821e6, 0x38d01377, 0xbe5466cf, 0x34e90c6c, + 0xc0ac29b7, 0xc97c50dd, 0x3f84d5b5, 0xb5470917, + 0x9216d5d9, 0x8979fb1b + } +}; + +static unsigned char BF_itoa64[64 + 1] = + "./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; + +static unsigned char BF_atoi64[0x60] = { + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 0, 1, + 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 64, 64, 64, 64, 64, + 64, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 64, 64, 64, 64, 64, + 64, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, + 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 64, 64, 64, 64, 64 +}; + +#define BF_safe_atoi64(dst, src) \ +{ \ + tmp = (unsigned char)(src); \ + if (tmp == '$') break; /* PHP hack */ \ + if ((unsigned int)(tmp -= 0x20) >= 0x60) return -1; \ + tmp = BF_atoi64[tmp]; \ + if (tmp > 63) return -1; \ + (dst) = tmp; \ +} + +static int BF_decode(BF_word *dst, const char *src, int size) +{ + unsigned char *dptr = (unsigned char *)dst; + unsigned char *end = dptr + size; + const unsigned char *sptr = (const unsigned char *)src; + unsigned int tmp, c1, c2, c3, c4; + + do { + BF_safe_atoi64(c1, *sptr++); + BF_safe_atoi64(c2, *sptr++); + *dptr++ = (c1 << 2) | ((c2 & 0x30) >> 4); + if (dptr >= end) break; + + BF_safe_atoi64(c3, *sptr++); + *dptr++ = ((c2 & 0x0F) << 4) | ((c3 & 0x3C) >> 2); + if (dptr >= end) break; + + BF_safe_atoi64(c4, *sptr++); + *dptr++ = ((c3 & 0x03) << 6) | c4; + } while (dptr < end); + + if (end - dptr == size) { + return -1; + } + + while (dptr < end) /* PHP hack */ + *dptr++ = 0; + + return 0; +} + +static void BF_encode(char *dst, const BF_word *src, int size) +{ + const unsigned char *sptr = (const unsigned char *)src; + const unsigned char *end = sptr + size; + unsigned char *dptr = (unsigned char *)dst; + unsigned int c1, c2; + + do { + c1 = *sptr++; + *dptr++ = BF_itoa64[c1 >> 2]; + c1 = (c1 & 0x03) << 4; + if (sptr >= end) { + *dptr++ = BF_itoa64[c1]; + break; + } + + c2 = *sptr++; + c1 |= c2 >> 4; + *dptr++ = BF_itoa64[c1]; + c1 = (c2 & 0x0f) << 2; + if (sptr >= end) { + *dptr++ = BF_itoa64[c1]; + break; + } + + c2 = *sptr++; + c1 |= c2 >> 6; + *dptr++ = BF_itoa64[c1]; + *dptr++ = BF_itoa64[c2 & 0x3f]; + } while (sptr < end); +} + +static void BF_swap(BF_word *x, int count) +{ + static int endianness_check = 1; + char *is_little_endian = (char *)&endianness_check; + BF_word tmp; + + if (*is_little_endian) + do { + tmp = *x; + tmp = (tmp << 16) | (tmp >> 16); + *x++ = ((tmp & 0x00FF00FF) << 8) | ((tmp >> 8) & 0x00FF00FF); + } while (--count); +} + +#if BF_SCALE +/* Architectures which can shift addresses left by 2 bits with no extra cost */ +#define BF_ROUND(L, R, N) \ + tmp1 = L & 0xFF; \ + tmp2 = L >> 8; \ + tmp2 &= 0xFF; \ + tmp3 = L >> 16; \ + tmp3 &= 0xFF; \ + tmp4 = L >> 24; \ + tmp1 = data.ctx.S[3][tmp1]; \ + tmp2 = data.ctx.S[2][tmp2]; \ + tmp3 = data.ctx.S[1][tmp3]; \ + tmp3 += data.ctx.S[0][tmp4]; \ + tmp3 ^= tmp2; \ + R ^= data.ctx.P[N + 1]; \ + tmp3 += tmp1; \ + R ^= tmp3; +#else +/* Architectures with no complicated addressing modes supported */ +#define BF_INDEX(S, i) \ + (*((BF_word *)(((unsigned char *)S) + (i)))) +#define BF_ROUND(L, R, N) \ + tmp1 = L & 0xFF; \ + tmp1 <<= 2; \ + tmp2 = L >> 6; \ + tmp2 &= 0x3FC; \ + tmp3 = L >> 14; \ + tmp3 &= 0x3FC; \ + tmp4 = L >> 22; \ + tmp4 &= 0x3FC; \ + tmp1 = BF_INDEX(data.ctx.S[3], tmp1); \ + tmp2 = BF_INDEX(data.ctx.S[2], tmp2); \ + tmp3 = BF_INDEX(data.ctx.S[1], tmp3); \ + tmp3 += BF_INDEX(data.ctx.S[0], tmp4); \ + tmp3 ^= tmp2; \ + R ^= data.ctx.P[N + 1]; \ + tmp3 += tmp1; \ + R ^= tmp3; +#endif + +/* + * Encrypt one block, BF_N is hardcoded here. + */ +#define BF_ENCRYPT \ + L ^= data.ctx.P[0]; \ + BF_ROUND(L, R, 0); \ + BF_ROUND(R, L, 1); \ + BF_ROUND(L, R, 2); \ + BF_ROUND(R, L, 3); \ + BF_ROUND(L, R, 4); \ + BF_ROUND(R, L, 5); \ + BF_ROUND(L, R, 6); \ + BF_ROUND(R, L, 7); \ + BF_ROUND(L, R, 8); \ + BF_ROUND(R, L, 9); \ + BF_ROUND(L, R, 10); \ + BF_ROUND(R, L, 11); \ + BF_ROUND(L, R, 12); \ + BF_ROUND(R, L, 13); \ + BF_ROUND(L, R, 14); \ + BF_ROUND(R, L, 15); \ + tmp4 = R; \ + R = L; \ + L = tmp4 ^ data.ctx.P[BF_N + 1]; + +#if BF_ASM +#define BF_body() \ + _BF_body_r(&data.ctx); +#else +#define BF_body() \ + L = R = 0; \ + ptr = data.ctx.P; \ + do { \ + ptr += 2; \ + BF_ENCRYPT; \ + *(ptr - 2) = L; \ + *(ptr - 1) = R; \ + } while (ptr < &data.ctx.P[BF_N + 2]); \ +\ + ptr = data.ctx.S[0]; \ + do { \ + ptr += 2; \ + BF_ENCRYPT; \ + *(ptr - 2) = L; \ + *(ptr - 1) = R; \ + } while (ptr < &data.ctx.S[3][0xFF]); +#endif + +static void BF_set_key(const char *key, BF_key expanded, BF_key initial, + unsigned char flags) +{ + const char *ptr = key; + unsigned int bug, i, j; + BF_word safety, sign, diff, tmp[2]; + +/* + * There was a sign extension bug in older revisions of this function. While + * we would have liked to simply fix the bug and move on, we have to provide + * a backwards compatibility feature (essentially the bug) for some systems and + * a safety measure for some others. The latter is needed because for certain + * multiple inputs to the buggy algorithm there exist easily found inputs to + * the correct algorithm that produce the same hash. Thus, we optionally + * deviate from the correct algorithm just enough to avoid such collisions. + * While the bug itself affected the majority of passwords containing + * characters with the 8th bit set (although only a percentage of those in a + * collision-producing way), the anti-collision safety measure affects + * only a subset of passwords containing the '\xff' character (not even all of + * those passwords, just some of them). This character is not found in valid + * UTF-8 sequences and is rarely used in popular 8-bit character encodings. + * Thus, the safety measure is unlikely to cause much annoyance, and is a + * reasonable tradeoff to use when authenticating against existing hashes that + * are not reliably known to have been computed with the correct algorithm. + * + * We use an approach that tries to minimize side-channel leaks of password + * information - that is, we mostly use fixed-cost bitwise operations instead + * of branches or table lookups. (One conditional branch based on password + * length remains. It is not part of the bug aftermath, though, and is + * difficult and possibly unreasonable to avoid given the use of C strings by + * the caller, which results in similar timing leaks anyway.) + * + * For actual implementation, we set an array index in the variable "bug" + * (0 means no bug, 1 means sign extension bug emulation) and a flag in the + * variable "safety" (bit 16 is set when the safety measure is requested). + * Valid combinations of settings are: + * + * Prefix "$2a$": bug = 0, safety = 0x10000 + * Prefix "$2b$": bug = 0, safety = 0 + * Prefix "$2x$": bug = 1, safety = 0 + * Prefix "$2y$": bug = 0, safety = 0 + */ + bug = (unsigned int)flags & 1; + safety = ((BF_word)flags & 2) << 15; + + sign = diff = 0; + + for (i = 0; i < BF_N + 2; i++) { + tmp[0] = tmp[1] = 0; + for (j = 0; j < 4; j++) { + tmp[0] <<= 8; + tmp[0] |= (unsigned char)*ptr; /* correct */ + tmp[1] <<= 8; + tmp[1] |= (BF_word_signed)(signed char)*ptr; /* bug */ +/* + * Sign extension in the first char has no effect - nothing to overwrite yet, + * and those extra 24 bits will be fully shifted out of the 32-bit word. For + * chars 2, 3, 4 in each four-char block, we set bit 7 of "sign" if sign + * extension in tmp[1] occurs. Once this flag is set, it remains set. + */ + if (j) + sign |= tmp[1] & 0x80; + if (!*ptr) + ptr = key; + else + ptr++; + } + diff |= tmp[0] ^ tmp[1]; /* Non-zero on any differences */ + + expanded[i] = tmp[bug]; + initial[i] = BF_init_state.P[i] ^ tmp[bug]; + } + +/* + * At this point, "diff" is zero iff the correct and buggy algorithms produced + * exactly the same result. If so and if "sign" is non-zero, which indicates + * that there was a non-benign sign extension, this means that we have a + * collision between the correctly computed hash for this password and a set of + * passwords that could be supplied to the buggy algorithm. Our safety measure + * is meant to protect from such many-buggy to one-correct collisions, by + * deviating from the correct algorithm in such cases. Let's check for this. + */ + diff |= diff >> 16; /* still zero iff exact match */ + diff &= 0xffff; /* ditto */ + diff += 0xffff; /* bit 16 set iff "diff" was non-zero (on non-match) */ + sign <<= 9; /* move the non-benign sign extension flag to bit 16 */ + sign &= ~diff & safety; /* action needed? */ + +/* + * If we have determined that we need to deviate from the correct algorithm, + * flip bit 16 in initial expanded key. (The choice of 16 is arbitrary, but + * let's stick to it now. It came out of the approach we used above, and it's + * not any worse than any other choice we could make.) + * + * It is crucial that we don't do the same to the expanded key used in the main + * Eksblowfish loop. By doing it to only one of these two, we deviate from a + * state that could be directly specified by a password to the buggy algorithm + * (and to the fully correct one as well, but that's a side-effect). + */ + initial[0] ^= sign; +} + +static const unsigned char flags_by_subtype[26] = + {2, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 4, 0}; + +static char *BF_crypt(const char *key, const char *setting, + char *output, int size, + BF_word min) +{ +#if BF_ASM + extern void _BF_body_r(BF_ctx *ctx); +#endif + struct { + BF_ctx ctx; + BF_key expanded_key; + union { + BF_word salt[4]; + BF_word output[6]; + } binary; + } data; + BF_word L, R; + BF_word tmp1, tmp2, tmp3, tmp4; + BF_word *ptr; + BF_word count; + int i; + + if (size < 7 + 22 + 31 + 1) { + __set_errno(ERANGE); + return NULL; + } + + if (setting[0] != '$' || + setting[1] != '2' || + setting[2] < 'a' || setting[2] > 'z' || + !flags_by_subtype[(unsigned int)(unsigned char)setting[2] - 'a'] || + setting[3] != '$' || + setting[4] < '0' || setting[4] > '3' || + setting[5] < '0' || setting[5] > '9' || + (setting[4] == '3' && setting[5] > '1') || + setting[6] != '$') { + __set_errno(EINVAL); + return NULL; + } + + count = (BF_word)1 << ((setting[4] - '0') * 10 + (setting[5] - '0')); + if (count < min || BF_decode(data.binary.salt, &setting[7], 16)) { + __set_errno(EINVAL); + return NULL; + } + BF_swap(data.binary.salt, 4); + + BF_set_key(key, data.expanded_key, data.ctx.P, + flags_by_subtype[(unsigned int)(unsigned char)setting[2] - 'a']); + + memcpy(data.ctx.S, BF_init_state.S, sizeof(data.ctx.S)); + + L = R = 0; + for (i = 0; i < BF_N + 2; i += 2) { + L ^= data.binary.salt[i & 2]; + R ^= data.binary.salt[(i & 2) + 1]; + BF_ENCRYPT; + data.ctx.P[i] = L; + data.ctx.P[i + 1] = R; + } + + ptr = data.ctx.S[0]; + do { + ptr += 4; + L ^= data.binary.salt[(BF_N + 2) & 3]; + R ^= data.binary.salt[(BF_N + 3) & 3]; + BF_ENCRYPT; + *(ptr - 4) = L; + *(ptr - 3) = R; + + L ^= data.binary.salt[(BF_N + 4) & 3]; + R ^= data.binary.salt[(BF_N + 5) & 3]; + BF_ENCRYPT; + *(ptr - 2) = L; + *(ptr - 1) = R; + } while (ptr < &data.ctx.S[3][0xFF]); + + do { + int done; + + for (i = 0; i < BF_N + 2; i += 2) { + data.ctx.P[i] ^= data.expanded_key[i]; + data.ctx.P[i + 1] ^= data.expanded_key[i + 1]; + } + + done = 0; + do { + BF_body(); + if (done) + break; + done = 1; + + tmp1 = data.binary.salt[0]; + tmp2 = data.binary.salt[1]; + tmp3 = data.binary.salt[2]; + tmp4 = data.binary.salt[3]; + for (i = 0; i < BF_N; i += 4) { + data.ctx.P[i] ^= tmp1; + data.ctx.P[i + 1] ^= tmp2; + data.ctx.P[i + 2] ^= tmp3; + data.ctx.P[i + 3] ^= tmp4; + } + data.ctx.P[16] ^= tmp1; + data.ctx.P[17] ^= tmp2; + } while (1); + } while (--count); + + for (i = 0; i < 6; i += 2) { + L = BF_magic_w[i]; + R = BF_magic_w[i + 1]; + + count = 64; + do { + BF_ENCRYPT; + } while (--count); + + data.binary.output[i] = L; + data.binary.output[i + 1] = R; + } + + memcpy(output, setting, 7 + 22 - 1); + output[7 + 22 - 1] = BF_itoa64[(int) + BF_atoi64[(int)setting[7 + 22 - 1] - 0x20] & 0x30]; + +/* This has to be bug-compatible with the original implementation, so + * only encode 23 of the 24 bytes. :-) */ + BF_swap(data.binary.output, 6); + BF_encode(&output[7 + 22], data.binary.output, 23); + output[7 + 22 + 31] = '\0'; + + return output; +} + +static int _crypt_output_magic(const char *setting, char *output, int size) +{ + if (size < 3) + return -1; + + output[0] = '*'; + output[1] = '0'; + output[2] = '\0'; + + if (setting[0] == '*' && setting[1] == '0') + output[1] = '1'; + + return 0; +} + +/* + * Please preserve the runtime self-test. It serves two purposes at once: + * + * 1. We really can't afford the risk of producing incompatible hashes e.g. + * when there's something like gcc bug 26587 again, whereas an application or + * library integrating this code might not also integrate our external tests or + * it might not run them after every build. Even if it does, the miscompile + * might only occur on the production build, but not on a testing build (such + * as because of different optimization settings). It is painful to recover + * from incorrectly-computed hashes - merely fixing whatever broke is not + * enough. Thus, a proactive measure like this self-test is needed. + * + * 2. We don't want to leave sensitive data from our actual password hash + * computation on the stack or in registers. Previous revisions of the code + * would do explicit cleanups, but simply running the self-test after hash + * computation is more reliable. + * + * The performance cost of this quick self-test is around 0.6% at the "$2a$08" + * setting. + */ +char *php_crypt_blowfish_rn(const char *key, const char *setting, + char *output, int size) +{ + const char *test_key = "8b \xd0\xc1\xd2\xcf\xcc\xd8"; + const char *test_setting = "$2a$00$abcdefghijklmnopqrstuu"; + static const char * const test_hashes[2] = + {"i1D709vfamulimlGcq0qq3UvuUasvEa\0\x55", /* 'a', 'b', 'y' */ + "VUrPmXD6q/nVSSp7pNDhCR9071IfIRe\0\x55"}; /* 'x' */ + const char *test_hash = test_hashes[0]; + char *retval; + const char *p; + int save_errno, ok; + struct { + char s[7 + 22 + 1]; + char o[7 + 22 + 31 + 1 + 1 + 1]; + } buf; + +/* Hash the supplied password */ + _crypt_output_magic(setting, output, size); + retval = BF_crypt(key, setting, output, size, 16); + save_errno = errno; + +/* + * Do a quick self-test. It is important that we make both calls to BF_crypt() + * from the same scope such that they likely use the same stack locations, + * which makes the second call overwrite the first call's sensitive data on the + * stack and makes it more likely that any alignment related issues would be + * detected by the self-test. + */ + memcpy(buf.s, test_setting, sizeof(buf.s)); + if (retval) { + unsigned int flags = flags_by_subtype[ + (unsigned int)(unsigned char)setting[2] - 'a']; + test_hash = test_hashes[flags & 1]; + buf.s[2] = setting[2]; + } + memset(buf.o, 0x55, sizeof(buf.o)); + buf.o[sizeof(buf.o) - 1] = 0; + p = BF_crypt(test_key, buf.s, buf.o, sizeof(buf.o) - (1 + 1), 1); + + ok = (p == buf.o && + !memcmp(p, buf.s, 7 + 22) && + !memcmp(p + (7 + 22), test_hash, 31 + 1 + 1 + 1)); + + { + const char *k = "\xff\xa3" "34" "\xff\xff\xff\xa3" "345"; + BF_key ae, ai, ye, yi; + BF_set_key(k, ae, ai, 2); /* $2a$ */ + BF_set_key(k, ye, yi, 4); /* $2y$ */ + ai[0] ^= 0x10000; /* undo the safety (for comparison) */ + ok = ok && ai[0] == 0xdb9c59bc && ye[17] == 0x33343500 && + !memcmp(ae, ye, sizeof(ae)) && + !memcmp(ai, yi, sizeof(ai)); + } + + __set_errno(save_errno); + if (ok) + return retval; + +/* Should not happen */ + _crypt_output_magic(setting, output, size); + __set_errno(EINVAL); /* pretend we don't support this hash type */ + return NULL; +} + +#if 0 +char *_crypt_gensalt_blowfish_rn(const char *prefix, unsigned long count, + const char *input, int size, char *output, int output_size) +{ + if (size < 16 || output_size < 7 + 22 + 1 || + (count && (count < 4 || count > 31)) || + prefix[0] != '$' || prefix[1] != '2' || + (prefix[2] != 'a' && prefix[2] != 'b' && prefix[2] != 'y')) { + if (output_size > 0) output[0] = '\0'; + __set_errno((output_size < 7 + 22 + 1) ? ERANGE : EINVAL); + return NULL; + } + + if (!count) count = 5; + + output[0] = '$'; + output[1] = '2'; + output[2] = prefix[2]; + output[3] = '$'; + output[4] = '0' + count / 10; + output[5] = '0' + count % 10; + output[6] = '$'; + + BF_encode(&output[7], (const BF_word *)input, 16); + output[7 + 22] = '\0'; + + return output; +} +#endif diff --git a/ext/standard/tests/crypt/bcrypt_salt_dollar.phpt b/ext/standard/tests/crypt/bcrypt_salt_dollar.phpt new file mode 100644 index 0000000000000..32e335f4b087e --- /dev/null +++ b/ext/standard/tests/crypt/bcrypt_salt_dollar.phpt @@ -0,0 +1,82 @@ +--TEST-- +bcrypt correctly rejects salts containing $ +--FILE-- + +--EXPECT-- +string(8) "$2y$04$$" +string(2) "*0" +bool(false) +string(9) "$2y$04$0$" +string(2) "*0" +bool(false) +string(10) "$2y$04$00$" +string(2) "*0" +bool(false) +string(11) "$2y$04$000$" +string(2) "*0" +bool(false) +string(12) "$2y$04$0000$" +string(2) "*0" +bool(false) +string(13) "$2y$04$00000$" +string(2) "*0" +bool(false) +string(14) "$2y$04$000000$" +string(2) "*0" +bool(false) +string(15) "$2y$04$0000000$" +string(2) "*0" +bool(false) +string(16) "$2y$04$00000000$" +string(2) "*0" +bool(false) +string(17) "$2y$04$000000000$" +string(2) "*0" +bool(false) +string(18) "$2y$04$0000000000$" +string(2) "*0" +bool(false) +string(19) "$2y$04$00000000000$" +string(2) "*0" +bool(false) +string(20) "$2y$04$000000000000$" +string(2) "*0" +bool(false) +string(21) "$2y$04$0000000000000$" +string(2) "*0" +bool(false) +string(22) "$2y$04$00000000000000$" +string(2) "*0" +bool(false) +string(23) "$2y$04$000000000000000$" +string(2) "*0" +bool(false) +string(24) "$2y$04$0000000000000000$" +string(2) "*0" +bool(false) +string(25) "$2y$04$00000000000000000$" +string(2) "*0" +bool(false) +string(26) "$2y$04$000000000000000000$" +string(2) "*0" +bool(false) +string(27) "$2y$04$0000000000000000000$" +string(2) "*0" +bool(false) +string(28) "$2y$04$00000000000000000000$" +string(2) "*0" +bool(false) +string(29) "$2y$04$000000000000000000000$" +string(2) "*0" +bool(false) +string(30) "$2y$04$0000000000000000000000$" +string(60) "$2y$04$000000000000000000000u2a2UpVexIt9k3FMJeAVr3c04F5tcI8K" +bool(false) diff --git a/ext/standard/tests/password/password_bcrypt_short.phpt b/ext/standard/tests/password/password_bcrypt_short.phpt new file mode 100644 index 0000000000000..085bc8a239045 --- /dev/null +++ b/ext/standard/tests/password/password_bcrypt_short.phpt @@ -0,0 +1,8 @@ +--TEST-- +Test that password_hash() does not overread buffers when a short hash is passed +--FILE-- + +--EXPECT-- +bool(false) diff --git a/main/rfc1867.c b/main/rfc1867.c index edef19c16d64e..0f4a52b3c3437 100644 --- a/main/rfc1867.c +++ b/main/rfc1867.c @@ -753,6 +753,13 @@ SAPI_API SAPI_POST_HANDLER_FUNC(rfc1867_post_handler) /* {{{ */ boundary_len = boundary_end-boundary; } + /* Boundaries larger than FILLUNIT-strlen("\r\n--") characters lead to + * erroneous parsing */ + if (boundary_len > FILLUNIT-strlen("\r\n--")) { + sapi_module.sapi_error(E_WARNING, "Boundary too large in multipart/form-data POST data"); + return; + } + /* Initialize the buffer */ if (!(mbuff = multipart_buffer_new(boundary, boundary_len))) { sapi_module.sapi_error(E_WARNING, "Unable to initialize the input buffer"); diff --git a/main/rfc1867.c.orig b/main/rfc1867.c.orig new file mode 100644 index 0000000000000..edef19c16d64e --- /dev/null +++ b/main/rfc1867.c.orig @@ -0,0 +1,1334 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 7 | + +----------------------------------------------------------------------+ + | Copyright (c) The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Rasmus Lerdorf | + | Jani Taskinen | + +----------------------------------------------------------------------+ + */ + +/* + * This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/). + * + */ + +#include +#include "php.h" +#include "php_open_temporary_file.h" +#include "zend_globals.h" +#include "php_globals.h" +#include "php_variables.h" +#include "rfc1867.h" +#include "ext/standard/php_string.h" +#include "zend_smart_string.h" + +#if defined(PHP_WIN32) && !defined(HAVE_ATOLL) +# define atoll(s) _atoi64(s) +# define HAVE_ATOLL 1 +#endif + +#ifndef DEBUG_FILE_UPLOAD +# define DEBUG_FILE_UPLOAD 0 +#endif + +static int dummy_encoding_translation(void) +{ + return 0; +} + +static char *php_ap_getword(const zend_encoding *encoding, char **line, char stop); +static char *php_ap_getword_conf(const zend_encoding *encoding, char *str); + +static php_rfc1867_encoding_translation_t php_rfc1867_encoding_translation = dummy_encoding_translation; +static php_rfc1867_get_detect_order_t php_rfc1867_get_detect_order = NULL; +static php_rfc1867_set_input_encoding_t php_rfc1867_set_input_encoding = NULL; +static php_rfc1867_getword_t php_rfc1867_getword = php_ap_getword; +static php_rfc1867_getword_conf_t php_rfc1867_getword_conf = php_ap_getword_conf; +static php_rfc1867_basename_t php_rfc1867_basename = NULL; + +PHPAPI int (*php_rfc1867_callback)(unsigned int event, void *event_data, void **extra) = NULL; + +static void safe_php_register_variable(char *var, char *strval, size_t val_len, zval *track_vars_array, zend_bool override_protection); + +/* The longest property name we use in an uploaded file array */ +#define MAX_SIZE_OF_INDEX sizeof("[tmp_name]") + +/* The longest anonymous name */ +#define MAX_SIZE_ANONNAME 33 + +/* Errors */ +#define UPLOAD_ERROR_OK 0 /* File upload successful */ +#define UPLOAD_ERROR_A 1 /* Uploaded file exceeded upload_max_filesize */ +#define UPLOAD_ERROR_B 2 /* Uploaded file exceeded MAX_FILE_SIZE */ +#define UPLOAD_ERROR_C 3 /* Partially uploaded */ +#define UPLOAD_ERROR_D 4 /* No file uploaded */ +#define UPLOAD_ERROR_E 6 /* Missing /tmp or similar directory */ +#define UPLOAD_ERROR_F 7 /* Failed to write file to disk */ +#define UPLOAD_ERROR_X 8 /* File upload stopped by extension */ + +void php_rfc1867_register_constants(void) /* {{{ */ +{ + REGISTER_MAIN_LONG_CONSTANT("UPLOAD_ERR_OK", UPLOAD_ERROR_OK, CONST_CS | CONST_PERSISTENT); + REGISTER_MAIN_LONG_CONSTANT("UPLOAD_ERR_INI_SIZE", UPLOAD_ERROR_A, CONST_CS | CONST_PERSISTENT); + REGISTER_MAIN_LONG_CONSTANT("UPLOAD_ERR_FORM_SIZE", UPLOAD_ERROR_B, CONST_CS | CONST_PERSISTENT); + REGISTER_MAIN_LONG_CONSTANT("UPLOAD_ERR_PARTIAL", UPLOAD_ERROR_C, CONST_CS | CONST_PERSISTENT); + REGISTER_MAIN_LONG_CONSTANT("UPLOAD_ERR_NO_FILE", UPLOAD_ERROR_D, CONST_CS | CONST_PERSISTENT); + REGISTER_MAIN_LONG_CONSTANT("UPLOAD_ERR_NO_TMP_DIR", UPLOAD_ERROR_E, CONST_CS | CONST_PERSISTENT); + REGISTER_MAIN_LONG_CONSTANT("UPLOAD_ERR_CANT_WRITE", UPLOAD_ERROR_F, CONST_CS | CONST_PERSISTENT); + REGISTER_MAIN_LONG_CONSTANT("UPLOAD_ERR_EXTENSION", UPLOAD_ERROR_X, CONST_CS | CONST_PERSISTENT); +} +/* }}} */ + +static void normalize_protected_variable(char *varname) /* {{{ */ +{ + char *s = varname, *index = NULL, *indexend = NULL, *p; + + /* overjump leading space */ + while (*s == ' ') { + s++; + } + + /* and remove it */ + if (s != varname) { + memmove(varname, s, strlen(s)+1); + } + + for (p = varname; *p && *p != '['; p++) { + switch(*p) { + case ' ': + case '.': + *p = '_'; + break; + } + } + + /* find index */ + index = strchr(varname, '['); + if (index) { + index++; + s = index; + } else { + return; + } + + /* done? */ + while (index) { + while (*index == ' ' || *index == '\r' || *index == '\n' || *index=='\t') { + index++; + } + indexend = strchr(index, ']'); + indexend = indexend ? indexend + 1 : index + strlen(index); + + if (s != index) { + memmove(s, index, strlen(index)+1); + s += indexend-index; + } else { + s = indexend; + } + + if (*s == '[') { + s++; + index = s; + } else { + index = NULL; + } + } + *s = '\0'; +} +/* }}} */ + +static void add_protected_variable(char *varname) /* {{{ */ +{ + normalize_protected_variable(varname); + zend_hash_str_add_empty_element(&PG(rfc1867_protected_variables), varname, strlen(varname)); +} +/* }}} */ + +static zend_bool is_protected_variable(char *varname) /* {{{ */ +{ + normalize_protected_variable(varname); + return zend_hash_str_exists(&PG(rfc1867_protected_variables), varname, strlen(varname)); +} +/* }}} */ + +static void safe_php_register_variable(char *var, char *strval, size_t val_len, zval *track_vars_array, zend_bool override_protection) /* {{{ */ +{ + if (override_protection || !is_protected_variable(var)) { + php_register_variable_safe(var, strval, val_len, track_vars_array); + } +} +/* }}} */ + +static void safe_php_register_variable_ex(char *var, zval *val, zval *track_vars_array, zend_bool override_protection) /* {{{ */ +{ + if (override_protection || !is_protected_variable(var)) { + php_register_variable_ex(var, val, track_vars_array); + } +} +/* }}} */ + +static void register_http_post_files_variable(char *strvar, char *val, zval *http_post_files, zend_bool override_protection) /* {{{ */ +{ + safe_php_register_variable(strvar, val, strlen(val), http_post_files, override_protection); +} +/* }}} */ + +static void register_http_post_files_variable_ex(char *var, zval *val, zval *http_post_files, zend_bool override_protection) /* {{{ */ +{ + safe_php_register_variable_ex(var, val, http_post_files, override_protection); +} +/* }}} */ + +static void free_filename(zval *el) { + zend_string *filename = Z_STR_P(el); + zend_string_release_ex(filename, 0); +} + +PHPAPI void destroy_uploaded_files_hash(void) /* {{{ */ +{ + zval *el; + + ZEND_HASH_FOREACH_VAL(SG(rfc1867_uploaded_files), el) { + zend_string *filename = Z_STR_P(el); + VCWD_UNLINK(ZSTR_VAL(filename)); + } ZEND_HASH_FOREACH_END(); + zend_hash_destroy(SG(rfc1867_uploaded_files)); + FREE_HASHTABLE(SG(rfc1867_uploaded_files)); +} +/* }}} */ + +/* {{{ Following code is based on apache_multipart_buffer.c from libapreq-0.33 package. */ + +#define FILLUNIT (1024 * 5) + +typedef struct { + + /* read buffer */ + char *buffer; + char *buf_begin; + int bufsize; + int bytes_in_buffer; + + /* boundary info */ + char *boundary; + char *boundary_next; + int boundary_next_len; + + const zend_encoding *input_encoding; + const zend_encoding **detect_order; + size_t detect_order_size; +} multipart_buffer; + +typedef struct { + char *key; + char *value; +} mime_header_entry; + +/* + * Fill up the buffer with client data. + * Returns number of bytes added to buffer. + */ +static int fill_buffer(multipart_buffer *self) +{ + int bytes_to_read, total_read = 0, actual_read = 0; + + /* shift the existing data if necessary */ + if (self->bytes_in_buffer > 0 && self->buf_begin != self->buffer) { + memmove(self->buffer, self->buf_begin, self->bytes_in_buffer); + } + + self->buf_begin = self->buffer; + + /* calculate the free space in the buffer */ + bytes_to_read = self->bufsize - self->bytes_in_buffer; + + /* read the required number of bytes */ + while (bytes_to_read > 0) { + + char *buf = self->buffer + self->bytes_in_buffer; + + actual_read = (int)sapi_module.read_post(buf, bytes_to_read); + + /* update the buffer length */ + if (actual_read > 0) { + self->bytes_in_buffer += actual_read; + SG(read_post_bytes) += actual_read; + total_read += actual_read; + bytes_to_read -= actual_read; + } else { + break; + } + } + + return total_read; +} + +/* eof if we are out of bytes, or if we hit the final boundary */ +static int multipart_buffer_eof(multipart_buffer *self) +{ + return self->bytes_in_buffer == 0 && fill_buffer(self) < 1; +} + +/* create new multipart_buffer structure */ +static multipart_buffer *multipart_buffer_new(char *boundary, int boundary_len) +{ + multipart_buffer *self = (multipart_buffer *) ecalloc(1, sizeof(multipart_buffer)); + + int minsize = boundary_len + 6; + if (minsize < FILLUNIT) minsize = FILLUNIT; + + self->buffer = (char *) ecalloc(1, minsize + 1); + self->bufsize = minsize; + + spprintf(&self->boundary, 0, "--%s", boundary); + + self->boundary_next_len = (int)spprintf(&self->boundary_next, 0, "\n--%s", boundary); + + self->buf_begin = self->buffer; + self->bytes_in_buffer = 0; + + if (php_rfc1867_encoding_translation()) { + php_rfc1867_get_detect_order(&self->detect_order, &self->detect_order_size); + } else { + self->detect_order = NULL; + self->detect_order_size = 0; + } + + self->input_encoding = NULL; + + return self; +} + +/* + * Gets the next CRLF terminated line from the input buffer. + * If it doesn't find a CRLF, and the buffer isn't completely full, returns + * NULL; otherwise, returns the beginning of the null-terminated line, + * minus the CRLF. + * + * Note that we really just look for LF terminated lines. This works + * around a bug in internet explorer for the macintosh which sends mime + * boundaries that are only LF terminated when you use an image submit + * button in a multipart/form-data form. + */ +static char *next_line(multipart_buffer *self) +{ + /* look for LF in the data */ + char* line = self->buf_begin; + char* ptr = memchr(self->buf_begin, '\n', self->bytes_in_buffer); + + if (ptr) { /* LF found */ + + /* terminate the string, remove CRLF */ + if ((ptr - line) > 0 && *(ptr-1) == '\r') { + *(ptr-1) = 0; + } else { + *ptr = 0; + } + + /* bump the pointer */ + self->buf_begin = ptr + 1; + self->bytes_in_buffer -= (self->buf_begin - line); + + } else { /* no LF found */ + + /* buffer isn't completely full, fail */ + if (self->bytes_in_buffer < self->bufsize) { + return NULL; + } + /* return entire buffer as a partial line */ + line[self->bufsize] = 0; + self->buf_begin = ptr; + self->bytes_in_buffer = 0; + } + + return line; +} + +/* Returns the next CRLF terminated line from the client */ +static char *get_line(multipart_buffer *self) +{ + char* ptr = next_line(self); + + if (!ptr) { + fill_buffer(self); + ptr = next_line(self); + } + + return ptr; +} + +/* Free header entry */ +static void php_free_hdr_entry(mime_header_entry *h) +{ + if (h->key) { + efree(h->key); + } + if (h->value) { + efree(h->value); + } +} + +/* finds a boundary */ +static int find_boundary(multipart_buffer *self, char *boundary) +{ + char *line; + + /* loop through lines */ + while( (line = get_line(self)) ) + { + /* finished if we found the boundary */ + if (!strcmp(line, boundary)) { + return 1; + } + } + + /* didn't find the boundary */ + return 0; +} + +/* parse headers */ +static int multipart_buffer_headers(multipart_buffer *self, zend_llist *header) +{ + char *line; + mime_header_entry entry = {0}; + smart_string buf_value = {0}; + char *key = NULL; + + /* didn't find boundary, abort */ + if (!find_boundary(self, self->boundary)) { + return 0; + } + + /* get lines of text, or CRLF_CRLF */ + + while ((line = get_line(self)) && line[0] != '\0') { + /* add header to table */ + char *value = NULL; + + if (php_rfc1867_encoding_translation()) { + self->input_encoding = zend_multibyte_encoding_detector((const unsigned char *) line, strlen(line), self->detect_order, self->detect_order_size); + } + + /* space in the beginning means same header */ + if (!isspace(line[0])) { + value = strchr(line, ':'); + } + + if (value) { + if (buf_value.c && key) { + /* new entry, add the old one to the list */ + smart_string_0(&buf_value); + entry.key = key; + entry.value = buf_value.c; + zend_llist_add_element(header, &entry); + buf_value.c = NULL; + key = NULL; + } + + *value = '\0'; + do { value++; } while (isspace(*value)); + + key = estrdup(line); + smart_string_appends(&buf_value, value); + } else if (buf_value.c) { /* If no ':' on the line, add to previous line */ + smart_string_appends(&buf_value, line); + } else { + continue; + } + } + + if (buf_value.c && key) { + /* add the last one to the list */ + smart_string_0(&buf_value); + entry.key = key; + entry.value = buf_value.c; + zend_llist_add_element(header, &entry); + } + + return 1; +} + +static char *php_mime_get_hdr_value(zend_llist header, char *key) +{ + mime_header_entry *entry; + + if (key == NULL) { + return NULL; + } + + entry = zend_llist_get_first(&header); + while (entry) { + if (!strcasecmp(entry->key, key)) { + return entry->value; + } + entry = zend_llist_get_next(&header); + } + + return NULL; +} + +static char *php_ap_getword(const zend_encoding *encoding, char **line, char stop) +{ + char *pos = *line, quote; + char *res; + + while (*pos && *pos != stop) { + if ((quote = *pos) == '"' || quote == '\'') { + ++pos; + while (*pos && *pos != quote) { + if (*pos == '\\' && pos[1] && pos[1] == quote) { + pos += 2; + } else { + ++pos; + } + } + if (*pos) { + ++pos; + } + } else ++pos; + } + if (*pos == '\0') { + res = estrdup(*line); + *line += strlen(*line); + return res; + } + + res = estrndup(*line, pos - *line); + + while (*pos == stop) { + ++pos; + } + + *line = pos; + return res; +} + +static char *substring_conf(char *start, int len, char quote) +{ + char *result = emalloc(len + 1); + char *resp = result; + int i; + + for (i = 0; i < len && start[i] != quote; ++i) { + if (start[i] == '\\' && (start[i + 1] == '\\' || (quote && start[i + 1] == quote))) { + *resp++ = start[++i]; + } else { + *resp++ = start[i]; + } + } + + *resp = '\0'; + return result; +} + +static char *php_ap_getword_conf(const zend_encoding *encoding, char *str) +{ + while (*str && isspace(*str)) { + ++str; + } + + if (!*str) { + return estrdup(""); + } + + if (*str == '"' || *str == '\'') { + char quote = *str; + + str++; + return substring_conf(str, (int)strlen(str), quote); + } else { + char *strend = str; + + while (*strend && !isspace(*strend)) { + ++strend; + } + return substring_conf(str, strend - str, 0); + } +} + +static char *php_ap_basename(const zend_encoding *encoding, char *path) +{ + char *s = strrchr(path, '\\'); + char *s2 = strrchr(path, '/'); + + if (s && s2) { + if (s > s2) { + ++s; + } else { + s = ++s2; + } + return s; + } else if (s) { + return ++s; + } else if (s2) { + return ++s2; + } + return path; +} + +/* + * Search for a string in a fixed-length byte string. + * If partial is true, partial matches are allowed at the end of the buffer. + * Returns NULL if not found, or a pointer to the start of the first match. + */ +static void *php_ap_memstr(char *haystack, int haystacklen, char *needle, int needlen, int partial) +{ + int len = haystacklen; + char *ptr = haystack; + + /* iterate through first character matches */ + while( (ptr = memchr(ptr, needle[0], len)) ) { + + /* calculate length after match */ + len = haystacklen - (ptr - (char *)haystack); + + /* done if matches up to capacity of buffer */ + if (memcmp(needle, ptr, needlen < len ? needlen : len) == 0 && (partial || len >= needlen)) { + break; + } + + /* next character */ + ptr++; len--; + } + + return ptr; +} + +/* read until a boundary condition */ +static size_t multipart_buffer_read(multipart_buffer *self, char *buf, size_t bytes, int *end) +{ + size_t len, max; + char *bound; + + /* fill buffer if needed */ + if (bytes > (size_t)self->bytes_in_buffer) { + fill_buffer(self); + } + + /* look for a potential boundary match, only read data up to that point */ + if ((bound = php_ap_memstr(self->buf_begin, self->bytes_in_buffer, self->boundary_next, self->boundary_next_len, 1))) { + max = bound - self->buf_begin; + if (end && php_ap_memstr(self->buf_begin, self->bytes_in_buffer, self->boundary_next, self->boundary_next_len, 0)) { + *end = 1; + } + } else { + max = self->bytes_in_buffer; + } + + /* maximum number of bytes we are reading */ + len = max < bytes-1 ? max : bytes-1; + + /* if we read any data... */ + if (len > 0) { + + /* copy the data */ + memcpy(buf, self->buf_begin, len); + buf[len] = 0; + + if (bound && len > 0 && buf[len-1] == '\r') { + buf[--len] = 0; + } + + /* update the buffer */ + self->bytes_in_buffer -= (int)len; + self->buf_begin += len; + } + + return len; +} + +/* + XXX: this is horrible memory-usage-wise, but we only expect + to do this on small pieces of form data. +*/ +static char *multipart_buffer_read_body(multipart_buffer *self, size_t *len) +{ + char buf[FILLUNIT], *out=NULL; + size_t total_bytes=0, read_bytes=0; + + while((read_bytes = multipart_buffer_read(self, buf, sizeof(buf), NULL))) { + out = erealloc(out, total_bytes + read_bytes + 1); + memcpy(out + total_bytes, buf, read_bytes); + total_bytes += read_bytes; + } + + if (out) { + out[total_bytes] = '\0'; + } + *len = total_bytes; + + return out; +} +/* }}} */ + +/* + * The combined READER/HANDLER + * + */ + +SAPI_API SAPI_POST_HANDLER_FUNC(rfc1867_post_handler) /* {{{ */ +{ + char *boundary, *s = NULL, *boundary_end = NULL, *start_arr = NULL, *array_index = NULL; + char *lbuf = NULL, *abuf = NULL; + zend_string *temp_filename = NULL; + int boundary_len = 0, cancel_upload = 0, is_arr_upload = 0; + size_t array_len = 0; + int64_t total_bytes = 0, max_file_size = 0; + int skip_upload = 0, anonindex = 0, is_anonymous; + HashTable *uploaded_files = NULL; + multipart_buffer *mbuff; + zval *array_ptr = (zval *) arg; + int fd = -1; + zend_llist header; + void *event_extra_data = NULL; + unsigned int llen = 0; + int upload_cnt = INI_INT("max_file_uploads"); + const zend_encoding *internal_encoding = zend_multibyte_get_internal_encoding(); + php_rfc1867_getword_t getword; + php_rfc1867_getword_conf_t getword_conf; + php_rfc1867_basename_t _basename; + zend_long count = 0; + + if (php_rfc1867_encoding_translation() && internal_encoding) { + getword = php_rfc1867_getword; + getword_conf = php_rfc1867_getword_conf; + _basename = php_rfc1867_basename; + } else { + getword = php_ap_getword; + getword_conf = php_ap_getword_conf; + _basename = php_ap_basename; + } + + if (SG(post_max_size) > 0 && SG(request_info).content_length > SG(post_max_size)) { + sapi_module.sapi_error(E_WARNING, "POST Content-Length of " ZEND_LONG_FMT " bytes exceeds the limit of " ZEND_LONG_FMT " bytes", SG(request_info).content_length, SG(post_max_size)); + return; + } + + /* Get the boundary */ + boundary = strstr(content_type_dup, "boundary"); + if (!boundary) { + int content_type_len = (int)strlen(content_type_dup); + char *content_type_lcase = estrndup(content_type_dup, content_type_len); + + php_strtolower(content_type_lcase, content_type_len); + boundary = strstr(content_type_lcase, "boundary"); + if (boundary) { + boundary = content_type_dup + (boundary - content_type_lcase); + } + efree(content_type_lcase); + } + + if (!boundary || !(boundary = strchr(boundary, '='))) { + sapi_module.sapi_error(E_WARNING, "Missing boundary in multipart/form-data POST data"); + return; + } + + boundary++; + boundary_len = (int)strlen(boundary); + + if (boundary[0] == '"') { + boundary++; + boundary_end = strchr(boundary, '"'); + if (!boundary_end) { + sapi_module.sapi_error(E_WARNING, "Invalid boundary in multipart/form-data POST data"); + return; + } + } else { + /* search for the end of the boundary */ + boundary_end = strpbrk(boundary, ",;"); + } + if (boundary_end) { + boundary_end[0] = '\0'; + boundary_len = boundary_end-boundary; + } + + /* Initialize the buffer */ + if (!(mbuff = multipart_buffer_new(boundary, boundary_len))) { + sapi_module.sapi_error(E_WARNING, "Unable to initialize the input buffer"); + return; + } + + /* Initialize $_FILES[] */ + zend_hash_init(&PG(rfc1867_protected_variables), 8, NULL, NULL, 0); + + ALLOC_HASHTABLE(uploaded_files); + zend_hash_init(uploaded_files, 8, NULL, free_filename, 0); + SG(rfc1867_uploaded_files) = uploaded_files; + + if (Z_TYPE(PG(http_globals)[TRACK_VARS_FILES]) != IS_ARRAY) { + /* php_auto_globals_create_files() might have already done that */ + array_init(&PG(http_globals)[TRACK_VARS_FILES]); + } + + zend_llist_init(&header, sizeof(mime_header_entry), (llist_dtor_func_t) php_free_hdr_entry, 0); + + if (php_rfc1867_callback != NULL) { + multipart_event_start event_start; + + event_start.content_length = SG(request_info).content_length; + if (php_rfc1867_callback(MULTIPART_EVENT_START, &event_start, &event_extra_data) == FAILURE) { + goto fileupload_done; + } + } + + while (!multipart_buffer_eof(mbuff)) + { + char buff[FILLUNIT]; + char *cd = NULL, *param = NULL, *filename = NULL, *tmp = NULL; + size_t blen = 0, wlen = 0; + zend_off_t offset; + + zend_llist_clean(&header); + + if (!multipart_buffer_headers(mbuff, &header)) { + goto fileupload_done; + } + + if ((cd = php_mime_get_hdr_value(header, "Content-Disposition"))) { + char *pair = NULL; + int end = 0; + + while (isspace(*cd)) { + ++cd; + } + + while (*cd && (pair = getword(mbuff->input_encoding, &cd, ';'))) + { + char *key = NULL, *word = pair; + + while (isspace(*cd)) { + ++cd; + } + + if (strchr(pair, '=')) { + key = getword(mbuff->input_encoding, &pair, '='); + + if (!strcasecmp(key, "name")) { + if (param) { + efree(param); + } + param = getword_conf(mbuff->input_encoding, pair); + if (mbuff->input_encoding && internal_encoding) { + unsigned char *new_param; + size_t new_param_len; + if ((size_t)-1 != zend_multibyte_encoding_converter(&new_param, &new_param_len, (unsigned char *)param, strlen(param), internal_encoding, mbuff->input_encoding)) { + efree(param); + param = (char *)new_param; + } + } + } else if (!strcasecmp(key, "filename")) { + if (filename) { + efree(filename); + } + filename = getword_conf(mbuff->input_encoding, pair); + if (mbuff->input_encoding && internal_encoding) { + unsigned char *new_filename; + size_t new_filename_len; + if ((size_t)-1 != zend_multibyte_encoding_converter(&new_filename, &new_filename_len, (unsigned char *)filename, strlen(filename), internal_encoding, mbuff->input_encoding)) { + efree(filename); + filename = (char *)new_filename; + } + } + } + } + if (key) { + efree(key); + } + efree(word); + } + + /* Normal form variable, safe to read all data into memory */ + if (!filename && param) { + size_t value_len; + char *value = multipart_buffer_read_body(mbuff, &value_len); + size_t new_val_len; /* Dummy variable */ + + if (!value) { + value = estrdup(""); + value_len = 0; + } + + if (mbuff->input_encoding && internal_encoding) { + unsigned char *new_value; + size_t new_value_len; + if ((size_t)-1 != zend_multibyte_encoding_converter(&new_value, &new_value_len, (unsigned char *)value, value_len, internal_encoding, mbuff->input_encoding)) { + efree(value); + value = (char *)new_value; + value_len = new_value_len; + } + } + + if (++count <= PG(max_input_vars) && sapi_module.input_filter(PARSE_POST, param, &value, value_len, &new_val_len)) { + if (php_rfc1867_callback != NULL) { + multipart_event_formdata event_formdata; + size_t newlength = new_val_len; + + event_formdata.post_bytes_processed = SG(read_post_bytes); + event_formdata.name = param; + event_formdata.value = &value; + event_formdata.length = new_val_len; + event_formdata.newlength = &newlength; + if (php_rfc1867_callback(MULTIPART_EVENT_FORMDATA, &event_formdata, &event_extra_data) == FAILURE) { + efree(param); + efree(value); + continue; + } + new_val_len = newlength; + } + safe_php_register_variable(param, value, new_val_len, array_ptr, 0); + } else { + if (count == PG(max_input_vars) + 1) { + php_error_docref(NULL, E_WARNING, "Input variables exceeded " ZEND_LONG_FMT ". To increase the limit change max_input_vars in php.ini.", PG(max_input_vars)); + } + + if (php_rfc1867_callback != NULL) { + multipart_event_formdata event_formdata; + + event_formdata.post_bytes_processed = SG(read_post_bytes); + event_formdata.name = param; + event_formdata.value = &value; + event_formdata.length = value_len; + event_formdata.newlength = NULL; + php_rfc1867_callback(MULTIPART_EVENT_FORMDATA, &event_formdata, &event_extra_data); + } + } + + if (!strcasecmp(param, "MAX_FILE_SIZE")) { +#ifdef HAVE_ATOLL + max_file_size = atoll(value); +#else + max_file_size = strtoll(value, NULL, 10); +#endif + } + + efree(param); + efree(value); + continue; + } + + /* If file_uploads=off, skip the file part */ + if (!PG(file_uploads)) { + skip_upload = 1; + } else if (upload_cnt <= 0) { + skip_upload = 1; + sapi_module.sapi_error(E_WARNING, "Maximum number of allowable file uploads has been exceeded"); + } + + /* Return with an error if the posted data is garbled */ + if (!param && !filename) { + sapi_module.sapi_error(E_WARNING, "File Upload Mime headers garbled"); + goto fileupload_done; + } + + if (!param) { + is_anonymous = 1; + param = emalloc(MAX_SIZE_ANONNAME); + snprintf(param, MAX_SIZE_ANONNAME, "%u", anonindex++); + } else { + is_anonymous = 0; + } + + /* New Rule: never repair potential malicious user input */ + if (!skip_upload) { + long c = 0; + tmp = param; + + while (*tmp) { + if (*tmp == '[') { + c++; + } else if (*tmp == ']') { + c--; + if (tmp[1] && tmp[1] != '[') { + skip_upload = 1; + break; + } + } + if (c < 0) { + skip_upload = 1; + break; + } + tmp++; + } + /* Brackets should always be closed */ + if(c != 0) { + skip_upload = 1; + } + } + + total_bytes = cancel_upload = 0; + temp_filename = NULL; + fd = -1; + + if (!skip_upload && php_rfc1867_callback != NULL) { + multipart_event_file_start event_file_start; + + event_file_start.post_bytes_processed = SG(read_post_bytes); + event_file_start.name = param; + event_file_start.filename = &filename; + if (php_rfc1867_callback(MULTIPART_EVENT_FILE_START, &event_file_start, &event_extra_data) == FAILURE) { + temp_filename = NULL; + efree(param); + efree(filename); + continue; + } + } + + if (skip_upload) { + efree(param); + efree(filename); + continue; + } + + if (filename[0] == '\0') { +#if DEBUG_FILE_UPLOAD + sapi_module.sapi_error(E_NOTICE, "No file uploaded"); +#endif + cancel_upload = UPLOAD_ERROR_D; + } + + offset = 0; + end = 0; + + if (!cancel_upload) { + /* only bother to open temp file if we have data */ + blen = multipart_buffer_read(mbuff, buff, sizeof(buff), &end); +#if DEBUG_FILE_UPLOAD + if (blen > 0) { +#else + /* in non-debug mode we have no problem with 0-length files */ + { +#endif + fd = php_open_temporary_fd_ex(PG(upload_tmp_dir), "php", &temp_filename, PHP_TMP_FILE_OPEN_BASEDIR_CHECK_ON_FALLBACK); + upload_cnt--; + if (fd == -1) { + sapi_module.sapi_error(E_WARNING, "File upload error - unable to create a temporary file"); + cancel_upload = UPLOAD_ERROR_E; + } + } + } + + while (!cancel_upload && (blen > 0)) + { + if (php_rfc1867_callback != NULL) { + multipart_event_file_data event_file_data; + + event_file_data.post_bytes_processed = SG(read_post_bytes); + event_file_data.offset = offset; + event_file_data.data = buff; + event_file_data.length = blen; + event_file_data.newlength = &blen; + if (php_rfc1867_callback(MULTIPART_EVENT_FILE_DATA, &event_file_data, &event_extra_data) == FAILURE) { + cancel_upload = UPLOAD_ERROR_X; + continue; + } + } + + if (PG(upload_max_filesize) > 0 && (zend_long)(total_bytes+blen) > PG(upload_max_filesize)) { +#if DEBUG_FILE_UPLOAD + sapi_module.sapi_error(E_NOTICE, "upload_max_filesize of " ZEND_LONG_FMT " bytes exceeded - file [%s=%s] not saved", PG(upload_max_filesize), param, filename); +#endif + cancel_upload = UPLOAD_ERROR_A; + } else if (max_file_size && ((zend_long)(total_bytes+blen) > max_file_size)) { +#if DEBUG_FILE_UPLOAD + sapi_module.sapi_error(E_NOTICE, "MAX_FILE_SIZE of %" PRId64 " bytes exceeded - file [%s=%s] not saved", max_file_size, param, filename); +#endif + cancel_upload = UPLOAD_ERROR_B; + } else if (blen > 0) { +#ifdef PHP_WIN32 + wlen = write(fd, buff, (unsigned int)blen); +#else + wlen = write(fd, buff, blen); +#endif + + if (wlen == (size_t)-1) { + /* write failed */ +#if DEBUG_FILE_UPLOAD + sapi_module.sapi_error(E_NOTICE, "write() failed - %s", strerror(errno)); +#endif + cancel_upload = UPLOAD_ERROR_F; + } else if (wlen < blen) { +#if DEBUG_FILE_UPLOAD + sapi_module.sapi_error(E_NOTICE, "Only %zd bytes were written, expected to write %zd", wlen, blen); +#endif + cancel_upload = UPLOAD_ERROR_F; + } else { + total_bytes += wlen; + } + offset += wlen; + } + + /* read data for next iteration */ + blen = multipart_buffer_read(mbuff, buff, sizeof(buff), &end); + } + + if (fd != -1) { /* may not be initialized if file could not be created */ + close(fd); + } + + if (!cancel_upload && !end) { +#if DEBUG_FILE_UPLOAD + sapi_module.sapi_error(E_NOTICE, "Missing mime boundary at the end of the data for file %s", filename[0] != '\0' ? filename : ""); +#endif + cancel_upload = UPLOAD_ERROR_C; + } +#if DEBUG_FILE_UPLOAD + if (filename[0] != '\0' && total_bytes == 0 && !cancel_upload) { + sapi_module.sapi_error(E_WARNING, "Uploaded file size 0 - file [%s=%s] not saved", param, filename); + cancel_upload = 5; + } +#endif + if (php_rfc1867_callback != NULL) { + multipart_event_file_end event_file_end; + + event_file_end.post_bytes_processed = SG(read_post_bytes); + event_file_end.temp_filename = temp_filename ? ZSTR_VAL(temp_filename) : NULL; + event_file_end.cancel_upload = cancel_upload; + if (php_rfc1867_callback(MULTIPART_EVENT_FILE_END, &event_file_end, &event_extra_data) == FAILURE) { + cancel_upload = UPLOAD_ERROR_X; + } + } + + if (cancel_upload) { + if (temp_filename) { + if (cancel_upload != UPLOAD_ERROR_E) { /* file creation failed */ + unlink(ZSTR_VAL(temp_filename)); + } + zend_string_release_ex(temp_filename, 0); + } + temp_filename = NULL; + } else { + zend_hash_add_ptr(SG(rfc1867_uploaded_files), temp_filename, temp_filename); + } + + /* is_arr_upload is true when name of file upload field + * ends in [.*] + * start_arr is set to point to 1st [ */ + is_arr_upload = (start_arr = strchr(param,'[')) && (param[strlen(param)-1] == ']'); + + if (is_arr_upload) { + array_len = strlen(start_arr); + if (array_index) { + efree(array_index); + } + array_index = estrndup(start_arr + 1, array_len - 2); + } + + /* Add $foo_name */ + if (llen < strlen(param) + MAX_SIZE_OF_INDEX + 1) { + llen = (int)strlen(param); + lbuf = (char *) safe_erealloc(lbuf, llen, 1, MAX_SIZE_OF_INDEX + 1); + llen += MAX_SIZE_OF_INDEX + 1; + } + + if (is_arr_upload) { + if (abuf) efree(abuf); + abuf = estrndup(param, strlen(param)-array_len); + snprintf(lbuf, llen, "%s_name[%s]", abuf, array_index); + } else { + snprintf(lbuf, llen, "%s_name", param); + } + + /* The \ check should technically be needed for win32 systems only where + * it is a valid path separator. However, IE in all it's wisdom always sends + * the full path of the file on the user's filesystem, which means that unless + * the user does basename() they get a bogus file name. Until IE's user base drops + * to nill or problem is fixed this code must remain enabled for all systems. */ + s = _basename(internal_encoding, filename); + if (!s) { + s = filename; + } + + if (!is_anonymous) { + safe_php_register_variable(lbuf, s, strlen(s), NULL, 0); + } + + /* Add $foo[name] */ + if (is_arr_upload) { + snprintf(lbuf, llen, "%s[name][%s]", abuf, array_index); + } else { + snprintf(lbuf, llen, "%s[name]", param); + } + register_http_post_files_variable(lbuf, s, &PG(http_globals)[TRACK_VARS_FILES], 0); + efree(filename); + s = NULL; + + /* Possible Content-Type: */ + if (cancel_upload || !(cd = php_mime_get_hdr_value(header, "Content-Type"))) { + cd = ""; + } else { + /* fix for Opera 6.01 */ + s = strchr(cd, ';'); + if (s != NULL) { + *s = '\0'; + } + } + + /* Add $foo_type */ + if (is_arr_upload) { + snprintf(lbuf, llen, "%s_type[%s]", abuf, array_index); + } else { + snprintf(lbuf, llen, "%s_type", param); + } + if (!is_anonymous) { + safe_php_register_variable(lbuf, cd, strlen(cd), NULL, 0); + } + + /* Add $foo[type] */ + if (is_arr_upload) { + snprintf(lbuf, llen, "%s[type][%s]", abuf, array_index); + } else { + snprintf(lbuf, llen, "%s[type]", param); + } + register_http_post_files_variable(lbuf, cd, &PG(http_globals)[TRACK_VARS_FILES], 0); + + /* Restore Content-Type Header */ + if (s != NULL) { + *s = ';'; + } + s = ""; + + { + /* store temp_filename as-is (in case upload_tmp_dir + * contains escapeable characters. escape only the variable name.) */ + zval zfilename; + + /* Initialize variables */ + add_protected_variable(param); + + /* if param is of form xxx[.*] this will cut it to xxx */ + if (!is_anonymous) { + if (temp_filename) { + ZVAL_STR_COPY(&zfilename, temp_filename); + } else { + ZVAL_EMPTY_STRING(&zfilename); + } + safe_php_register_variable_ex(param, &zfilename, NULL, 1); + } + + /* Add $foo[tmp_name] */ + if (is_arr_upload) { + snprintf(lbuf, llen, "%s[tmp_name][%s]", abuf, array_index); + } else { + snprintf(lbuf, llen, "%s[tmp_name]", param); + } + add_protected_variable(lbuf); + if (temp_filename) { + ZVAL_STR_COPY(&zfilename, temp_filename); + } else { + ZVAL_EMPTY_STRING(&zfilename); + } + register_http_post_files_variable_ex(lbuf, &zfilename, &PG(http_globals)[TRACK_VARS_FILES], 1); + } + + { + zval file_size, error_type; + int size_overflow = 0; + char file_size_buf[65]; + + ZVAL_LONG(&error_type, cancel_upload); + + /* Add $foo[error] */ + if (cancel_upload) { + ZVAL_LONG(&file_size, 0); + } else { + if (total_bytes > ZEND_LONG_MAX) { +#ifdef PHP_WIN32 + if (_i64toa_s(total_bytes, file_size_buf, 65, 10)) { + file_size_buf[0] = '0'; + file_size_buf[1] = '\0'; + } +#else + { + int __len = snprintf(file_size_buf, 65, "%" PRId64, total_bytes); + file_size_buf[__len] = '\0'; + } +#endif + size_overflow = 1; + + } else { + ZVAL_LONG(&file_size, total_bytes); + } + } + + if (is_arr_upload) { + snprintf(lbuf, llen, "%s[error][%s]", abuf, array_index); + } else { + snprintf(lbuf, llen, "%s[error]", param); + } + register_http_post_files_variable_ex(lbuf, &error_type, &PG(http_globals)[TRACK_VARS_FILES], 0); + + /* Add $foo_size */ + if (is_arr_upload) { + snprintf(lbuf, llen, "%s_size[%s]", abuf, array_index); + } else { + snprintf(lbuf, llen, "%s_size", param); + } + if (!is_anonymous) { + if (size_overflow) { + ZVAL_STRING(&file_size, file_size_buf); + } + safe_php_register_variable_ex(lbuf, &file_size, NULL, size_overflow); + } + + /* Add $foo[size] */ + if (is_arr_upload) { + snprintf(lbuf, llen, "%s[size][%s]", abuf, array_index); + } else { + snprintf(lbuf, llen, "%s[size]", param); + } + if (size_overflow) { + ZVAL_STRING(&file_size, file_size_buf); + } + register_http_post_files_variable_ex(lbuf, &file_size, &PG(http_globals)[TRACK_VARS_FILES], size_overflow); + } + efree(param); + } + } + +fileupload_done: + if (php_rfc1867_callback != NULL) { + multipart_event_end event_end; + + event_end.post_bytes_processed = SG(read_post_bytes); + php_rfc1867_callback(MULTIPART_EVENT_END, &event_end, &event_extra_data); + } + + if (lbuf) efree(lbuf); + if (abuf) efree(abuf); + if (array_index) efree(array_index); + zend_hash_destroy(&PG(rfc1867_protected_variables)); + zend_llist_destroy(&header); + if (mbuff->boundary_next) efree(mbuff->boundary_next); + if (mbuff->boundary) efree(mbuff->boundary); + if (mbuff->buffer) efree(mbuff->buffer); + if (mbuff) efree(mbuff); +} +/* }}} */ + +SAPI_API void php_rfc1867_set_multibyte_callbacks( + php_rfc1867_encoding_translation_t encoding_translation, + php_rfc1867_get_detect_order_t get_detect_order, + php_rfc1867_set_input_encoding_t set_input_encoding, + php_rfc1867_getword_t getword, + php_rfc1867_getword_conf_t getword_conf, + php_rfc1867_basename_t basename) /* {{{ */ +{ + php_rfc1867_encoding_translation = encoding_translation; + php_rfc1867_get_detect_order = get_detect_order; + php_rfc1867_set_input_encoding = set_input_encoding; + php_rfc1867_getword = getword; + php_rfc1867_getword_conf = getword_conf; + php_rfc1867_basename = basename; +} +/* }}} */ diff --git a/sapi/cgi/cgi_main.c b/sapi/cgi/cgi_main.c index cd79475fde4d0..4e210d9215205 100644 --- a/sapi/cgi/cgi_main.c +++ b/sapi/cgi/cgi_main.c @@ -1927,18 +1927,17 @@ int main(int argc, char *argv[]) /* check force_cgi after startup, so we have proper output */ if (cgi && CGIG(force_redirect)) { - /* Apache will generate REDIRECT_STATUS, - * Netscape and redirect.so will generate HTTP_REDIRECT_STATUS. - * redirect.so and installation instructions available from - * http://www.koehntopp.de/php. - * -- kk@netuse.de - */ - if (!getenv("REDIRECT_STATUS") && - !getenv ("HTTP_REDIRECT_STATUS") && - /* this is to allow a different env var to be configured - * in case some server does something different than above */ - (!CGIG(redirect_status_env) || !getenv(CGIG(redirect_status_env))) - ) { + /* This is to allow a different environment variable to be configured + * in case the we cannot auto-detect which environment variable to use. + * Checking this first to allow user overrides in case the environment + * variable can be set by an untrusted party. */ + const char *redirect_status_env = CGIG(redirect_status_env); + if (!redirect_status_env) { + /* Apache will generate REDIRECT_STATUS. */ + redirect_status_env = "REDIRECT_STATUS"; + } + + if (!getenv(redirect_status_env)) { zend_try { SG(sapi_headers).http_response_code = 400; PUTS("Security Alert! The PHP CGI cannot be accessed directly.\n\n\ diff --git a/sapi/cgi/cgi_main.c.orig b/sapi/cgi/cgi_main.c.orig new file mode 100644 index 0000000000000..cd79475fde4d0 --- /dev/null +++ b/sapi/cgi/cgi_main.c.orig @@ -0,0 +1,2711 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 7 | + +----------------------------------------------------------------------+ + | Copyright (c) The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Rasmus Lerdorf | + | Stig Bakken | + | Zeev Suraski | + | FastCGI: Ben Mansell | + | Shane Caraveo | + | Dmitry Stogov | + +----------------------------------------------------------------------+ +*/ + +#include "php.h" +#include "php_globals.h" +#include "php_variables.h" +#include "zend_modules.h" + +#include "SAPI.h" + +#include + +#ifdef PHP_WIN32 +# include "win32/time.h" +# include "win32/signal.h" +# include "win32/winutil.h" +# include +#endif + +#if HAVE_SYS_TIME_H +# include +#endif + +#if HAVE_UNISTD_H +# include +#endif + +#include + +#include + +#if HAVE_SYS_TYPES_H +# include +#endif + +#if HAVE_SYS_WAIT_H +# include +#endif + +#include "zend.h" +#include "zend_extensions.h" +#include "php_ini.h" +#include "php_globals.h" +#include "php_main.h" +#include "fopen_wrappers.h" +#include "http_status_codes.h" +#include "ext/standard/php_standard.h" +#include "ext/standard/url.h" + +#ifdef PHP_WIN32 +# include +# include +# include "win32/php_registry.h" +#endif + +#ifdef __riscos__ +# include +int __riscosify_control = __RISCOSIFY_STRICT_UNIX_SPECS; +#endif + +#include "zend_compile.h" +#include "zend_execute.h" +#include "zend_highlight.h" + +#include "php_getopt.h" + +#include "fastcgi.h" + +#if defined(PHP_WIN32) && defined(HAVE_OPENSSL) +# include "openssl/applink.c" +#endif + +#ifdef HAVE_VALGRIND +# include "valgrind/callgrind.h" +#endif + +#ifndef PHP_WIN32 +/* XXX this will need to change later when threaded fastcgi is implemented. shane */ +struct sigaction act, old_term, old_quit, old_int; +#endif + +static void (*php_php_import_environment_variables)(zval *array_ptr); + +/* these globals used for forking children on unix systems */ +/** + * Number of child processes that will get created to service requests + */ +static int children = 0; + + +/** + * Set to non-zero if we are the parent process + */ +static int parent = 1; + +#ifndef PHP_WIN32 +/* Did parent received exit signals SIG_TERM/SIG_INT/SIG_QUIT */ +static int exit_signal = 0; + +/* Is Parent waiting for children to exit */ +static int parent_waiting = 0; + +/** + * Process group + */ +static pid_t pgroup; +#endif + +#define PHP_MODE_STANDARD 1 +#define PHP_MODE_HIGHLIGHT 2 +#define PHP_MODE_LINT 4 +#define PHP_MODE_STRIP 5 + +static char *php_optarg = NULL; +static int php_optind = 1; +static zend_module_entry cgi_module_entry; + +static const opt_struct OPTIONS[] = { + {'a', 0, "interactive"}, + {'b', 1, "bindpath"}, + {'C', 0, "no-chdir"}, + {'c', 1, "php-ini"}, + {'d', 1, "define"}, + {'e', 0, "profile-info"}, + {'f', 1, "file"}, + {'h', 0, "help"}, + {'i', 0, "info"}, + {'l', 0, "syntax-check"}, + {'m', 0, "modules"}, + {'n', 0, "no-php-ini"}, + {'q', 0, "no-header"}, + {'s', 0, "syntax-highlight"}, + {'s', 0, "syntax-highlighting"}, + {'w', 0, "strip"}, + {'?', 0, "usage"},/* help alias (both '?' and 'usage') */ + {'v', 0, "version"}, + {'z', 1, "zend-extension"}, + {'T', 1, "timing"}, + {'-', 0, NULL} /* end of args */ +}; + +typedef struct _php_cgi_globals_struct { + HashTable user_config_cache; + char *redirect_status_env; + zend_bool rfc2616_headers; + zend_bool nph; + zend_bool check_shebang_line; + zend_bool fix_pathinfo; + zend_bool force_redirect; + zend_bool discard_path; + zend_bool fcgi_logging; +#ifdef PHP_WIN32 + zend_bool impersonate; +#endif +} php_cgi_globals_struct; + +/* {{{ user_config_cache + * + * Key for each cache entry is dirname(PATH_TRANSLATED). + * + * NOTE: Each cache entry config_hash contains the combination from all user ini files found in + * the path starting from doc_root through to dirname(PATH_TRANSLATED). There is no point + * storing per-file entries as it would not be possible to detect added / deleted entries + * between separate files. + */ +typedef struct _user_config_cache_entry { + time_t expires; + HashTable *user_config; +} user_config_cache_entry; + +static void user_config_cache_entry_dtor(zval *el) +{ + user_config_cache_entry *entry = (user_config_cache_entry *)Z_PTR_P(el); + zend_hash_destroy(entry->user_config); + free(entry->user_config); + free(entry); +} +/* }}} */ + +#ifdef ZTS +static int php_cgi_globals_id; +#define CGIG(v) ZEND_TSRMG(php_cgi_globals_id, php_cgi_globals_struct *, v) +#if defined(PHP_WIN32) +ZEND_TSRMLS_CACHE_DEFINE() +#endif +#else +static php_cgi_globals_struct php_cgi_globals; +#define CGIG(v) (php_cgi_globals.v) +#endif + +#ifdef PHP_WIN32 +#define TRANSLATE_SLASHES(path) \ + { \ + char *tmp = path; \ + while (*tmp) { \ + if (*tmp == '\\') *tmp = '/'; \ + tmp++; \ + } \ + } +#else +#define TRANSLATE_SLASHES(path) +#endif + +#ifdef PHP_WIN32 +#define WIN32_MAX_SPAWN_CHILDREN 64 +HANDLE kid_cgi_ps[WIN32_MAX_SPAWN_CHILDREN]; +int kids, cleaning_up = 0; +HANDLE job = NULL; +JOBOBJECT_EXTENDED_LIMIT_INFORMATION job_info = { 0 }; +CRITICAL_SECTION cleanup_lock; +#endif + +#ifndef HAVE_ATTRIBUTE_WEAK +static void fcgi_log(int type, const char *format, ...) { + va_list ap; + + va_start(ap, format); + vfprintf(stderr, format, ap); + va_end(ap); +} +#endif + +static int module_name_cmp(const void *a, const void *b) +{ + Bucket *f = (Bucket *) a; + Bucket *s = (Bucket *) b; + + return strcasecmp( ((zend_module_entry *)Z_PTR(f->val))->name, + ((zend_module_entry *)Z_PTR(s->val))->name); +} + +static void print_modules(void) +{ + HashTable sorted_registry; + zend_module_entry *module; + + zend_hash_init(&sorted_registry, 64, NULL, NULL, 1); + zend_hash_copy(&sorted_registry, &module_registry, NULL); + zend_hash_sort(&sorted_registry, module_name_cmp, 0); + ZEND_HASH_FOREACH_PTR(&sorted_registry, module) { + php_printf("%s\n", module->name); + } ZEND_HASH_FOREACH_END(); + zend_hash_destroy(&sorted_registry); +} + +static int print_extension_info(zend_extension *ext, void *arg) +{ + php_printf("%s\n", ext->name); + return 0; +} + +static int extension_name_cmp(const zend_llist_element **f, const zend_llist_element **s) +{ + zend_extension *fe = (zend_extension*)(*f)->data; + zend_extension *se = (zend_extension*)(*s)->data; + return strcmp(fe->name, se->name); +} + +static void print_extensions(void) +{ + zend_llist sorted_exts; + + zend_llist_copy(&sorted_exts, &zend_extensions); + sorted_exts.dtor = NULL; + zend_llist_sort(&sorted_exts, extension_name_cmp); + zend_llist_apply_with_argument(&sorted_exts, (llist_apply_with_arg_func_t) print_extension_info, NULL); + zend_llist_destroy(&sorted_exts); +} + +#ifndef STDOUT_FILENO +#define STDOUT_FILENO 1 +#endif + +static inline size_t sapi_cgi_single_write(const char *str, size_t str_length) +{ +#ifdef PHP_WRITE_STDOUT + int ret; + + ret = write(STDOUT_FILENO, str, str_length); + if (ret <= 0) return 0; + return ret; +#else + size_t ret; + + ret = fwrite(str, 1, MIN(str_length, 16384), stdout); + return ret; +#endif +} + +static size_t sapi_cgi_ub_write(const char *str, size_t str_length) +{ + const char *ptr = str; + size_t remaining = str_length; + size_t ret; + + while (remaining > 0) { + ret = sapi_cgi_single_write(ptr, remaining); + if (!ret) { + php_handle_aborted_connection(); + return str_length - remaining; + } + ptr += ret; + remaining -= ret; + } + + return str_length; +} + +static size_t sapi_fcgi_ub_write(const char *str, size_t str_length) +{ + const char *ptr = str; + size_t remaining = str_length; + fcgi_request *request = (fcgi_request*) SG(server_context); + + while (remaining > 0) { + int to_write = remaining > INT_MAX ? INT_MAX : (int)remaining; + int ret = fcgi_write(request, FCGI_STDOUT, ptr, to_write); + + if (ret <= 0) { + php_handle_aborted_connection(); + return str_length - remaining; + } + ptr += ret; + remaining -= ret; + } + + return str_length; +} + +static void sapi_cgi_flush(void *server_context) +{ + if (fflush(stdout) == EOF) { + php_handle_aborted_connection(); + } +} + +static void sapi_fcgi_flush(void *server_context) +{ + fcgi_request *request = (fcgi_request*) server_context; + + if ( + !parent && + request && !fcgi_flush(request, 0)) { + + php_handle_aborted_connection(); + } +} + +#define SAPI_CGI_MAX_HEADER_LENGTH 1024 + +static int sapi_cgi_send_headers(sapi_headers_struct *sapi_headers) +{ + sapi_header_struct *h; + zend_llist_position pos; + zend_bool ignore_status = 0; + int response_status = SG(sapi_headers).http_response_code; + + if (SG(request_info).no_headers == 1) { + return SAPI_HEADER_SENT_SUCCESSFULLY; + } + + if (CGIG(nph) || SG(sapi_headers).http_response_code != 200) + { + int len; + zend_bool has_status = 0; + char buf[SAPI_CGI_MAX_HEADER_LENGTH]; + + if (CGIG(rfc2616_headers) && SG(sapi_headers).http_status_line) { + char *s; + len = slprintf(buf, SAPI_CGI_MAX_HEADER_LENGTH, "%s\r\n", SG(sapi_headers).http_status_line); + if ((s = strchr(SG(sapi_headers).http_status_line, ' '))) { + response_status = atoi((s + 1)); + } + + if (len > SAPI_CGI_MAX_HEADER_LENGTH) { + len = SAPI_CGI_MAX_HEADER_LENGTH; + } + + } else { + char *s; + + if (SG(sapi_headers).http_status_line && + (s = strchr(SG(sapi_headers).http_status_line, ' ')) != 0 && + (s - SG(sapi_headers).http_status_line) >= 5 && + strncasecmp(SG(sapi_headers).http_status_line, "HTTP/", 5) == 0 + ) { + len = slprintf(buf, sizeof(buf), "Status:%s\r\n", s); + response_status = atoi((s + 1)); + } else { + h = (sapi_header_struct*)zend_llist_get_first_ex(&sapi_headers->headers, &pos); + while (h) { + if (h->header_len > sizeof("Status:")-1 && + strncasecmp(h->header, "Status:", sizeof("Status:")-1) == 0 + ) { + has_status = 1; + break; + } + h = (sapi_header_struct*)zend_llist_get_next_ex(&sapi_headers->headers, &pos); + } + if (!has_status) { + http_response_status_code_pair *err = (http_response_status_code_pair*)http_status_map; + + while (err->code != 0) { + if (err->code == SG(sapi_headers).http_response_code) { + break; + } + err++; + } + if (err->str) { + len = slprintf(buf, sizeof(buf), "Status: %d %s\r\n", SG(sapi_headers).http_response_code, err->str); + } else { + len = slprintf(buf, sizeof(buf), "Status: %d\r\n", SG(sapi_headers).http_response_code); + } + } + } + } + + if (!has_status) { + PHPWRITE_H(buf, len); + ignore_status = 1; + } + } + + h = (sapi_header_struct*)zend_llist_get_first_ex(&sapi_headers->headers, &pos); + while (h) { + /* prevent CRLFCRLF */ + if (h->header_len) { + if (h->header_len > sizeof("Status:")-1 && + strncasecmp(h->header, "Status:", sizeof("Status:")-1) == 0 + ) { + if (!ignore_status) { + ignore_status = 1; + PHPWRITE_H(h->header, h->header_len); + PHPWRITE_H("\r\n", 2); + } + } else if (response_status == 304 && h->header_len > sizeof("Content-Type:")-1 && + strncasecmp(h->header, "Content-Type:", sizeof("Content-Type:")-1) == 0 + ) { + h = (sapi_header_struct*)zend_llist_get_next_ex(&sapi_headers->headers, &pos); + continue; + } else { + PHPWRITE_H(h->header, h->header_len); + PHPWRITE_H("\r\n", 2); + } + } + h = (sapi_header_struct*)zend_llist_get_next_ex(&sapi_headers->headers, &pos); + } + PHPWRITE_H("\r\n", 2); + + return SAPI_HEADER_SENT_SUCCESSFULLY; +} + +#ifndef STDIN_FILENO +# define STDIN_FILENO 0 +#endif + +static size_t sapi_cgi_read_post(char *buffer, size_t count_bytes) +{ + size_t read_bytes = 0; + int tmp_read_bytes; + size_t remaining_bytes; + + assert(SG(request_info).content_length >= SG(read_post_bytes)); + + remaining_bytes = (size_t)(SG(request_info).content_length - SG(read_post_bytes)); + + count_bytes = MIN(count_bytes, remaining_bytes); + while (read_bytes < count_bytes) { +#ifdef PHP_WIN32 + size_t diff = count_bytes - read_bytes; + unsigned int to_read = (diff > UINT_MAX) ? UINT_MAX : (unsigned int)diff; + + tmp_read_bytes = read(STDIN_FILENO, buffer + read_bytes, to_read); +#else + tmp_read_bytes = read(STDIN_FILENO, buffer + read_bytes, count_bytes - read_bytes); +#endif + if (tmp_read_bytes <= 0) { + break; + } + read_bytes += tmp_read_bytes; + } + return read_bytes; +} + +static size_t sapi_fcgi_read_post(char *buffer, size_t count_bytes) +{ + size_t read_bytes = 0; + int tmp_read_bytes; + fcgi_request *request = (fcgi_request*) SG(server_context); + size_t remaining = SG(request_info).content_length - SG(read_post_bytes); + + if (remaining < count_bytes) { + count_bytes = remaining; + } + while (read_bytes < count_bytes) { + size_t diff = count_bytes - read_bytes; + int to_read = (diff > INT_MAX) ? INT_MAX : (int)diff; + + tmp_read_bytes = fcgi_read(request, buffer + read_bytes, to_read); + if (tmp_read_bytes <= 0) { + break; + } + read_bytes += tmp_read_bytes; + } + return read_bytes; +} + +#ifdef PHP_WIN32 +/* The result needs to be freed! See sapi_getenv(). */ +static char *cgi_getenv_win32(const char *name, size_t name_len) +{ + char *ret = NULL; + wchar_t *keyw, *valw; + size_t size; + int rc; + + keyw = php_win32_cp_conv_any_to_w(name, name_len, PHP_WIN32_CP_IGNORE_LEN_P); + if (!keyw) { + return NULL; + } + + rc = _wgetenv_s(&size, NULL, 0, keyw); + if (rc || 0 == size) { + free(keyw); + return NULL; + } + + valw = emalloc((size + 1) * sizeof(wchar_t)); + + rc = _wgetenv_s(&size, valw, size, keyw); + if (!rc) { + ret = php_win32_cp_w_to_any(valw); + } + + free(keyw); + efree(valw); + + return ret; +} +#endif + +static char *sapi_cgi_getenv(char *name, size_t name_len) +{ +#ifndef PHP_WIN32 + return getenv(name); +#else + return cgi_getenv_win32(name, name_len); +#endif +} + +static char *sapi_fcgi_getenv(char *name, size_t name_len) +{ + /* when php is started by mod_fastcgi, no regular environment + * is provided to PHP. It is always sent to PHP at the start + * of a request. So we have to do our own lookup to get env + * vars. This could probably be faster somehow. */ + fcgi_request *request = (fcgi_request*) SG(server_context); + char *ret = fcgi_getenv(request, name, (int)name_len); + +#ifndef PHP_WIN32 + if (ret) return ret; + /* if cgi, or fastcgi and not found in fcgi env + check the regular environment */ + return getenv(name); +#else + if (ret) { + /* The functions outside here don't know, where does it come + from. They'll need to free the returned memory as it's + not necessary from the fcgi env. */ + return strdup(ret); + } + /* if cgi, or fastcgi and not found in fcgi env + check the regular environment */ + return cgi_getenv_win32(name, name_len); +#endif +} + +static char *_sapi_cgi_putenv(char *name, size_t name_len, char *value) +{ +#if !HAVE_SETENV || !HAVE_UNSETENV + size_t len; + char *buf; +#endif + +#if HAVE_SETENV + if (value) { + setenv(name, value, 1); + } +#endif +#if HAVE_UNSETENV + if (!value) { + unsetenv(name); + } +#endif + +#if !HAVE_SETENV || !HAVE_UNSETENV + /* if cgi, or fastcgi and not found in fcgi env + check the regular environment + this leaks, but it's only cgi anyway, we'll fix + it for 5.0 + */ + len = name_len + (value ? strlen(value) : 0) + sizeof("=") + 2; + buf = (char *) malloc(len); + if (buf == NULL) { + return getenv(name); + } +#endif +#if !HAVE_SETENV + if (value) { + len = slprintf(buf, len - 1, "%s=%s", name, value); + putenv(buf); + } +#endif +#if !HAVE_UNSETENV + if (!value) { + len = slprintf(buf, len - 1, "%s=", name); + putenv(buf); + } +#endif + return getenv(name); +} + +static char *sapi_cgi_read_cookies(void) +{ + return getenv("HTTP_COOKIE"); +} + +static char *sapi_fcgi_read_cookies(void) +{ + fcgi_request *request = (fcgi_request*) SG(server_context); + + return FCGI_GETENV(request, "HTTP_COOKIE"); +} + +static void cgi_php_load_env_var(char *var, unsigned int var_len, char *val, unsigned int val_len, void *arg) +{ + zval *array_ptr = (zval*)arg; + int filter_arg = (Z_ARR_P(array_ptr) == Z_ARR(PG(http_globals)[TRACK_VARS_ENV]))?PARSE_ENV:PARSE_SERVER; + size_t new_val_len; + + if (sapi_module.input_filter(filter_arg, var, &val, strlen(val), &new_val_len)) { + php_register_variable_safe(var, val, new_val_len, array_ptr); + } +} + +static void cgi_php_import_environment_variables(zval *array_ptr) +{ + if (PG(variables_order) && (strchr(PG(variables_order),'E') || strchr(PG(variables_order),'e'))) { + if (Z_TYPE(PG(http_globals)[TRACK_VARS_ENV]) != IS_ARRAY) { + zend_is_auto_global_str("_ENV", sizeof("_ENV")-1); + } + + if (Z_TYPE(PG(http_globals)[TRACK_VARS_ENV]) == IS_ARRAY && + Z_ARR_P(array_ptr) != Z_ARR(PG(http_globals)[TRACK_VARS_ENV])) { + zend_array_destroy(Z_ARR_P(array_ptr)); + Z_ARR_P(array_ptr) = zend_array_dup(Z_ARR(PG(http_globals)[TRACK_VARS_ENV])); + return; + } + } + + /* call php's original import as a catch-all */ + php_php_import_environment_variables(array_ptr); + + if (fcgi_is_fastcgi()) { + fcgi_request *request = (fcgi_request*) SG(server_context); + fcgi_loadenv(request, cgi_php_load_env_var, array_ptr); + } +} + +static void sapi_cgi_register_variables(zval *track_vars_array) +{ + size_t php_self_len; + char *php_self; + + /* In CGI mode, we consider the environment to be a part of the server + * variables + */ + php_import_environment_variables(track_vars_array); + + if (CGIG(fix_pathinfo)) { + char *script_name = SG(request_info).request_uri; + char *path_info; + int free_php_self; + ALLOCA_FLAG(use_heap) + + if (fcgi_is_fastcgi()) { + fcgi_request *request = (fcgi_request*) SG(server_context); + + path_info = FCGI_GETENV(request, "PATH_INFO"); + } else { + path_info = getenv("PATH_INFO"); + } + + if (path_info) { + size_t path_info_len = strlen(path_info); + + if (script_name) { + size_t script_name_len = strlen(script_name); + + php_self_len = script_name_len + path_info_len; + php_self = do_alloca(php_self_len + 1, use_heap); + memcpy(php_self, script_name, script_name_len + 1); + memcpy(php_self + script_name_len, path_info, path_info_len + 1); + free_php_self = 1; + } else { + php_self = path_info; + php_self_len = path_info_len; + free_php_self = 0; + } + } else if (script_name) { + php_self = script_name; + php_self_len = strlen(script_name); + free_php_self = 0; + } else { + php_self = ""; + php_self_len = 0; + free_php_self = 0; + } + + /* Build the special-case PHP_SELF variable for the CGI version */ + if (sapi_module.input_filter(PARSE_SERVER, "PHP_SELF", &php_self, php_self_len, &php_self_len)) { + php_register_variable_safe("PHP_SELF", php_self, php_self_len, track_vars_array); + } + if (free_php_self) { + free_alloca(php_self, use_heap); + } + } else { + php_self = SG(request_info).request_uri ? SG(request_info).request_uri : ""; + php_self_len = strlen(php_self); + if (sapi_module.input_filter(PARSE_SERVER, "PHP_SELF", &php_self, php_self_len, &php_self_len)) { + php_register_variable_safe("PHP_SELF", php_self, php_self_len, track_vars_array); + } + } +} + +static void sapi_cgi_log_message(char *message, int syslog_type_int) +{ + if (fcgi_is_fastcgi() && CGIG(fcgi_logging)) { + fcgi_request *request; + + request = (fcgi_request*) SG(server_context); + if (request) { + int ret, len = (int)strlen(message); + char *buf = malloc(len+2); + + memcpy(buf, message, len); + memcpy(buf + len, "\n", sizeof("\n")); + ret = fcgi_write(request, FCGI_STDERR, buf, (int)(len + 1)); + free(buf); + if (ret < 0) { + php_handle_aborted_connection(); + } + } else { + fprintf(stderr, "%s\n", message); + } + /* ignore return code */ + } else { + fprintf(stderr, "%s\n", message); + } +} + +/* {{{ php_cgi_ini_activate_user_config + */ +static void php_cgi_ini_activate_user_config(char *path, size_t path_len, const char *doc_root, size_t doc_root_len) +{ + user_config_cache_entry *new_entry, *entry; + time_t request_time = (time_t)sapi_get_request_time(); + + /* Find cached config entry: If not found, create one */ + if ((entry = zend_hash_str_find_ptr(&CGIG(user_config_cache), path, path_len)) == NULL) { + new_entry = pemalloc(sizeof(user_config_cache_entry), 1); + new_entry->expires = 0; + new_entry->user_config = (HashTable *) pemalloc(sizeof(HashTable), 1); + zend_hash_init(new_entry->user_config, 8, NULL, (dtor_func_t) config_zval_dtor, 1); + entry = zend_hash_str_update_ptr(&CGIG(user_config_cache), path, path_len, new_entry); + } + + /* Check whether cache entry has expired and rescan if it is */ + if (request_time > entry->expires) { + char *real_path = NULL; + char *s1, *s2; + size_t s_len; + + /* Clear the expired config */ + zend_hash_clean(entry->user_config); + + if (!IS_ABSOLUTE_PATH(path, path_len)) { + size_t real_path_len; + real_path = tsrm_realpath(path, NULL); + if (real_path == NULL) { + return; + } + real_path_len = strlen(real_path); + path = real_path; + path_len = real_path_len; + } + + if (path_len > doc_root_len) { + s1 = (char *) doc_root; + s2 = path; + s_len = doc_root_len; + } else { + s1 = path; + s2 = (char *) doc_root; + s_len = path_len; + } + + /* we have to test if path is part of DOCUMENT_ROOT. + if it is inside the docroot, we scan the tree up to the docroot + to find more user.ini, if not we only scan the current path. + */ +#ifdef PHP_WIN32 + if (strnicmp(s1, s2, s_len) == 0) { +#else + if (strncmp(s1, s2, s_len) == 0) { +#endif + char *ptr = s2 + doc_root_len; +#ifdef PHP_WIN32 + while ((ptr = strpbrk(ptr, "\\/")) != NULL) { +#else + while ((ptr = strchr(ptr, DEFAULT_SLASH)) != NULL) { +#endif + *ptr = 0; + php_parse_user_ini_file(path, PG(user_ini_filename), entry->user_config); + *ptr = '/'; + ptr++; + } + } else { + php_parse_user_ini_file(path, PG(user_ini_filename), entry->user_config); + } + + if (real_path) { + efree(real_path); + } + entry->expires = request_time + PG(user_ini_cache_ttl); + } + + /* Activate ini entries with values from the user config hash */ + php_ini_activate_config(entry->user_config, PHP_INI_PERDIR, PHP_INI_STAGE_HTACCESS); +} +/* }}} */ + +static int sapi_cgi_activate(void) +{ + /* PATH_TRANSLATED should be defined at this stage but better safe than sorry :) */ + if (!SG(request_info).path_translated) { + return FAILURE; + } + + if (php_ini_has_per_host_config()) { + char *server_name; + + /* Activate per-host-system-configuration defined in php.ini and stored into configuration_hash during startup */ + if (fcgi_is_fastcgi()) { + fcgi_request *request = (fcgi_request*) SG(server_context); + + server_name = FCGI_GETENV(request, "SERVER_NAME"); + } else { + server_name = getenv("SERVER_NAME"); + } + /* SERVER_NAME should also be defined at this stage..but better check it anyway */ + if (server_name) { + size_t server_name_len = strlen(server_name); + server_name = estrndup(server_name, server_name_len); + zend_str_tolower(server_name, server_name_len); + php_ini_activate_per_host_config(server_name, server_name_len); + efree(server_name); + } + } + + if (php_ini_has_per_dir_config() || + (PG(user_ini_filename) && *PG(user_ini_filename)) + ) { + char *path; + size_t path_len; + + /* Prepare search path */ + path_len = strlen(SG(request_info).path_translated); + + /* Make sure we have trailing slash! */ + if (!IS_SLASH(SG(request_info).path_translated[path_len])) { + path = emalloc(path_len + 2); + memcpy(path, SG(request_info).path_translated, path_len + 1); + path_len = zend_dirname(path, path_len); + path[path_len++] = DEFAULT_SLASH; + } else { + path = estrndup(SG(request_info).path_translated, path_len); + path_len = zend_dirname(path, path_len); + } + path[path_len] = 0; + + /* Activate per-dir-system-configuration defined in php.ini and stored into configuration_hash during startup */ + php_ini_activate_per_dir_config(path, path_len); /* Note: for global settings sake we check from root to path */ + + /* Load and activate user ini files in path starting from DOCUMENT_ROOT */ + if (PG(user_ini_filename) && *PG(user_ini_filename)) { + char *doc_root; + + if (fcgi_is_fastcgi()) { + fcgi_request *request = (fcgi_request*) SG(server_context); + + doc_root = FCGI_GETENV(request, "DOCUMENT_ROOT"); + } else { + doc_root = getenv("DOCUMENT_ROOT"); + } + /* DOCUMENT_ROOT should also be defined at this stage..but better check it anyway */ + if (doc_root) { + size_t doc_root_len = strlen(doc_root); + if (doc_root_len > 0 && IS_SLASH(doc_root[doc_root_len - 1])) { + --doc_root_len; + } +#ifdef PHP_WIN32 + /* paths on windows should be case-insensitive */ + doc_root = estrndup(doc_root, doc_root_len); + zend_str_tolower(doc_root, doc_root_len); +#endif + php_cgi_ini_activate_user_config(path, path_len, doc_root, doc_root_len); + +#ifdef PHP_WIN32 + efree(doc_root); +#endif + } + } + + efree(path); + } + + return SUCCESS; +} + +static int sapi_cgi_deactivate(void) +{ + /* flush only when SAPI was started. The reasons are: + 1. SAPI Deactivate is called from two places: module init and request shutdown + 2. When the first call occurs and the request is not set up, flush fails on FastCGI. + */ + if (SG(sapi_started)) { + if (fcgi_is_fastcgi()) { + if ( + !parent && + !fcgi_finish_request((fcgi_request*)SG(server_context), 0)) { + php_handle_aborted_connection(); + } + } else { + sapi_cgi_flush(SG(server_context)); + } + } + return SUCCESS; +} + +static int php_cgi_startup(sapi_module_struct *sapi_module) +{ + if (php_module_startup(sapi_module, &cgi_module_entry, 1) == FAILURE) { + return FAILURE; + } + return SUCCESS; +} + +/* {{{ sapi_module_struct cgi_sapi_module + */ +static sapi_module_struct cgi_sapi_module = { + "cgi-fcgi", /* name */ + "CGI/FastCGI", /* pretty name */ + + php_cgi_startup, /* startup */ + php_module_shutdown_wrapper, /* shutdown */ + + sapi_cgi_activate, /* activate */ + sapi_cgi_deactivate, /* deactivate */ + + sapi_cgi_ub_write, /* unbuffered write */ + sapi_cgi_flush, /* flush */ + NULL, /* get uid */ + sapi_cgi_getenv, /* getenv */ + + php_error, /* error handler */ + + NULL, /* header handler */ + sapi_cgi_send_headers, /* send headers handler */ + NULL, /* send header handler */ + + sapi_cgi_read_post, /* read POST data */ + sapi_cgi_read_cookies, /* read Cookies */ + + sapi_cgi_register_variables, /* register server variables */ + sapi_cgi_log_message, /* Log message */ + NULL, /* Get request time */ + NULL, /* Child terminate */ + + STANDARD_SAPI_MODULE_PROPERTIES +}; +/* }}} */ + +/* {{{ arginfo ext/standard/dl.c */ +ZEND_BEGIN_ARG_INFO(arginfo_dl, 0) + ZEND_ARG_INFO(0, extension_filename) +ZEND_END_ARG_INFO() +/* }}} */ + +static const zend_function_entry additional_functions[] = { + ZEND_FE(dl, arginfo_dl) + PHP_FE_END +}; + +/* {{{ php_cgi_usage + */ +static void php_cgi_usage(char *argv0) +{ + char *prog; + + prog = strrchr(argv0, '/'); + if (prog) { + prog++; + } else { + prog = "php"; + } + + php_printf( "Usage: %s [-q] [-h] [-s] [-v] [-i] [-f ]\n" + " %s [args...]\n" + " -a Run interactively\n" + " -b | Bind Path for external FASTCGI Server mode\n" + " -C Do not chdir to the script's directory\n" + " -c | Look for php.ini file in this directory\n" + " -n No php.ini file will be used\n" + " -d foo[=bar] Define INI entry foo with value 'bar'\n" + " -e Generate extended information for debugger/profiler\n" + " -f Parse . Implies `-q'\n" + " -h This help\n" + " -i PHP information\n" + " -l Syntax check only (lint)\n" + " -m Show compiled in modules\n" + " -q Quiet-mode. Suppress HTTP Header output.\n" + " -s Display colour syntax highlighted source.\n" + " -v Version number\n" + " -w Display source with stripped comments and whitespace.\n" + " -z Load Zend extension .\n" + " -T Measure execution time of script repeated times.\n", + prog, prog); +} +/* }}} */ + +/* {{{ is_valid_path + * + * some server configurations allow '..' to slip through in the + * translated path. We'll just refuse to handle such a path. + */ +static int is_valid_path(const char *path) +{ + const char *p = path; + + if (UNEXPECTED(!p)) { + return 0; + } + if (UNEXPECTED(*p == '.') && *(p+1) == '.' && (!*(p+2) || IS_SLASH(*(p+2)))) { + return 0; + } + while (*p) { + if (IS_SLASH(*p)) { + p++; + if (UNEXPECTED(*p == '.')) { + p++; + if (UNEXPECTED(*p == '.')) { + p++; + if (UNEXPECTED(!*p) || UNEXPECTED(IS_SLASH(*p))) { + return 0; + } + } + } + } + p++; + } + return 1; +} +/* }}} */ + +#define CGI_GETENV(name) \ + ((has_env) ? \ + FCGI_GETENV(request, name) : \ + getenv(name)) + +#define CGI_PUTENV(name, value) \ + ((has_env) ? \ + FCGI_PUTENV(request, name, value) : \ + _sapi_cgi_putenv(name, sizeof(name)-1, value)) + +/* {{{ init_request_info + + initializes request_info structure + + specificly in this section we handle proper translations + for: + + PATH_INFO + derived from the portion of the URI path following + the script name but preceding any query data + may be empty + + PATH_TRANSLATED + derived by taking any path-info component of the + request URI and performing any virtual-to-physical + translation appropriate to map it onto the server's + document repository structure + + empty if PATH_INFO is empty + + The env var PATH_TRANSLATED **IS DIFFERENT** than the + request_info.path_translated variable, the latter should + match SCRIPT_FILENAME instead. + + SCRIPT_NAME + set to a URL path that could identify the CGI script + rather than the interpreter. PHP_SELF is set to this + + REQUEST_URI + uri section following the domain:port part of a URI + + SCRIPT_FILENAME + The virtual-to-physical translation of SCRIPT_NAME (as per + PATH_TRANSLATED) + + These settings are documented at + http://cgi-spec.golux.com/ + + + Based on the following URL request: + + http://localhost/info.php/test?a=b + + should produce, which btw is the same as if + we were running under mod_cgi on apache (ie. not + using ScriptAlias directives): + + PATH_INFO=/test + PATH_TRANSLATED=/docroot/test + SCRIPT_NAME=/info.php + REQUEST_URI=/info.php/test?a=b + SCRIPT_FILENAME=/docroot/info.php + QUERY_STRING=a=b + + but what we get is (cgi/mod_fastcgi under apache): + + PATH_INFO=/info.php/test + PATH_TRANSLATED=/docroot/info.php/test + SCRIPT_NAME=/php/php-cgi (from the Action setting I suppose) + REQUEST_URI=/info.php/test?a=b + SCRIPT_FILENAME=/path/to/php/bin/php-cgi (Action setting translated) + QUERY_STRING=a=b + + Comments in the code below refer to using the above URL in a request + + */ +static void init_request_info(fcgi_request *request) +{ + int has_env = fcgi_has_env(request); + char *env_script_filename = CGI_GETENV("SCRIPT_FILENAME"); + char *env_path_translated = CGI_GETENV("PATH_TRANSLATED"); + char *script_path_translated = env_script_filename; + + /* some broken servers do not have script_filename or argv0 + * an example, IIS configured in some ways. then they do more + * broken stuff and set path_translated to the cgi script location */ + if (!script_path_translated && env_path_translated) { + script_path_translated = env_path_translated; + } + + /* initialize the defaults */ + SG(request_info).path_translated = NULL; + SG(request_info).request_method = NULL; + SG(request_info).proto_num = 1000; + SG(request_info).query_string = NULL; + SG(request_info).request_uri = NULL; + SG(request_info).content_type = NULL; + SG(request_info).content_length = 0; + SG(sapi_headers).http_response_code = 200; + + /* script_path_translated being set is a good indication that + * we are running in a cgi environment, since it is always + * null otherwise. otherwise, the filename + * of the script will be retrieved later via argc/argv */ + if (script_path_translated) { + const char *auth; + char *content_length = CGI_GETENV("CONTENT_LENGTH"); + char *content_type = CGI_GETENV("CONTENT_TYPE"); + char *env_path_info = CGI_GETENV("PATH_INFO"); + char *env_script_name = CGI_GETENV("SCRIPT_NAME"); + +#ifdef PHP_WIN32 + /* Hack for buggy IIS that sets incorrect PATH_INFO */ + char *env_server_software = CGI_GETENV("SERVER_SOFTWARE"); + + if (env_server_software && + env_script_name && + env_path_info && + strncmp(env_server_software, "Microsoft-IIS", sizeof("Microsoft-IIS")-1) == 0 && + strncmp(env_path_info, env_script_name, strlen(env_script_name)) == 0 + ) { + env_path_info = CGI_PUTENV("ORIG_PATH_INFO", env_path_info); + env_path_info += strlen(env_script_name); + if (*env_path_info == 0) { + env_path_info = NULL; + } + env_path_info = CGI_PUTENV("PATH_INFO", env_path_info); + } +#endif + + if (CGIG(fix_pathinfo)) { + zend_stat_t st; + char *real_path = NULL; + char *env_redirect_url = CGI_GETENV("REDIRECT_URL"); + char *env_document_root = CGI_GETENV("DOCUMENT_ROOT"); + char *orig_path_translated = env_path_translated; + char *orig_path_info = env_path_info; + char *orig_script_name = env_script_name; + char *orig_script_filename = env_script_filename; + size_t script_path_translated_len; + + if (!env_document_root && PG(doc_root)) { + env_document_root = CGI_PUTENV("DOCUMENT_ROOT", PG(doc_root)); + /* fix docroot */ + TRANSLATE_SLASHES(env_document_root); + } + + if (env_path_translated != NULL && env_redirect_url != NULL && + env_path_translated != script_path_translated && + strcmp(env_path_translated, script_path_translated) != 0) { + /* + * pretty much apache specific. If we have a redirect_url + * then our script_filename and script_name point to the + * php executable + */ + script_path_translated = env_path_translated; + /* we correct SCRIPT_NAME now in case we don't have PATH_INFO */ + env_script_name = env_redirect_url; + } + +#ifdef __riscos__ + /* Convert path to unix format*/ + __riscosify_control |= __RISCOSIFY_DONT_CHECK_DIR; + script_path_translated = __unixify(script_path_translated, 0, NULL, 1, 0); +#endif + + /* + * if the file doesn't exist, try to extract PATH_INFO out + * of it by stat'ing back through the '/' + * this fixes url's like /info.php/test + */ + if (script_path_translated && + (script_path_translated_len = strlen(script_path_translated)) > 0 && + (script_path_translated[script_path_translated_len-1] == '/' || +#ifdef PHP_WIN32 + script_path_translated[script_path_translated_len-1] == '\\' || +#endif + (real_path = tsrm_realpath(script_path_translated, NULL)) == NULL) + ) { + char *pt = estrndup(script_path_translated, script_path_translated_len); + size_t len = script_path_translated_len; + char *ptr; + + while ((ptr = strrchr(pt, '/')) || (ptr = strrchr(pt, '\\'))) { + *ptr = 0; + if (zend_stat(pt, &st) == 0 && S_ISREG(st.st_mode)) { + /* + * okay, we found the base script! + * work out how many chars we had to strip off; + * then we can modify PATH_INFO + * accordingly + * + * we now have the makings of + * PATH_INFO=/test + * SCRIPT_FILENAME=/docroot/info.php + * + * we now need to figure out what docroot is. + * if DOCUMENT_ROOT is set, this is easy, otherwise, + * we have to play the game of hide and seek to figure + * out what SCRIPT_NAME should be + */ + size_t slen = len - strlen(pt); + size_t pilen = env_path_info ? strlen(env_path_info) : 0; + char *path_info = env_path_info ? env_path_info + pilen - slen : NULL; + + if (orig_path_info != path_info) { + if (orig_path_info) { + char old; + + CGI_PUTENV("ORIG_PATH_INFO", orig_path_info); + old = path_info[0]; + path_info[0] = 0; + if (!orig_script_name || + strcmp(orig_script_name, env_path_info) != 0) { + if (orig_script_name) { + CGI_PUTENV("ORIG_SCRIPT_NAME", orig_script_name); + } + SG(request_info).request_uri = CGI_PUTENV("SCRIPT_NAME", env_path_info); + } else { + SG(request_info).request_uri = orig_script_name; + } + path_info[0] = old; + } + env_path_info = CGI_PUTENV("PATH_INFO", path_info); + } + if (!orig_script_filename || + strcmp(orig_script_filename, pt) != 0) { + if (orig_script_filename) { + CGI_PUTENV("ORIG_SCRIPT_FILENAME", orig_script_filename); + } + script_path_translated = CGI_PUTENV("SCRIPT_FILENAME", pt); + } + TRANSLATE_SLASHES(pt); + + /* figure out docroot + * SCRIPT_FILENAME minus SCRIPT_NAME + */ + if (env_document_root) { + size_t l = strlen(env_document_root); + size_t path_translated_len = 0; + char *path_translated = NULL; + + if (l && env_document_root[l - 1] == '/') { + --l; + } + + /* we have docroot, so we should have: + * DOCUMENT_ROOT=/docroot + * SCRIPT_FILENAME=/docroot/info.php + */ + + /* PATH_TRANSLATED = DOCUMENT_ROOT + PATH_INFO */ + path_translated_len = l + (env_path_info ? strlen(env_path_info) : 0); + path_translated = (char *) emalloc(path_translated_len + 1); + memcpy(path_translated, env_document_root, l); + if (env_path_info) { + memcpy(path_translated + l, env_path_info, (path_translated_len - l)); + } + path_translated[path_translated_len] = '\0'; + if (orig_path_translated) { + CGI_PUTENV("ORIG_PATH_TRANSLATED", orig_path_translated); + } + env_path_translated = CGI_PUTENV("PATH_TRANSLATED", path_translated); + efree(path_translated); + } else if ( env_script_name && + strstr(pt, env_script_name) + ) { + /* PATH_TRANSLATED = PATH_TRANSLATED - SCRIPT_NAME + PATH_INFO */ + size_t ptlen = strlen(pt) - strlen(env_script_name); + size_t path_translated_len = ptlen + (env_path_info ? strlen(env_path_info) : 0); + + char *path_translated = (char *) emalloc(path_translated_len + 1); + memcpy(path_translated, pt, ptlen); + if (env_path_info) { + memcpy(path_translated + ptlen, env_path_info, path_translated_len - ptlen); + } + path_translated[path_translated_len] = '\0'; + if (orig_path_translated) { + CGI_PUTENV("ORIG_PATH_TRANSLATED", orig_path_translated); + } + env_path_translated = CGI_PUTENV("PATH_TRANSLATED", path_translated); + efree(path_translated); + } + break; + } + } + if (!ptr) { + /* + * if we stripped out all the '/' and still didn't find + * a valid path... we will fail, badly. of course we would + * have failed anyway... we output 'no input file' now. + */ + if (orig_script_filename) { + CGI_PUTENV("ORIG_SCRIPT_FILENAME", orig_script_filename); + } + script_path_translated = CGI_PUTENV("SCRIPT_FILENAME", NULL); + SG(sapi_headers).http_response_code = 404; + } + if (!SG(request_info).request_uri) { + if (!orig_script_name || + strcmp(orig_script_name, env_script_name) != 0) { + if (orig_script_name) { + CGI_PUTENV("ORIG_SCRIPT_NAME", orig_script_name); + } + SG(request_info).request_uri = CGI_PUTENV("SCRIPT_NAME", env_script_name); + } else { + SG(request_info).request_uri = orig_script_name; + } + } + if (pt) { + efree(pt); + } + } else { + /* make sure path_info/translated are empty */ + if (!orig_script_filename || + (script_path_translated != orig_script_filename && + strcmp(script_path_translated, orig_script_filename) != 0)) { + if (orig_script_filename) { + CGI_PUTENV("ORIG_SCRIPT_FILENAME", orig_script_filename); + } + script_path_translated = CGI_PUTENV("SCRIPT_FILENAME", script_path_translated); + } + if (env_redirect_url) { + if (orig_path_info) { + CGI_PUTENV("ORIG_PATH_INFO", orig_path_info); + CGI_PUTENV("PATH_INFO", NULL); + } + if (orig_path_translated) { + CGI_PUTENV("ORIG_PATH_TRANSLATED", orig_path_translated); + CGI_PUTENV("PATH_TRANSLATED", NULL); + } + } + if (env_script_name != orig_script_name) { + if (orig_script_name) { + CGI_PUTENV("ORIG_SCRIPT_NAME", orig_script_name); + } + SG(request_info).request_uri = CGI_PUTENV("SCRIPT_NAME", env_script_name); + } else { + SG(request_info).request_uri = env_script_name; + } + efree(real_path); + } + } else { + /* pre 4.3 behaviour, shouldn't be used but provides BC */ + if (env_path_info) { + SG(request_info).request_uri = env_path_info; + } else { + SG(request_info).request_uri = env_script_name; + } + if (!CGIG(discard_path) && env_path_translated) { + script_path_translated = env_path_translated; + } + } + + if (is_valid_path(script_path_translated)) { + SG(request_info).path_translated = estrdup(script_path_translated); + } + + SG(request_info).request_method = CGI_GETENV("REQUEST_METHOD"); + /* FIXME - Work out proto_num here */ + SG(request_info).query_string = CGI_GETENV("QUERY_STRING"); + SG(request_info).content_type = (content_type ? content_type : "" ); + SG(request_info).content_length = (content_length ? atol(content_length) : 0); + + /* The CGI RFC allows servers to pass on unvalidated Authorization data */ + auth = CGI_GETENV("HTTP_AUTHORIZATION"); + php_handle_auth_data(auth); + } +} +/* }}} */ + +#ifndef PHP_WIN32 +/** + * Clean up child processes upon exit + */ +void fastcgi_cleanup(int signal) +{ +#ifdef DEBUG_FASTCGI + fprintf(stderr, "FastCGI shutdown, pid %d\n", getpid()); +#endif + + sigaction(SIGTERM, &old_term, 0); + + /* Kill all the processes in our process group */ + kill(-pgroup, SIGTERM); + + if (parent && parent_waiting) { + exit_signal = 1; + } else { + exit(0); + } +} +#else +BOOL WINAPI fastcgi_cleanup(DWORD sig) +{ + int i = kids; + + EnterCriticalSection(&cleanup_lock); + cleaning_up = 1; + LeaveCriticalSection(&cleanup_lock); + + while (0 < i--) { + if (NULL == kid_cgi_ps[i]) { + continue; + } + + TerminateProcess(kid_cgi_ps[i], 0); + CloseHandle(kid_cgi_ps[i]); + kid_cgi_ps[i] = NULL; + } + + if (job) { + CloseHandle(job); + } + + parent = 0; + + return TRUE; +} +#endif + +PHP_INI_BEGIN() + STD_PHP_INI_ENTRY("cgi.rfc2616_headers", "0", PHP_INI_ALL, OnUpdateBool, rfc2616_headers, php_cgi_globals_struct, php_cgi_globals) + STD_PHP_INI_ENTRY("cgi.nph", "0", PHP_INI_ALL, OnUpdateBool, nph, php_cgi_globals_struct, php_cgi_globals) + STD_PHP_INI_ENTRY("cgi.check_shebang_line", "1", PHP_INI_SYSTEM, OnUpdateBool, check_shebang_line, php_cgi_globals_struct, php_cgi_globals) + STD_PHP_INI_ENTRY("cgi.force_redirect", "1", PHP_INI_SYSTEM, OnUpdateBool, force_redirect, php_cgi_globals_struct, php_cgi_globals) + STD_PHP_INI_ENTRY("cgi.redirect_status_env", NULL, PHP_INI_SYSTEM, OnUpdateString, redirect_status_env, php_cgi_globals_struct, php_cgi_globals) + STD_PHP_INI_ENTRY("cgi.fix_pathinfo", "1", PHP_INI_SYSTEM, OnUpdateBool, fix_pathinfo, php_cgi_globals_struct, php_cgi_globals) + STD_PHP_INI_ENTRY("cgi.discard_path", "0", PHP_INI_SYSTEM, OnUpdateBool, discard_path, php_cgi_globals_struct, php_cgi_globals) + STD_PHP_INI_ENTRY("fastcgi.logging", "1", PHP_INI_SYSTEM, OnUpdateBool, fcgi_logging, php_cgi_globals_struct, php_cgi_globals) +#ifdef PHP_WIN32 + STD_PHP_INI_ENTRY("fastcgi.impersonate", "0", PHP_INI_SYSTEM, OnUpdateBool, impersonate, php_cgi_globals_struct, php_cgi_globals) +#endif +PHP_INI_END() + +/* {{{ php_cgi_globals_ctor + */ +static void php_cgi_globals_ctor(php_cgi_globals_struct *php_cgi_globals) +{ +#if defined(ZTS) && defined(PHP_WIN32) + ZEND_TSRMLS_CACHE_UPDATE(); +#endif + php_cgi_globals->rfc2616_headers = 0; + php_cgi_globals->nph = 0; + php_cgi_globals->check_shebang_line = 1; + php_cgi_globals->force_redirect = 1; + php_cgi_globals->redirect_status_env = NULL; + php_cgi_globals->fix_pathinfo = 1; + php_cgi_globals->discard_path = 0; + php_cgi_globals->fcgi_logging = 1; +#ifdef PHP_WIN32 + php_cgi_globals->impersonate = 0; +#endif + zend_hash_init(&php_cgi_globals->user_config_cache, 8, NULL, user_config_cache_entry_dtor, 1); +} +/* }}} */ + +/* {{{ PHP_MINIT_FUNCTION + */ +static PHP_MINIT_FUNCTION(cgi) +{ + REGISTER_INI_ENTRIES(); + return SUCCESS; +} +/* }}} */ + +/* {{{ PHP_MSHUTDOWN_FUNCTION + */ +static PHP_MSHUTDOWN_FUNCTION(cgi) +{ + zend_hash_destroy(&CGIG(user_config_cache)); + + UNREGISTER_INI_ENTRIES(); + return SUCCESS; +} +/* }}} */ + +/* {{{ PHP_MINFO_FUNCTION + */ +static PHP_MINFO_FUNCTION(cgi) +{ + DISPLAY_INI_ENTRIES(); +} +/* }}} */ + +PHP_FUNCTION(apache_child_terminate) /* {{{ */ +{ + if (zend_parse_parameters_none()) { + return; + } + if (fcgi_is_fastcgi()) { + fcgi_terminate(); + } +} +/* }}} */ + + +PHP_FUNCTION(apache_request_headers) /* {{{ */ +{ + if (zend_parse_parameters_none()) { + return; + } + array_init(return_value); + if (fcgi_is_fastcgi()) { + fcgi_request *request = (fcgi_request*) SG(server_context); + + fcgi_loadenv(request, sapi_add_request_header, return_value); + } else { + char buf[128]; + char **env, *p, *q, *var, *val, *t = buf; + size_t alloc_size = sizeof(buf); + zend_ulong var_len; + + for (env = environ; env != NULL && *env != NULL; env++) { + val = strchr(*env, '='); + if (!val) { /* malformed entry? */ + continue; + } + var_len = val - *env; + if (var_len >= alloc_size) { + alloc_size = var_len + 64; + t = (t == buf ? emalloc(alloc_size): erealloc(t, alloc_size)); + } + var = *env; + if (var_len > 5 && + var[0] == 'H' && + var[1] == 'T' && + var[2] == 'T' && + var[3] == 'P' && + var[4] == '_') { + + var_len -= 5; + + if (var_len >= alloc_size) { + alloc_size = var_len + 64; + t = (t == buf ? emalloc(alloc_size): erealloc(t, alloc_size)); + } + p = var + 5; + + var = q = t; + /* First char keep uppercase */ + *q++ = *p++; + while (*p) { + if (*p == '=') { + /* End of name */ + break; + } else if (*p == '_') { + *q++ = '-'; + p++; + /* First char after - keep uppercase */ + if (*p && *p!='=') { + *q++ = *p++; + } + } else if (*p >= 'A' && *p <= 'Z') { + /* lowercase */ + *q++ = (*p++ - 'A' + 'a'); + } else { + *q++ = *p++; + } + } + *q = 0; + } else if (var_len == sizeof("CONTENT_TYPE")-1 && + memcmp(var, "CONTENT_TYPE", sizeof("CONTENT_TYPE")-1) == 0) { + var = "Content-Type"; + } else if (var_len == sizeof("CONTENT_LENGTH")-1 && + memcmp(var, "CONTENT_LENGTH", sizeof("CONTENT_LENGTH")-1) == 0) { + var = "Content-Length"; + } else { + continue; + } + val++; + add_assoc_string_ex(return_value, var, var_len, val); + } + if (t != buf && t != NULL) { + efree(t); + } + } +} +/* }}} */ + +static void add_response_header(sapi_header_struct *h, zval *return_value) /* {{{ */ +{ + if (h->header_len > 0) { + char *s; + size_t len = 0; + ALLOCA_FLAG(use_heap) + + char *p = strchr(h->header, ':'); + if (NULL != p) { + len = p - h->header; + } + if (len > 0) { + while (len != 0 && (h->header[len-1] == ' ' || h->header[len-1] == '\t')) { + len--; + } + if (len) { + s = do_alloca(len + 1, use_heap); + memcpy(s, h->header, len); + s[len] = 0; + do { + p++; + } while (*p == ' ' || *p == '\t'); + add_assoc_stringl_ex(return_value, s, len, p, h->header_len - (p - h->header)); + free_alloca(s, use_heap); + } + } + } +} +/* }}} */ + +PHP_FUNCTION(apache_response_headers) /* {{{ */ +{ + if (zend_parse_parameters_none() == FAILURE) { + return; + } + + array_init(return_value); + zend_llist_apply_with_argument(&SG(sapi_headers).headers, (llist_apply_with_arg_func_t)add_response_header, return_value); +} +/* }}} */ + +ZEND_BEGIN_ARG_INFO(arginfo_no_args, 0) +ZEND_END_ARG_INFO() + +static const zend_function_entry cgi_functions[] = { + PHP_FE(apache_child_terminate, arginfo_no_args) + PHP_FE(apache_request_headers, arginfo_no_args) + PHP_FE(apache_response_headers, arginfo_no_args) + PHP_FALIAS(getallheaders, apache_request_headers, arginfo_no_args) + PHP_FE_END +}; + +static zend_module_entry cgi_module_entry = { + STANDARD_MODULE_HEADER, + "cgi-fcgi", + cgi_functions, + PHP_MINIT(cgi), + PHP_MSHUTDOWN(cgi), + NULL, + NULL, + PHP_MINFO(cgi), + PHP_VERSION, + STANDARD_MODULE_PROPERTIES +}; + +/* {{{ main + */ +int main(int argc, char *argv[]) +{ + int free_query_string = 0; + int exit_status = SUCCESS; + int cgi = 0, c, i; + size_t len; + zend_file_handle file_handle; + char *s; + + /* temporary locals */ + int behavior = PHP_MODE_STANDARD; + int no_headers = 0; + int orig_optind = php_optind; + char *orig_optarg = php_optarg; + char *script_file = NULL; + size_t ini_entries_len = 0; + /* end of temporary locals */ + + int max_requests = 500; + int requests = 0; + int fastcgi; + char *bindpath = NULL; + int fcgi_fd = 0; + fcgi_request *request = NULL; + int warmup_repeats = 0; + int repeats = 1; + int benchmark = 0; +#if HAVE_GETTIMEOFDAY + struct timeval start, end; +#else + time_t start, end; +#endif +#ifndef PHP_WIN32 + int status = 0; +#endif + char *query_string; + char *decoded_query_string; + int skip_getopt = 0; + +#if defined(SIGPIPE) && defined(SIG_IGN) + signal(SIGPIPE, SIG_IGN); /* ignore SIGPIPE in standalone mode so + that sockets created via fsockopen() + don't kill PHP if the remote site + closes it. in apache|apxs mode apache + does that for us! thies@thieso.net + 20000419 */ +#endif + +#ifdef ZTS + php_tsrm_startup(); +# ifdef PHP_WIN32 + ZEND_TSRMLS_CACHE_UPDATE(); +# endif +#endif + + zend_signal_startup(); + +#ifdef ZTS + ts_allocate_id(&php_cgi_globals_id, sizeof(php_cgi_globals_struct), (ts_allocate_ctor) php_cgi_globals_ctor, NULL); +#else + php_cgi_globals_ctor(&php_cgi_globals); +#endif + + sapi_startup(&cgi_sapi_module); + fastcgi = fcgi_is_fastcgi(); + cgi_sapi_module.php_ini_path_override = NULL; + +#ifdef PHP_WIN32 + _fmode = _O_BINARY; /* sets default for file streams to binary */ + setmode(_fileno(stdin), O_BINARY); /* make the stdio mode be binary */ + setmode(_fileno(stdout), O_BINARY); /* make the stdio mode be binary */ + setmode(_fileno(stderr), O_BINARY); /* make the stdio mode be binary */ +#endif + + if (!fastcgi) { + /* Make sure we detect we are a cgi - a bit redundancy here, + * but the default case is that we have to check only the first one. */ + if (getenv("SERVER_SOFTWARE") || + getenv("SERVER_NAME") || + getenv("GATEWAY_INTERFACE") || + getenv("REQUEST_METHOD") + ) { + cgi = 1; + } + } + + if((query_string = getenv("QUERY_STRING")) != NULL && strchr(query_string, '=') == NULL) { + /* we've got query string that has no = - apache CGI will pass it to command line */ + unsigned char *p; + decoded_query_string = strdup(query_string); + php_url_decode(decoded_query_string, strlen(decoded_query_string)); + for (p = (unsigned char *)decoded_query_string; *p && *p <= ' '; p++) { + /* skip all leading spaces */ + } + if(*p == '-') { + skip_getopt = 1; + } + free(decoded_query_string); + } + + while (!skip_getopt && (c = php_getopt(argc, argv, OPTIONS, &php_optarg, &php_optind, 0, 2)) != -1) { + switch (c) { + case 'c': + if (cgi_sapi_module.php_ini_path_override) { + free(cgi_sapi_module.php_ini_path_override); + } + cgi_sapi_module.php_ini_path_override = strdup(php_optarg); + break; + case 'n': + cgi_sapi_module.php_ini_ignore = 1; + break; + case 'd': { + /* define ini entries on command line */ + size_t len = strlen(php_optarg); + char *val; + + if ((val = strchr(php_optarg, '='))) { + val++; + if (!isalnum(*val) && *val != '"' && *val != '\'' && *val != '\0') { + cgi_sapi_module.ini_entries = realloc(cgi_sapi_module.ini_entries, ini_entries_len + len + sizeof("\"\"\n\0")); + memcpy(cgi_sapi_module.ini_entries + ini_entries_len, php_optarg, (val - php_optarg)); + ini_entries_len += (val - php_optarg); + memcpy(cgi_sapi_module.ini_entries + ini_entries_len, "\"", 1); + ini_entries_len++; + memcpy(cgi_sapi_module.ini_entries + ini_entries_len, val, len - (val - php_optarg)); + ini_entries_len += len - (val - php_optarg); + memcpy(cgi_sapi_module.ini_entries + ini_entries_len, "\"\n\0", sizeof("\"\n\0")); + ini_entries_len += sizeof("\n\0\"") - 2; + } else { + cgi_sapi_module.ini_entries = realloc(cgi_sapi_module.ini_entries, ini_entries_len + len + sizeof("\n\0")); + memcpy(cgi_sapi_module.ini_entries + ini_entries_len, php_optarg, len); + memcpy(cgi_sapi_module.ini_entries + ini_entries_len + len, "\n\0", sizeof("\n\0")); + ini_entries_len += len + sizeof("\n\0") - 2; + } + } else { + cgi_sapi_module.ini_entries = realloc(cgi_sapi_module.ini_entries, ini_entries_len + len + sizeof("=1\n\0")); + memcpy(cgi_sapi_module.ini_entries + ini_entries_len, php_optarg, len); + memcpy(cgi_sapi_module.ini_entries + ini_entries_len + len, "=1\n\0", sizeof("=1\n\0")); + ini_entries_len += len + sizeof("=1\n\0") - 2; + } + break; + } + /* if we're started on command line, check to see if + * we are being started as an 'external' fastcgi + * server by accepting a bindpath parameter. */ + case 'b': + if (!fastcgi) { + bindpath = strdup(php_optarg); + } + break; + case 's': /* generate highlighted HTML from source */ + behavior = PHP_MODE_HIGHLIGHT; + break; + } + } + php_optind = orig_optind; + php_optarg = orig_optarg; + + if (fastcgi || bindpath) { + /* Override SAPI callbacks */ + cgi_sapi_module.ub_write = sapi_fcgi_ub_write; + cgi_sapi_module.flush = sapi_fcgi_flush; + cgi_sapi_module.read_post = sapi_fcgi_read_post; + cgi_sapi_module.getenv = sapi_fcgi_getenv; + cgi_sapi_module.read_cookies = sapi_fcgi_read_cookies; + } + +#ifdef ZTS + SG(request_info).path_translated = NULL; +#endif + + cgi_sapi_module.executable_location = argv[0]; + if (!cgi && !fastcgi && !bindpath) { + cgi_sapi_module.additional_functions = additional_functions; + } + + /* startup after we get the above ini override se we get things right */ + if (cgi_sapi_module.startup(&cgi_sapi_module) == FAILURE) { +#ifdef ZTS + tsrm_shutdown(); +#endif + free(bindpath); + return FAILURE; + } + + /* check force_cgi after startup, so we have proper output */ + if (cgi && CGIG(force_redirect)) { + /* Apache will generate REDIRECT_STATUS, + * Netscape and redirect.so will generate HTTP_REDIRECT_STATUS. + * redirect.so and installation instructions available from + * http://www.koehntopp.de/php. + * -- kk@netuse.de + */ + if (!getenv("REDIRECT_STATUS") && + !getenv ("HTTP_REDIRECT_STATUS") && + /* this is to allow a different env var to be configured + * in case some server does something different than above */ + (!CGIG(redirect_status_env) || !getenv(CGIG(redirect_status_env))) + ) { + zend_try { + SG(sapi_headers).http_response_code = 400; + PUTS("Security Alert! The PHP CGI cannot be accessed directly.\n\n\ +

This PHP CGI binary was compiled with force-cgi-redirect enabled. This\n\ +means that a page will only be served up if the REDIRECT_STATUS CGI variable is\n\ +set, e.g. via an Apache Action directive.

\n\ +

For more information as to why this behaviour exists, see the \ +manual page for CGI security.

\n\ +

For more information about changing this behaviour or re-enabling this webserver,\n\ +consult the installation file that came with this distribution, or visit \n\ +the manual page.

\n"); + } zend_catch { + } zend_end_try(); +#if defined(ZTS) && !defined(PHP_DEBUG) + /* XXX we're crashing here in msvc6 debug builds at + * php_message_handler_for_zend:839 because + * SG(request_info).path_translated is an invalid pointer. + * It still happens even though I set it to null, so something + * weird is going on. + */ + tsrm_shutdown(); +#endif + free(bindpath); + return FAILURE; + } + } + +#ifndef HAVE_ATTRIBUTE_WEAK + fcgi_set_logger(fcgi_log); +#endif + + if (bindpath) { + int backlog = 128; + if (getenv("PHP_FCGI_BACKLOG")) { + backlog = atoi(getenv("PHP_FCGI_BACKLOG")); + } + fcgi_fd = fcgi_listen(bindpath, backlog); + if (fcgi_fd < 0) { + fprintf(stderr, "Couldn't create FastCGI listen socket on port %s\n", bindpath); +#ifdef ZTS + tsrm_shutdown(); +#endif + return FAILURE; + } + fastcgi = fcgi_is_fastcgi(); + } + + /* make php call us to get _ENV vars */ + php_php_import_environment_variables = php_import_environment_variables; + php_import_environment_variables = cgi_php_import_environment_variables; + + if (fastcgi) { + /* How many times to run PHP scripts before dying */ + if (getenv("PHP_FCGI_MAX_REQUESTS")) { + max_requests = atoi(getenv("PHP_FCGI_MAX_REQUESTS")); + if (max_requests < 0) { + fprintf(stderr, "PHP_FCGI_MAX_REQUESTS is not valid\n"); + return FAILURE; + } + } + + /* library is already initialized, now init our request */ + request = fcgi_init_request(fcgi_fd, NULL, NULL, NULL); + + /* Pre-fork or spawn, if required */ + if (getenv("PHP_FCGI_CHILDREN")) { + char * children_str = getenv("PHP_FCGI_CHILDREN"); + children = atoi(children_str); + if (children < 0) { + fprintf(stderr, "PHP_FCGI_CHILDREN is not valid\n"); + return FAILURE; + } + fcgi_set_mgmt_var("FCGI_MAX_CONNS", sizeof("FCGI_MAX_CONNS")-1, children_str, strlen(children_str)); + /* This is the number of concurrent requests, equals FCGI_MAX_CONNS */ + fcgi_set_mgmt_var("FCGI_MAX_REQS", sizeof("FCGI_MAX_REQS")-1, children_str, strlen(children_str)); + } else { +#ifdef PHP_WIN32 + /* If this env var is set, the process was invoked as a child. Let + it show the original PHP_FCGI_CHILDREN value, while don't care + otherwise. */ + char * children_str = getenv("PHP_FCGI_CHILDREN_FOR_KID"); + if (children_str) { + char putenv_buf[sizeof("PHP_FCGI_CHILDREN")+5]; + + snprintf(putenv_buf, sizeof(putenv_buf), "%s=%s", "PHP_FCGI_CHILDREN", children_str); + putenv(putenv_buf); + putenv("PHP_FCGI_CHILDREN_FOR_KID="); + + SetEnvironmentVariable("PHP_FCGI_CHILDREN", children_str); + SetEnvironmentVariable("PHP_FCGI_CHILDREN_FOR_KID", NULL); + } +#endif + fcgi_set_mgmt_var("FCGI_MAX_CONNS", sizeof("FCGI_MAX_CONNS")-1, "1", sizeof("1")-1); + fcgi_set_mgmt_var("FCGI_MAX_REQS", sizeof("FCGI_MAX_REQS")-1, "1", sizeof("1")-1); + } + +#ifndef PHP_WIN32 + if (children) { + int running = 0; + pid_t pid; + + /* Create a process group for ourself & children */ + setsid(); + pgroup = getpgrp(); +#ifdef DEBUG_FASTCGI + fprintf(stderr, "Process group %d\n", pgroup); +#endif + + /* Set up handler to kill children upon exit */ + act.sa_flags = 0; + act.sa_handler = fastcgi_cleanup; + if (sigaction(SIGTERM, &act, &old_term) || + sigaction(SIGINT, &act, &old_int) || + sigaction(SIGQUIT, &act, &old_quit) + ) { + perror("Can't set signals"); + exit(1); + } + + if (fcgi_in_shutdown()) { + goto parent_out; + } + + while (parent) { + do { +#ifdef DEBUG_FASTCGI + fprintf(stderr, "Forking, %d running\n", running); +#endif + pid = fork(); + switch (pid) { + case 0: + /* One of the children. + * Make sure we don't go round the + * fork loop any more + */ + parent = 0; + + /* don't catch our signals */ + sigaction(SIGTERM, &old_term, 0); + sigaction(SIGQUIT, &old_quit, 0); + sigaction(SIGINT, &old_int, 0); + zend_signal_init(); + break; + case -1: + perror("php (pre-forking)"); + exit(1); + break; + default: + /* Fine */ + running++; + break; + } + } while (parent && (running < children)); + + if (parent) { +#ifdef DEBUG_FASTCGI + fprintf(stderr, "Wait for kids, pid %d\n", getpid()); +#endif + parent_waiting = 1; + while (1) { + if (wait(&status) >= 0) { + running--; + break; + } else if (exit_signal) { + break; + } + } + if (exit_signal) { +#if 0 + while (running > 0) { + while (wait(&status) < 0) { + } + running--; + } +#endif + goto parent_out; + } + } + } + } else { + parent = 0; + zend_signal_init(); + } + +#else + if (children) { + wchar_t *cmd_line_tmp, cmd_line[PHP_WIN32_IOUTIL_MAXPATHLEN]; + size_t cmd_line_len; + char kid_buf[16]; + int i; + + ZeroMemory(&kid_cgi_ps, sizeof(kid_cgi_ps)); + kids = children < WIN32_MAX_SPAWN_CHILDREN ? children : WIN32_MAX_SPAWN_CHILDREN; + + InitializeCriticalSection(&cleanup_lock); + SetConsoleCtrlHandler(fastcgi_cleanup, TRUE); + + /* kids will inherit the env, don't let them spawn */ + SetEnvironmentVariable("PHP_FCGI_CHILDREN", NULL); + /* instead, set a temporary env var, so then the child can read and + show the actual setting correctly. */ + snprintf(kid_buf, 16, "%d", children); + SetEnvironmentVariable("PHP_FCGI_CHILDREN_FOR_KID", kid_buf); + + /* The current command line is used as is. This should normally be no issue, + even if there were some I/O redirection. If some issues turn out, an + extra parsing might be needed here. */ + cmd_line_tmp = GetCommandLineW(); + if (!cmd_line_tmp) { + DWORD err = GetLastError(); + char *err_text = php_win32_error_to_msg(err); + + fprintf(stderr, "unable to get current command line: [0x%08lx]: %s\n", err, err_text); + php_win32_error_msg_free(err_text); + + goto parent_out; + } + + cmd_line_len = wcslen(cmd_line_tmp); + if (cmd_line_len > sizeof(cmd_line) - 1) { + fprintf(stderr, "command line is too long\n"); + goto parent_out; + } + memmove(cmd_line, cmd_line_tmp, (cmd_line_len + 1)*sizeof(wchar_t)); + + job = CreateJobObject(NULL, NULL); + if (!job) { + DWORD err = GetLastError(); + char *err_text = php_win32_error_to_msg(err); + + fprintf(stderr, "unable to create job object: [0x%08lx]: %s\n", err, err_text); + + php_win32_error_msg_free(err_text); + + goto parent_out; + } + + job_info.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE; + if (!SetInformationJobObject(job, JobObjectExtendedLimitInformation, &job_info, sizeof(job_info))) { + DWORD err = GetLastError(); + char *err_text = php_win32_error_to_msg(err); + + fprintf(stderr, "unable to configure job object: [0x%08lx]: %s\n", err, err_text); + php_win32_error_msg_free(err_text); + } + + while (parent) { + EnterCriticalSection(&cleanup_lock); + if (cleaning_up) { + goto parent_loop_end; + } + LeaveCriticalSection(&cleanup_lock); + + i = kids; + while (0 < i--) { + DWORD status; + + if (NULL != kid_cgi_ps[i]) { + if(!GetExitCodeProcess(kid_cgi_ps[i], &status) || status != STILL_ACTIVE) { + CloseHandle(kid_cgi_ps[i]); + kid_cgi_ps[i] = NULL; + } + } + } + + i = kids; + while (0 < i--) { + PROCESS_INFORMATION pi; + STARTUPINFOW si; + + if (NULL != kid_cgi_ps[i]) { + continue; + } + + ZeroMemory(&si, sizeof(si)); + si.cb = sizeof(si); + ZeroMemory(&pi, sizeof(pi)); + + si.dwFlags = STARTF_USESTDHANDLES; + si.hStdOutput = INVALID_HANDLE_VALUE; + si.hStdInput = (HANDLE)_get_osfhandle(fcgi_fd); + si.hStdError = INVALID_HANDLE_VALUE; + + if (CreateProcessW(NULL, cmd_line, NULL, NULL, TRUE, CREATE_NO_WINDOW, NULL, NULL, &si, &pi)) { + kid_cgi_ps[i] = pi.hProcess; + if (!AssignProcessToJobObject(job, pi.hProcess)) { + DWORD err = GetLastError(); + char *err_text = php_win32_error_to_msg(err); + + fprintf(stderr, "unable to assign child process to job object: [0x%08lx]: %s\n", err, err_text); + php_win32_error_msg_free(err_text); + } + CloseHandle(pi.hThread); + } else { + DWORD err = GetLastError(); + char *err_text = php_win32_error_to_msg(err); + + kid_cgi_ps[i] = NULL; + + fprintf(stderr, "unable to spawn: [0x%08lx]: %s\n", err, err_text); + php_win32_error_msg_free(err_text); + } + } + + WaitForMultipleObjects(kids, kid_cgi_ps, FALSE, INFINITE); + } + +parent_loop_end: + /* restore my env */ + SetEnvironmentVariable("PHP_FCGI_CHILDREN", kid_buf); + + DeleteCriticalSection(&cleanup_lock); + + goto parent_out; + } else { + parent = 0; + } +#endif /* WIN32 */ + } + + zend_first_try { + while (!skip_getopt && (c = php_getopt(argc, argv, OPTIONS, &php_optarg, &php_optind, 1, 2)) != -1) { + switch (c) { + case 'T': + benchmark = 1; + { + char *comma = strchr(php_optarg, ','); + if (comma) { + warmup_repeats = atoi(php_optarg); + repeats = atoi(comma + 1); +#ifdef HAVE_VALGRIND + if (warmup_repeats > 0) { + CALLGRIND_STOP_INSTRUMENTATION; + } +#endif + } else { + repeats = atoi(php_optarg); + } + } +#ifdef HAVE_GETTIMEOFDAY + gettimeofday(&start, NULL); +#else + time(&start); +#endif + break; + case 'h': + case '?': + case PHP_GETOPT_INVALID_ARG: + if (request) { + fcgi_destroy_request(request); + } + fcgi_shutdown(); + no_headers = 1; + SG(headers_sent) = 1; + php_cgi_usage(argv[0]); + php_output_end_all(); + exit_status = 0; + if (c == PHP_GETOPT_INVALID_ARG) { + exit_status = 1; + } + goto out; + } + } + php_optind = orig_optind; + php_optarg = orig_optarg; + + /* start of FAST CGI loop */ + /* Initialise FastCGI request structure */ +#ifdef PHP_WIN32 + /* attempt to set security impersonation for fastcgi + * will only happen on NT based OS, others will ignore it. */ + if (fastcgi && CGIG(impersonate)) { + fcgi_impersonate(); + } +#endif + while (!fastcgi || fcgi_accept_request(request) >= 0) { + SG(server_context) = fastcgi ? (void *)request : (void *) 1; + init_request_info(request); + + if (!cgi && !fastcgi) { + while ((c = php_getopt(argc, argv, OPTIONS, &php_optarg, &php_optind, 0, 2)) != -1) { + switch (c) { + + case 'a': /* interactive mode */ + printf("Interactive mode enabled\n\n"); + break; + + case 'C': /* don't chdir to the script directory */ + SG(options) |= SAPI_OPTION_NO_CHDIR; + break; + + case 'e': /* enable extended info output */ + CG(compiler_options) |= ZEND_COMPILE_EXTENDED_INFO; + break; + + case 'f': /* parse file */ + if (script_file) { + efree(script_file); + } + script_file = estrdup(php_optarg); + no_headers = 1; + break; + + case 'i': /* php info & quit */ + if (script_file) { + efree(script_file); + } + if (php_request_startup() == FAILURE) { + SG(server_context) = NULL; + php_module_shutdown(); + free(bindpath); + return FAILURE; + } + if (no_headers) { + SG(headers_sent) = 1; + SG(request_info).no_headers = 1; + } + php_print_info(0xFFFFFFFF); + php_request_shutdown((void *) 0); + fcgi_shutdown(); + exit_status = 0; + goto out; + + case 'l': /* syntax check mode */ + no_headers = 1; + behavior = PHP_MODE_LINT; + break; + + case 'm': /* list compiled in modules */ + if (script_file) { + efree(script_file); + } + SG(headers_sent) = 1; + php_printf("[PHP Modules]\n"); + print_modules(); + php_printf("\n[Zend Modules]\n"); + print_extensions(); + php_printf("\n"); + php_output_end_all(); + fcgi_shutdown(); + exit_status = 0; + goto out; + + case 'q': /* do not generate HTTP headers */ + no_headers = 1; + break; + + case 'v': /* show php version & quit */ + if (script_file) { + efree(script_file); + } + no_headers = 1; + if (php_request_startup() == FAILURE) { + SG(server_context) = NULL; + php_module_shutdown(); + free(bindpath); + return FAILURE; + } + SG(headers_sent) = 1; + SG(request_info).no_headers = 1; +#if ZEND_DEBUG + php_printf("PHP %s (%s) (built: %s %s) (DEBUG)\nCopyright (c) The PHP Group\n%s", PHP_VERSION, sapi_module.name, __DATE__, __TIME__, get_zend_version()); +#else + php_printf("PHP %s (%s) (built: %s %s)\nCopyright (c) The PHP Group\n%s", PHP_VERSION, sapi_module.name, __DATE__, __TIME__, get_zend_version()); +#endif + php_request_shutdown((void *) 0); + fcgi_shutdown(); + exit_status = 0; + goto out; + + case 'w': + behavior = PHP_MODE_STRIP; + break; + + case 'z': /* load extension file */ + zend_load_extension(php_optarg); + break; + + default: + break; + } + } + + if (script_file) { + /* override path_translated if -f on command line */ + if (SG(request_info).path_translated) efree(SG(request_info).path_translated); + SG(request_info).path_translated = script_file; + /* before registering argv to module exchange the *new* argv[0] */ + /* we can achieve this without allocating more memory */ + SG(request_info).argc = argc - (php_optind - 1); + SG(request_info).argv = &argv[php_optind - 1]; + SG(request_info).argv[0] = script_file; + } else if (argc > php_optind) { + /* file is on command line, but not in -f opt */ + if (SG(request_info).path_translated) efree(SG(request_info).path_translated); + SG(request_info).path_translated = estrdup(argv[php_optind]); + /* arguments after the file are considered script args */ + SG(request_info).argc = argc - php_optind; + SG(request_info).argv = &argv[php_optind]; + } + + if (no_headers) { + SG(headers_sent) = 1; + SG(request_info).no_headers = 1; + } + + /* all remaining arguments are part of the query string + * this section of code concatenates all remaining arguments + * into a single string, separating args with a & + * this allows command lines like: + * + * test.php v1=test v2=hello+world! + * test.php "v1=test&v2=hello world!" + * test.php v1=test "v2=hello world!" + */ + if (!SG(request_info).query_string && argc > php_optind) { + size_t slen = strlen(PG(arg_separator).input); + len = 0; + for (i = php_optind; i < argc; i++) { + if (i < (argc - 1)) { + len += strlen(argv[i]) + slen; + } else { + len += strlen(argv[i]); + } + } + + len += 2; + s = malloc(len); + *s = '\0'; /* we are pretending it came from the environment */ + for (i = php_optind; i < argc; i++) { + strlcat(s, argv[i], len); + if (i < (argc - 1)) { + strlcat(s, PG(arg_separator).input, len); + } + } + SG(request_info).query_string = s; + free_query_string = 1; + } + } /* end !cgi && !fastcgi */ + + /* + we never take stdin if we're (f)cgi, always + rely on the web server giving us the info + we need in the environment. + */ + if (SG(request_info).path_translated || cgi || fastcgi) { + zend_stream_init_filename(&file_handle, SG(request_info).path_translated); + } else { + zend_stream_init_fp(&file_handle, stdin, "Standard input code"); + } + + /* request startup only after we've done all we can to + * get path_translated */ + if (php_request_startup() == FAILURE) { + if (fastcgi) { + fcgi_finish_request(request, 1); + } + SG(server_context) = NULL; + php_module_shutdown(); + return FAILURE; + } + if (no_headers) { + SG(headers_sent) = 1; + SG(request_info).no_headers = 1; + } + + /* + at this point path_translated will be set if: + 1. we are running from shell and got filename was there + 2. we are running as cgi or fastcgi + */ + if (cgi || fastcgi || SG(request_info).path_translated) { + if (php_fopen_primary_script(&file_handle) == FAILURE) { + zend_try { + if (errno == EACCES) { + SG(sapi_headers).http_response_code = 403; + PUTS("Access denied.\n"); + } else { + SG(sapi_headers).http_response_code = 404; + PUTS("No input file specified.\n"); + } + } zend_catch { + } zend_end_try(); + /* we want to serve more requests if this is fastcgi + * so cleanup and continue, request shutdown is + * handled later */ + if (fastcgi) { + goto fastcgi_request_done; + } + + if (SG(request_info).path_translated) { + efree(SG(request_info).path_translated); + SG(request_info).path_translated = NULL; + } + + if (free_query_string && SG(request_info).query_string) { + free(SG(request_info).query_string); + SG(request_info).query_string = NULL; + } + + php_request_shutdown((void *) 0); + SG(server_context) = NULL; + php_module_shutdown(); + sapi_shutdown(); +#ifdef ZTS + tsrm_shutdown(); +#endif + free(bindpath); + return FAILURE; + } + } + + if (CGIG(check_shebang_line)) { + CG(skip_shebang) = 1; + } + + switch (behavior) { + case PHP_MODE_STANDARD: + php_execute_script(&file_handle); + break; + case PHP_MODE_LINT: + PG(during_request_startup) = 0; + exit_status = php_lint_script(&file_handle); + if (exit_status == SUCCESS) { + zend_printf("No syntax errors detected in %s\n", file_handle.filename); + } else { + zend_printf("Errors parsing %s\n", file_handle.filename); + } + break; + case PHP_MODE_STRIP: + if (open_file_for_scanning(&file_handle) == SUCCESS) { + zend_strip(); + zend_file_handle_dtor(&file_handle); + php_output_teardown(); + } + return SUCCESS; + break; + case PHP_MODE_HIGHLIGHT: + { + zend_syntax_highlighter_ini syntax_highlighter_ini; + + if (open_file_for_scanning(&file_handle) == SUCCESS) { + php_get_highlight_struct(&syntax_highlighter_ini); + zend_highlight(&syntax_highlighter_ini); + if (fastcgi) { + goto fastcgi_request_done; + } + zend_file_handle_dtor(&file_handle); + php_output_teardown(); + } + return SUCCESS; + } + break; + } + +fastcgi_request_done: + { + if (SG(request_info).path_translated) { + efree(SG(request_info).path_translated); + SG(request_info).path_translated = NULL; + } + + php_request_shutdown((void *) 0); + + if (exit_status == 0) { + exit_status = EG(exit_status); + } + + if (free_query_string && SG(request_info).query_string) { + free(SG(request_info).query_string); + SG(request_info).query_string = NULL; + } + } + + if (!fastcgi) { + if (benchmark) { + if (warmup_repeats) { + warmup_repeats--; + if (!warmup_repeats) { +#ifdef HAVE_GETTIMEOFDAY + gettimeofday(&start, NULL); +#else + time(&start); +#endif +#ifdef HAVE_VALGRIND + CALLGRIND_START_INSTRUMENTATION; +#endif + } + continue; + } else { + repeats--; + if (repeats > 0) { + script_file = NULL; + php_optind = orig_optind; + php_optarg = orig_optarg; + continue; + } + } + } + break; + } + + /* only fastcgi will get here */ + requests++; + if (max_requests && (requests == max_requests)) { + fcgi_finish_request(request, 1); + free(bindpath); + if (max_requests != 1) { + /* no need to return exit_status of the last request */ + exit_status = 0; + } + break; + } + /* end of fastcgi loop */ + } + + if (request) { + fcgi_destroy_request(request); + } + fcgi_shutdown(); + + if (cgi_sapi_module.php_ini_path_override) { + free(cgi_sapi_module.php_ini_path_override); + } + if (cgi_sapi_module.ini_entries) { + free(cgi_sapi_module.ini_entries); + } + } zend_catch { + exit_status = 255; + } zend_end_try(); + +out: + if (benchmark) { + int sec; +#ifdef HAVE_GETTIMEOFDAY + int usec; + + gettimeofday(&end, NULL); + sec = (int)(end.tv_sec - start.tv_sec); + if (end.tv_usec >= start.tv_usec) { + usec = (int)(end.tv_usec - start.tv_usec); + } else { + sec -= 1; + usec = (int)(end.tv_usec + 1000000 - start.tv_usec); + } + fprintf(stderr, "\nElapsed time: %d.%06d sec\n", sec, usec); +#else + time(&end); + sec = (int)(end - start); + fprintf(stderr, "\nElapsed time: %d sec\n", sec); +#endif + } + +parent_out: + + SG(server_context) = NULL; + php_module_shutdown(); + sapi_shutdown(); + +#ifdef ZTS + tsrm_shutdown(); +#endif + +#if defined(PHP_WIN32) && ZEND_DEBUG && 0 + _CrtDumpMemoryLeaks(); +#endif + + return exit_status; +} +/* }}} */ diff --git a/sapi/fpm/fpm/fpm_stdio.c b/sapi/fpm/fpm/fpm_stdio.c index ddedfb48c7cfc..9d87273314ad2 100644 --- a/sapi/fpm/fpm/fpm_stdio.c +++ b/sapi/fpm/fpm/fpm_stdio.c @@ -177,7 +177,7 @@ static void fpm_stdio_child_said(struct fpm_event_s *ev, short which, void *arg) if ((sizeof(FPM_STDIO_CMD_FLUSH) - cmd_pos) <= in_buf && !memcmp(buf, &FPM_STDIO_CMD_FLUSH[cmd_pos], sizeof(FPM_STDIO_CMD_FLUSH) - cmd_pos)) { zlog_stream_finish(log_stream); - start = cmd_pos; + start = sizeof(FPM_STDIO_CMD_FLUSH) - cmd_pos; } else { zlog_stream_str(log_stream, &FPM_STDIO_CMD_FLUSH[0], cmd_pos); } diff --git a/sapi/fpm/fpm/fpm_stdio.c.orig b/sapi/fpm/fpm/fpm_stdio.c.orig new file mode 100644 index 0000000000000..ddedfb48c7cfc --- /dev/null +++ b/sapi/fpm/fpm/fpm_stdio.c.orig @@ -0,0 +1,356 @@ + /* (c) 2007,2008 Andrei Nigmatulin */ + +#include "fpm_config.h" + +#include +#include +#include +#include +#include +#include + +#include "php_syslog.h" + +#include "fpm.h" +#include "fpm_children.h" +#include "fpm_cleanup.h" +#include "fpm_events.h" +#include "fpm_sockets.h" +#include "fpm_stdio.h" +#include "zlog.h" + +static int fd_stdout[2]; +static int fd_stderr[2]; + +int fpm_stdio_init_main() /* {{{ */ +{ + int fd = open("/dev/null", O_RDWR); + + if (0 > fd) { + zlog(ZLOG_SYSERROR, "failed to init stdio: open(\"/dev/null\")"); + return -1; + } + + if (0 > dup2(fd, STDIN_FILENO) || 0 > dup2(fd, STDOUT_FILENO)) { + zlog(ZLOG_SYSERROR, "failed to init stdio: dup2()"); + close(fd); + return -1; + } + close(fd); + return 0; +} +/* }}} */ + +static inline int fpm_use_error_log() { /* {{{ */ + /* + * the error_log is NOT used when running in foreground + * and from a tty (user looking at output). + * So, error_log is used by + * - SysV init launch php-fpm as a daemon + * - Systemd launch php-fpm in foreground + */ +#if HAVE_UNISTD_H + if (fpm_global_config.daemonize || (!isatty(STDERR_FILENO) && !fpm_globals.force_stderr)) { +#else + if (fpm_global_config.daemonize) { +#endif + return 1; + } + return 0; +} + +/* }}} */ +int fpm_stdio_init_final() /* {{{ */ +{ + if (fpm_use_error_log()) { + /* prevent duping if logging to syslog */ + if (fpm_globals.error_log_fd > 0 && fpm_globals.error_log_fd != STDERR_FILENO) { + + /* there might be messages to stderr from other parts of the code, we need to log them all */ + if (0 > dup2(fpm_globals.error_log_fd, STDERR_FILENO)) { + zlog(ZLOG_SYSERROR, "failed to init stdio: dup2()"); + return -1; + } + } +#ifdef HAVE_SYSLOG_H + else if (fpm_globals.error_log_fd == ZLOG_SYSLOG) { + /* dup to /dev/null when using syslog */ + dup2(STDOUT_FILENO, STDERR_FILENO); + } +#endif + } + zlog_set_launched(); + return 0; +} +/* }}} */ + +int fpm_stdio_init_child(struct fpm_worker_pool_s *wp) /* {{{ */ +{ +#ifdef HAVE_SYSLOG_H + if (fpm_globals.error_log_fd == ZLOG_SYSLOG) { + closelog(); /* ensure to close syslog not to interrupt with PHP syslog code */ + } else +#endif + + /* Notice: child cannot use master error_log + * because not aware when being reopen + * else, should use if (!fpm_use_error_log()) + */ + if (fpm_globals.error_log_fd > 0) { + close(fpm_globals.error_log_fd); + } + fpm_globals.error_log_fd = -1; + zlog_set_fd(-1); + + return 0; +} +/* }}} */ + +#define FPM_STDIO_CMD_FLUSH "\0fscf" + +int fpm_stdio_flush_child() /* {{{ */ +{ + return write(STDERR_FILENO, FPM_STDIO_CMD_FLUSH, sizeof(FPM_STDIO_CMD_FLUSH)); +} +/* }}} */ + +static void fpm_stdio_child_said(struct fpm_event_s *ev, short which, void *arg) /* {{{ */ +{ + static const int max_buf_size = 1024; + int fd = ev->fd; + char buf[max_buf_size]; + struct fpm_child_s *child; + int is_stdout; + struct fpm_event_s *event; + int in_buf = 0, cmd_pos = 0, pos, start; + int read_fail = 0, create_log_stream; + struct zlog_stream *log_stream; + + if (!arg) { + return; + } + child = (struct fpm_child_s *)arg; + + is_stdout = (fd == child->fd_stdout); + if (is_stdout) { + event = &child->ev_stdout; + } else { + event = &child->ev_stderr; + } + + create_log_stream = !child->log_stream; + if (create_log_stream) { + log_stream = child->log_stream = malloc(sizeof(struct zlog_stream)); + zlog_stream_init_ex(log_stream, ZLOG_WARNING, STDERR_FILENO); + zlog_stream_set_decorating(log_stream, child->wp->config->decorate_workers_output); + zlog_stream_set_wrapping(log_stream, ZLOG_TRUE); + zlog_stream_set_msg_prefix(log_stream, STREAM_SET_MSG_PREFIX_FMT, + child->wp->config->name, (int) child->pid, is_stdout ? "stdout" : "stderr"); + zlog_stream_set_msg_quoting(log_stream, ZLOG_TRUE); + zlog_stream_set_is_stdout(log_stream, is_stdout); + zlog_stream_set_child_pid(log_stream, (int)child->pid); + } else { + log_stream = child->log_stream; + // if fd type (stdout/stderr) or child's pid is changed, + // then the stream will be finished and msg's prefix will be reinitialized + if (log_stream->is_stdout != (unsigned int)is_stdout || log_stream->child_pid != (int)child->pid) { + zlog_stream_finish(log_stream); + zlog_stream_set_msg_prefix(log_stream, STREAM_SET_MSG_PREFIX_FMT, + child->wp->config->name, (int) child->pid, is_stdout ? "stdout" : "stderr"); + zlog_stream_set_is_stdout(log_stream, is_stdout); + zlog_stream_set_child_pid(log_stream, (int)child->pid); + } + } + + while (1) { +stdio_read: + in_buf = read(fd, buf, max_buf_size - 1); + if (in_buf <= 0) { /* no data */ + if (in_buf == 0 || (errno != EAGAIN && errno != EWOULDBLOCK)) { + /* pipe is closed or error */ + read_fail = (in_buf < 0) ? in_buf : 1; + } + break; + } + start = 0; + if (cmd_pos > 0) { + if ((sizeof(FPM_STDIO_CMD_FLUSH) - cmd_pos) <= in_buf && + !memcmp(buf, &FPM_STDIO_CMD_FLUSH[cmd_pos], sizeof(FPM_STDIO_CMD_FLUSH) - cmd_pos)) { + zlog_stream_finish(log_stream); + start = cmd_pos; + } else { + zlog_stream_str(log_stream, &FPM_STDIO_CMD_FLUSH[0], cmd_pos); + } + cmd_pos = 0; + } + for (pos = start; pos < in_buf; pos++) { + switch (buf[pos]) { + case '\n': + zlog_stream_str(log_stream, buf + start, pos - start); + zlog_stream_finish(log_stream); + start = pos + 1; + break; + case '\0': + if (pos + sizeof(FPM_STDIO_CMD_FLUSH) <= in_buf) { + if (!memcmp(buf + pos, FPM_STDIO_CMD_FLUSH, sizeof(FPM_STDIO_CMD_FLUSH))) { + zlog_stream_str(log_stream, buf + start, pos - start); + zlog_stream_finish(log_stream); + start = pos + sizeof(FPM_STDIO_CMD_FLUSH); + pos = start - 1; + } + } else if (!memcmp(buf + pos, FPM_STDIO_CMD_FLUSH, in_buf - pos)) { + cmd_pos = in_buf - pos; + zlog_stream_str(log_stream, buf + start, pos - start); + goto stdio_read; + } + break; + } + } + if (start < pos) { + zlog_stream_str(log_stream, buf + start, pos - start); + } + } + + if (read_fail) { + if (create_log_stream) { + zlog_stream_set_msg_suffix(log_stream, NULL, ", pipe is closed"); + zlog_stream_finish(log_stream); + } + if (read_fail < 0) { + zlog(ZLOG_SYSERROR, "unable to read what child say"); + } + + fpm_event_del(event); + + if (is_stdout) { + close(child->fd_stdout); + child->fd_stdout = -1; + } else { + close(child->fd_stderr); + child->fd_stderr = -1; + } + } +} +/* }}} */ + +int fpm_stdio_prepare_pipes(struct fpm_child_s *child) /* {{{ */ +{ + if (0 == child->wp->config->catch_workers_output) { /* not required */ + return 0; + } + + if (0 > pipe(fd_stdout)) { + zlog(ZLOG_SYSERROR, "failed to prepare the stdout pipe"); + return -1; + } + + if (0 > pipe(fd_stderr)) { + zlog(ZLOG_SYSERROR, "failed to prepare the stderr pipe"); + close(fd_stdout[0]); + close(fd_stdout[1]); + return -1; + } + + if (0 > fd_set_blocked(fd_stdout[0], 0) || 0 > fd_set_blocked(fd_stderr[0], 0)) { + zlog(ZLOG_SYSERROR, "failed to unblock pipes"); + close(fd_stdout[0]); + close(fd_stdout[1]); + close(fd_stderr[0]); + close(fd_stderr[1]); + return -1; + } + return 0; +} +/* }}} */ + +int fpm_stdio_parent_use_pipes(struct fpm_child_s *child) /* {{{ */ +{ + if (0 == child->wp->config->catch_workers_output) { /* not required */ + return 0; + } + + close(fd_stdout[1]); + close(fd_stderr[1]); + + child->fd_stdout = fd_stdout[0]; + child->fd_stderr = fd_stderr[0]; + + fpm_event_set(&child->ev_stdout, child->fd_stdout, FPM_EV_READ, fpm_stdio_child_said, child); + fpm_event_add(&child->ev_stdout, 0); + + fpm_event_set(&child->ev_stderr, child->fd_stderr, FPM_EV_READ, fpm_stdio_child_said, child); + fpm_event_add(&child->ev_stderr, 0); + return 0; +} +/* }}} */ + +int fpm_stdio_discard_pipes(struct fpm_child_s *child) /* {{{ */ +{ + if (0 == child->wp->config->catch_workers_output) { /* not required */ + return 0; + } + + close(fd_stdout[1]); + close(fd_stderr[1]); + + close(fd_stdout[0]); + close(fd_stderr[0]); + return 0; +} +/* }}} */ + +void fpm_stdio_child_use_pipes(struct fpm_child_s *child) /* {{{ */ +{ + if (child->wp->config->catch_workers_output) { + dup2(fd_stdout[1], STDOUT_FILENO); + dup2(fd_stderr[1], STDERR_FILENO); + close(fd_stdout[0]); close(fd_stdout[1]); + close(fd_stderr[0]); close(fd_stderr[1]); + } else { + /* stdout of parent is always /dev/null */ + dup2(STDOUT_FILENO, STDERR_FILENO); + } +} +/* }}} */ + +int fpm_stdio_open_error_log(int reopen) /* {{{ */ +{ + int fd; + +#ifdef HAVE_SYSLOG_H + if (!strcasecmp(fpm_global_config.error_log, "syslog")) { + php_openlog(fpm_global_config.syslog_ident, LOG_PID | LOG_CONS, fpm_global_config.syslog_facility); + fpm_globals.error_log_fd = ZLOG_SYSLOG; + if (fpm_use_error_log()) { + zlog_set_fd(fpm_globals.error_log_fd); + } + return 0; + } +#endif + + fd = open(fpm_global_config.error_log, O_WRONLY | O_APPEND | O_CREAT, S_IRUSR | S_IWUSR); + if (0 > fd) { + zlog(ZLOG_SYSERROR, "failed to open error_log (%s)", fpm_global_config.error_log); + return -1; + } + + if (reopen) { + if (fpm_use_error_log()) { + dup2(fd, STDERR_FILENO); + } + + dup2(fd, fpm_globals.error_log_fd); + close(fd); + fd = fpm_globals.error_log_fd; /* for FD_CLOSEXEC to work */ + } else { + fpm_globals.error_log_fd = fd; + if (fpm_use_error_log()) { + zlog_set_fd(fpm_globals.error_log_fd); + } + } + if (0 > fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC)) { + zlog(ZLOG_WARNING, "failed to change attribute of error_log"); + } + return 0; +} +/* }}} */ diff --git a/sapi/fpm/tests/log-bwp-msg-flush-split-sep-pos-end.phpt b/sapi/fpm/tests/log-bwp-msg-flush-split-sep-pos-end.phpt new file mode 100644 index 0000000000000..528263200803e --- /dev/null +++ b/sapi/fpm/tests/log-bwp-msg-flush-split-sep-pos-end.phpt @@ -0,0 +1,47 @@ +--TEST-- +FPM: Buffered worker output plain log with msg with flush split position towards separator end +--SKIPIF-- + +--FILE-- +start(); +$tester->expectLogStartNotices(); +$tester->request()->expectEmptyBody(); +$tester->expectLogLine(str_repeat('a', 1013) . "Quarkslab", decorated: false); +$tester->expectLogLine("Quarkslab", decorated: false); +$tester->terminate(); +$tester->expectLogTerminatingNotices(); +$tester->close(); + +?> +Done +--EXPECT-- +Done +--CLEAN-- + diff --git a/sapi/fpm/tests/log-bwp-msg-flush-split-sep-pos-start.phpt b/sapi/fpm/tests/log-bwp-msg-flush-split-sep-pos-start.phpt new file mode 100644 index 0000000000000..3490593855328 --- /dev/null +++ b/sapi/fpm/tests/log-bwp-msg-flush-split-sep-pos-start.phpt @@ -0,0 +1,47 @@ +--TEST-- +FPM: Buffered worker output plain log with msg with flush split position towards separator start +--SKIPIF-- + +--FILE-- +start(); +$tester->expectLogStartNotices(); +$tester->request()->expectEmptyBody(); +$tester->expectLogLine(str_repeat('a', 1009) . "Quarkslab", decorated: false); +$tester->expectLogLine("Quarkslab", decorated: false); +$tester->terminate(); +$tester->expectLogTerminatingNotices(); +$tester->close(); + +?> +Done +--EXPECT-- +Done +--CLEAN-- + diff --git a/tests/basic/GHSA-9pqp-7h25-4f32.inc b/tests/basic/GHSA-9pqp-7h25-4f32.inc new file mode 100644 index 0000000000000..adf72a361a2cb --- /dev/null +++ b/tests/basic/GHSA-9pqp-7h25-4f32.inc @@ -0,0 +1,3 @@ + +--FILE-- + '1', + 'CONTENT_TYPE' => "multipart/form-data; boundary=$boundary", + 'CONTENT_LENGTH' => strlen($body), + 'REQUEST_METHOD' => 'POST', + 'SCRIPT_FILENAME' => __DIR__ . '/GHSA-9pqp-7h25-4f32.inc', + ]); + + $spec = [ + 0 => ['pipe', 'r'], + 1 => STDOUT, + 2 => STDOUT, + ]; + + $pipes = []; + + print "Starting...\n"; + + $handle = proc_open($cmd, $spec, $pipes, getcwd(), $env); + + fwrite($pipes[0], $body); + + $status = proc_close($handle); + + print "\n"; +} + +for ($offset = -1; $offset <= 1; $offset++) { + test(FILLUNIT - strlen("\r\n--") + $offset); +} + +?> +--EXPECTF-- +Boundary len: 5115 +Starting... +X-Powered-By: %s +Content-type: text/html; charset=UTF-8 + +Hello world +array(1) { + ["koko"]=> + string(5124) "BBB +--AAA%sCCC" +} + +Boundary len: 5116 +Starting... +X-Powered-By: %s +Content-type: text/html; charset=UTF-8 + +Hello world +array(1) { + ["koko"]=> + string(5125) "BBB +--AAA%sCCC" +} + +Boundary len: 5117 +Starting... +X-Powered-By: %s +Content-type: text/html; charset=UTF-8 + +
+Warning: Boundary too large in multipart/form-data POST data in Unknown on line 0
+Hello world +array(0) { +} +