/wp-content/themes/arras/library/timthumb.php

https://github.com/digitalstrategyworks/Reese-WordPress · PHP · 1433 lines · 371 code · 820 blank · 242 comment · 83 complexity · d3bc10b160122b1733ac7d89e7874c47 MD5 · raw file

  1. <?php
  2. /*
  3. TimThumb script created by Tim McDaniels and Darren Hoyt with tweaks by Ben Gillbanks
  4. http://code.google.com/p/timthumb/
  5. MIT License: http://www.opensource.org/licenses/mit-license.php
  6. Paramters
  7. ---------
  8. w: width
  9. h: height
  10. zc: zoom crop (0 or 1)
  11. q: quality (default is 75 and max is 100)
  12. HTML example: <img src="/scripts/timthumb.php?src=/images/whatever.jpg&w=150&h=200&zc=1" alt="" />
  13. */
  14. /*
  15. $sizeLimits = array(
  16. "100x100",
  17. "150x150",
  18. );
  19. error_reporting(E_ALL);
  20. ini_set("display_errors", 1);
  21. */
  22. // check to see if GD function exist
  23. if(!function_exists('imagecreatetruecolor')) {
  24. displayError('GD Library Error: imagecreatetruecolor does not exist - please contact your webhost and ask them to install the GD library');
  25. }
  26. define ('CACHE_SIZE', 250); // number of files to store before clearing cache
  27. define ('CACHE_CLEAR', 5); // maximum number of files to delete on each cache clear
  28. define ('VERSION', '1.12'); // version number (to force a cache refresh
  29. if (function_exists('imagefilter') && defined('IMG_FILTER_NEGATE')) {
  30. $imageFilters = array(
  31. "1" => array(IMG_FILTER_NEGATE, 0),
  32. "2" => array(IMG_FILTER_GRAYSCALE, 0),
  33. "3" => array(IMG_FILTER_BRIGHTNESS, 1),
  34. "4" => array(IMG_FILTER_CONTRAST, 1),
  35. "5" => array(IMG_FILTER_COLORIZE, 4),
  36. "6" => array(IMG_FILTER_EDGEDETECT, 0),
  37. "7" => array(IMG_FILTER_EMBOSS, 0),
  38. "8" => array(IMG_FILTER_GAUSSIAN_BLUR, 0),
  39. "9" => array(IMG_FILTER_SELECTIVE_BLUR, 0),
  40. "10" => array(IMG_FILTER_MEAN_REMOVAL, 0),
  41. "11" => array(IMG_FILTER_SMOOTH, 0),
  42. );
  43. }
  44. // sort out image source
  45. $src = get_request("src", "");
  46. if($src == '' || strlen($src) <= 3) {
  47. displayError ('no image specified');
  48. }
  49. // clean params before use
  50. $src = cleanSource($src);
  51. // last modified time (for caching)
  52. $lastModified = filemtime($src);
  53. // get properties
  54. $new_width = preg_replace("/[^0-9]+/", "", get_request("w", 0));
  55. $new_height = preg_replace("/[^0-9]+/", "", get_request("h", 0));
  56. $zoom_crop = preg_replace("/[^0-9]+/", "", get_request("zc", 1));
  57. $quality = preg_replace("/[^0-9]+/", "", get_request("q", 80));
  58. $filters = get_request("f", "");
  59. if ($new_width == 0 && $new_height == 0) {
  60. $new_width = 100;
  61. $new_height = 100;
  62. }
  63. // set path to cache directory (default is ./cache)
  64. // this can be changed to a different location
  65. $cache_dir = './cache';
  66. // get mime type of src
  67. $mime_type = mime_type($src);
  68. // check to see if this image is in the cache already
  69. check_cache ($cache_dir, $mime_type);
  70. // if not in cache then clear some space and generate a new file
  71. cleanCache();
  72. ini_set('memory_limit', "50M");
  73. // make sure that the src is gif/jpg/png
  74. if(!valid_src_mime_type($mime_type)) {
  75. displayError("Invalid src mime type: " .$mime_type);
  76. }
  77. if(strlen($src) && file_exists($src)) {
  78. // open the existing image
  79. $image = open_image($mime_type, $src);
  80. if($image === false) {
  81. displayError('Unable to open image : ' . $src);
  82. }
  83. // Get original width and height
  84. $width = imagesx($image);
  85. $height = imagesy($image);
  86. // generate new w/h if not provided
  87. if( $new_width && !$new_height ) {
  88. $new_height = $height * ( $new_width / $width );
  89. } elseif($new_height && !$new_width) {
  90. $new_width = $width * ( $new_height / $height );
  91. } elseif(!$new_width && !$new_height) {
  92. $new_width = $width;
  93. $new_height = $height;
  94. }
  95. // create a new true color image
  96. $canvas = imagecreatetruecolor( $new_width, $new_height );
  97. imagealphablending($canvas, false);
  98. // Create a new transparent color for image
  99. $color = imagecolorallocatealpha($canvas, 0, 0, 0, 127);
  100. // Completely fill the background of the new image with allocated color.
  101. imagefill($canvas, 0, 0, $color);
  102. // Restore transparency blending
  103. imagesavealpha($canvas, true);
  104. if( $zoom_crop ) {
  105. $src_x = $src_y = 0;
  106. $src_w = $width;
  107. $src_h = $height;
  108. $cmp_x = $width / $new_width;
  109. $cmp_y = $height / $new_height;
  110. // calculate x or y coordinate and width or height of source
  111. if ( $cmp_x > $cmp_y ) {
  112. $src_w = round( ( $width / $cmp_x * $cmp_y ) );
  113. $src_x = round( ( $width - ( $width / $cmp_x * $cmp_y ) ) / 2 );
  114. } elseif ( $cmp_y > $cmp_x ) {
  115. $src_h = round( ( $height / $cmp_y * $cmp_x ) );
  116. $src_y = round( ( $height - ( $height / $cmp_y * $cmp_x ) ) / 2 );
  117. }
  118. imagecopyresampled( $canvas, $image, 0, 0, $src_x, $src_y, $new_width, $new_height, $src_w, $src_h );
  119. } else {
  120. // copy and resize part of an image with resampling
  121. imagecopyresampled( $canvas, $image, 0, 0, 0, 0, $new_width, $new_height, $width, $height );
  122. }
  123. if ($filters != '' && function_exists('imagefilter') && defined('IMG_FILTER_NEGATE')) {
  124. // apply filters to image
  125. $filterList = explode("|", $filters);
  126. foreach($filterList as $fl) {
  127. $filterSettings = explode(",", $fl);
  128. if(isset($imageFilters[$filterSettings[0]])) {
  129. for($i = 0; $i < 4; $i ++) {
  130. if(!isset($filterSettings[$i])) {
  131. $filterSettings[$i] = null;
  132. }
  133. }
  134. switch($imageFilters[$filterSettings[0]][1]) {
  135. case 1:
  136. imagefilter($canvas, $imageFilters[$filterSettings[0]][0], $filterSettings[1]);
  137. break;
  138. case 2:
  139. imagefilter($canvas, $imageFilters[$filterSettings[0]][0], $filterSettings[1], $filterSettings[2]);
  140. break;
  141. case 3:
  142. imagefilter($canvas, $imageFilters[$filterSettings[0]][0], $filterSettings[1], $filterSettings[2], $filterSettings[3]);
  143. break;
  144. default:
  145. imagefilter($canvas, $imageFilters[$filterSettings[0]][0]);
  146. break;
  147. }
  148. }
  149. }
  150. }
  151. // output image to browser based on mime type
  152. show_image($mime_type, $canvas, $cache_dir);
  153. // remove image from memory
  154. imagedestroy($canvas);
  155. } else {
  156. if(strlen($src)) {
  157. displayError("image " . $src . " not found");
  158. } else {
  159. displayError("no source specified");
  160. }
  161. }
  162. /**
  163. *
  164. */
  165. function show_image($mime_type, $image_resized, $cache_dir) {
  166. global $quality;
  167. // check to see if we can write to the cache directory
  168. $is_writable = 0;
  169. $cache_file_name = $cache_dir . '/' . get_cache_file();
  170. if (touch($cache_file_name)) {
  171. // give 666 permissions so that the developer
  172. // can overwrite web server user
  173. chmod ($cache_file_name, 0666);
  174. $is_writable = 1;
  175. } else {
  176. $cache_file_name = NULL;
  177. header ('Content-type: ' . $mime_type);
  178. }
  179. switch ($mime_type) {
  180. case 'image/jpeg':
  181. imagejpeg($image_resized, $cache_file_name, $quality);
  182. break;
  183. default :
  184. $quality = floor ($quality * 0.09);
  185. imagepng($image_resized, $cache_file_name, $quality);
  186. }
  187. if ($is_writable) {
  188. show_cache_file ($cache_dir, $mime_type);
  189. }
  190. imagedestroy ($image_resized);
  191. displayError ("error showing image");
  192. }
  193. /**
  194. *
  195. */
  196. function get_request( $property, $default = 0 ) {
  197. if( isset($_REQUEST[$property]) ) {
  198. return $_REQUEST[$property];
  199. } else {
  200. return $default;
  201. }
  202. }
  203. /**
  204. *
  205. */
  206. function open_image($mime_type, $src) {
  207. $mime_type = strtolower($mime_type);
  208. if (stristr ($mime_type, 'gif')) {
  209. $image = imagecreatefromgif($src);
  210. } elseif (stristr($mime_type, 'jpeg')) {
  211. @ini_set ('gd.jpeg_ignore_warning', 1);
  212. $image = imagecreatefromjpeg($src);
  213. } elseif (stristr ($mime_type, 'png')) {
  214. $image = imagecreatefrompng($src);
  215. }
  216. return $image;
  217. }
  218. /**
  219. * clean out old files from the cache
  220. * you can change the number of files to store and to delete per loop in the defines at the top of the code
  221. */
  222. function cleanCache() {
  223. $files = glob("cache/*", GLOB_BRACE);
  224. if (count($files) > 0) {
  225. $yesterday = time() - (24 * 60 * 60);
  226. usort($files, 'filemtime_compare');
  227. $i = 0;
  228. if (count($files) > CACHE_SIZE) {
  229. foreach ($files as $file) {
  230. $i ++;
  231. if ($i >= CACHE_CLEAR) {
  232. return;
  233. }
  234. if (@filemtime($file) > $yesterday) {
  235. return;
  236. }
  237. if (file_exists($file)) {
  238. unlink($file);
  239. }
  240. }
  241. }
  242. }
  243. }
  244. /**
  245. * compare the file time of two files
  246. */
  247. function filemtime_compare($a, $b) {
  248. return filemtime($a) - filemtime($b);
  249. }
  250. /**
  251. * determine the file mime type
  252. */
  253. function mime_type($file) {
  254. if (stristr(PHP_OS, 'WIN')) {
  255. $os = 'WIN';
  256. } else {
  257. $os = PHP_OS;
  258. }
  259. $mime_type = '';
  260. if (function_exists('mime_content_type')) {
  261. $mime_type = mime_content_type($file);
  262. }
  263. // use PECL fileinfo to determine mime type
  264. if (!valid_src_mime_type($mime_type)) {
  265. if (function_exists('finfo_open')) {
  266. $finfo = @finfo_open(FILEINFO_MIME);
  267. if ($finfo != '') {
  268. $mime_type = finfo_file($finfo, $file);
  269. finfo_close($finfo);
  270. }
  271. }
  272. }
  273. // try to determine mime type by using unix file command
  274. // this should not be executed on windows
  275. if (!valid_src_mime_type($mime_type) && $os != "WIN") {
  276. if (preg_match("/FREEBSD|LINUX/", $os)) {
  277. $mime_type = trim(@shell_exec('file -bi ' . escapeshellarg($file)));
  278. }
  279. }
  280. // use file's extension to determine mime type
  281. if (!valid_src_mime_type($mime_type)) {
  282. // set defaults
  283. $mime_type = 'image/png';
  284. // file details
  285. $fileDetails = pathinfo($file);
  286. $ext = strtolower($fileDetails["extension"]);
  287. // mime types
  288. $types = array(
  289. 'jpg' => 'image/jpeg',
  290. 'jpeg' => 'image/jpeg',
  291. 'png' => 'image/png',
  292. 'gif' => 'image/gif'
  293. );
  294. if (strlen($ext) && strlen($types[$ext])) {
  295. $mime_type = $types[$ext];
  296. }
  297. }
  298. return $mime_type;
  299. }
  300. /**
  301. *
  302. */
  303. function valid_src_mime_type($mime_type) {
  304. if (preg_match("/jpg|jpeg|gif|png/i", $mime_type)) {
  305. return true;
  306. }
  307. return false;
  308. }
  309. /**
  310. *
  311. */
  312. function check_cache ($cache_dir, $mime_type) {
  313. // make sure cache dir exists
  314. if (!file_exists($cache_dir)) {
  315. // give 777 permissions so that developer can overwrite
  316. // files created by web server user
  317. mkdir($cache_dir);
  318. chmod($cache_dir, 0777);
  319. }
  320. show_cache_file ($cache_dir, $mime_type);
  321. }
  322. /**
  323. *
  324. */
  325. function show_cache_file ($cache_dir, $mime_type) {
  326. $cache_file = $cache_dir . '/' . get_cache_file();
  327. if (file_exists($cache_file)) {
  328. $gmdate_mod = gmdate("D, d M Y H:i:s", filemtime($cache_file));
  329. if(! strstr($gmdate_mod, "GMT")) {
  330. $gmdate_mod .= " GMT";
  331. }
  332. if (isset($_SERVER["HTTP_IF_MODIFIED_SINCE"])) {
  333. // check for updates
  334. $if_modified_since = preg_replace ("/;.*$/", "", $_SERVER["HTTP_IF_MODIFIED_SINCE"]);
  335. if ($if_modified_since == $gmdate_mod) {
  336. header("HTTP/1.1 304 Not Modified");
  337. die();
  338. }
  339. }
  340. $fileSize = filesize ($cache_file);
  341. // send headers then display image
  342. header ('Content-Type: ' . $mime_type);
  343. header ('Accept-Ranges: bytes');
  344. header ('Last-Modified: ' . $gmdate_mod);
  345. header ('Content-Length: ' . $fileSize);
  346. header ('Cache-Control: max-age=9999, must-revalidate');
  347. header ('Expires: ' . $gmdate_mod);
  348. readfile ($cache_file);
  349. die();
  350. }
  351. }
  352. /**
  353. *
  354. */
  355. function get_cache_file() {
  356. global $lastModified;
  357. static $cache_file;
  358. if (!$cache_file) {
  359. $cachename = $_SERVER['QUERY_STRING'] . VERSION . $lastModified;
  360. $cache_file = md5($cachename) . '.png';
  361. }
  362. return $cache_file;
  363. }
  364. /**
  365. * check to if the url is valid or not
  366. */
  367. function valid_extension ($ext) {
  368. if (preg_match("/jpg|jpeg|png|gif/i", $ext)) {
  369. return TRUE;
  370. } else {
  371. return FALSE;
  372. }
  373. }
  374. /**
  375. *
  376. */
  377. function checkExternal ($src) {
  378. $allowedSites = array(
  379. 'flickr.com',
  380. 'picasa.com',
  381. 'blogger.com',
  382. 'wordpress.com',
  383. 'img.youtube.com',
  384. );
  385. if (ereg('http://', $src) == true) {
  386. $url_info = parse_url ($src);
  387. $isAllowedSite = false;
  388. foreach ($allowedSites as $site) {
  389. if (ereg($site, $url_info['host']) == true) {
  390. $isAllowedSite = true;
  391. }
  392. }
  393. if ($isAllowedSite) {
  394. $fileDetails = pathinfo($src);
  395. $ext = strtolower($fileDetails['extension']);
  396. $filename = md5($src);
  397. $local_filepath = 'temp/' . $filename . '.' . $ext;
  398. if (!file_exists($local_filepath)) {
  399. if (function_exists('curl_init')) {
  400. $fh = fopen($local_filepath, 'w');
  401. $ch = curl_init($src);
  402. curl_setopt($ch, CURLOPT_URL, $src);
  403. curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
  404. curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
  405. curl_setopt($ch, CURLOPT_HEADER, 0);
  406. 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');
  407. curl_setopt($ch, CURLOPT_FILE, $fh);
  408. if (curl_exec($ch) === FALSE) {
  409. if (file_exists($local_filepath)) {
  410. unlink($local_filepath);
  411. }
  412. displayError('error reading file ' . $src . ' from remote host: ' . curl_error($ch));
  413. }
  414. curl_close($ch);
  415. fclose($fh);
  416. } else {
  417. if (!$img = file_get_contents($src)) {
  418. displayError('remote file for ' . $src . ' can not be accessed. It is likely that the file permissions are restricted');
  419. }
  420. if (file_put_contents($local_filepath, $img) == FALSE) {
  421. displayError('error writing temporary file');
  422. }
  423. }
  424. if (!file_exists($local_filepath)) {
  425. displayError('local file for ' . $src . ' can not be created');
  426. }
  427. }
  428. $src = $local_filepath;
  429. } else {
  430. displayError('remote host "' . $url_info['host'] . '" not allowed');
  431. }
  432. }
  433. return $src;
  434. }
  435. /**
  436. * tidy up the image source url
  437. */
  438. function cleanSource($src) {
  439. $host = str_replace('www.', '', $_SERVER['HTTP_HOST']);
  440. $regex = "/^((ht|f)tp(s|):\/\/)(www\.|)" . $host . "/i";
  441. $src = preg_replace ($regex, '', $src);
  442. $src = htmlentities ($src);
  443. $src = checkExternal ($src);
  444. // remove slash from start of string
  445. if (strpos($src, '/') === 0) {
  446. $src = substr ($src, -(strlen($src) - 1));
  447. }
  448. // don't allow users the ability to use '../'
  449. // in order to gain access to files below document root
  450. $src = preg_replace("/\.\.+\//", "", $src);
  451. // get path to image on file system
  452. $src = get_document_root($src) . '/' . $src;
  453. return $src;
  454. }
  455. /**
  456. *
  457. */
  458. function get_document_root ($src) {
  459. // check for unix servers
  460. if(file_exists($_SERVER['DOCUMENT_ROOT'] . '/' . $src)) {
  461. return $_SERVER['DOCUMENT_ROOT'];
  462. }
  463. // check from script filename (to get all directories to timthumb location)
  464. $parts = array_diff(explode('/', $_SERVER['SCRIPT_FILENAME']), explode('/', $_SERVER['DOCUMENT_ROOT']));
  465. $path = $_SERVER['DOCUMENT_ROOT'];
  466. foreach ($parts as $part) {
  467. $path .= '/' . $part;
  468. if (file_exists($path . '/' . $src)) {
  469. return $path;
  470. }
  471. }
  472. // the relative paths below are useful if timthumb is moved outside of document root
  473. // specifically if installed in wordpress themes like mimbo pro:
  474. // /wp-content/themes/mimbopro/scripts/timthumb.php
  475. $paths = array(
  476. ".",
  477. "..",
  478. "../..",
  479. "../../..",
  480. "../../../..",
  481. "../../../../.."
  482. );
  483. foreach ($paths as $path) {
  484. if(file_exists($path . '/' . $src)) {
  485. return $path;
  486. }
  487. }
  488. // special check for microsoft servers
  489. if (!isset($_SERVER['DOCUMENT_ROOT'])) {
  490. $path = str_replace("/", "\\", $_SERVER['ORIG_PATH_INFO']);
  491. $path = str_replace($path, "", $_SERVER['SCRIPT_FILENAME']);
  492. if (file_exists($path . '/' . $src)) {
  493. return $path;
  494. }
  495. }
  496. displayError('file not found ' . $src);
  497. }
  498. /**
  499. * generic error message
  500. */
  501. function displayError($errorString = '') {
  502. header('HTTP/1.1 400 Bad Request');
  503. die($errorString);
  504. }
  505. ?>