wp/wp-content/plugins/codecanyon-3027163-content-timeline-responsive-wordpress-plugin/timthumb/timthumb.php
changeset 14 00ac8f60d73f
parent 13 d255fe9cd479
child 15 3d4e9c994f10
equal deleted inserted replaced
13:d255fe9cd479 14:00ac8f60d73f
     1 <?php
       
     2 /**
       
     3  * TimThumb by Ben Gillbanks and Mark Maunder
       
     4  * Based on work done by Tim McDaniels and Darren Hoyt
       
     5  * http://code.google.com/p/timthumb/
       
     6  * 
       
     7  * GNU General Public License, version 2
       
     8  * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
       
     9  *
       
    10  * Examples and documentation available on the project homepage
       
    11  * http://www.binarymoon.co.uk/projects/timthumb/
       
    12  * 
       
    13  * $Rev$
       
    14  */
       
    15 
       
    16 /*
       
    17  * --- TimThumb CONFIGURATION ---
       
    18  * To edit the configs it is best to create a file called timthumb-config.php
       
    19  * and define variables you want to customize in there. It will automatically be
       
    20  * loaded by timthumb. This will save you having to re-edit these variables
       
    21  * everytime you download a new version
       
    22 */
       
    23 define ('VERSION', '2.8.10');																		// Version of this script 
       
    24 //Load a config file if it exists. Otherwise, use the values below
       
    25 if( file_exists(dirname(__FILE__) . '/timthumb-config.php'))	require_once('timthumb-config.php');
       
    26 if(! defined('DEBUG_ON') )					define ('DEBUG_ON', false);								// Enable debug logging to web server error log (STDERR)
       
    27 if(! defined('DEBUG_LEVEL') )				define ('DEBUG_LEVEL', 1);								// Debug level 1 is less noisy and 3 is the most noisy
       
    28 if(! defined('MEMORY_LIMIT') )				define ('MEMORY_LIMIT', '30M');							// Set PHP memory limit
       
    29 if(! defined('BLOCK_EXTERNAL_LEECHERS') ) 	define ('BLOCK_EXTERNAL_LEECHERS', false);				// If the image or webshot is being loaded on an external site, display a red "No Hotlinking" gif.
       
    30 
       
    31 //Image fetching and caching
       
    32 if(! defined('ALLOW_EXTERNAL') )			define ('ALLOW_EXTERNAL', TRUE);						// Allow image fetching from external websites. Will check against ALLOWED_SITES if ALLOW_ALL_EXTERNAL_SITES is false
       
    33 if(! defined('ALLOW_ALL_EXTERNAL_SITES') ) 	define ('ALLOW_ALL_EXTERNAL_SITES', false);				// Less secure. 
       
    34 if(! defined('FILE_CACHE_ENABLED') ) 		define ('FILE_CACHE_ENABLED', TRUE);					// Should we store resized/modified images on disk to speed things up?
       
    35 if(! defined('FILE_CACHE_TIME_BETWEEN_CLEANS'))	define ('FILE_CACHE_TIME_BETWEEN_CLEANS', 86400);	// How often the cache is cleaned 
       
    36 
       
    37 if(! defined('FILE_CACHE_MAX_FILE_AGE') ) 	define ('FILE_CACHE_MAX_FILE_AGE', 86400);				// How old does a file have to be to be deleted from the cache
       
    38 if(! defined('FILE_CACHE_SUFFIX') ) 		define ('FILE_CACHE_SUFFIX', '.timthumb.txt');			// What to put at the end of all files in the cache directory so we can identify them
       
    39 if(! defined('FILE_CACHE_PREFIX') ) 		define ('FILE_CACHE_PREFIX', 'timthumb');				// What to put at the beg of all files in the cache directory so we can identify them
       
    40 if(! defined('FILE_CACHE_DIRECTORY') ) 		define ('FILE_CACHE_DIRECTORY', './cache');				// Directory where images are cached. Left blank it will use the system temporary directory (which is better for security)
       
    41 if(! defined('MAX_FILE_SIZE') )				define ('MAX_FILE_SIZE', 10485760);						// 10 Megs is 10485760. This is the max internal or external file size that we'll process.  
       
    42 if(! defined('CURL_TIMEOUT') )				define ('CURL_TIMEOUT', 20);							// Timeout duration for Curl. This only applies if you have Curl installed and aren't using PHP's default URL fetching mechanism.
       
    43 if(! defined('WAIT_BETWEEN_FETCH_ERRORS') )	define ('WAIT_BETWEEN_FETCH_ERRORS', 3600);				//Time to wait between errors fetching remote file
       
    44 
       
    45 //Browser caching
       
    46 if(! defined('BROWSER_CACHE_MAX_AGE') ) 	define ('BROWSER_CACHE_MAX_AGE', 864000);				// Time to cache in the browser
       
    47 if(! defined('BROWSER_CACHE_DISABLE') ) 	define ('BROWSER_CACHE_DISABLE', false);				// Use for testing if you want to disable all browser caching
       
    48 
       
    49 //Image size and defaults
       
    50 if(! defined('MAX_WIDTH') ) 			define ('MAX_WIDTH', 1500);									// Maximum image width
       
    51 if(! defined('MAX_HEIGHT') ) 			define ('MAX_HEIGHT', 1500);								// Maximum image height
       
    52 if(! defined('NOT_FOUND_IMAGE') )		define ('NOT_FOUND_IMAGE', '');								// Image to serve if any 404 occurs 
       
    53 if(! defined('ERROR_IMAGE') )			define ('ERROR_IMAGE', '');									// Image to serve if an error occurs instead of showing error message 
       
    54 if(! defined('PNG_IS_TRANSPARENT') ) 	define ('PNG_IS_TRANSPARENT', FALSE);  //42 Define if a png image should have a transparent background color. Use False value if you want to display a custom coloured canvas_colour 
       
    55 if(! defined('DEFAULT_Q') )				define ('DEFAULT_Q', 90);									// Default image quality. Allows overrid in timthumb-config.php
       
    56 if(! defined('DEFAULT_ZC') )			define ('DEFAULT_ZC', 1);									// Default zoom/crop setting. Allows overrid in timthumb-config.php
       
    57 if(! defined('DEFAULT_F') )				define ('DEFAULT_F', '');									// Default image filters. Allows overrid in timthumb-config.php
       
    58 if(! defined('DEFAULT_S') )				define ('DEFAULT_S', 0);									// Default sharpen value. Allows overrid in timthumb-config.php
       
    59 if(! defined('DEFAULT_CC') )			define ('DEFAULT_CC', 'ffffff');							// Default canvas colour. Allows overrid in timthumb-config.php
       
    60 
       
    61 
       
    62 //Image compression is enabled if either of these point to valid paths
       
    63 
       
    64 //These are now disabled by default because the file sizes of PNGs (and GIFs) are much smaller than we used to generate. 
       
    65 //They only work for PNGs. GIFs and JPEGs are not affected.
       
    66 if(! defined('OPTIPNG_ENABLED') ) 		define ('OPTIPNG_ENABLED', false);  
       
    67 if(! defined('OPTIPNG_PATH') ) 			define ('OPTIPNG_PATH', '/usr/bin/optipng'); //This will run first because it gives better compression than pngcrush. 
       
    68 if(! defined('PNGCRUSH_ENABLED') ) 		define ('PNGCRUSH_ENABLED', false); 
       
    69 if(! defined('PNGCRUSH_PATH') ) 		define ('PNGCRUSH_PATH', '/usr/bin/pngcrush'); //This will only run if OPTIPNG_PATH is not set or is not valid
       
    70 
       
    71 /*
       
    72 	-------====Website Screenshots configuration - BETA====-------
       
    73 	
       
    74 	If you just want image thumbnails and don't want website screenshots, you can safely leave this as is.	
       
    75 	
       
    76 	If you would like to get website screenshots set up, you will need root access to your own server.
       
    77 
       
    78 	Enable ALLOW_ALL_EXTERNAL_SITES so you can fetch any external web page. This is more secure now that we're using a non-web folder for cache.
       
    79 	Enable BLOCK_EXTERNAL_LEECHERS so that your site doesn't generate thumbnails for the whole Internet.
       
    80 
       
    81 	Instructions to get website screenshots enabled on Ubuntu Linux:
       
    82 
       
    83 	1. Install Xvfb with the following command: sudo apt-get install subversion libqt4-webkit libqt4-dev g++ xvfb
       
    84 	2. Go to a directory where you can download some code
       
    85 	3. Check-out the latest version of CutyCapt with the following command: svn co https://cutycapt.svn.sourceforge.net/svnroot/cutycapt
       
    86 	4. Compile CutyCapt by doing: cd cutycapt/CutyCapt
       
    87 	5. qmake
       
    88 	6. make
       
    89 	7. cp CutyCapt /usr/local/bin/
       
    90 	8. Test it by running: xvfb-run --server-args="-screen 0, 1024x768x24" CutyCapt --url="http://markmaunder.com/" --out=test.png
       
    91 	9. If you get a file called test.png with something in it, it probably worked. Now test the script by accessing it as follows:
       
    92 	10. http://yoursite.com/path/to/timthumb.php?src=http://markmaunder.com/&webshot=1
       
    93 
       
    94 	Notes on performance: 
       
    95 	The first time a webshot loads, it will take a few seconds.
       
    96 	From then on it uses the regular timthumb caching mechanism with the configurable options above
       
    97 	and loading will be very fast.
       
    98 
       
    99 	--ADVANCED USERS ONLY--
       
   100 	If you'd like a slight speedup (about 25%) and you know Linux, you can run the following command which will keep Xvfb running in the background.
       
   101 	nohup Xvfb :100 -ac -nolisten tcp -screen 0, 1024x768x24 > /dev/null 2>&1 &
       
   102 	Then set WEBSHOT_XVFB_RUNNING = true below. This will save your server having to fire off a new Xvfb server and shut it down every time a new shot is generated. 
       
   103 	You will need to take responsibility for keeping Xvfb running in case it crashes. (It seems pretty stable)
       
   104 	You will also need to take responsibility for server security if you're running Xvfb as root. 
       
   105 
       
   106 
       
   107 */
       
   108 if(! defined('WEBSHOT_ENABLED') ) 	define ('WEBSHOT_ENABLED', false);			//Beta feature. Adding webshot=1 to your query string will cause the script to return a browser screenshot rather than try to fetch an image.
       
   109 if(! defined('WEBSHOT_CUTYCAPT') ) 	define ('WEBSHOT_CUTYCAPT', '/usr/local/bin/CutyCapt'); //The path to CutyCapt. 
       
   110 if(! defined('WEBSHOT_XVFB') ) 		define ('WEBSHOT_XVFB', '/usr/bin/xvfb-run');		//The path to the Xvfb server
       
   111 if(! defined('WEBSHOT_SCREEN_X') ) 	define ('WEBSHOT_SCREEN_X', '1024');			//1024 works ok
       
   112 if(! defined('WEBSHOT_SCREEN_Y') ) 	define ('WEBSHOT_SCREEN_Y', '768');			//768 works ok
       
   113 if(! defined('WEBSHOT_COLOR_DEPTH') ) 	define ('WEBSHOT_COLOR_DEPTH', '24');			//I haven't tested anything besides 24
       
   114 if(! defined('WEBSHOT_IMAGE_FORMAT') ) 	define ('WEBSHOT_IMAGE_FORMAT', 'png');			//png is about 2.5 times the size of jpg but is a LOT better quality
       
   115 if(! defined('WEBSHOT_TIMEOUT') ) 	define ('WEBSHOT_TIMEOUT', '20');			//Seconds to wait for a webshot
       
   116 if(! defined('WEBSHOT_USER_AGENT') ) 	define ('WEBSHOT_USER_AGENT', "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-GB; rv:1.9.2.18) Gecko/20110614 Firefox/3.6.18"); //I hate to do this, but a non-browser robot user agent might not show what humans see. So we pretend to be Firefox
       
   117 if(! defined('WEBSHOT_JAVASCRIPT_ON') ) define ('WEBSHOT_JAVASCRIPT_ON', true);			//Setting to false might give you a slight speedup and block ads. But it could cause other issues.
       
   118 if(! defined('WEBSHOT_JAVA_ON') ) 	define ('WEBSHOT_JAVA_ON', false);			//Have only tested this as fase
       
   119 if(! defined('WEBSHOT_PLUGINS_ON') ) 	define ('WEBSHOT_PLUGINS_ON', true);			//Enable flash and other plugins
       
   120 if(! defined('WEBSHOT_PROXY') ) 	define ('WEBSHOT_PROXY', '');				//In case you're behind a proxy server. 
       
   121 if(! defined('WEBSHOT_XVFB_RUNNING') )	define ('WEBSHOT_XVFB_RUNNING', false);			//ADVANCED: Enable this if you've got Xvfb running in the background.
       
   122 
       
   123 
       
   124 // If ALLOW_EXTERNAL is true and ALLOW_ALL_EXTERNAL_SITES is false, then external images will only be fetched from these domains and their subdomains. 
       
   125 if(! isset($ALLOWED_SITES)){
       
   126 	$ALLOWED_SITES = array (
       
   127 		'flickr.com',
       
   128 		'staticflickr.com',
       
   129 		'picasa.com',
       
   130 		'img.youtube.com',
       
   131 		'upload.wikimedia.org',
       
   132 		'photobucket.com',
       
   133 		'imgur.com',
       
   134 		'imageshack.us',
       
   135 		'tinypic.com',
       
   136 	);
       
   137 }
       
   138 // -------------------------------------------------------------
       
   139 // -------------- STOP EDITING CONFIGURATION HERE --------------
       
   140 // -------------------------------------------------------------
       
   141 
       
   142 timthumb::start();
       
   143 
       
   144 class timthumb {
       
   145 	protected $src = "";
       
   146 	protected $is404 = false;
       
   147 	protected $docRoot = "";
       
   148 	protected $lastURLError = false;
       
   149 	protected $localImage = "";
       
   150 	protected $localImageMTime = 0;
       
   151 	protected $url = false;
       
   152 	protected $myHost = "";
       
   153 	protected $isURL = false;
       
   154 	protected $cachefile = '';
       
   155 	protected $errors = array();
       
   156 	protected $toDeletes = array();
       
   157 	protected $cacheDirectory = '';
       
   158 	protected $startTime = 0;
       
   159 	protected $lastBenchTime = 0;
       
   160 	protected $cropTop = false;
       
   161 	protected $salt = "";
       
   162 	protected $fileCacheVersion = 1; //Generally if timthumb.php is modifed (upgraded) then the salt changes and all cache files are recreated. This is a backup mechanism to force regen.
       
   163 	protected $filePrependSecurityBlock = "<?php die('Execution denied!'); //"; //Designed to have three letter mime type, space, question mark and greater than symbol appended. 6 bytes total.
       
   164 	protected static $curlDataWritten = 0;
       
   165 	protected static $curlFH = false;
       
   166 	public static function start(){
       
   167 		$tim = new timthumb();
       
   168 		$tim->handleErrors();
       
   169 		$tim->securityChecks();
       
   170 		if($tim->tryBrowserCache()){
       
   171 			exit(0);
       
   172 		}
       
   173 		$tim->handleErrors();
       
   174 		if(FILE_CACHE_ENABLED && $tim->tryServerCache()){
       
   175 			exit(0);
       
   176 		}
       
   177 		$tim->handleErrors();
       
   178 		$tim->run();
       
   179 		$tim->handleErrors();
       
   180 		exit(0);
       
   181 	}
       
   182 	public function __construct(){
       
   183 		global $ALLOWED_SITES;
       
   184 		$this->startTime = microtime(true);
       
   185 		date_default_timezone_set('UTC');
       
   186 		$this->debug(1, "Starting new request from " . $this->getIP() . " to " . $_SERVER['REQUEST_URI']);
       
   187 		$this->calcDocRoot();
       
   188 		//On windows systems I'm assuming fileinode returns an empty string or a number that doesn't change. Check this.
       
   189 		$this->salt = @filemtime(__FILE__) . '-' . @fileinode(__FILE__);
       
   190 		$this->debug(3, "Salt is: " . $this->salt);
       
   191 		if(FILE_CACHE_DIRECTORY){
       
   192 			if(! is_dir(FILE_CACHE_DIRECTORY)){
       
   193 				@mkdir(FILE_CACHE_DIRECTORY);
       
   194 				if(! is_dir(FILE_CACHE_DIRECTORY)){
       
   195 					$this->error("Could not create the file cache directory.");
       
   196 					return false;
       
   197 				}
       
   198 			}
       
   199 			$this->cacheDirectory = FILE_CACHE_DIRECTORY;
       
   200 			if (!touch($this->cacheDirectory . '/index.html')) {
       
   201 				$this->error("Could not create the index.html file - to fix this create an empty file named index.html file in the cache directory.");
       
   202 			}
       
   203 		} else {
       
   204 			$this->cacheDirectory = sys_get_temp_dir();
       
   205 		}
       
   206 		//Clean the cache before we do anything because we don't want the first visitor after FILE_CACHE_TIME_BETWEEN_CLEANS expires to get a stale image. 
       
   207 		$this->cleanCache();
       
   208 		
       
   209 		$this->myHost = preg_replace('/^www\./i', '', $_SERVER['HTTP_HOST']);
       
   210 		$this->src = $this->param('src');
       
   211 		$this->url = parse_url($this->src);
       
   212 		$this->src = preg_replace('/https?:\/\/(?:www\.)?' . $this->myHost . '/i', '', $this->src);
       
   213 		
       
   214 		if(strlen($this->src) <= 3){
       
   215 			$this->error("No image specified");
       
   216 			return false;
       
   217 		}
       
   218 		if(BLOCK_EXTERNAL_LEECHERS && array_key_exists('HTTP_REFERER', $_SERVER) && (! preg_match('/^https?:\/\/(?:www\.)?' . $this->myHost . '(?:$|\/)/i', $_SERVER['HTTP_REFERER']))){
       
   219 			// base64 encoded red image that says 'no hotlinkers'
       
   220 			// nothing to worry about! :)
       
   221 			$imgData = base64_decode("R0lGODlhUAAMAIAAAP8AAP///yH5BAAHAP8ALAAAAABQAAwAAAJpjI+py+0Po5y0OgAMjjv01YUZ\nOGplhWXfNa6JCLnWkXplrcBmW+spbwvaVr/cDyg7IoFC2KbYVC2NQ5MQ4ZNao9Ynzjl9ScNYpneb\nDULB3RP6JuPuaGfuuV4fumf8PuvqFyhYtjdoeFgAADs=");
       
   222 			header('Content-Type: image/gif');
       
   223 			header('Content-Length: ' . sizeof($imgData));
       
   224 			header('Cache-Control: no-store, no-cache, must-revalidate, max-age=0');
       
   225 			header("Pragma: no-cache");
       
   226 			header('Expires: ' . gmdate ('D, d M Y H:i:s', time()));
       
   227 			echo $imgData;
       
   228 			return false;
       
   229 			exit(0);
       
   230 		}
       
   231 		if(preg_match('/^https?:\/\/[^\/]+/i', $this->src)){
       
   232 			$this->debug(2, "Is a request for an external URL: " . $this->src);
       
   233 			$this->isURL = true;
       
   234 		} else {
       
   235 			$this->debug(2, "Is a request for an internal file: " . $this->src);
       
   236 		}
       
   237 		if($this->isURL && (! ALLOW_EXTERNAL)){
       
   238 			$this->error("You are not allowed to fetch images from an external website.");
       
   239 			return false;
       
   240 		}
       
   241 		if($this->isURL){
       
   242 			if(ALLOW_ALL_EXTERNAL_SITES){
       
   243 				$this->debug(2, "Fetching from all external sites is enabled.");
       
   244 			} else {
       
   245 				$this->debug(2, "Fetching only from selected external sites is enabled.");
       
   246 				$allowed = false;
       
   247 				foreach($ALLOWED_SITES as $site){
       
   248 					if ((strtolower(substr($this->url['host'],-strlen($site)-1)) === strtolower(".$site")) || (strtolower($this->url['host'])===strtolower($site))) {
       
   249 						$this->debug(3, "URL hostname {$this->url['host']} matches $site so allowing.");
       
   250 						$allowed = true;
       
   251 					}
       
   252 				}
       
   253 				if(! $allowed){
       
   254 					return $this->error("You may not fetch images from that site. To enable this site in timthumb, you can either add it to \$ALLOWED_SITES and set ALLOW_EXTERNAL=true. Or you can set ALLOW_ALL_EXTERNAL_SITES=true, depending on your security needs.");
       
   255 				}
       
   256 			}
       
   257 		}
       
   258 
       
   259 		$cachePrefix = ($this->isURL ? '_ext_' : '_int_');
       
   260 		if($this->isURL){
       
   261 			$arr = explode('&', $_SERVER ['QUERY_STRING']);
       
   262 			asort($arr);
       
   263 			$this->cachefile = $this->cacheDirectory . '/' . FILE_CACHE_PREFIX . $cachePrefix . md5($this->salt . implode('', $arr) . $this->fileCacheVersion) . FILE_CACHE_SUFFIX;
       
   264 		} else {
       
   265 			$this->localImage = $this->getLocalImagePath($this->src);
       
   266 			if(! $this->localImage){
       
   267 				$this->debug(1, "Could not find the local image: {$this->localImage}");
       
   268 				$this->error("Could not find the internal image you specified.");
       
   269 				$this->set404();
       
   270 				return false;
       
   271 			}
       
   272 			$this->debug(1, "Local image path is {$this->localImage}");
       
   273 			$this->localImageMTime = @filemtime($this->localImage);
       
   274 			//We include the mtime of the local file in case in changes on disk.
       
   275 			$this->cachefile = $this->cacheDirectory . '/' . FILE_CACHE_PREFIX . $cachePrefix . md5($this->salt . $this->localImageMTime . $_SERVER ['QUERY_STRING'] . $this->fileCacheVersion) . FILE_CACHE_SUFFIX;
       
   276 		}
       
   277 		$this->debug(2, "Cache file is: " . $this->cachefile);
       
   278 
       
   279 		return true;
       
   280 	}
       
   281 	public function __destruct(){
       
   282 		foreach($this->toDeletes as $del){
       
   283 			$this->debug(2, "Deleting temp file $del");
       
   284 			@unlink($del);
       
   285 		}
       
   286 	}
       
   287 	public function run(){
       
   288 		if($this->isURL){
       
   289 			if(! ALLOW_EXTERNAL){
       
   290 				$this->debug(1, "Got a request for an external image but ALLOW_EXTERNAL is disabled so returning error msg.");
       
   291 				$this->error("You are not allowed to fetch images from an external website.");
       
   292 				return false;
       
   293 			}
       
   294 			$this->debug(3, "Got request for external image. Starting serveExternalImage.");
       
   295 			if($this->param('webshot')){
       
   296 				if(WEBSHOT_ENABLED){
       
   297 					$this->debug(3, "webshot param is set, so we're going to take a webshot.");
       
   298 					$this->serveWebshot();
       
   299 				} else {
       
   300 					$this->error("You added the webshot parameter but webshots are disabled on this server. You need to set WEBSHOT_ENABLED == true to enable webshots.");
       
   301 				}
       
   302 			} else {
       
   303 				$this->debug(3, "webshot is NOT set so we're going to try to fetch a regular image.");
       
   304 				$this->serveExternalImage();
       
   305 
       
   306 			}
       
   307 		} else {
       
   308 			$this->debug(3, "Got request for internal image. Starting serveInternalImage()");
       
   309 			$this->serveInternalImage();
       
   310 		}
       
   311 		return true;
       
   312 	}
       
   313 	protected function handleErrors(){
       
   314 		if($this->haveErrors()){ 
       
   315 			if(NOT_FOUND_IMAGE && $this->is404()){
       
   316 				if($this->serveImg(NOT_FOUND_IMAGE)){
       
   317 					exit(0);
       
   318 				} else {
       
   319 					$this->error("Additionally, the 404 image that is configured could not be found or there was an error serving it.");
       
   320 				}
       
   321 			}
       
   322 			if(ERROR_IMAGE){
       
   323 				if($this->serveImg(ERROR_IMAGE)){
       
   324 					exit(0);
       
   325 				} else {
       
   326 					$this->error("Additionally, the error image that is configured could not be found or there was an error serving it.");
       
   327 				}
       
   328 			}
       
   329 			$this->serveErrors(); 
       
   330 			exit(0); 
       
   331 		}
       
   332 		return false;
       
   333 	}
       
   334 	protected function tryBrowserCache(){
       
   335 		if(BROWSER_CACHE_DISABLE){ $this->debug(3, "Browser caching is disabled"); return false; }
       
   336 		if(!empty($_SERVER['HTTP_IF_MODIFIED_SINCE']) ){
       
   337 			$this->debug(3, "Got a conditional get");
       
   338 			$mtime = false;
       
   339 			//We've already checked if the real file exists in the constructor
       
   340 			if(! is_file($this->cachefile)){
       
   341 				//If we don't have something cached, regenerate the cached image.
       
   342 				return false;
       
   343 			}
       
   344 			if($this->localImageMTime){
       
   345 				$mtime = $this->localImageMTime;
       
   346 				$this->debug(3, "Local real file's modification time is $mtime");
       
   347 			} else if(is_file($this->cachefile)){ //If it's not a local request then use the mtime of the cached file to determine the 304
       
   348 				$mtime = @filemtime($this->cachefile);
       
   349 				$this->debug(3, "Cached file's modification time is $mtime");
       
   350 			}
       
   351 			if(! $mtime){ return false; }
       
   352 
       
   353 			$iftime = strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']);
       
   354 			$this->debug(3, "The conditional get's if-modified-since unixtime is $iftime");
       
   355 			if($iftime < 1){
       
   356 				$this->debug(3, "Got an invalid conditional get modified since time. Returning false.");
       
   357 				return false;
       
   358 			}
       
   359 			if($iftime < $mtime){ //Real file or cache file has been modified since last request, so force refetch.
       
   360 				$this->debug(3, "File has been modified since last fetch.");
       
   361 				return false;
       
   362 			} else { //Otherwise serve a 304
       
   363 				$this->debug(3, "File has not been modified since last get, so serving a 304.");
       
   364 				header ($_SERVER['SERVER_PROTOCOL'] . ' 304 Not Modified');
       
   365 				$this->debug(1, "Returning 304 not modified");
       
   366 				return true;
       
   367 			}
       
   368 		}
       
   369 		return false;
       
   370 	}
       
   371 	protected function tryServerCache(){
       
   372 		$this->debug(3, "Trying server cache");
       
   373 		if(file_exists($this->cachefile)){
       
   374 			$this->debug(3, "Cachefile {$this->cachefile} exists");
       
   375 			if($this->isURL){
       
   376 				$this->debug(3, "This is an external request, so checking if the cachefile is empty which means the request failed previously.");
       
   377 				if(filesize($this->cachefile) < 1){
       
   378 					$this->debug(3, "Found an empty cachefile indicating a failed earlier request. Checking how old it is.");
       
   379 					//Fetching error occured previously
       
   380 					if(time() - @filemtime($this->cachefile) > WAIT_BETWEEN_FETCH_ERRORS){
       
   381 						$this->debug(3, "File is older than " . WAIT_BETWEEN_FETCH_ERRORS . " seconds. Deleting and returning false so app can try and load file.");
       
   382 						@unlink($this->cachefile);
       
   383 						return false; //to indicate we didn't serve from cache and app should try and load
       
   384 					} else {
       
   385 						$this->debug(3, "Empty cachefile is still fresh so returning message saying we had an error fetching this image from remote host.");
       
   386 						$this->set404();
       
   387 						$this->error("An error occured fetching image.");
       
   388 						return false; 
       
   389 					}
       
   390 				}
       
   391 			} else {
       
   392 				$this->debug(3, "Trying to serve cachefile {$this->cachefile}");
       
   393 			}
       
   394 			if($this->serveCacheFile()){
       
   395 				$this->debug(3, "Succesfully served cachefile {$this->cachefile}");
       
   396 				return true;
       
   397 			} else {
       
   398 				$this->debug(3, "Failed to serve cachefile {$this->cachefile} - Deleting it from cache.");
       
   399 				//Image serving failed. We can't retry at this point, but lets remove it from cache so the next request recreates it
       
   400 				@unlink($this->cachefile);
       
   401 				return true;
       
   402 			}
       
   403 		}
       
   404 	}
       
   405 	protected function error($err){
       
   406 		$this->debug(3, "Adding error message: $err");
       
   407 		$this->errors[] = $err;
       
   408 		return false;
       
   409 
       
   410 	}
       
   411 	protected function haveErrors(){
       
   412 		if(sizeof($this->errors) > 0){
       
   413 			return true;
       
   414 		}
       
   415 		return false;
       
   416 	}
       
   417 	protected function serveErrors(){
       
   418 		header ($_SERVER['SERVER_PROTOCOL'] . ' 400 Bad Request');
       
   419 		$html = '<ul>';
       
   420 		foreach($this->errors as $err){
       
   421 			$html .= '<li>' . htmlentities($err) . '</li>';
       
   422 		}
       
   423 		$html .= '</ul>';
       
   424 		echo '<h1>A TimThumb error has occured</h1>The following error(s) occured:<br />' . $html . '<br />';
       
   425 		echo '<br />Query String : ' . htmlentities ($_SERVER['QUERY_STRING']);
       
   426 		echo '<br />TimThumb version : ' . VERSION . '</pre>';
       
   427 	}
       
   428 	protected function serveInternalImage(){
       
   429 		$this->debug(3, "Local image path is $this->localImage");
       
   430 		if(! $this->localImage){
       
   431 			$this->sanityFail("localImage not set after verifying it earlier in the code.");
       
   432 			return false;
       
   433 		}
       
   434 		$fileSize = filesize($this->localImage);
       
   435 		if($fileSize > MAX_FILE_SIZE){
       
   436 			$this->error("The file you specified is greater than the maximum allowed file size.");
       
   437 			return false;
       
   438 		}
       
   439 		if($fileSize <= 0){
       
   440 			$this->error("The file you specified is <= 0 bytes.");
       
   441 			return false;
       
   442 		}
       
   443 		$this->debug(3, "Calling processImageAndWriteToCache() for local image.");
       
   444 		if($this->processImageAndWriteToCache($this->localImage)){
       
   445 			$this->serveCacheFile();
       
   446 			return true;
       
   447 		} else { 
       
   448 			return false;
       
   449 		}
       
   450 	}
       
   451 	protected function cleanCache(){
       
   452 		if (FILE_CACHE_TIME_BETWEEN_CLEANS < 0) {
       
   453 			return;
       
   454 		}
       
   455 		$this->debug(3, "cleanCache() called");
       
   456 		$lastCleanFile = $this->cacheDirectory . '/timthumb_cacheLastCleanTime.touch';
       
   457 		
       
   458 		//If this is a new timthumb installation we need to create the file
       
   459 		if(! is_file($lastCleanFile)){
       
   460 			$this->debug(1, "File tracking last clean doesn't exist. Creating $lastCleanFile");
       
   461 			if (!touch($lastCleanFile)) {
       
   462 				$this->error("Could not create cache clean timestamp file.");
       
   463 			}
       
   464 			return;
       
   465 		}
       
   466 		if(@filemtime($lastCleanFile) < (time() - FILE_CACHE_TIME_BETWEEN_CLEANS) ){ //Cache was last cleaned more than 1 day ago
       
   467 			$this->debug(1, "Cache was last cleaned more than " . FILE_CACHE_TIME_BETWEEN_CLEANS . " seconds ago. Cleaning now.");
       
   468 			// Very slight race condition here, but worst case we'll have 2 or 3 servers cleaning the cache simultaneously once a day.
       
   469 			if (!touch($lastCleanFile)) {
       
   470 				$this->error("Could not create cache clean timestamp file.");
       
   471 			}
       
   472 			$files = glob($this->cacheDirectory . '/*' . FILE_CACHE_SUFFIX);
       
   473 			if ($files) {
       
   474 				$timeAgo = time() - FILE_CACHE_MAX_FILE_AGE;
       
   475 				foreach($files as $file){
       
   476 					if(@filemtime($file) < $timeAgo){
       
   477 						$this->debug(3, "Deleting cache file $file older than max age: " . FILE_CACHE_MAX_FILE_AGE . " seconds");
       
   478 						@unlink($file);
       
   479 					}
       
   480 				}
       
   481 			}
       
   482 			return true;
       
   483 		} else {
       
   484 			$this->debug(3, "Cache was cleaned less than " . FILE_CACHE_TIME_BETWEEN_CLEANS . " seconds ago so no cleaning needed.");
       
   485 		}
       
   486 		return false;
       
   487 	}
       
   488 	protected function processImageAndWriteToCache($localImage){
       
   489 		$sData = getimagesize($localImage);
       
   490 		$origType = $sData[2];
       
   491 		$mimeType = $sData['mime'];
       
   492 
       
   493 		$this->debug(3, "Mime type of image is $mimeType");
       
   494 		if(! preg_match('/^image\/(?:gif|jpg|jpeg|png)$/i', $mimeType)){
       
   495 			return $this->error("The image being resized is not a valid gif, jpg or png.");
       
   496 		}
       
   497 
       
   498 		if (!function_exists ('imagecreatetruecolor')) {
       
   499 		    return $this->error('GD Library Error: imagecreatetruecolor does not exist - please contact your webhost and ask them to install the GD library');
       
   500 		}
       
   501 
       
   502 		if (function_exists ('imagefilter') && defined ('IMG_FILTER_NEGATE')) {
       
   503 			$imageFilters = array (
       
   504 				1 => array (IMG_FILTER_NEGATE, 0),
       
   505 				2 => array (IMG_FILTER_GRAYSCALE, 0),
       
   506 				3 => array (IMG_FILTER_BRIGHTNESS, 1),
       
   507 				4 => array (IMG_FILTER_CONTRAST, 1),
       
   508 				5 => array (IMG_FILTER_COLORIZE, 4),
       
   509 				6 => array (IMG_FILTER_EDGEDETECT, 0),
       
   510 				7 => array (IMG_FILTER_EMBOSS, 0),
       
   511 				8 => array (IMG_FILTER_GAUSSIAN_BLUR, 0),
       
   512 				9 => array (IMG_FILTER_SELECTIVE_BLUR, 0),
       
   513 				10 => array (IMG_FILTER_MEAN_REMOVAL, 0),
       
   514 				11 => array (IMG_FILTER_SMOOTH, 0),
       
   515 			);
       
   516 		}
       
   517 
       
   518 		// get standard input properties		
       
   519 		$new_width =  (int) abs ($this->param('w', 0));
       
   520 		$new_height = (int) abs ($this->param('h', 0));
       
   521 		$zoom_crop = (int) $this->param('zc', DEFAULT_ZC);
       
   522 		$quality = (int) abs ($this->param('q', DEFAULT_Q));
       
   523 		$align = $this->cropTop ? 't' : $this->param('a', 'c');
       
   524 		$filters = $this->param('f', DEFAULT_F);
       
   525 		$sharpen = (bool) $this->param('s', DEFAULT_S);
       
   526 		$canvas_color = $this->param('cc', DEFAULT_CC);
       
   527 		$canvas_trans = (bool) $this->param('ct', '1');
       
   528 
       
   529 		// set default width and height if neither are set already
       
   530 		if ($new_width == 0 && $new_height == 0) {
       
   531 		    $new_width = 100;
       
   532 		    $new_height = 100;
       
   533 		}
       
   534 
       
   535 		// ensure size limits can not be abused
       
   536 		$new_width = min ($new_width, MAX_WIDTH);
       
   537 		$new_height = min ($new_height, MAX_HEIGHT);
       
   538 
       
   539 		// set memory limit to be able to have enough space to resize larger images
       
   540 		$this->setMemoryLimit();
       
   541 
       
   542 		// open the existing image
       
   543 		$image = $this->openImage ($mimeType, $localImage);
       
   544 		if ($image === false) {
       
   545 			return $this->error('Unable to open image.');
       
   546 		}
       
   547 
       
   548 		// Get original width and height
       
   549 		$width = imagesx ($image);
       
   550 		$height = imagesy ($image);
       
   551 		$origin_x = 0;
       
   552 		$origin_y = 0;
       
   553 
       
   554 		// generate new w/h if not provided
       
   555 		if ($new_width && !$new_height) {
       
   556 			$new_height = floor ($height * ($new_width / $width));
       
   557 		} else if ($new_height && !$new_width) {
       
   558 			$new_width = floor ($width * ($new_height / $height));
       
   559 		}
       
   560 
       
   561 		// scale down and add borders
       
   562 		if ($zoom_crop == 3) {
       
   563 
       
   564 			$final_height = $height * ($new_width / $width);
       
   565 
       
   566 			if ($final_height > $new_height) {
       
   567 				$new_width = $width * ($new_height / $height);
       
   568 			} else {
       
   569 				$new_height = $final_height;
       
   570 			}
       
   571 
       
   572 		}
       
   573 
       
   574 		// create a new true color image
       
   575 		$canvas = imagecreatetruecolor ($new_width, $new_height);
       
   576 		imagealphablending ($canvas, false);
       
   577 
       
   578 		if (strlen($canvas_color) == 3) { //if is 3-char notation, edit string into 6-char notation
       
   579 			$canvas_color =  str_repeat(substr($canvas_color, 0, 1), 2) . str_repeat(substr($canvas_color, 1, 1), 2) . str_repeat(substr($canvas_color, 2, 1), 2); 
       
   580 		} else if (strlen($canvas_color) != 6) {
       
   581 			$canvas_color = DEFAULT_CC; // on error return default canvas color
       
   582  		}
       
   583 
       
   584 		$canvas_color_R = hexdec (substr ($canvas_color, 0, 2));
       
   585 		$canvas_color_G = hexdec (substr ($canvas_color, 2, 2));
       
   586 		$canvas_color_B = hexdec (substr ($canvas_color, 4, 2));
       
   587 
       
   588 		// Create a new transparent color for image
       
   589 	    // If is a png and PNG_IS_TRANSPARENT is false then remove the alpha transparency 
       
   590 		// (and if is set a canvas color show it in the background)
       
   591 		if(preg_match('/^image\/png$/i', $mimeType) && !PNG_IS_TRANSPARENT && $canvas_trans){ 
       
   592 			$color = imagecolorallocatealpha ($canvas, $canvas_color_R, $canvas_color_G, $canvas_color_B, 127);		
       
   593 		}else{
       
   594 			$color = imagecolorallocatealpha ($canvas, $canvas_color_R, $canvas_color_G, $canvas_color_B, 0);
       
   595 		}
       
   596 
       
   597 
       
   598 		// Completely fill the background of the new image with allocated color.
       
   599 		imagefill ($canvas, 0, 0, $color);
       
   600 
       
   601 		// scale down and add borders
       
   602 		if ($zoom_crop == 2) {
       
   603 
       
   604 			$final_height = $height * ($new_width / $width);
       
   605 
       
   606 			if ($final_height > $new_height) {
       
   607 
       
   608 				$origin_x = $new_width / 2;
       
   609 				$new_width = $width * ($new_height / $height);
       
   610 				$origin_x = round ($origin_x - ($new_width / 2));
       
   611 
       
   612 			} else {
       
   613 
       
   614 				$origin_y = $new_height / 2;
       
   615 				$new_height = $final_height;
       
   616 				$origin_y = round ($origin_y - ($new_height / 2));
       
   617 
       
   618 			}
       
   619 
       
   620 		}
       
   621 
       
   622 		// Restore transparency blending
       
   623 		imagesavealpha ($canvas, true);
       
   624 
       
   625 		if ($zoom_crop > 0) {
       
   626 
       
   627 			$src_x = $src_y = 0;
       
   628 			$src_w = $width;
       
   629 			$src_h = $height;
       
   630 
       
   631 			$cmp_x = $width / $new_width;
       
   632 			$cmp_y = $height / $new_height;
       
   633 
       
   634 			// calculate x or y coordinate and width or height of source
       
   635 			if ($cmp_x > $cmp_y) {
       
   636 
       
   637 				$src_w = round ($width / $cmp_x * $cmp_y);
       
   638 				$src_x = round (($width - ($width / $cmp_x * $cmp_y)) / 2);
       
   639 
       
   640 			} else if ($cmp_y > $cmp_x) {
       
   641 
       
   642 				$src_h = round ($height / $cmp_y * $cmp_x);
       
   643 				$src_y = round (($height - ($height / $cmp_y * $cmp_x)) / 2);
       
   644 
       
   645 			}
       
   646 
       
   647 			// positional cropping!
       
   648 			if ($align) {
       
   649 				if (strpos ($align, 't') !== false) {
       
   650 					$src_y = 0;
       
   651 				}
       
   652 				if (strpos ($align, 'b') !== false) {
       
   653 					$src_y = $height - $src_h;
       
   654 				}
       
   655 				if (strpos ($align, 'l') !== false) {
       
   656 					$src_x = 0;
       
   657 				}
       
   658 				if (strpos ($align, 'r') !== false) {
       
   659 					$src_x = $width - $src_w;
       
   660 				}
       
   661 			}
       
   662 
       
   663 			imagecopyresampled ($canvas, $image, $origin_x, $origin_y, $src_x, $src_y, $new_width, $new_height, $src_w, $src_h);
       
   664 
       
   665 		} else {
       
   666 
       
   667 			// copy and resize part of an image with resampling
       
   668 			imagecopyresampled ($canvas, $image, 0, 0, 0, 0, $new_width, $new_height, $width, $height);
       
   669 
       
   670 		}
       
   671 
       
   672 		if ($filters != '' && function_exists ('imagefilter') && defined ('IMG_FILTER_NEGATE')) {
       
   673 			// apply filters to image
       
   674 			$filterList = explode ('|', $filters);
       
   675 			foreach ($filterList as $fl) {
       
   676 
       
   677 				$filterSettings = explode (',', $fl);
       
   678 				if (isset ($imageFilters[$filterSettings[0]])) {
       
   679 
       
   680 					for ($i = 0; $i < 4; $i ++) {
       
   681 						if (!isset ($filterSettings[$i])) {
       
   682 							$filterSettings[$i] = null;
       
   683 						} else {
       
   684 							$filterSettings[$i] = (int) $filterSettings[$i];
       
   685 						}
       
   686 					}
       
   687 
       
   688 					switch ($imageFilters[$filterSettings[0]][1]) {
       
   689 
       
   690 						case 1:
       
   691 
       
   692 							imagefilter ($canvas, $imageFilters[$filterSettings[0]][0], $filterSettings[1]);
       
   693 							break;
       
   694 
       
   695 						case 2:
       
   696 
       
   697 							imagefilter ($canvas, $imageFilters[$filterSettings[0]][0], $filterSettings[1], $filterSettings[2]);
       
   698 							break;
       
   699 
       
   700 						case 3:
       
   701 
       
   702 							imagefilter ($canvas, $imageFilters[$filterSettings[0]][0], $filterSettings[1], $filterSettings[2], $filterSettings[3]);
       
   703 							break;
       
   704 
       
   705 						case 4:
       
   706 
       
   707 							imagefilter ($canvas, $imageFilters[$filterSettings[0]][0], $filterSettings[1], $filterSettings[2], $filterSettings[3], $filterSettings[4]);
       
   708 							break;
       
   709 
       
   710 						default:
       
   711 
       
   712 							imagefilter ($canvas, $imageFilters[$filterSettings[0]][0]);
       
   713 							break;
       
   714 
       
   715 					}
       
   716 				}
       
   717 			}
       
   718 		}
       
   719 
       
   720 		// sharpen image
       
   721 		if ($sharpen && function_exists ('imageconvolution')) {
       
   722 
       
   723 			$sharpenMatrix = array (
       
   724 					array (-1,-1,-1),
       
   725 					array (-1,16,-1),
       
   726 					array (-1,-1,-1),
       
   727 					);
       
   728 
       
   729 			$divisor = 8;
       
   730 			$offset = 0;
       
   731 
       
   732 			imageconvolution ($canvas, $sharpenMatrix, $divisor, $offset);
       
   733 
       
   734 		}
       
   735 		//Straight from Wordpress core code. Reduces filesize by up to 70% for PNG's
       
   736 		if ( (IMAGETYPE_PNG == $origType || IMAGETYPE_GIF == $origType) && function_exists('imageistruecolor') && !imageistruecolor( $image ) && imagecolortransparent( $image ) > 0 ){
       
   737 			imagetruecolortopalette( $canvas, false, imagecolorstotal( $image ) );
       
   738 		}
       
   739 
       
   740 		$imgType = "";
       
   741 		$tempfile = tempnam($this->cacheDirectory, 'timthumb_tmpimg_');
       
   742 		if(preg_match('/^image\/(?:jpg|jpeg)$/i', $mimeType)){ 
       
   743 			$imgType = 'jpg';
       
   744 			imagejpeg($canvas, $tempfile, $quality); 
       
   745 		} else if(preg_match('/^image\/png$/i', $mimeType)){ 
       
   746 			$imgType = 'png';
       
   747 			imagepng($canvas, $tempfile, floor($quality * 0.09));
       
   748 		} else if(preg_match('/^image\/gif$/i', $mimeType)){
       
   749 			$imgType = 'gif';
       
   750 			imagegif($canvas, $tempfile);
       
   751 		} else {
       
   752 			return $this->sanityFail("Could not match mime type after verifying it previously.");
       
   753 		}
       
   754 
       
   755 		if($imgType == 'png' && OPTIPNG_ENABLED && OPTIPNG_PATH && @is_file(OPTIPNG_PATH)){
       
   756 			$exec = OPTIPNG_PATH;
       
   757 			$this->debug(3, "optipng'ing $tempfile");
       
   758 			$presize = filesize($tempfile);
       
   759 			$out = `$exec -o1 $tempfile`; //you can use up to -o7 but it really slows things down
       
   760 			clearstatcache();
       
   761 			$aftersize = filesize($tempfile);
       
   762 			$sizeDrop = $presize - $aftersize;
       
   763 			if($sizeDrop > 0){
       
   764 				$this->debug(1, "optipng reduced size by $sizeDrop");
       
   765 			} else if($sizeDrop < 0){
       
   766 				$this->debug(1, "optipng increased size! Difference was: $sizeDrop");
       
   767 			} else {
       
   768 				$this->debug(1, "optipng did not change image size.");
       
   769 			}
       
   770 		} else if($imgType == 'png' && PNGCRUSH_ENABLED && PNGCRUSH_PATH && @is_file(PNGCRUSH_PATH)){
       
   771 			$exec = PNGCRUSH_PATH;
       
   772 			$tempfile2 = tempnam($this->cacheDirectory, 'timthumb_tmpimg_');
       
   773 			$this->debug(3, "pngcrush'ing $tempfile to $tempfile2");
       
   774 			$out = `$exec $tempfile $tempfile2`;
       
   775 			$todel = "";
       
   776 			if(is_file($tempfile2)){
       
   777 				$sizeDrop = filesize($tempfile) - filesize($tempfile2);
       
   778 				if($sizeDrop > 0){
       
   779 					$this->debug(1, "pngcrush was succesful and gave a $sizeDrop byte size reduction");
       
   780 					$todel = $tempfile;
       
   781 					$tempfile = $tempfile2;
       
   782 				} else {
       
   783 					$this->debug(1, "pngcrush did not reduce file size. Difference was $sizeDrop bytes.");
       
   784 					$todel = $tempfile2;
       
   785 				}
       
   786 			} else {
       
   787 				$this->debug(3, "pngcrush failed with output: $out");
       
   788 				$todel = $tempfile2;
       
   789 			}
       
   790 			@unlink($todel);
       
   791 		}
       
   792 
       
   793 		$this->debug(3, "Rewriting image with security header.");
       
   794 		$tempfile4 = tempnam($this->cacheDirectory, 'timthumb_tmpimg_');
       
   795 		$context = stream_context_create ();
       
   796 		$fp = fopen($tempfile,'r',0,$context);
       
   797 		file_put_contents($tempfile4, $this->filePrependSecurityBlock . $imgType . ' ?' . '>'); //6 extra bytes, first 3 being image type 
       
   798 		file_put_contents($tempfile4, $fp, FILE_APPEND);
       
   799 		fclose($fp);
       
   800 		@unlink($tempfile);
       
   801 		$this->debug(3, "Locking and replacing cache file.");
       
   802 		$lockFile = $this->cachefile . '.lock';
       
   803 		$fh = fopen($lockFile, 'w');
       
   804 		if(! $fh){
       
   805 			return $this->error("Could not open the lockfile for writing an image.");
       
   806 		}
       
   807 		if(flock($fh, LOCK_EX)){
       
   808 			@unlink($this->cachefile); //rename generally overwrites, but doing this in case of platform specific quirks. File might not exist yet.
       
   809 			rename($tempfile4, $this->cachefile);
       
   810 			flock($fh, LOCK_UN);
       
   811 			fclose($fh);
       
   812 			@unlink($lockFile);
       
   813 		} else {
       
   814 			fclose($fh);
       
   815 			@unlink($lockFile);
       
   816 			@unlink($tempfile4);
       
   817 			return $this->error("Could not get a lock for writing.");
       
   818 		}
       
   819 		$this->debug(3, "Done image replace with security header. Cleaning up and running cleanCache()");
       
   820 		imagedestroy($canvas);
       
   821 		imagedestroy($image);
       
   822 		return true;
       
   823 	}
       
   824 	protected function calcDocRoot(){
       
   825 		$docRoot = @$_SERVER['DOCUMENT_ROOT'];
       
   826 		if (defined('LOCAL_FILE_BASE_DIRECTORY')) {
       
   827 			$docRoot = LOCAL_FILE_BASE_DIRECTORY;   
       
   828 		}
       
   829 		if(!isset($docRoot)){ 
       
   830 			$this->debug(3, "DOCUMENT_ROOT is not set. This is probably windows. Starting search 1.");
       
   831 			if(isset($_SERVER['SCRIPT_FILENAME'])){
       
   832 				$docRoot = str_replace( '\\', '/', substr($_SERVER['SCRIPT_FILENAME'], 0, 0-strlen($_SERVER['PHP_SELF'])));
       
   833 				$this->debug(3, "Generated docRoot using SCRIPT_FILENAME and PHP_SELF as: $docRoot");
       
   834 			} 
       
   835 		}
       
   836 		if(!isset($docRoot)){ 
       
   837 			$this->debug(3, "DOCUMENT_ROOT still is not set. Starting search 2.");
       
   838 			if(isset($_SERVER['PATH_TRANSLATED'])){
       
   839 				$docRoot = str_replace( '\\', '/', substr(str_replace('\\\\', '\\', $_SERVER['PATH_TRANSLATED']), 0, 0-strlen($_SERVER['PHP_SELF'])));
       
   840 				$this->debug(3, "Generated docRoot using PATH_TRANSLATED and PHP_SELF as: $docRoot");
       
   841 			} 
       
   842 		}
       
   843 		if($docRoot && $_SERVER['DOCUMENT_ROOT'] != '/'){ $docRoot = preg_replace('/\/$/', '', $docRoot); }
       
   844 		$this->debug(3, "Doc root is: " . $docRoot);
       
   845 		$this->docRoot = $docRoot;
       
   846 
       
   847 	}
       
   848 	protected function getLocalImagePath($src){
       
   849 		$src = ltrim($src, '/'); //strip off the leading '/'
       
   850 		if(! $this->docRoot){
       
   851 			$this->debug(3, "We have no document root set, so as a last resort, lets check if the image is in the current dir and serve that.");
       
   852 			//We don't support serving images outside the current dir if we don't have a doc root for security reasons.
       
   853 			$file = preg_replace('/^.*?([^\/\\\\]+)$/', '$1', $src); //strip off any path info and just leave the filename.
       
   854 			if(is_file($file)){
       
   855 				return $this->realpath($file);
       
   856 			}
       
   857 			return $this->error("Could not find your website document root and the file specified doesn't exist in timthumbs directory. We don't support serving files outside timthumb's directory without a document root for security reasons.");
       
   858 		} //Do not go past this point without docRoot set
       
   859 
       
   860 		//Try src under docRoot
       
   861 		if(file_exists ($this->docRoot . '/' . $src)) {
       
   862 			$this->debug(3, "Found file as " . $this->docRoot . '/' . $src);
       
   863 			$real = $this->realpath($this->docRoot . '/' . $src);
       
   864 			if(stripos($real, $this->docRoot) === 0){
       
   865 				return $real;
       
   866 			} else {
       
   867 				$this->debug(1, "Security block: The file specified occurs outside the document root.");
       
   868 				//allow search to continue
       
   869 			}
       
   870 		}
       
   871 		//Check absolute paths and then verify the real path is under doc root
       
   872 		$absolute = $this->realpath('/' . $src);
       
   873 		if($absolute && file_exists($absolute)){ //realpath does file_exists check, so can probably skip the exists check here
       
   874 			$this->debug(3, "Found absolute path: $absolute");
       
   875 			if(! $this->docRoot){ $this->sanityFail("docRoot not set when checking absolute path."); }
       
   876 			if(stripos($absolute, $this->docRoot) === 0){
       
   877 				return $absolute;
       
   878 			} else {
       
   879 				$this->debug(1, "Security block: The file specified occurs outside the document root.");
       
   880 				//and continue search
       
   881 			}
       
   882 		}
       
   883 		
       
   884 		$base = $this->docRoot;
       
   885 		
       
   886 		// account for Windows directory structure
       
   887 		if (strstr($_SERVER['SCRIPT_FILENAME'],':')) {
       
   888 			$sub_directories = explode('\\', str_replace($this->docRoot, '', $_SERVER['SCRIPT_FILENAME']));
       
   889 		} else {
       
   890 			$sub_directories = explode('/', str_replace($this->docRoot, '', $_SERVER['SCRIPT_FILENAME']));
       
   891 		}
       
   892 
       
   893 		foreach ($sub_directories as $sub){
       
   894 			$base .= $sub . '/';
       
   895 			$this->debug(3, "Trying file as: " . $base . $src);
       
   896 			if(file_exists($base . $src)){
       
   897 				$this->debug(3, "Found file as: " . $base . $src);
       
   898 				$real = $this->realpath($base . $src);
       
   899 				if(stripos($real, $this->realpath($this->docRoot)) === 0){
       
   900 					return $real;
       
   901 				} else {
       
   902 					$this->debug(1, "Security block: The file specified occurs outside the document root.");
       
   903 					//And continue search
       
   904 				}
       
   905 			}
       
   906 		}
       
   907 		return false;
       
   908 	}
       
   909 	protected function realpath($path){
       
   910 		//try to remove any relative paths
       
   911 		$remove_relatives = '/\w+\/\.\.\//';
       
   912 		while(preg_match($remove_relatives,$path)){
       
   913 		    $path = preg_replace($remove_relatives, '', $path);
       
   914 		}
       
   915 		//if any remain use PHP realpath to strip them out, otherwise return $path
       
   916 		//if using realpath, any symlinks will also be resolved
       
   917 		return preg_match('#^\.\./|/\.\./#', $path) ? realpath($path) : $path;
       
   918 	}
       
   919 	protected function toDelete($name){
       
   920 		$this->debug(3, "Scheduling file $name to delete on destruct.");
       
   921 		$this->toDeletes[] = $name;
       
   922 	}
       
   923 	protected function serveWebshot(){
       
   924 		$this->debug(3, "Starting serveWebshot");
       
   925 		$instr = "Please follow the instructions at http://code.google.com/p/timthumb/ to set your server up for taking website screenshots.";
       
   926 		if(! is_file(WEBSHOT_CUTYCAPT)){
       
   927 			return $this->error("CutyCapt is not installed. $instr");
       
   928 		}
       
   929 		if(! is_file(WEBSHOT_XVFB)){
       
   930 			return $this->Error("Xvfb is not installed. $instr");
       
   931 		}
       
   932 		$cuty = WEBSHOT_CUTYCAPT;
       
   933 		$xv = WEBSHOT_XVFB;
       
   934 		$screenX = WEBSHOT_SCREEN_X;
       
   935 		$screenY = WEBSHOT_SCREEN_Y;
       
   936 		$colDepth = WEBSHOT_COLOR_DEPTH;
       
   937 		$format = WEBSHOT_IMAGE_FORMAT;
       
   938 		$timeout = WEBSHOT_TIMEOUT * 1000;
       
   939 		$ua = WEBSHOT_USER_AGENT;
       
   940 		$jsOn = WEBSHOT_JAVASCRIPT_ON ? 'on' : 'off';
       
   941 		$javaOn = WEBSHOT_JAVA_ON ? 'on' : 'off';
       
   942 		$pluginsOn = WEBSHOT_PLUGINS_ON ? 'on' : 'off';
       
   943 		$proxy = WEBSHOT_PROXY ? ' --http-proxy=' . WEBSHOT_PROXY : '';
       
   944 		$tempfile = tempnam($this->cacheDirectory, 'timthumb_webshot');
       
   945 		$url = $this->src;
       
   946 		if(! preg_match('/^https?:\/\/[a-zA-Z0-9\.\-]+/i', $url)){
       
   947 			return $this->error("Invalid URL supplied.");
       
   948 		}
       
   949 		$url = preg_replace('/[^A-Za-z0-9\-\.\_\~:\/\?\#\[\]\@\!\$\&\'\(\)\*\+\,\;\=]+/', '', $url); //RFC 3986
       
   950 		//Very important we don't allow injection of shell commands here. URL is between quotes and we are only allowing through chars allowed by a the RFC 
       
   951 		// which AFAIKT can't be used for shell injection. 
       
   952 		if(WEBSHOT_XVFB_RUNNING){
       
   953 			putenv('DISPLAY=:100.0');
       
   954 			$command = "$cuty $proxy --max-wait=$timeout --user-agent=\"$ua\" --javascript=$jsOn --java=$javaOn --plugins=$pluginsOn --js-can-open-windows=off --url=\"$url\" --out-format=$format --out=$tempfile";
       
   955 		} else {
       
   956 			$command = "$xv --server-args=\"-screen 0, {$screenX}x{$screenY}x{$colDepth}\" $cuty $proxy --max-wait=$timeout --user-agent=\"$ua\" --javascript=$jsOn --java=$javaOn --plugins=$pluginsOn --js-can-open-windows=off --url=\"$url\" --out-format=$format --out=$tempfile";
       
   957 		}
       
   958 		$this->debug(3, "Executing command: $command");
       
   959 		$out = `$command`;
       
   960 		$this->debug(3, "Received output: $out");
       
   961 		if(! is_file($tempfile)){
       
   962 			$this->set404();
       
   963 			return $this->error("The command to create a thumbnail failed.");
       
   964 		}
       
   965 		$this->cropTop = true;
       
   966 		if($this->processImageAndWriteToCache($tempfile)){
       
   967 			$this->debug(3, "Image processed succesfully. Serving from cache");
       
   968 			return $this->serveCacheFile();
       
   969 		} else {
       
   970 			return false;
       
   971 		}
       
   972 	}
       
   973 	protected function serveExternalImage(){
       
   974 		if(! preg_match('/^https?:\/\/[a-zA-Z0-9\-\.]+/i', $this->src)){
       
   975 			$this->error("Invalid URL supplied.");
       
   976 			return false;
       
   977 		}
       
   978 		$tempfile = tempnam($this->cacheDirectory, 'timthumb');
       
   979 		$this->debug(3, "Fetching external image into temporary file $tempfile");
       
   980 		$this->toDelete($tempfile);
       
   981 		#fetch file here
       
   982 		if(! $this->getURL($this->src, $tempfile)){
       
   983 			@unlink($this->cachefile);
       
   984 			touch($this->cachefile);
       
   985 			$this->debug(3, "Error fetching URL: " . $this->lastURLError);
       
   986 			$this->error("Error reading the URL you specified from remote host." . $this->lastURLError);
       
   987 			return false;
       
   988 		}
       
   989 
       
   990 		$mimeType = $this->getMimeType($tempfile);
       
   991 		if(! preg_match("/^image\/(?:jpg|jpeg|gif|png)$/i", $mimeType)){
       
   992 			$this->debug(3, "Remote file has invalid mime type: $mimeType");
       
   993 			@unlink($this->cachefile);
       
   994 			touch($this->cachefile);
       
   995 			$this->error("The remote file is not a valid image.");
       
   996 			return false;
       
   997 		}
       
   998 		if($this->processImageAndWriteToCache($tempfile)){
       
   999 			$this->debug(3, "Image processed succesfully. Serving from cache");
       
  1000 			return $this->serveCacheFile();
       
  1001 		} else {
       
  1002 			return false;
       
  1003 		}
       
  1004 	}
       
  1005 	public static function curlWrite($h, $d){
       
  1006 		fwrite(self::$curlFH, $d);
       
  1007 		self::$curlDataWritten += strlen($d);
       
  1008 		if(self::$curlDataWritten > MAX_FILE_SIZE){
       
  1009 			return 0;
       
  1010 		} else {
       
  1011 			return strlen($d);
       
  1012 		}
       
  1013 	}
       
  1014 	protected function serveCacheFile(){
       
  1015 		$this->debug(3, "Serving {$this->cachefile}");
       
  1016 		if(! is_file($this->cachefile)){
       
  1017 			$this->error("serveCacheFile called in timthumb but we couldn't find the cached file.");
       
  1018 			return false;
       
  1019 		}
       
  1020 		$fp = fopen($this->cachefile, 'rb');
       
  1021 		if(! $fp){ return $this->error("Could not open cachefile."); }
       
  1022 		fseek($fp, strlen($this->filePrependSecurityBlock), SEEK_SET);
       
  1023 		$imgType = fread($fp, 3);
       
  1024 		fseek($fp, 3, SEEK_CUR);
       
  1025 		if(ftell($fp) != strlen($this->filePrependSecurityBlock) + 6){
       
  1026 			@unlink($this->cachefile);
       
  1027 			return $this->error("The cached image file seems to be corrupt.");
       
  1028 		}
       
  1029 		$imageDataSize = filesize($this->cachefile) - (strlen($this->filePrependSecurityBlock) + 6);
       
  1030 		$this->sendImageHeaders($imgType, $imageDataSize);
       
  1031 		$bytesSent = @fpassthru($fp);
       
  1032 		fclose($fp);
       
  1033 		if($bytesSent > 0){
       
  1034 			return true;
       
  1035 		}
       
  1036 		$content = file_get_contents ($this->cachefile);
       
  1037 		if ($content != FALSE) {
       
  1038 			$content = substr($content, strlen($this->filePrependSecurityBlock) + 6);
       
  1039 			echo $content;
       
  1040 			$this->debug(3, "Served using file_get_contents and echo");
       
  1041 			return true;
       
  1042 		} else {
       
  1043 			$this->error("Cache file could not be loaded.");
       
  1044 			return false;
       
  1045 		}
       
  1046 	}
       
  1047 	protected function sendImageHeaders($mimeType, $dataSize){
       
  1048 		if(! preg_match('/^image\//i', $mimeType)){
       
  1049 			$mimeType = 'image/' . $mimeType;
       
  1050 		}
       
  1051 		if(strtolower($mimeType) == 'image/jpg'){
       
  1052 			$mimeType = 'image/jpeg';
       
  1053 		}
       
  1054 		$gmdate_expires = gmdate ('D, d M Y H:i:s', strtotime ('now +10 days')) . ' GMT';
       
  1055 		$gmdate_modified = gmdate ('D, d M Y H:i:s') . ' GMT';
       
  1056 		// send content headers then display image
       
  1057 		header ('Content-Type: ' . $mimeType);
       
  1058 		header ('Accept-Ranges: none'); //Changed this because we don't accept range requests
       
  1059 		header ('Last-Modified: ' . $gmdate_modified);
       
  1060 		header ('Content-Length: ' . $dataSize);
       
  1061 		if(BROWSER_CACHE_DISABLE){
       
  1062 			$this->debug(3, "Browser cache is disabled so setting non-caching headers.");
       
  1063 			header('Cache-Control: no-store, no-cache, must-revalidate, max-age=0');
       
  1064 			header("Pragma: no-cache");
       
  1065 			header('Expires: ' . gmdate ('D, d M Y H:i:s', time()));
       
  1066 		} else {
       
  1067 			$this->debug(3, "Browser caching is enabled");
       
  1068 			header('Cache-Control: max-age=' . BROWSER_CACHE_MAX_AGE . ', must-revalidate');
       
  1069 			header('Expires: ' . $gmdate_expires);
       
  1070 		}
       
  1071 		return true;
       
  1072 	}
       
  1073 	protected function securityChecks(){
       
  1074 	}
       
  1075 	protected function param($property, $default = ''){
       
  1076 		if (isset ($_GET[$property])) {
       
  1077 			return $_GET[$property];
       
  1078 		} else {
       
  1079 			return $default;
       
  1080 		}
       
  1081 	}
       
  1082 	protected function openImage($mimeType, $src){
       
  1083 		switch ($mimeType) {
       
  1084 			case 'image/jpeg':
       
  1085 				$image = imagecreatefromjpeg ($src);
       
  1086 				break;
       
  1087 
       
  1088 			case 'image/png':
       
  1089 				$image = imagecreatefrompng ($src);
       
  1090 				break;
       
  1091 
       
  1092 			case 'image/gif':
       
  1093 				$image = imagecreatefromgif ($src);
       
  1094 				break;
       
  1095 			
       
  1096 			default:
       
  1097 				$this->error("Unrecognised mimeType");
       
  1098 		}
       
  1099 
       
  1100 		return $image;
       
  1101 	}
       
  1102 	protected function getIP(){
       
  1103 		$rem = @$_SERVER["REMOTE_ADDR"];
       
  1104 		$ff = @$_SERVER["HTTP_X_FORWARDED_FOR"];
       
  1105 		$ci = @$_SERVER["HTTP_CLIENT_IP"];
       
  1106 		if(preg_match('/^(?:192\.168|172\.16|10\.|127\.)/', $rem)){ 
       
  1107 			if($ff){ return $ff; }
       
  1108 			if($ci){ return $ci; }
       
  1109 			return $rem;
       
  1110 		} else {
       
  1111 			if($rem){ return $rem; }
       
  1112 			if($ff){ return $ff; }
       
  1113 			if($ci){ return $ci; }
       
  1114 			return "UNKNOWN";
       
  1115 		}
       
  1116 	}
       
  1117 	protected function debug($level, $msg){
       
  1118 		if(DEBUG_ON && $level <= DEBUG_LEVEL){
       
  1119 			$execTime = sprintf('%.6f', microtime(true) - $this->startTime);
       
  1120 			$tick = sprintf('%.6f', 0);
       
  1121 			if($this->lastBenchTime > 0){
       
  1122 				$tick = sprintf('%.6f', microtime(true) - $this->lastBenchTime);
       
  1123 			}
       
  1124 			$this->lastBenchTime = microtime(true);
       
  1125 			error_log("TimThumb Debug line " . __LINE__ . " [$execTime : $tick]: $msg");
       
  1126 		}
       
  1127 	}
       
  1128 	protected function sanityFail($msg){
       
  1129 		return $this->error("There is a problem in the timthumb code. Message: Please report this error at <a href='http://code.google.com/p/timthumb/issues/list'>timthumb's bug tracking page</a>: $msg");
       
  1130 	}
       
  1131 	protected function getMimeType($file){
       
  1132 		$info = getimagesize($file);
       
  1133 		if(is_array($info) && $info['mime']){
       
  1134 			return $info['mime'];
       
  1135 		}
       
  1136 		return '';
       
  1137 	}
       
  1138 	protected function setMemoryLimit(){
       
  1139 		$inimem = ini_get('memory_limit');
       
  1140 		$inibytes = timthumb::returnBytes($inimem);
       
  1141 		$ourbytes = timthumb::returnBytes(MEMORY_LIMIT);
       
  1142 		if($inibytes < $ourbytes){
       
  1143 			ini_set ('memory_limit', MEMORY_LIMIT);
       
  1144 			$this->debug(3, "Increased memory from $inimem to " . MEMORY_LIMIT);
       
  1145 		} else {
       
  1146 			$this->debug(3, "Not adjusting memory size because the current setting is " . $inimem . " and our size of " . MEMORY_LIMIT . " is smaller.");
       
  1147 		}
       
  1148 	}
       
  1149 	protected static function returnBytes($size_str){
       
  1150 		switch (substr ($size_str, -1))
       
  1151 		{
       
  1152 			case 'M': case 'm': return (int)$size_str * 1048576;
       
  1153 			case 'K': case 'k': return (int)$size_str * 1024;
       
  1154 			case 'G': case 'g': return (int)$size_str * 1073741824;
       
  1155 			default: return $size_str;
       
  1156 		}
       
  1157 	}
       
  1158 	protected function getURL($url, $tempfile){
       
  1159 		$this->lastURLError = false;
       
  1160 		$url = preg_replace('/ /', '%20', $url);
       
  1161 		if(function_exists('curl_init')){
       
  1162 			$this->debug(3, "Curl is installed so using it to fetch URL.");
       
  1163 			self::$curlFH = fopen($tempfile, 'w');
       
  1164 			if(! self::$curlFH){
       
  1165 				$this->error("Could not open $tempfile for writing.");
       
  1166 				return false;
       
  1167 			}
       
  1168 			self::$curlDataWritten = 0;
       
  1169 			$this->debug(3, "Fetching url with curl: $url");
       
  1170 			$curl = curl_init($url);
       
  1171 			curl_setopt ($curl, CURLOPT_TIMEOUT, CURL_TIMEOUT);
       
  1172 			curl_setopt ($curl, CURLOPT_USERAGENT, "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/534.30 (KHTML, like Gecko) Chrome/12.0.742.122 Safari/534.30");
       
  1173 			curl_setopt ($curl, CURLOPT_RETURNTRANSFER, TRUE);
       
  1174 			curl_setopt ($curl, CURLOPT_HEADER, 0);
       
  1175 			curl_setopt ($curl, CURLOPT_SSL_VERIFYPEER, FALSE);
       
  1176 			curl_setopt ($curl, CURLOPT_WRITEFUNCTION, 'timthumb::curlWrite');
       
  1177 			@curl_setopt ($curl, CURLOPT_FOLLOWLOCATION, true);
       
  1178 			@curl_setopt ($curl, CURLOPT_MAXREDIRS, 10);
       
  1179 			
       
  1180 			$curlResult = curl_exec($curl);
       
  1181 			fclose(self::$curlFH);
       
  1182 			$httpStatus = curl_getinfo($curl, CURLINFO_HTTP_CODE);
       
  1183 			if($httpStatus == 404){
       
  1184 				$this->set404();
       
  1185 			}
       
  1186 			if($curlResult){
       
  1187 				curl_close($curl);
       
  1188 				return true;
       
  1189 			} else {
       
  1190 				$this->lastURLError = curl_error($curl);
       
  1191 				curl_close($curl);
       
  1192 				return false;
       
  1193 			}
       
  1194 		} else {
       
  1195 			$img = @file_get_contents ($url);
       
  1196 			if($img === false){
       
  1197 				$err = error_get_last();
       
  1198 				if(is_array($err) && $err['message']){
       
  1199 					$this->lastURLError = $err['message'];
       
  1200 				} else {
       
  1201 					$this->lastURLError = $err;
       
  1202 				}
       
  1203 				if(preg_match('/404/', $this->lastURLError)){
       
  1204 					$this->set404();
       
  1205 				}
       
  1206 
       
  1207 				return false;
       
  1208 			}
       
  1209 			if(! file_put_contents($tempfile, $img)){
       
  1210 				$this->error("Could not write to $tempfile.");
       
  1211 				return false;
       
  1212 			}
       
  1213 			return true;
       
  1214 		}
       
  1215 
       
  1216 	}
       
  1217 	protected function serveImg($file){
       
  1218 		$s = getimagesize($file);
       
  1219 		if(! ($s && $s['mime'])){
       
  1220 			return false;
       
  1221 		}
       
  1222 		header ('Content-Type: ' . $s['mime']);
       
  1223 		header ('Content-Length: ' . filesize($file) );
       
  1224 		header ('Cache-Control: no-store, no-cache, must-revalidate, max-age=0');
       
  1225 		header ("Pragma: no-cache");
       
  1226 		$bytes = @readfile($file);
       
  1227 		if($bytes > 0){
       
  1228 			return true;
       
  1229 		}
       
  1230 		$content = @file_get_contents ($file);
       
  1231 		if ($content != FALSE){
       
  1232 			echo $content;
       
  1233 			return true;
       
  1234 		}
       
  1235 		return false;
       
  1236 
       
  1237 	}
       
  1238 	protected function set404(){
       
  1239 		$this->is404 = true;
       
  1240 	}
       
  1241 	protected function is404(){
       
  1242 		return $this->is404;
       
  1243 	}
       
  1244 }