" . $exception->getMessage() . "
" . $exception->getTraceAsString(), ""); }); /** * Get input from query string or return default value if not set. * * @param mixed $key as string or array of string values to look for in $_GET. * @param mixed $default value to return when $key is not set in $_GET. * * @return mixed value from $_GET or default value. */ function get($key, $default = null) { if (is_array($key)) { foreach ($key as $val) { if (isset($_GET[$val])) { return $_GET[$val]; } } } elseif (isset($_GET[$key])) { return $_GET[$key]; } return $default; } /** * Get input from query string and set to $defined if defined or else $undefined. * * @param mixed $key as string or array of string values to look for in $_GET. * @param mixed $defined value to return when $key is set in $_GET. * @param mixed $undefined value to return when $key is not set in $_GET. * * @return mixed value as $defined or $undefined. */ function getDefined($key, $defined, $undefined) { return get($key) === null ? $undefined : $defined; } /** * Get value from config array or default if key is not set in config array. * * @param string $key the key in the config array. * @param mixed $default value to be default if $key is not set in config. * * @return mixed value as $config[$key] or $default. */ function getConfig($key, $default) { global $config; return isset($config[$key]) ? $config[$key] : $default; } /** * Log when verbose mode, when used without argument it returns the result. * * @param string $msg to log. * * @return void or array. */ function verbose($msg = null) { global $verbose; static $log = array(); if (!$verbose) { return; } if (is_null($msg)) { return $log; } $log[] = $msg; } /** * Get configuration options from file, if the file exists, else use $config * if its defined or create an empty $config. */ $configFile = __DIR__.'/'.basename(__FILE__, '.php').'_config.php'; if (is_file($configFile)) { $config = require $configFile; } else if (!isset($config)) { $config = array(); } /** * verbose, v - do a verbose dump of what happens */ $verbose = getDefined(array('verbose', 'v'), true, false); verbose("img.php version = $version"); /** * Set mode as strict, production or development. * Default is production environment. */ $mode = getConfig('mode', 'production'); // Settings for any mode set_time_limit(20); ini_set('gd.jpeg_ignore_warning', 1); if (!extension_loaded('gd')) { errorPage("Extension gd is nod loaded."); } // Specific settings for each mode if ($mode == 'strict') { error_reporting(0); ini_set('display_errors', 0); ini_set('log_errors', 1); $verbose = false; } else if ($mode == 'production') { error_reporting(-1); ini_set('display_errors', 0); ini_set('log_errors', 1); $verbose = false; } else if ($mode == 'development') { error_reporting(-1); ini_set('display_errors', 1); ini_set('log_errors', 0); } else { errorPage("Unknown mode: $mode"); } verbose("mode = $mode"); verbose("error log = " . ini_get('error_log')); /** * Set default timezone if not set or if its set in the config-file. */ $defaultTimezone = getConfig('default_timezone', null); if ($defaultTimezone) { date_default_timezone_set($defaultTimezone); } else if (!ini_get('default_timezone')) { date_default_timezone_set('UTC'); } /** * Check if passwords are configured, used and match. * Options decide themself if they require passwords to be used. */ $pwdConfig = getConfig('password', false); $pwdAlways = getConfig('password_always', false); $pwd = get(array('password', 'pwd'), null); // Check if passwords match, if configured to use passwords $passwordMatch = null; if ($pwdAlways) { $passwordMatch = ($pwdConfig === $pwd); if (!$passwordMatch) { errorPage("Password required and does not match or exists."); } } elseif ($pwdConfig && $pwd) { $passwordMatch = ($pwdConfig === $pwd); } verbose("password match = $passwordMatch"); /** * Prevent hotlinking, leeching, of images by controlling who access them * from where. * */ $allowHotlinking = getConfig('allow_hotlinking', true); $hotlinkingWhitelist = getConfig('hotlinking_whitelist', array()); $serverName = isset($_SERVER['SERVER_NAME']) ? $_SERVER['SERVER_NAME'] : null; $referer = isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : null; $refererHost = parse_url($referer, PHP_URL_HOST); if (!$allowHotlinking) { if ($passwordMatch) { ; // Always allow when password match } else if ($passwordMatch === false) { errorPage("Hotlinking/leeching not allowed when password missmatch."); } else if (!$referer) { errorPage("Hotlinking/leeching not allowed and referer is missing."); } else if (strcmp($serverName, $refererHost) == 0) { ; // Allow when serverName matches refererHost } else if (!empty($hotlinkingWhitelist)) { $allowedByWhitelist = false; foreach ($hotlinkingWhitelist as $val) { if (preg_match($val, $refererHost)) { $allowedByWhitelist = true; } } if (!$allowedByWhitelist) { errorPage("Hotlinking/leeching not allowed by whitelist."); } } else { errorPage("Hotlinking/leeching not allowed."); } } verbose("allow_hotlinking = $allowHotlinking"); verbose("referer = $referer"); verbose("referer host = $refererHost"); /** * Get the source files. */ $autoloader = getConfig('autoloader', false); $cimageClass = getConfig('cimage_class', false); if ($autoloader) { require $autoloader; } else if ($cimageClass) { require $cimageClass; } /** * Create the class for the image. */ $img = new CImage(); $img->setVerbose($verbose); /** * Allow or disallow remote download of images from other servers. * Passwords apply if used. * */ $allowRemote = getConfig('remote_allow', false); if ($allowRemote && $passwordMatch !== false) { $pattern = getConfig('remote_pattern', null); $img->setRemoteDownload($allowRemote, $pattern); $whitelist = getConfig('remote_whitelist', null); $img->setRemoteHostWhitelist($whitelist); } /** * shortcut, sc - extend arguments with a constant value, defined * in config-file. */ $shortcut = get(array('shortcut', 'sc'), null); $shortcutConfig = getConfig('shortcut', array( 'sepia' => "&f=grayscale&f0=brightness,-10&f1=contrast,-20&f2=colorize,120,60,0,0&sharpen", )); verbose("shortcut = $shortcut"); if (isset($shortcut) && isset($shortcutConfig[$shortcut])) { parse_str($shortcutConfig[$shortcut], $get); verbose("shortcut-constant = {$shortcutConfig[$shortcut]}"); $_GET = array_merge($_GET, $get); } /** * src - the source image file. */ $srcImage = get('src') or errorPage('Must set src-attribute.'); // Check for valid/invalid characters $imagePath = getConfig('image_path', __DIR__ . '/img/'); $imagePathConstraint = getConfig('image_path_constraint', true); $validFilename = getConfig('valid_filename', '#^[a-z0-9A-Z-/_\.:]+$#'); preg_match($validFilename, $srcImage) or errorPage('Filename contains invalid characters.'); if ($allowRemote && $img->isRemoteSource($srcImage)) { // If source is a remote file, ignore local file checks. } else if ($imagePathConstraint) { // Check that the image is a file below the directory 'image_path'. $pathToImage = realpath($imagePath . $srcImage); $imageDir = realpath($imagePath); is_file($pathToImage) or errorPage( 'Source image is not a valid file, check the filename and that a matching file exists on the filesystem.' ); substr_compare($imageDir, $pathToImage, 0, strlen($imageDir)) == 0 or errorPage( 'Security constraint: Source image is not below the directory "image_path" as specified in the config file img_config.php.' ); } verbose("src = $srcImage"); /** * Manage size constants from config file, use constants to replace values * for width and height. */ $sizeConstant = getConfig('size_constant', function () { // Set sizes to map constant to value, easier to use with width or height $sizes = array( 'w1' => 613, 'w2' => 630, ); // Add grid column width, useful for use as predefined size for width (or height). $gridColumnWidth = 30; $gridGutterWidth = 10; $gridColumns = 24; for ($i = 1; $i <= $gridColumns; $i++) { $sizes['c' . $i] = ($gridColumnWidth + $gridGutterWidth) * $i - $gridGutterWidth; } return $sizes; }); $sizes = call_user_func($sizeConstant); /** * width, w - set target width, affecting the resulting image width, height and resize options */ $newWidth = get(array('width', 'w')); $maxWidth = getConfig('max_width', 2000); // Check to replace predefined size if (isset($sizes[$newWidth])) { $newWidth = $sizes[$newWidth]; } // Support width as % of original width if ($newWidth[strlen($newWidth)-1] == '%') { is_numeric(substr($newWidth, 0, -1)) or errorPage('Width % not numeric.'); } else { is_null($newWidth) or ($newWidth > 10 && $newWidth <= $maxWidth) or errorPage('Width out of range.'); } verbose("new width = $newWidth"); /** * height, h - set target height, affecting the resulting image width, height and resize options */ $newHeight = get(array('height', 'h')); $maxHeight = getConfig('max_height', 2000); // Check to replace predefined size if (isset($sizes[$newHeight])) { $newHeight = $sizes[$newHeight]; } // height if ($newHeight[strlen($newHeight)-1] == '%') { is_numeric(substr($newHeight, 0, -1)) or errorPage('Height % out of range.'); } else { is_null($newHeight) or ($newHeight > 10 && $newHeight <= $maxHeight) or errorPage('Hight out of range.'); } verbose("new height = $newHeight"); /** * aspect-ratio, ar - affecting the resulting image width, height and resize options */ $aspectRatio = get(array('aspect-ratio', 'ar')); $aspectRatioConstant = getConfig('aspect_ratio_constant', function () { return array( '3:1' => 3/1, '3:2' => 3/2, '4:3' => 4/3, '8:5' => 8/5, '16:10' => 16/10, '16:9' => 16/9, 'golden' => 1.618, ); }); // Check to replace predefined aspect ratio $aspectRatios = call_user_func($aspectRatioConstant); $negateAspectRatio = ($aspectRatio[0] == '!') ? true : false; $aspectRatio = $negateAspectRatio ? substr($aspectRatio, 1) : $aspectRatio; if (isset($aspectRatios[$aspectRatio])) { $aspectRatio = $aspectRatios[$aspectRatio]; } if ($negateAspectRatio) { $aspectRatio = 1 / $aspectRatio; } is_null($aspectRatio) or is_numeric($aspectRatio) or errorPage('Aspect ratio out of range'); verbose("aspect ratio = $aspectRatio"); /** * crop-to-fit, cf - affecting the resulting image width, height and resize options */ $cropToFit = getDefined(array('crop-to-fit', 'cf'), true, false); verbose("crop to fit = $cropToFit"); /** * Set default background color from config file. */ $backgroundColor = getConfig('background_color', null); if ($backgroundColor) { $img->setDefaultBackgroundColor($backgroundColor); verbose("Using default background_color = $backgroundColor"); } /** * bgColor - Default background color to use */ $bgColor = get(array('bgColor', 'bg-color', 'bgc'), null); verbose("bgColor = $bgColor"); /** * fill-to-fit, ff - affecting the resulting image width, height and resize options */ $fillToFit = get(array('fill-to-fit', 'ff'), null); verbose("fill-to-fit = $fillToFit"); if ($fillToFit !== null) { if (!empty($fillToFit)) { $bgColor = $fillToFit; verbose("fillToFit changed bgColor to = $bgColor"); } $fillToFit = true; verbose("fill-to-fit (fixed) = $fillToFit"); } /** * no-ratio, nr, stretch - affecting the resulting image width, height and resize options */ $keepRatio = getDefined(array('no-ratio', 'nr', 'stretch'), false, true); verbose("keep ratio = $keepRatio"); /** * crop, c - affecting the resulting image width, height and resize options */ $crop = get(array('crop', 'c')); verbose("crop = $crop"); /** * area, a - affecting the resulting image width, height and resize options */ $area = get(array('area', 'a')); verbose("area = $area"); /** * skip-original, so - skip the original image and always process a new image */ $useOriginal = getDefined(array('skip-original', 'so'), false, true); verbose("use original = $useOriginal"); /** * no-cache, nc - skip the cached version and process and create a new version in cache. */ $useCache = getDefined(array('no-cache', 'nc'), false, true); verbose("use cache = $useCache"); /** * quality, q - set level of quality for jpeg images */ $quality = get(array('quality', 'q')); is_null($quality) or ($quality > 0 and $quality <= 100) or errorPage('Quality out of range'); verbose("quality = $quality"); /** * compress, co - what strategy to use when compressing png images */ $compress = get(array('compress', 'co')); is_null($compress) or ($compress > 0 and $compress <= 9) or errorPage('Compress out of range'); verbose("compress = $compress"); /** * save-as, sa - what type of image to save */ $saveAs = get(array('save-as', 'sa')); verbose("save as = $saveAs"); /** * scale, s - Processing option, scale up or down the image prior actual resize */ $scale = get(array('scale', 's')); is_null($scale) or ($scale >= 0 and $scale <= 400) or errorPage('Scale out of range'); verbose("scale = $scale"); /** * palette, p - Processing option, create a palette version of the image */ $palette = getDefined(array('palette', 'p'), true, false); verbose("palette = $palette"); /** * sharpen - Processing option, post filter for sharpen effect */ $sharpen = getDefined('sharpen', true, null); verbose("sharpen = $sharpen"); /** * emboss - Processing option, post filter for emboss effect */ $emboss = getDefined('emboss', true, null); verbose("emboss = $emboss"); /** * blur - Processing option, post filter for blur effect */ $blur = getDefined('blur', true, null); verbose("blur = $blur"); /** * rotateBefore - Rotate the image with an angle, before processing */ $rotateBefore = get(array('rotateBefore', 'rotate-before', 'rb')); is_null($rotateBefore) or ($rotateBefore >= -360 and $rotateBefore <= 360) or errorPage('RotateBefore out of range'); verbose("rotateBefore = $rotateBefore"); /** * rotateAfter - Rotate the image with an angle, before processing */ $rotateAfter = get(array('rotateAfter', 'rotate-after', 'ra', 'rotate', 'r')); is_null($rotateAfter) or ($rotateAfter >= -360 and $rotateAfter <= 360) or errorPage('RotateBefore out of range'); verbose("rotateAfter = $rotateAfter"); /** * autoRotate - Auto rotate based on EXIF information */ $autoRotate = getDefined(array('autoRotate', 'auto-rotate', 'aro'), true, false); verbose("autoRotate = $autoRotate"); /** * filter, f, f0-f9 - Processing option, post filter for various effects using imagefilter() */ $filters = array(); $filter = get(array('filter', 'f')); if ($filter) { $filters[] = $filter; } for ($i = 0; $i < 10; $i++) { $filter = get(array("filter{$i}", "f{$i}")); if ($filter) { $filters[] = $filter; } } verbose("filters = " . print_r($filters, 1)); /** * json - output the image as a JSON object with details on the image. */ $outputFormat = getDefined('json', 'json', null); verbose("json = $outputFormat"); /** * dpr - change to get larger image to easier support larger dpr, such as retina. */ $dpr = get(array('ppi', 'dpr', 'device-pixel-ratio'), 1); verbose("dpr = $dpr"); /** * convolve - image convolution as in http://php.net/manual/en/function.imageconvolution.php */ $convolve = get('convolve', null); $convolutionConstant = getConfig('convolution_constant', array()); // Check if the convolve is matching an existing constant if ($convolve && isset($convolutionConstant)) { $img->addConvolveExpressions($convolutionConstant); verbose("convolve constant = " . print_r($convolutionConstant, 1)); } verbose("convolve = " . print_r($convolve, 1)); /** * no-upscale, nu - Do not upscale smaller image to larger dimension. */ $upscale = getDefined(array('no-upscale', 'nu'), false, true); verbose("upscale = $upscale"); /** * Get details for post processing */ $postProcessing = getConfig('postprocessing', array( 'png_filter' => false, 'png_filter_cmd' => '/usr/local/bin/optipng -q', 'png_deflate' => false, 'png_deflate_cmd' => '/usr/local/bin/pngout -q', 'jpeg_optimize' => false, 'jpeg_optimize_cmd' => '/usr/local/bin/jpegtran -copy none -optimize', )); /** * alias - Save resulting image to another alias name. * Password always apply, must be defined. */ $alias = get('alias', null); $aliasPath = getConfig('alias_path', null); $validAliasname = getConfig('valid_aliasname', '#^[a-z0-9A-Z-_]+$#'); $aliasTarget = null; if ($alias && $aliasPath && $passwordMatch) { $aliasTarget = $aliasPath . $alias; $useCache = false; is_writable($aliasPath) or errorPage("Directory for alias is not writable."); preg_match($validAliasname, $alias) or errorPage('Filename for alias contains invalid characters. Do not add extension.'); } else if ($alias) { errorPage('Alias is not enabled in the config file or password not matching.'); } verbose("alias = $alias"); /** * Display image if verbose mode */ if ($verbose) { $query = array(); parse_str($_SERVER['QUERY_STRING'], $query); unset($query['verbose']); unset($query['v']); unset($query['nocache']); unset($query['nc']); unset($query['json']); $url1 = '?' . htmlentities(urldecode(http_build_query($query))); $url2 = '?' . urldecode(http_build_query($query)); echo <<
$url1