1
0
mirror of https://github.com/mosbth/cimage.git synced 2025-08-05 15:47:30 +02:00

* Using CWhitelist for checking hotlinking to images, fix #88.

* Added mode for `test` which enables logging verbose mode to file, fix #97.
This commit is contained in:
Mikael Roos
2015-07-25 22:21:12 +02:00
parent 01a868d925
commit 8bc710f3b8
9 changed files with 467 additions and 150 deletions

View File

@@ -318,6 +318,22 @@ class CImage
private $useCache = true; private $useCache = true;
/*
* Set whitelist for valid hostnames from where remote source can be
* downloaded.
*/
private $remoteHostWhitelist = null;
/*
* Do verbose logging to file by setting this to a filename.
*/
private $verboseFileName = null;
/** /**
* Properties, the class is mutable and the method setOptions() * Properties, the class is mutable and the method setOptions()
* decides (partly) what properties are created. * decides (partly) what properties are created.
@@ -418,10 +434,12 @@ class CImage
$this->allowRemote = $allow; $this->allowRemote = $allow;
$this->remotePattern = is_null($pattern) ? $this->remotePattern : $pattern; $this->remotePattern = is_null($pattern) ? $this->remotePattern : $pattern;
$this->log("Set remote download to: " $this->log(
"Set remote download to: "
. ($this->allowRemote ? "true" : "false") . ($this->allowRemote ? "true" : "false")
. " using pattern " . " using pattern "
. $this->remotePattern); . $this->remotePattern
);
return $this; return $this;
} }
@@ -455,7 +473,10 @@ class CImage
public function setRemoteHostWhitelist($whitelist = null) public function setRemoteHostWhitelist($whitelist = null)
{ {
$this->remoteHostWhitelist = $whitelist; $this->remoteHostWhitelist = $whitelist;
$this->log("Setting remote host whitelist to: " . print_r($this->remoteHostWhitelist, 1)); $this->log(
"Setting remote host whitelist to: "
. (is_null($whitelist) ? "null" : print_r($whitelist, 1))
);
return $this; return $this;
} }
@@ -472,14 +493,18 @@ class CImage
public function isRemoteSourceOnWhitelist($src) public function isRemoteSourceOnWhitelist($src)
{ {
if (is_null($this->remoteHostWhitelist)) { if (is_null($this->remoteHostWhitelist)) {
$allow = true; $this->log("Remote host on whitelist not configured - allowing.");
} else { return true;
$whitelist = new CWhitelist();
$hostname = parse_url($src, PHP_URL_HOST);
$allow = $whitelist->check($hostname, $this->remoteHostWhitelist);
} }
$this->log("Remote host is on whitelist: " . ($allow ? "true" : "false")); $whitelist = new CWhitelist();
$hostname = parse_url($src, PHP_URL_HOST);
$allow = $whitelist->check($hostname, $this->remoteHostWhitelist);
$this->log(
"Remote host is on whitelist: "
. ($allow ? "true" : "false")
);
return $allow; return $allow;
} }
@@ -2253,7 +2278,10 @@ class CImage
if ($this->verbose) { if ($this->verbose) {
$this->log("Last modified: " . $gmdate . " GMT"); $this->log("Last modified: " . $gmdate . " GMT");
$this->verboseOutput(); $this->verboseOutput();
exit;
if (is_null($this->verboseFileName)) {
exit;
}
} }
// Get details on image // Get details on image
@@ -2331,6 +2359,21 @@ class CImage
/**
* Do verbose output to a file.
*
* @param string $fileName where to write the verbose output.
*
* @return void
*/
public function setVerboseToFile($fileName)
{
$this->log("Setting verbose output to file.");
$this->verboseFileName = $fileName;
}
/** /**
* Do verbose output and print out the log and the actual images. * Do verbose output and print out the log and the actual images.
* *
@@ -2356,10 +2399,17 @@ class CImage
} }
} }
echo <<<EOD if (!is_null($this->verboseFileName)) {
file_put_contents(
$this->verboseFileName,
str_replace("<br/>", "\n", $log)
);
} else {
echo <<<EOD
<h1>CImage Verbose Output</h1> <h1>CImage Verbose Output</h1>
<pre>{$log}</pre> <pre>{$log}</pre>
EOD; EOD;
}
} }

View File

@@ -23,11 +23,11 @@ class CWhitelist
*/ */
public function set($whitelist = array()) public function set($whitelist = array())
{ {
if (is_array($whitelist)) { if (!is_array($whitelist)) {
$this->whitelist = $whitelist;
} else {
throw new Exception("Whitelist is not of a supported format."); throw new Exception("Whitelist is not of a supported format.");
} }
$this->whitelist = $whitelist;
return $this; return $this;
} }

View File

@@ -8,6 +8,7 @@ Revision history
v0.7.0.x (latest) v0.7.0.x (latest)
------------------------------------- -------------------------------------
* Added mode for test which enables logging verbose mode to file.
* Improved codestyle and added `phpcs.xml` to start using phpcs to check code style, fix #95. * Improved codestyle and added `phpcs.xml` to start using phpcs to check code style, fix #95.
* Adding `composer.json` for publishing on packagist. * Adding `composer.json` for publishing on packagist.
* Add permalink to setup for comparing images with `webroot/compare/compare.php`, fix #92. * Add permalink to setup for comparing images with `webroot/compare/compare.php`, fix #92.

View File

