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