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