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