@@ -162,4 +162,19 @@ class CImageRemoteDownloadTest extends \PHPUnit_Framework_TestCase
$res = $img->isRemoteSourceOnWhitelist("http://$hostname/img.jpg"); $res = $img->isRemoteSourceOnWhitelist("http://$hostname/img.jpg");
$this->assertFalse($res, "Should not be a valid hostname on the whitelist: '$hostname'."); $this->assertFalse($res, "Should not be a valid hostname on the whitelist: '$hostname'.");
} }
/**
* Test
*
* @return void
*
*/
public function testRemoteHostWhitelistNotConfigured()
{
$img = new CImage();
$res = $img->isRemoteSourceOnWhitelist(null);
$this->assertTrue($res, "Should allow when whitelist not configured.");
}
} }

View File

@@ -27,10 +27,10 @@ function errorPage($msg)
if ($mode == 'development') { if ($mode == 'development') {
die("[img.php] $msg"); die("[img.php] $msg");
} else {
error_log("[img.php] $msg");
die("HTTP/1.0 500 Internal Server Error");
} }
error_log("[img.php] $msg");
die("HTTP/1.0 500 Internal Server Error");
} }
@@ -39,7 +39,13 @@ function errorPage($msg)
* Custom exception handler. * Custom exception handler.
*/ */
set_exception_handler(function ($exception) { set_exception_handler(function ($exception) {
errorPage("<p><b>img.php: Uncaught exception:</b> <p>" . $exception->getMessage() . "</p><pre>" . $exception->getTraceAsString(), "</pre>"); errorPage(
"<p><b>img.php: Uncaught exception:</b> <p>"
. $exception->getMessage()
. "</p><pre>"
. $exception->getTraceAsString()
. "</pre>"
);
}); });
@@ -111,10 +117,10 @@ function getConfig($key, $default)
*/ */
function verbose($msg = null) function verbose($msg = null)
{ {
global $verbose; global $verbose, $verboseFile;
static $log = array(); static $log = array();
if (!$verbose) { if (!($verbose || $verboseFile)) {
return; return;
} }
@@ -143,8 +149,10 @@ if (is_file($configFile)) {
/** /**
* verbose, v - do a verbose dump of what happens * verbose, v - do a verbose dump of what happens
* vf - do verbose dump to file
*/ */
$verbose = getDefined(array('verbose', 'v'), true, false); $verbose = getDefined(array('verbose', 'v'), true, false);
$verboseFile = getDefined('vf', true, false);
verbose("img.php version = $version"); verbose("img.php version = $version");
@@ -170,19 +178,28 @@ if ($mode == 'strict') {
ini_set('display_errors', 0); ini_set('display_errors', 0);
ini_set('log_errors', 1); ini_set('log_errors', 1);
$verbose = false; $verbose = false;
$verboseFile = false;
} elseif ($mode == 'production') { } elseif ($mode == 'production') {
error_reporting(-1); error_reporting(-1);
ini_set('display_errors', 0); ini_set('display_errors', 0);
ini_set('log_errors', 1); ini_set('log_errors', 1);
$verbose = false; $verbose = false;
$verboseFile = false;
} elseif ($mode == 'development') { } elseif ($mode == 'development') {
error_reporting(-1); error_reporting(-1);
ini_set('display_errors', 1); ini_set('display_errors', 1);
ini_set('log_errors', 0); ini_set('log_errors', 0);
$verboseFile = false;
} elseif ($mode == 'test') {
error_reporting(-1);
ini_set('display_errors', 1);
ini_set('log_errors', 0);
} else { } else {
errorPage("Unknown mode: $mode"); errorPage("Unknown mode: $mode");
@@ -247,23 +264,22 @@ $refererHost = parse_url($referer, PHP_URL_HOST);
if (!$allowHotlinking) { if (!$allowHotlinking) {
if ($passwordMatch) { if ($passwordMatch) {
; // Always allow when password match ; // Always allow when password match
verbose("Hotlinking since passwordmatch");
} elseif ($passwordMatch === false) { } elseif ($passwordMatch === false) {
errorPage("Hotlinking/leeching not allowed when password missmatch."); errorPage("Hotlinking/leeching not allowed when password missmatch.");
} elseif (!$referer) { } elseif (!$referer) {
errorPage("Hotlinking/leeching not allowed and referer is missing."); errorPage("Hotlinking/leeching not allowed and referer is missing.");
} elseif (strcmp($serverName, $refererHost) == 0) { } elseif (strcmp($serverName, $refererHost) == 0) {
; // Allow when serverName matches refererHost ; // Allow when serverName matches refererHost
verbose("Hotlinking disallowed but serverName matches refererHost.");
} elseif (!empty($hotlinkingWhitelist)) { } elseif (!empty($hotlinkingWhitelist)) {
$whitelist = new CWhitelist();
$allowedByWhitelist = $whitelist->check($refererHost, $hotlinkingWhitelist);
$allowedByWhitelist = false; if ($allowedByWhitelist) {
foreach ($hotlinkingWhitelist as $val) { verbose("Hotlinking/leeching allowed by whitelist.");
if (preg_match($val, $refererHost)) { } else {
$allowedByWhitelist = true; errorPage("Hotlinking/leeching not allowed by whitelist. Referer: $referer.");
}
}
if (!$allowedByWhitelist) {
errorPage("Hotlinking/leeching not allowed by whitelist.");
} }
} else { } else {
@@ -295,7 +311,7 @@ if ($autoloader) {
* Create the class for the image. * Create the class for the image.
*/ */
$img = new CImage(); $img = new CImage();
$img->setVerbose($verbose); $img->setVerbose($verbose || $verboseFile);
@@ -815,6 +831,13 @@ verbose("alias = $alias");
/**
* Get the cachepath from config.
*/
$cachePath = getConfig('cache_path', __DIR__ . '/../cache/');
/** /**
* Display image if verbose mode * Display image if verbose mode
*/ */
@@ -853,15 +876,17 @@ EOD;
/** /**
* Get the cachepath from config. * Log verbose details to file
*/ */
$cachePath = getConfig('cache_path', __DIR__ . '/../cache/'); if ($verboseFile) {
$img->setVerboseToFile("$cachePath/log.txt");
}
/** /**
* Load, process and output the image * Load, process and output the image
*/ */
$img->log("Incoming arguments: " . print_r(verbose(), 1)) $img->log("Incoming arguments: " . print_r(verbose(), 1))
->setSaveFolder($cachePath) ->setSaveFolder($cachePath)
->useCache($useCache) ->useCache($useCache)

View File

@@ -201,9 +201,12 @@ return array(
/** /**
* Prevent leeching of images by controlling who can access them from where. * Prevent leeching of images by controlling the hostname of those who
* Default it to allow hotlinking. * can access the images. Default is to allow hotlinking.
* Password apply when hotlinking is disallowed, use password to allow. *
* Password apply when hotlinking is disallowed, use password to allow
* hotlinking.
*
* The whitelist is an array of regexpes for allowed hostnames that can * The whitelist is an array of regexpes for allowed hostnames that can
* hotlink images. * hotlink images.
* *
@@ -214,13 +217,11 @@ return array(
/* /*
'allow_hotlinking' => false, 'allow_hotlinking' => false,
'hotlinking_whitelist' => array( 'hotlinking_whitelist' => array(
'#^localhost$#', '^dbwebb\.se$',
'#^dbwebb\.se$#',
), ),
*/ */
/** /**
* Create custom shortcuts for more advanced expressions. * Create custom shortcuts for more advanced expressions.
* *

View File

@@ -916,6 +916,22 @@ class CImage
private $useCache = true; private $useCache = true;
/*
* Set whitelist for valid hostnames from where remote source can be
* downloaded.
*/
private $remoteHostWhitelist = null;
/*
* Do verbose logging to file by setting this to a filename.
*/
private $verboseFileName = null;
/** /**
* Properties, the class is mutable and the method setOptions() * Properties, the class is mutable and the method setOptions()
* decides (partly) what properties are created. * decides (partly) what properties are created.
@@ -1016,10 +1032,12 @@ class CImage
$this->allowRemote = $allow; $this->allowRemote = $allow;
$this->remotePattern = is_null($pattern) ? $this->remotePattern : $pattern; $this->remotePattern = is_null($pattern) ? $this->remotePattern : $pattern;
$this->log("Set remote download to: " $this->log(
"Set remote download to: "
. ($this->allowRemote ? "true" : "false") . ($this->allowRemote ? "true" : "false")
. " using pattern " . " using pattern "
. $this->remotePattern); . $this->remotePattern
);
return $this; return $this;
} }
@@ -1053,7 +1071,10 @@ class CImage
public function setRemoteHostWhitelist($whitelist = null) public function setRemoteHostWhitelist($whitelist = null)
{ {
$this->remoteHostWhitelist = $whitelist; $this->remoteHostWhitelist = $whitelist;
$this->log("Setting remote host whitelist to: " . print_r($this->remoteHostWhitelist, 1)); $this->log(
"Setting remote host whitelist to: "
. (is_null($whitelist) ? "null" : print_r($whitelist, 1))
);
return $this; return $this;
} }
@@ -1070,14 +1091,18 @@ class CImage
public function isRemoteSourceOnWhitelist($src) public function isRemoteSourceOnWhitelist($src)
{ {
if (is_null($this->remoteHostWhitelist)) { if (is_null($this->remoteHostWhitelist)) {
$allow = true; $this->log("Remote host on whitelist not configured - allowing.");
} else { return true;
$whitelist = new CWhitelist();
$hostname = parse_url($src, PHP_URL_HOST);
$allow = $whitelist->check($hostname, $this->remoteHostWhitelist);
} }
$this->log("Remote host is on whitelist: " . ($allow ? "true" : "false")); $whitelist = new CWhitelist();
$hostname = parse_url($src, PHP_URL_HOST);
$allow = $whitelist->check($hostname, $this->remoteHostWhitelist);
$this->log(
"Remote host is on whitelist: "
. ($allow ? "true" : "false")
);
return $allow; return $allow;
} }
@@ -2851,7 +2876,10 @@ class CImage
if ($this->verbose) { if ($this->verbose) {
$this->log("Last modified: " . $gmdate . " GMT"); $this->log("Last modified: " . $gmdate . " GMT");
$this->verboseOutput(); $this->verboseOutput();
exit;
if (is_null($this->verboseFileName)) {
exit;
}
} }
// Get details on image // Get details on image
@@ -2929,6 +2957,21 @@ class CImage
/**
* Do verbose output to a file.
*
* @param string $fileName where to write the verbose output.
*
* @return void
*/
public function setVerboseToFile($fileName)
{
$this->log("Setting verbose output to file.");
$this->verboseFileName = $fileName;
}
/** /**
* Do verbose output and print out the log and the actual images. * Do verbose output and print out the log and the actual images.
* *
@@ -2954,10 +2997,17 @@ class CImage
} }
} }
echo <<<EOD if (!is_null($this->verboseFileName)) {
file_put_contents(
$this->verboseFileName,
str_replace("<br/>", "\n", $log)
);
} else {
echo <<<EOD
<h1>CImage Verbose Output</h1> <h1>CImage Verbose Output</h1>
<pre>{$log}</pre> <pre>{$log}</pre>
EOD; EOD;
}
} }
@@ -3002,11 +3052,11 @@ class CWhitelist
*/ */
public function set($whitelist = array()) public function set($whitelist = array())
{ {
if (is_array($whitelist)) { if (!is_array($whitelist)) {
$this->whitelist = $whitelist;
} else {
throw new Exception("Whitelist is not of a supported format."); throw new Exception("Whitelist is not of a supported format.");
} }
$this->whitelist = $whitelist;
return $this; return $this;
} }
@@ -3070,10 +3120,10 @@ function errorPage($msg)
if ($mode == 'development') { if ($mode == 'development') {
die("[img.php] $msg"); die("[img.php] $msg");
} else {
error_log("[img.php] $msg");
die("HTTP/1.0 500 Internal Server Error");
} }
error_log("[img.php] $msg");
die("HTTP/1.0 500 Internal Server Error");
} }
@@ -3082,7 +3132,13 @@ function errorPage($msg)
* Custom exception handler. * Custom exception handler.
*/ */
set_exception_handler(function ($exception) { set_exception_handler(function ($exception) {
errorPage("<p><b>img.php: Uncaught exception:</b> <p>" . $exception->getMessage() . "</p><pre>" . $exception->getTraceAsString(), "</pre>"); errorPage(
"<p><b>img.php: Uncaught exception:</b> <p>"
. $exception->getMessage()
. "</p><pre>"
. $exception->getTraceAsString()
. "</pre>"
);
}); });
@@ -3154,10 +3210,10 @@ function getConfig($key, $default)
*/ */
function verbose($msg = null) function verbose($msg = null)
{ {
global $verbose; global $verbose, $verboseFile;
static $log = array(); static $log = array();
if (!$verbose) { if (!($verbose || $verboseFile)) {
return; return;
} }
@@ -3186,8 +3242,10 @@ if (is_file($configFile)) {
/** /**
* verbose, v - do a verbose dump of what happens * verbose, v - do a verbose dump of what happens
* vf - do verbose dump to file
*/ */
$verbose = getDefined(array('verbose', 'v'), true, false); $verbose = getDefined(array('verbose', 'v'), true, false);
$verboseFile = getDefined('vf', true, false);
verbose("img.php version = $version"); verbose("img.php version = $version");
@@ -3213,19 +3271,28 @@ if ($mode == 'strict') {
ini_set('display_errors', 0); ini_set('display_errors', 0);
ini_set('log_errors', 1); ini_set('log_errors', 1);
$verbose = false; $verbose = false;
$verboseFile = false;
} elseif ($mode == 'production') { } elseif ($mode == 'production') {
error_reporting(-1); error_reporting(-1);
ini_set('display_errors', 0); ini_set('display_errors', 0);
ini_set('log_errors', 1); ini_set('log_errors', 1);
$verbose = false; $verbose = false;
$verboseFile = false;
} elseif ($mode == 'development') { } elseif ($mode == 'development') {
error_reporting(-1); error_reporting(-1);
ini_set('display_errors', 1); ini_set('display_errors', 1);
ini_set('log_errors', 0); ini_set('log_errors', 0);
$verboseFile = false;
} elseif ($mode == 'test') {
error_reporting(-1);
ini_set('display_errors', 1);
ini_set('log_errors', 0);
} else { } else {
errorPage("Unknown mode: $mode"); errorPage("Unknown mode: $mode");
@@ -3290,23 +3357,22 @@ $refererHost = parse_url($referer, PHP_URL_HOST);
if (!$allowHotlinking) { if (!$allowHotlinking) {
if ($passwordMatch) { if ($passwordMatch) {
; // Always allow when password match ; // Always allow when password match
verbose("Hotlinking since passwordmatch");
} elseif ($passwordMatch === false) { } elseif ($passwordMatch === false) {
errorPage("Hotlinking/leeching not allowed when password missmatch."); errorPage("Hotlinking/leeching not allowed when password missmatch.");
} elseif (!$referer) { } elseif (!$referer) {
errorPage("Hotlinking/leeching not allowed and referer is missing."); errorPage("Hotlinking/leeching not allowed and referer is missing.");
} elseif (strcmp($serverName, $refererHost) == 0) { } elseif (strcmp($serverName, $refererHost) == 0) {
; // Allow when serverName matches refererHost ; // Allow when serverName matches refererHost
verbose("Hotlinking disallowed but serverName matches refererHost.");
} elseif (!empty($hotlinkingWhitelist)) { } elseif (!empty($hotlinkingWhitelist)) {
$whitelist = new CWhitelist();
$allowedByWhitelist = $whitelist->check($refererHost, $hotlinkingWhitelist);
$allowedByWhitelist = false; if ($allowedByWhitelist) {
foreach ($hotlinkingWhitelist as $val) { verbose("Hotlinking/leeching allowed by whitelist.");
if (preg_match($val, $refererHost)) { } else {
$allowedByWhitelist = true; errorPage("Hotlinking/leeching not allowed by whitelist. Referer: $referer.");
}
}
if (!$allowedByWhitelist) {
errorPage("Hotlinking/leeching not allowed by whitelist.");
} }
} else { } else {
@@ -3338,7 +3404,7 @@ if ($autoloader) {
* Create the class for the image. * Create the class for the image.
*/ */
$img = new CImage(); $img = new CImage();
$img->setVerbose($verbose); $img->setVerbose($verbose || $verboseFile);
@@ -3858,6 +3924,13 @@ verbose("alias = $alias");
/**
* Get the cachepath from config.
*/
$cachePath = getConfig('cache_path', __DIR__ . '/../cache/');
/** /**
* Display image if verbose mode * Display image if verbose mode
*/ */
@@ -3896,15 +3969,17 @@ EOD;
/** /**
* Get the cachepath from config. * Log verbose details to file
*/ */
$cachePath = getConfig('cache_path', __DIR__ . '/../cache/'); if ($verboseFile) {
$img->setVerboseToFile("$cachePath/log.txt");
}
/** /**
* Load, process and output the image * Load, process and output the image
*/ */
$img->log("Incoming arguments: " . print_r(verbose(), 1)) $img->log("Incoming arguments: " . print_r(verbose(), 1))
->setSaveFolder($cachePath) ->setSaveFolder($cachePath)
->useCache($useCache) ->useCache($useCache)

View File

@@ -916,6 +916,22 @@ class CImage
private $useCache = true; private $useCache = true;
/*
* Set whitelist for valid hostnames from where remote source can be
* downloaded.
*/
private $remoteHostWhitelist = null;
/*
* Do verbose logging to file by setting this to a filename.
*/
private $verboseFileName = null;
/** /**
* Properties, the class is mutable and the method setOptions() * Properties, the class is mutable and the method setOptions()
* decides (partly) what properties are created. * decides (partly) what properties are created.
@@ -1016,10 +1032,12 @@ class CImage
$this->allowRemote = $allow; $this->allowRemote = $allow;
$this->remotePattern = is_null($pattern) ? $this->remotePattern : $pattern; $this->remotePattern = is_null($pattern) ? $this->remotePattern : $pattern;
$this->log("Set remote download to: " $this->log(
"Set remote download to: "
. ($this->allowRemote ? "true" : "false") . ($this->allowRemote ? "true" : "false")
. " using pattern " . " using pattern "
. $this->remotePattern); . $this->remotePattern
);
return $this; return $this;
} }
@@ -1053,7 +1071,10 @@ class CImage
public function setRemoteHostWhitelist($whitelist = null) public function setRemoteHostWhitelist($whitelist = null)
{ {
$this->remoteHostWhitelist = $whitelist; $this->remoteHostWhitelist = $whitelist;
$this->log("Setting remote host whitelist to: " . print_r($this->remoteHostWhitelist, 1)); $this->log(
"Setting remote host whitelist to: "
. (is_null($whitelist) ? "null" : print_r($whitelist, 1))
);
return $this; return $this;
} }
@@ -1070,14 +1091,18 @@ class CImage
public function isRemoteSourceOnWhitelist($src) public function isRemoteSourceOnWhitelist($src)
{ {
if (is_null($this->remoteHostWhitelist)) { if (is_null($this->remoteHostWhitelist)) {
$allow = true; $this->log("Remote host on whitelist not configured - allowing.");
} else { return true;
$whitelist = new CWhitelist();
$hostname = parse_url($src, PHP_URL_HOST);
$allow = $whitelist->check($hostname, $this->remoteHostWhitelist);
} }
$this->log("Remote host is on whitelist: " . ($allow ? "true" : "false")); $whitelist = new CWhitelist();
$hostname = parse_url($src, PHP_URL_HOST);
$allow = $whitelist->check($hostname, $this->remoteHostWhitelist);
$this->log(
"Remote host is on whitelist: "
. ($allow ? "true" : "false")
);
return $allow; return $allow;
} }
@@ -2851,7 +2876,10 @@ class CImage
if ($this->verbose) { if ($this->verbose) {
$this->log("Last modified: " . $gmdate . " GMT"); $this->log("Last modified: " . $gmdate . " GMT");
$this->verboseOutput(); $this->verboseOutput();
exit;
if (is_null($this->verboseFileName)) {
exit;
}
} }
// Get details on image // Get details on image
@@ -2929,6 +2957,21 @@ class CImage
/**
* Do verbose output to a file.
*
* @param string $fileName where to write the verbose output.
*
* @return void
*/
public function setVerboseToFile($fileName)
{
$this->log("Setting verbose output to file.");
$this->verboseFileName = $fileName;
}
/** /**
* Do verbose output and print out the log and the actual images. * Do verbose output and print out the log and the actual images.
* *
@@ -2954,10 +2997,17 @@ class CImage
} }
} }
echo <<<EOD if (!is_null($this->verboseFileName)) {
file_put_contents(
$this->verboseFileName,
str_replace("<br/>", "\n", $log)
);
} else {
echo <<<EOD
<h1>CImage Verbose Output</h1> <h1>CImage Verbose Output</h1>
<pre>{$log}</pre> <pre>{$log}</pre>
EOD; EOD;
}
} }
@@ -3002,11 +3052,11 @@ class CWhitelist
*/ */
public function set($whitelist = array()) public function set($whitelist = array())
{ {
if (is_array($whitelist)) { if (!is_array($whitelist)) {
$this->whitelist = $whitelist;
} else {
throw new Exception("Whitelist is not of a supported format."); throw new Exception("Whitelist is not of a supported format.");
} }
$this->whitelist = $whitelist;
return $this; return $this;
} }
@@ -3070,10 +3120,10 @@ function errorPage($msg)
if ($mode == 'development') { if ($mode == 'development') {
die("[img.php] $msg"); die("[img.php] $msg");
} else {
error_log("[img.php] $msg");
die("HTTP/1.0 500 Internal Server Error");
} }
error_log("[img.php] $msg");
die("HTTP/1.0 500 Internal Server Error");
} }
@@ -3082,7 +3132,13 @@ function errorPage($msg)
* Custom exception handler. * Custom exception handler.
*/ */
set_exception_handler(function ($exception) { set_exception_handler(function ($exception) {
errorPage("<p><b>img.php: Uncaught exception:</b> <p>" . $exception->getMessage() . "</p><pre>" . $exception->getTraceAsString(), "</pre>"); errorPage(
"<p><b>img.php: Uncaught exception:</b> <p>"
. $exception->getMessage()
. "</p><pre>"
. $exception->getTraceAsString()
. "</pre>"
);
}); });
@@ -3154,10 +3210,10 @@ function getConfig($key, $default)
*/ */
function verbose($msg = null) function verbose($msg = null)
{ {
global $verbose; global $verbose, $verboseFile;
static $log = array(); static $log = array();
if (!$verbose) { if (!($verbose || $verboseFile)) {
return; return;
} }
@@ -3186,8 +3242,10 @@ if (is_file($configFile)) {
/** /**
* verbose, v - do a verbose dump of what happens * verbose, v - do a verbose dump of what happens
* vf - do verbose dump to file
*/ */
$verbose = getDefined(array('verbose', 'v'), true, false); $verbose = getDefined(array('verbose', 'v'), true, false);
$verboseFile = getDefined('vf', true, false);
verbose("img.php version = $version"); verbose("img.php version = $version");
@@ -3213,19 +3271,28 @@ if ($mode == 'strict') {
ini_set('display_errors', 0); ini_set('display_errors', 0);
ini_set('log_errors', 1); ini_set('log_errors', 1);
$verbose = false; $verbose = false;
$verboseFile = false;
} elseif ($mode == 'production') { } elseif ($mode == 'production') {
error_reporting(-1); error_reporting(-1);
ini_set('display_errors', 0); ini_set('display_errors', 0);
ini_set('log_errors', 1); ini_set('log_errors', 1);
$verbose = false; $verbose = false;
$verboseFile = false;
} elseif ($mode == 'development') { } elseif ($mode == 'development') {
error_reporting(-1); error_reporting(-1);
ini_set('display_errors', 1); ini_set('display_errors', 1);
ini_set('log_errors', 0); ini_set('log_errors', 0);
$verboseFile = false;
} elseif ($mode == 'test') {
error_reporting(-1);
ini_set('display_errors', 1);
ini_set('log_errors', 0);
} else { } else {
errorPage("Unknown mode: $mode"); errorPage("Unknown mode: $mode");
@@ -3290,23 +3357,22 @@ $refererHost = parse_url($referer, PHP_URL_HOST);
if (!$allowHotlinking) { if (!$allowHotlinking) {
if ($passwordMatch) { if ($passwordMatch) {
; // Always allow when password match ; // Always allow when password match
verbose("Hotlinking since passwordmatch");
} elseif ($passwordMatch === false) { } elseif ($passwordMatch === false) {
errorPage("Hotlinking/leeching not allowed when password missmatch."); errorPage("Hotlinking/leeching not allowed when password missmatch.");
} elseif (!$referer) { } elseif (!$referer) {
errorPage("Hotlinking/leeching not allowed and referer is missing."); errorPage("Hotlinking/leeching not allowed and referer is missing.");
} elseif (strcmp($serverName, $refererHost) == 0) { } elseif (strcmp($serverName, $refererHost) == 0) {
; // Allow when serverName matches refererHost ; // Allow when serverName matches refererHost
verbose("Hotlinking disallowed but serverName matches refererHost.");
} elseif (!empty($hotlinkingWhitelist)) { } elseif (!empty($hotlinkingWhitelist)) {
$whitelist = new CWhitelist();
$allowedByWhitelist = $whitelist->check($refererHost, $hotlinkingWhitelist);
$allowedByWhitelist = false; if ($allowedByWhitelist) {
foreach ($hotlinkingWhitelist as $val) { verbose("Hotlinking/leeching allowed by whitelist.");
if (preg_match($val, $refererHost)) { } else {
$allowedByWhitelist = true; errorPage("Hotlinking/leeching not allowed by whitelist. Referer: $referer.");
}
}
if (!$allowedByWhitelist) {
errorPage("Hotlinking/leeching not allowed by whitelist.");
} }
} else { } else {
@@ -3338,7 +3404,7 @@ if ($autoloader) {
* Create the class for the image. * Create the class for the image.
*/ */
$img = new CImage(); $img = new CImage();
$img->setVerbose($verbose); $img->setVerbose($verbose || $verboseFile);
@@ -3858,6 +3924,13 @@ verbose("alias = $alias");
/**
* Get the cachepath from config.
*/
$cachePath = getConfig('cache_path', __DIR__ . '/../cache/');
/** /**
* Display image if verbose mode * Display image if verbose mode
*/ */
@@ -3896,15 +3969,17 @@ EOD;
/** /**
* Get the cachepath from config. * Log verbose details to file
*/ */
$cachePath = getConfig('cache_path', __DIR__ . '/../cache/'); if ($verboseFile) {
$img->setVerboseToFile("$cachePath/log.txt");
}
/** /**
* Load, process and output the image * Load, process and output the image
*/ */
$img->log("Incoming arguments: " . print_r(verbose(), 1)) $img->log("Incoming arguments: " . print_r(verbose(), 1))
->setSaveFolder($cachePath) ->setSaveFolder($cachePath)
->useCache($useCache) ->useCache($useCache)

View File

@@ -916,6 +916,22 @@ class CImage
private $useCache = true; private $useCache = true;
/*
* Set whitelist for valid hostnames from where remote source can be
* downloaded.
*/
private $remoteHostWhitelist = null;
/*
* Do verbose logging to file by setting this to a filename.
*/
private $verboseFileName = null;
/** /**
* Properties, the class is mutable and the method setOptions() * Properties, the class is mutable and the method setOptions()
* decides (partly) what properties are created. * decides (partly) what properties are created.
@@ -1016,10 +1032,12 @@ class CImage
$this->allowRemote = $allow; $this->allowRemote = $allow;
$this->remotePattern = is_null($pattern) ? $this->remotePattern : $pattern; $this->remotePattern = is_null($pattern) ? $this->remotePattern : $pattern;
$this->log("Set remote download to: " $this->log(
"Set remote download to: "
. ($this->allowRemote ? "true" : "false") . ($this->allowRemote ? "true" : "false")
. " using pattern " . " using pattern "
. $this->remotePattern); . $this->remotePattern
);
return $this; return $this;
} }
@@ -1053,7 +1071,10 @@ class CImage
public function setRemoteHostWhitelist($whitelist = null) public function setRemoteHostWhitelist($whitelist = null)
{ {
$this->remoteHostWhitelist = $whitelist; $this->remoteHostWhitelist = $whitelist;
$this->log("Setting remote host whitelist to: " . print_r($this->remoteHostWhitelist, 1)); $this->log(
"Setting remote host whitelist to: "
. (is_null($whitelist) ? "null" : print_r($whitelist, 1))
);
return $this; return $this;
} }
@@ -1070,14 +1091,18 @@ class CImage
public function isRemoteSourceOnWhitelist($src) public function isRemoteSourceOnWhitelist($src)
{ {
if (is_null($this->remoteHostWhitelist)) { if (is_null($this->remoteHostWhitelist)) {
$allow = true; $this->log("Remote host on whitelist not configured - allowing.");
} else { return true;
$whitelist = new CWhitelist();
$hostname = parse_url($src, PHP_URL_HOST);
$allow = $whitelist->check($hostname, $this->remoteHostWhitelist);
} }
$this->log("Remote host is on whitelist: " . ($allow ? "true" : "false")); $whitelist = new CWhitelist();
$hostname = parse_url($src, PHP_URL_HOST);
$allow = $whitelist->check($hostname, $this->remoteHostWhitelist);
$this->log(
"Remote host is on whitelist: "
. ($allow ? "true" : "false")
);
return $allow; return $allow;
} }
@@ -2851,7 +2876,10 @@ class CImage
if ($this->verbose) { if ($this->verbose) {
$this->log("Last modified: " . $gmdate . " GMT"); $this->log("Last modified: " . $gmdate . " GMT");
$this->verboseOutput(); $this->verboseOutput();
exit;
if (is_null($this->verboseFileName)) {
exit;
}
} }
// Get details on image // Get details on image
@@ -2929,6 +2957,21 @@ class CImage
/**
* Do verbose output to a file.
*
* @param string $fileName where to write the verbose output.
*
* @return void
*/
public function setVerboseToFile($fileName)
{
$this->log("Setting verbose output to file.");
$this->verboseFileName = $fileName;
}
/** /**
* Do verbose output and print out the log and the actual images. * Do verbose output and print out the log and the actual images.
* *
@@ -2954,10 +2997,17 @@ class CImage
} }
} }
echo <<<EOD if (!is_null($this->verboseFileName)) {
file_put_contents(
$this->verboseFileName,
str_replace("<br/>", "\n", $log)
);
} else {
echo <<<EOD
<h1>CImage Verbose Output</h1> <h1>CImage Verbose Output</h1>
<pre>{$log}</pre> <pre>{$log}</pre>
EOD; EOD;
}
} }
@@ -3002,11 +3052,11 @@ class CWhitelist
*/ */
public function set($whitelist = array()) public function set($whitelist = array())
{ {
if (is_array($whitelist)) { if (!is_array($whitelist)) {
$this->whitelist = $whitelist;
} else {
throw new Exception("Whitelist is not of a supported format."); throw new Exception("Whitelist is not of a supported format.");
} }
$this->whitelist = $whitelist;
return $this; return $this;
} }
@@ -3070,10 +3120,10 @@ function errorPage($msg)
if ($mode == 'development') { if ($mode == 'development') {
die("[img.php] $msg"); die("[img.php] $msg");
} else {
error_log("[img.php] $msg");
die("HTTP/1.0 500 Internal Server Error");
} }
error_log("[img.php] $msg");
die("HTTP/1.0 500 Internal Server Error");
} }
@@ -3082,7 +3132,13 @@ function errorPage($msg)
* Custom exception handler. * Custom exception handler.
*/ */
set_exception_handler(function ($exception) { set_exception_handler(function ($exception) {
errorPage("<p><b>img.php: Uncaught exception:</b> <p>" . $exception->getMessage() . "</p><pre>" . $exception->getTraceAsString(), "</pre>"); errorPage(
"<p><b>img.php: Uncaught exception:</b> <p>"
. $exception->getMessage()
. "</p><pre>"
. $exception->getTraceAsString()
. "</pre>"
);
}); });
@@ -3154,10 +3210,10 @@ function getConfig($key, $default)
*/ */
function verbose($msg = null) function verbose($msg = null)
{ {
global $verbose; global $verbose, $verboseFile;
static $log = array(); static $log = array();
if (!$verbose) { if (!($verbose || $verboseFile)) {
return; return;
} }
@@ -3186,8 +3242,10 @@ if (is_file($configFile)) {
/** /**
* verbose, v - do a verbose dump of what happens * verbose, v - do a verbose dump of what happens
* vf - do verbose dump to file
*/ */
$verbose = getDefined(array('verbose', 'v'), true, false); $verbose = getDefined(array('verbose', 'v'), true, false);
$verboseFile = getDefined('vf', true, false);
verbose("img.php version = $version"); verbose("img.php version = $version");
@@ -3213,19 +3271,28 @@ if ($mode == 'strict') {
ini_set('display_errors', 0); ini_set('display_errors', 0);
ini_set('log_errors', 1); ini_set('log_errors', 1);
$verbose = false; $verbose = false;
$verboseFile = false;
} elseif ($mode == 'production') { } elseif ($mode == 'production') {
error_reporting(-1); error_reporting(-1);
ini_set('display_errors', 0); ini_set('display_errors', 0);
ini_set('log_errors', 1); ini_set('log_errors', 1);
$verbose = false; $verbose = false;
$verboseFile = false;
} elseif ($mode == 'development') { } elseif ($mode == 'development') {
error_reporting(-1); error_reporting(-1);
ini_set('display_errors', 1); ini_set('display_errors', 1);
ini_set('log_errors', 0); ini_set('log_errors', 0);
$verboseFile = false;
} elseif ($mode == 'test') {
error_reporting(-1);
ini_set('display_errors', 1);
ini_set('log_errors', 0);
} else { } else {
errorPage("Unknown mode: $mode"); errorPage("Unknown mode: $mode");
@@ -3290,23 +3357,22 @@ $refererHost = parse_url($referer, PHP_URL_HOST);
if (!$allowHotlinking) { if (!$allowHotlinking) {
if ($passwordMatch) { if ($passwordMatch) {
; // Always allow when password match ; // Always allow when password match
verbose("Hotlinking since passwordmatch");
} elseif ($passwordMatch === false) { } elseif ($passwordMatch === false) {
errorPage("Hotlinking/leeching not allowed when password missmatch."); errorPage("Hotlinking/leeching not allowed when password missmatch.");
} elseif (!$referer) { } elseif (!$referer) {
errorPage("Hotlinking/leeching not allowed and referer is missing."); errorPage("Hotlinking/leeching not allowed and referer is missing.");
} elseif (strcmp($serverName, $refererHost) == 0) { } elseif (strcmp($serverName, $refererHost) == 0) {
; // Allow when serverName matches refererHost ; // Allow when serverName matches refererHost
verbose("Hotlinking disallowed but serverName matches refererHost.");
} elseif (!empty($hotlinkingWhitelist)) { } elseif (!empty($hotlinkingWhitelist)) {
$whitelist = new CWhitelist();
$allowedByWhitelist = $whitelist->check($refererHost, $hotlinkingWhitelist);
$allowedByWhitelist = false; if ($allowedByWhitelist) {
foreach ($hotlinkingWhitelist as $val) { verbose("Hotlinking/leeching allowed by whitelist.");
if (preg_match($val, $refererHost)) { } else {
$allowedByWhitelist = true; errorPage("Hotlinking/leeching not allowed by whitelist. Referer: $referer.");
}
}
if (!$allowedByWhitelist) {
errorPage("Hotlinking/leeching not allowed by whitelist.");
} }
} else { } else {
@@ -3338,7 +3404,7 @@ if ($autoloader) {
* Create the class for the image. * Create the class for the image.
*/ */
$img = new CImage(); $img = new CImage();
$img->setVerbose($verbose); $img->setVerbose($verbose || $verboseFile);
@@ -3858,6 +3924,13 @@ verbose("alias = $alias");
/**
* Get the cachepath from config.
*/
$cachePath = getConfig('cache_path', __DIR__ . '/../cache/');
/** /**
* Display image if verbose mode * Display image if verbose mode
*/ */
@@ -3896,15 +3969,17 @@ EOD;
/** /**
* Get the cachepath from config. * Log verbose details to file
*/ */
$cachePath = getConfig('cache_path', __DIR__ . '/../cache/'); if ($verboseFile) {
$img->setVerboseToFile("$cachePath/log.txt");
}
/** /**
* Load, process and output the image * Load, process and output the image
*/ */
$img->log("Incoming arguments: " . print_r(verbose(), 1)) $img->log("Incoming arguments: " . print_r(verbose(), 1))
->setSaveFolder($cachePath) ->setSaveFolder($cachePath)
->useCache($useCache) ->useCache($useCache)