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