PageRenderTime 28ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 2ms

/assets/snippets/phpthumb/phpthumb.class.php

https://github.com/modxcms/evolution
PHP | 4394 lines | 3610 code | 486 blank | 298 comment | 861 complexity | 7e84704f5056af89a27a09deebac0ffc MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.1, MIT, BSD-2-Clause, Apache-2.0, BSD-3-Clause

Large files files are truncated, but you can click here to view the full file

  1. <?php
  2. //////////////////////////////////////////////////////////////
  3. // phpThumb() by James Heinrich <info@silisoftware.com> //
  4. // available at http://phpthumb.sourceforge.net //
  5. // and/or https://github.com/JamesHeinrich/phpThumb //
  6. //////////////////////////////////////////////////////////////
  7. /// //
  8. // See: phpthumb.readme.txt for usage instructions //
  9. // ///
  10. //////////////////////////////////////////////////////////////
  11. ob_start();
  12. if (!include_once __DIR__ .'/phpthumb.functions.php' ) {
  13. ob_end_flush();
  14. die('failed to include_once("'. __DIR__ .'/phpthumb.functions.php")');
  15. }
  16. ob_end_clean();
  17. class phpthumb {
  18. // public:
  19. // START PARAMETERS (for object mode and phpThumb.php)
  20. // See phpthumb.readme.txt for descriptions of what each of these values are
  21. public $src = null; // SouRCe filename
  22. public $new = null; // NEW image (phpThumb.php only)
  23. public $w = null; // Width
  24. public $h = null; // Height
  25. public $wp = null; // Width (Portrait Images Only)
  26. public $hp = null; // Height (Portrait Images Only)
  27. public $wl = null; // Width (Landscape Images Only)
  28. public $hl = null; // Height (Landscape Images Only)
  29. public $ws = null; // Width (Square Images Only)
  30. public $hs = null; // Height (Square Images Only)
  31. public $f = null; // output image Format
  32. public $q = 75; // jpeg output Quality
  33. public $sx = null; // Source crop top-left X position
  34. public $sy = null; // Source crop top-left Y position
  35. public $sw = null; // Source crop Width
  36. public $sh = null; // Source crop Height
  37. public $zc = null; // Zoom Crop
  38. public $bc = null; // Border Color
  39. public $bg = null; // BackGround color
  40. public $fltr = array(); // FiLTeRs
  41. public $goto = null; // GO TO url after processing
  42. public $err = null; // default ERRor image filename
  43. public $xto = null; // extract eXif Thumbnail Only
  44. public $ra = null; // Rotate by Angle
  45. public $ar = null; // Auto Rotate
  46. public $aoe = null; // Allow Output Enlargement
  47. public $far = null; // Fixed Aspect Ratio
  48. public $iar = null; // Ignore Aspect Ratio
  49. public $maxb = null; // MAXimum Bytes
  50. public $down = null; // DOWNload thumbnail filename
  51. public $md5s = null; // MD5 hash of Source image
  52. public $sfn = 0; // Source Frame Number
  53. public $dpi = 150; // Dots Per Inch for vector source formats
  54. public $sia = null; // Save Image As filename
  55. public $file = null; // >>>deprecated, DO NOT USE, will be removed in future versions<<<
  56. public $phpThumbDebug = null;
  57. // END PARAMETERS
  58. // public:
  59. // START CONFIGURATION OPTIONS (for object mode only)
  60. // See phpThumb.config.php for descriptions of what each of these settings do
  61. // * Directory Configuration
  62. public $config_cache_directory = null;
  63. public $config_cache_directory_depth = 0;
  64. public $config_cache_disable_warning = true;
  65. public $config_cache_source_enabled = false;
  66. public $config_cache_source_directory = null;
  67. public $config_temp_directory = null;
  68. public $config_document_root = null;
  69. // * Default output configuration:
  70. public $config_output_format = 'jpeg';
  71. public $config_output_maxwidth = 0;
  72. public $config_output_maxheight = 0;
  73. public $config_output_interlace = true;
  74. // * Error message configuration
  75. public $config_error_image_width = 400;
  76. public $config_error_image_height = 100;
  77. public $config_error_message_image_default = '';
  78. public $config_error_bgcolor = 'CCCCFF';
  79. public $config_error_textcolor = 'FF0000';
  80. public $config_error_fontsize = 1;
  81. public $config_error_die_on_error = false;
  82. public $config_error_silent_die_on_error = false;
  83. public $config_error_die_on_source_failure = true;
  84. // * Anti-Hotlink Configuration:
  85. public $config_nohotlink_enabled = true;
  86. public $config_nohotlink_valid_domains = array();
  87. public $config_nohotlink_erase_image = true;
  88. public $config_nohotlink_text_message = 'Off-server thumbnailing is not allowed';
  89. // * Off-server Linking Configuration:
  90. public $config_nooffsitelink_enabled = false;
  91. public $config_nooffsitelink_valid_domains = array();
  92. public $config_nooffsitelink_require_refer = false;
  93. public $config_nooffsitelink_erase_image = true;
  94. public $config_nooffsitelink_watermark_src = '';
  95. public $config_nooffsitelink_text_message = 'Off-server linking is not allowed';
  96. // * Border & Background default colors
  97. public $config_border_hexcolor = '000000';
  98. public $config_background_hexcolor = 'FFFFFF';
  99. // * TrueType Fonts
  100. public $config_ttf_directory = './fonts';
  101. public $config_max_source_pixels = null;
  102. public $config_use_exif_thumbnail_for_speed = false;
  103. public $config_allow_local_http_src = false;
  104. public $config_imagemagick_path = null;
  105. public $config_prefer_imagemagick = true;
  106. public $config_imagemagick_use_thumbnail = true;
  107. public $config_cache_maxage = null;
  108. public $config_cache_maxsize = null;
  109. public $config_cache_maxfiles = null;
  110. public $config_cache_source_filemtime_ignore_local = false;
  111. public $config_cache_source_filemtime_ignore_remote = true;
  112. public $config_cache_default_only_suffix = false;
  113. public $config_cache_force_passthru = true;
  114. public $config_cache_prefix = ''; // default value set in the constructor below
  115. // * MySQL
  116. public $config_mysql_extension = null;
  117. public $config_mysql_query = null;
  118. public $config_mysql_hostname = null;
  119. public $config_mysql_username = null;
  120. public $config_mysql_password = null;
  121. public $config_mysql_database = null;
  122. // * Security
  123. public $config_high_security_enabled = true;
  124. public $config_high_security_password = null;
  125. public $config_high_security_url_separator = '&';
  126. public $config_disable_debug = true;
  127. public $config_allow_src_above_docroot = false;
  128. public $config_allow_src_above_phpthumb = true;
  129. public $config_auto_allow_symlinks = true; // allow symlink target directories without explicitly whitelisting them
  130. public $config_additional_allowed_dirs = array(); // additional directories to allow source images to be read from
  131. public $config_file_create_mask = 0755;
  132. public $config_dir_create_mask = 0755;
  133. // * HTTP fopen
  134. public $config_http_fopen_timeout = 10;
  135. public $config_http_follow_redirect = true;
  136. // * Compatability
  137. public $config_disable_pathinfo_parsing = false;
  138. public $config_disable_imagecopyresampled = false;
  139. public $config_disable_onlycreateable_passthru = false;
  140. public $config_disable_realpath = false;
  141. public $config_http_user_agent = 'Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.7.12) Gecko/20050915 Firefox/1.0.7';
  142. // END CONFIGURATION OPTIONS
  143. // public: error messages (read-only; persistant)
  144. public $debugmessages = array();
  145. public $debugtiming = array();
  146. public $fatalerror = null;
  147. // private: (should not be modified directly)
  148. public $thumbnailQuality = 75;
  149. public $thumbnailFormat = null;
  150. public $sourceFilename = null;
  151. public $rawImageData = null;
  152. public $IMresizedData = null;
  153. public $outputImageData = null;
  154. public $useRawIMoutput = false;
  155. public $gdimg_output = null;
  156. public $gdimg_source = null;
  157. public $getimagesizeinfo = null;
  158. public $source_width = null;
  159. public $source_height = null;
  160. public $thumbnailCropX = null;
  161. public $thumbnailCropY = null;
  162. public $thumbnailCropW = null;
  163. public $thumbnailCropH = null;
  164. public $exif_thumbnail_width = null;
  165. public $exif_thumbnail_height = null;
  166. public $exif_thumbnail_type = null;
  167. public $exif_thumbnail_data = null;
  168. public $exif_raw_data = null;
  169. public $thumbnail_width = null;
  170. public $thumbnail_height = null;
  171. public $thumbnail_image_width = null;
  172. public $thumbnail_image_height = null;
  173. public $tempFilesToDelete = array();
  174. public $cache_filename = null;
  175. public $AlphaCapableFormats = array( 'png', 'ico', 'gif');
  176. public $is_alpha = false;
  177. public $iswindows = null;
  178. public $issafemode = null;
  179. public $php_memory_limit = null;
  180. public $phpthumb_version = '1.7.15-201810050741';
  181. //////////////////////////////////////////////////////////////////////
  182. // public: constructor
  183. public function __construct() {
  184. $this->phpThumb();
  185. }
  186. public function phpThumb() {
  187. $this->DebugTimingMessage('phpThumb() constructor', __FILE__, __LINE__);
  188. $this->DebugMessage('phpThumb() v'.$this->phpthumb_version, __FILE__, __LINE__);
  189. foreach (array(ini_get('memory_limit'), get_cfg_var('memory_limit')) as $php_config_memory_limit) {
  190. if ('' !== $php_config_memory_limit) {
  191. if (strtoupper($php_config_memory_limit[ strlen($php_config_memory_limit) - 1 ]) == 'G') { // PHP memory limit expressed in Gigabytes
  192. $php_config_memory_limit = (int) substr($php_config_memory_limit, 0, -1) * 1073741824;
  193. } elseif (strtoupper($php_config_memory_limit[ strlen($php_config_memory_limit) - 1 ]) == 'M') { // PHP memory limit expressed in Megabytes
  194. $php_config_memory_limit = (int) substr($php_config_memory_limit, 0, -1) * 1048576;
  195. }
  196. $this->php_memory_limit = max($this->php_memory_limit, $php_config_memory_limit);
  197. }
  198. }
  199. if ($this->php_memory_limit > 0) { // could be "-1" for "no limit"
  200. $this->config_max_source_pixels = round($this->php_memory_limit * 0.20); // 20% of memory_limit
  201. }
  202. $this->iswindows = (bool) (strtoupper(substr(PHP_OS, 0, 3)) == 'WIN');
  203. $this->issafemode = (bool) preg_match('#(1|ON)#i', ini_get('safe_mode'));
  204. $this->config_document_root = (!empty($_SERVER['DOCUMENT_ROOT']) ? $_SERVER['DOCUMENT_ROOT'] : $this->config_document_root);
  205. $this->config_cache_prefix = ( isset($_SERVER['SERVER_NAME']) ? $_SERVER['SERVER_NAME'].'_' : '');
  206. $this->purgeTempFiles(); // purge existing temp files if re-initializing object
  207. $php_sapi_name = strtolower(function_exists('php_sapi_name') ? PHP_SAPI : '');
  208. if ($php_sapi_name == 'cli') {
  209. $this->config_allow_src_above_docroot = true;
  210. }
  211. if (!$this->config_disable_debug) {
  212. // if debug mode is enabled, force phpThumbDebug output, do not allow normal thumbnails to be generated
  213. $this->phpThumbDebug = (null === $this->phpThumbDebug ? 9 : max(1, (int) $this->phpThumbDebug));
  214. }
  215. }
  216. public function __destruct() {
  217. $this->purgeTempFiles();
  218. }
  219. // public:
  220. public function purgeTempFiles() {
  221. foreach ($this->tempFilesToDelete as $tempFileToDelete) {
  222. if (file_exists($tempFileToDelete)) {
  223. $this->DebugMessage('Deleting temp file "'.$tempFileToDelete.'"', __FILE__, __LINE__);
  224. @unlink($tempFileToDelete);
  225. }
  226. }
  227. $this->tempFilesToDelete = array();
  228. return true;
  229. }
  230. // public:
  231. public function setSourceFilename($sourceFilename) {
  232. //$this->resetObject();
  233. //$this->rawImageData = null;
  234. $this->sourceFilename = $sourceFilename;
  235. $this->src = $sourceFilename;
  236. if (null === $this->config_output_format) {
  237. $sourceFileExtension = strtolower(substr(strrchr($sourceFilename, '.'), 1));
  238. if (preg_match('#^[a-z]{3,4}$#', $sourceFileExtension)) {
  239. $this->config_output_format = $sourceFileExtension;
  240. $this->DebugMessage('setSourceFilename('.$sourceFilename.') set $this->config_output_format to "'.$sourceFileExtension.'"', __FILE__, __LINE__);
  241. } else {
  242. $this->DebugMessage('setSourceFilename('.$sourceFilename.') did NOT set $this->config_output_format to "'.$sourceFileExtension.'" because it did not seem like an appropriate image format', __FILE__, __LINE__);
  243. }
  244. }
  245. $this->DebugMessage('setSourceFilename('.$sourceFilename.') set $this->sourceFilename to "'.$this->sourceFilename.'"', __FILE__, __LINE__);
  246. return true;
  247. }
  248. // public:
  249. public function setSourceData($rawImageData, $sourceFilename='') {
  250. //$this->resetObject();
  251. //$this->sourceFilename = null;
  252. $this->rawImageData = $rawImageData;
  253. $this->DebugMessage('setSourceData() setting $this->rawImageData ('.strlen($this->rawImageData).' bytes; magic="'.substr($this->rawImageData, 0, 4).'" ('.phpthumb_functions::HexCharDisplay(substr($this->rawImageData, 0, 4)).'))', __FILE__, __LINE__);
  254. if ($this->config_cache_source_enabled) {
  255. $sourceFilename = ($sourceFilename ? $sourceFilename : md5($rawImageData));
  256. if (!is_dir($this->config_cache_source_directory)) {
  257. $this->ErrorImage('$this->config_cache_source_directory ('.$this->config_cache_source_directory.') is not a directory');
  258. } elseif (!@is_writable($this->config_cache_source_directory)) {
  259. $this->ErrorImage('$this->config_cache_source_directory ('.$this->config_cache_source_directory.') is not writable');
  260. }
  261. $this->DebugMessage('setSourceData() attempting to save source image to "'.$this->config_cache_source_directory.DIRECTORY_SEPARATOR.urlencode($sourceFilename).'"', __FILE__, __LINE__);
  262. if ($fp = @fopen($this->config_cache_source_directory.DIRECTORY_SEPARATOR.urlencode($sourceFilename), 'wb')) {
  263. fwrite($fp, $rawImageData);
  264. fclose($fp);
  265. } elseif (!$this->phpThumbDebug) {
  266. $this->ErrorImage('setSourceData() failed to write to source cache ('.$this->config_cache_source_directory.DIRECTORY_SEPARATOR.urlencode($sourceFilename).')');
  267. }
  268. }
  269. return true;
  270. }
  271. // public:
  272. public function setSourceImageResource($gdimg) {
  273. //$this->resetObject();
  274. $this->gdimg_source = $gdimg;
  275. return true;
  276. }
  277. // public:
  278. public function setParameter($param, $value) {
  279. if ($param == 'src') {
  280. $this->setSourceFilename($this->ResolveFilenameToAbsolute($value));
  281. } elseif (@is_array($this->$param)) {
  282. if (is_array($value)) {
  283. foreach ($value as $arraykey => $arrayvalue) {
  284. array_push($this->$param, $arrayvalue);
  285. }
  286. } else {
  287. array_push($this->$param, $value);
  288. }
  289. } else {
  290. $this->$param = $value;
  291. }
  292. return true;
  293. }
  294. // public:
  295. public function getParameter($param) {
  296. //if (property_exists('phpThumb', $param)) {
  297. return $this->$param;
  298. //}
  299. //$this->DebugMessage('setParameter() attempting to get non-existant parameter "'.$param.'"', __FILE__, __LINE__);
  300. //return false;
  301. }
  302. // public:
  303. public function GenerateThumbnail() {
  304. $this->setOutputFormat();
  305. $this->phpThumbDebug('8a');
  306. $this->ResolveSource();
  307. $this->phpThumbDebug('8b');
  308. $this->SetCacheFilename();
  309. $this->phpThumbDebug('8c');
  310. $this->ExtractEXIFgetImageSize();
  311. $this->phpThumbDebug('8d');
  312. if ($this->useRawIMoutput) {
  313. $this->DebugMessage('Skipping rest of GenerateThumbnail() because ($this->useRawIMoutput == true)', __FILE__, __LINE__);
  314. return true;
  315. }
  316. $this->phpThumbDebug('8e');
  317. if (!$this->SourceImageToGD()) {
  318. $this->DebugMessage('SourceImageToGD() failed', __FILE__, __LINE__);
  319. return false;
  320. }
  321. $this->phpThumbDebug('8f');
  322. $this->Rotate();
  323. $this->phpThumbDebug('8g');
  324. $this->CreateGDoutput();
  325. $this->phpThumbDebug('8h');
  326. // default values, also applicable for far="C"
  327. $destination_offset_x = round(($this->thumbnail_width - $this->thumbnail_image_width) / 2);
  328. $destination_offset_y = round(($this->thumbnail_height - $this->thumbnail_image_height) / 2);
  329. if (($this->far == 'L') || ($this->far == 'TL') || ($this->far == 'BL')) {
  330. $destination_offset_x = 0;
  331. }
  332. if (($this->far == 'R') || ($this->far == 'TR') || ($this->far == 'BR')) {
  333. $destination_offset_x = round($this->thumbnail_width - $this->thumbnail_image_width);
  334. }
  335. if (($this->far == 'T') || ($this->far == 'TL') || ($this->far == 'TR')) {
  336. $destination_offset_y = 0;
  337. }
  338. if (($this->far == 'B') || ($this->far == 'BL') || ($this->far == 'BR')) {
  339. $destination_offset_y = round($this->thumbnail_height - $this->thumbnail_image_height);
  340. }
  341. // // copy/resize image to appropriate dimensions
  342. // $borderThickness = 0;
  343. // if (!empty($this->fltr)) {
  344. // foreach ($this->fltr as $key => $value) {
  345. // if (preg_match('#^bord\|([0-9]+)#', $value, $matches)) {
  346. // $borderThickness = $matches[1];
  347. // break;
  348. // }
  349. // }
  350. // }
  351. // if ($borderThickness > 0) {
  352. // //$this->DebugMessage('Skipping ImageResizeFunction() because BorderThickness="'.$borderThickness.'"', __FILE__, __LINE__);
  353. // $this->thumbnail_image_height /= 2;
  354. // }
  355. $this->ImageResizeFunction(
  356. $this->gdimg_output,
  357. $this->gdimg_source,
  358. $destination_offset_x,
  359. $destination_offset_y,
  360. $this->thumbnailCropX,
  361. $this->thumbnailCropY,
  362. $this->thumbnail_image_width,
  363. $this->thumbnail_image_height,
  364. $this->thumbnailCropW,
  365. $this->thumbnailCropH
  366. );
  367. $this->DebugMessage('memory_get_usage() after copy-resize = '.(function_exists('memory_get_usage') ? @memory_get_usage() : 'n/a'), __FILE__, __LINE__);
  368. imagedestroy($this->gdimg_source);
  369. $this->DebugMessage('memory_get_usage() after imagedestroy = '.(function_exists('memory_get_usage') ? @memory_get_usage() : 'n/a'), __FILE__, __LINE__);
  370. $this->phpThumbDebug('8i');
  371. $this->AntiOffsiteLinking();
  372. $this->phpThumbDebug('8j');
  373. $this->ApplyFilters();
  374. $this->phpThumbDebug('8k');
  375. $this->AlphaChannelFlatten();
  376. $this->phpThumbDebug('8l');
  377. $this->MaxFileSize();
  378. $this->phpThumbDebug('8m');
  379. $this->DebugMessage('GenerateThumbnail() completed successfully', __FILE__, __LINE__);
  380. return true;
  381. }
  382. // public:
  383. public function RenderOutput() {
  384. if (!$this->useRawIMoutput && !is_resource($this->gdimg_output)) {
  385. $this->DebugMessage('RenderOutput() failed because !is_resource($this->gdimg_output)', __FILE__, __LINE__);
  386. return false;
  387. }
  388. if (!$this->thumbnailFormat) {
  389. $this->DebugMessage('RenderOutput() failed because $this->thumbnailFormat is empty', __FILE__, __LINE__);
  390. return false;
  391. }
  392. if ($this->useRawIMoutput) {
  393. $this->DebugMessage('RenderOutput copying $this->IMresizedData ('.strlen($this->IMresizedData).' bytes) to $this->outputImage', __FILE__, __LINE__);
  394. $this->outputImageData = $this->IMresizedData;
  395. return true;
  396. }
  397. $builtin_formats = array();
  398. if (function_exists('imagetypes')) {
  399. $imagetypes = imagetypes();
  400. $builtin_formats['wbmp'] = (bool) ($imagetypes & IMG_WBMP);
  401. $builtin_formats['jpg'] = (bool) ($imagetypes & IMG_JPG);
  402. $builtin_formats['gif'] = (bool) ($imagetypes & IMG_GIF);
  403. $builtin_formats['png'] = (bool) ($imagetypes & IMG_PNG);
  404. }
  405. $this->DebugMessage('imageinterlace($this->gdimg_output, '. (int) $this->config_output_interlace .')', __FILE__, __LINE__);
  406. imageinterlace($this->gdimg_output, (int) $this->config_output_interlace);
  407. $this->DebugMessage('RenderOutput() attempting image'.strtolower(@$this->thumbnailFormat).'($this->gdimg_output)', __FILE__, __LINE__);
  408. ob_start();
  409. switch ($this->thumbnailFormat) {
  410. case 'wbmp':
  411. if (empty($builtin_formats['wbmp'])) {
  412. $this->DebugMessage('GD does not have required built-in support for WBMP output', __FILE__, __LINE__);
  413. ob_end_clean();
  414. return false;
  415. }
  416. imagejpeg($this->gdimg_output, null, $this->thumbnailQuality);
  417. $this->outputImageData = ob_get_contents();
  418. break;
  419. case 'jpeg':
  420. case 'jpg': // should be "jpeg" not "jpg" but just in case...
  421. if (empty($builtin_formats['jpg'])) {
  422. $this->DebugMessage('GD does not have required built-in support for JPEG output', __FILE__, __LINE__);
  423. ob_end_clean();
  424. return false;
  425. }
  426. imagejpeg($this->gdimg_output, null, $this->thumbnailQuality);
  427. $this->outputImageData = ob_get_contents();
  428. break;
  429. case 'png':
  430. if (empty($builtin_formats['png'])) {
  431. $this->DebugMessage('GD does not have required built-in support for PNG output', __FILE__, __LINE__);
  432. ob_end_clean();
  433. return false;
  434. }
  435. if (phpthumb_functions::version_compare_replacement(PHP_VERSION, '5.1.2', '>=')) {
  436. // https://github.com/JamesHeinrich/phpThumb/issues/24
  437. /* http://php.net/manual/en/function.imagepng.php:
  438. from php source (gd.h):
  439. 2.0.12: Compression level: 0-9 or -1, where 0 is NO COMPRESSION at all,
  440. :: 1 is FASTEST but produces larger files, 9 provides the best
  441. :: compression (smallest files) but takes a long time to compress, and
  442. :: -1 selects the default compiled into the zlib library.
  443. Conclusion: Based on the Zlib manual (http://www.zlib.net/manual.html) the default compression level is set to 6.
  444. */
  445. if (($this->thumbnailQuality >= -1) && ($this->thumbnailQuality <= 9)) {
  446. $PNGquality = $this->thumbnailQuality;
  447. } else {
  448. $this->DebugMessage('Specified thumbnailQuality "'.$this->thumbnailQuality.'" is outside the accepted range (0-9, or -1). Using 6 as default value.', __FILE__, __LINE__);
  449. $PNGquality = 6;
  450. }
  451. imagepng($this->gdimg_output, null, $PNGquality);
  452. } else {
  453. imagepng($this->gdimg_output);
  454. }
  455. $this->outputImageData = ob_get_contents();
  456. break;
  457. case 'gif':
  458. if (empty($builtin_formats['gif'])) {
  459. $this->DebugMessage('GD does not have required built-in support for GIF output', __FILE__, __LINE__);
  460. ob_end_clean();
  461. return false;
  462. }
  463. imagegif($this->gdimg_output);
  464. $this->outputImageData = ob_get_contents();
  465. break;
  466. case 'bmp':
  467. if (!@include_once __DIR__ .'/phpthumb.bmp.php' ) {
  468. $this->DebugMessage('Error including "'. __DIR__ .'/phpthumb.bmp.php" which is required for BMP format output', __FILE__, __LINE__);
  469. ob_end_clean();
  470. return false;
  471. }
  472. $phpthumb_bmp = new phpthumb_bmp();
  473. $this->outputImageData = $phpthumb_bmp->GD2BMPstring($this->gdimg_output);
  474. unset($phpthumb_bmp);
  475. break;
  476. case 'ico':
  477. if (!@include_once __DIR__ .'/phpthumb.ico.php' ) {
  478. $this->DebugMessage('Error including "'. __DIR__ .'/phpthumb.ico.php" which is required for ICO format output', __FILE__, __LINE__);
  479. ob_end_clean();
  480. return false;
  481. }
  482. $phpthumb_ico = new phpthumb_ico();
  483. $arrayOfOutputImages = array($this->gdimg_output);
  484. $this->outputImageData = $phpthumb_ico->GD2ICOstring($arrayOfOutputImages);
  485. unset($phpthumb_ico);
  486. break;
  487. default:
  488. $this->DebugMessage('RenderOutput failed because $this->thumbnailFormat "'.$this->thumbnailFormat.'" is not valid', __FILE__, __LINE__);
  489. ob_end_clean();
  490. return false;
  491. }
  492. ob_end_clean();
  493. if (!$this->outputImageData) {
  494. $this->DebugMessage('RenderOutput() for "'.$this->thumbnailFormat.'" failed', __FILE__, __LINE__);
  495. ob_end_clean();
  496. return false;
  497. }
  498. $this->DebugMessage('RenderOutput() completing with $this->outputImageData = '.strlen($this->outputImageData).' bytes', __FILE__, __LINE__);
  499. return true;
  500. }
  501. // public:
  502. public function RenderToFile($filename) {
  503. if (preg_match('#^[a-z0-9]+://#i', $filename)) {
  504. $this->DebugMessage('RenderToFile() failed because $filename ('.$filename.') is a URL', __FILE__, __LINE__);
  505. return false;
  506. }
  507. // render thumbnail to this file only, do not cache, do not output to browser
  508. //$renderfilename = $this->ResolveFilenameToAbsolute(dirname($filename)).DIRECTORY_SEPARATOR.basename($filename);
  509. $renderfilename = $filename;
  510. if (($filename{0} != '/') && ($filename{0} != '\\') && ($filename{1} != ':')) {
  511. $renderfilename = $this->ResolveFilenameToAbsolute($renderfilename);
  512. }
  513. if (!@is_writable(dirname($renderfilename))) {
  514. $this->DebugMessage('RenderToFile() failed because "'.dirname($renderfilename).'/" is not writable', __FILE__, __LINE__);
  515. return false;
  516. }
  517. if (@is_file($renderfilename) && !@is_writable($renderfilename)) {
  518. $this->DebugMessage('RenderToFile() failed because "'.$renderfilename.'" is not writable', __FILE__, __LINE__);
  519. return false;
  520. }
  521. if ($this->RenderOutput()) {
  522. if (file_put_contents($renderfilename, $this->outputImageData)) {
  523. @chmod($renderfilename, $this->getParameter('config_file_create_mask'));
  524. $this->DebugMessage('RenderToFile('.$renderfilename.') succeeded', __FILE__, __LINE__);
  525. return true;
  526. }
  527. if (!@file_exists($renderfilename)) {
  528. $this->DebugMessage('RenderOutput ['.$this->thumbnailFormat.'('.$renderfilename.')] did not appear to fail, but the output image does not exist either...', __FILE__, __LINE__);
  529. }
  530. } else {
  531. $this->DebugMessage('RenderOutput ['.$this->thumbnailFormat.'('.$renderfilename.')] failed', __FILE__, __LINE__);
  532. }
  533. return false;
  534. }
  535. // public:
  536. public function OutputThumbnail() {
  537. $this->purgeTempFiles();
  538. if (!$this->useRawIMoutput && !is_resource($this->gdimg_output)) {
  539. $this->DebugMessage('OutputThumbnail() failed because !is_resource($this->gdimg_output)', __FILE__, __LINE__);
  540. return false;
  541. }
  542. if (headers_sent()) {
  543. return $this->ErrorImage('OutputThumbnail() failed - headers already sent');
  544. }
  545. $downloadfilename = phpthumb_functions::SanitizeFilename(is_string($this->sia) ? $this->sia : ($this->down ? $this->down : 'phpThumb_generated_thumbnail'.'.'.$this->thumbnailFormat));
  546. $this->DebugMessage('Content-Disposition header filename set to "'.$downloadfilename.'"', __FILE__, __LINE__);
  547. if ($downloadfilename) {
  548. header('Content-Disposition: '.($this->down ? 'attachment' : 'inline').'; filename="'.$downloadfilename.'"');
  549. } else {
  550. $this->DebugMessage('failed to send Content-Disposition header because $downloadfilename is empty', __FILE__, __LINE__);
  551. }
  552. if ($this->useRawIMoutput) {
  553. header('Content-Type: '.phpthumb_functions::ImageTypeToMIMEtype($this->thumbnailFormat));
  554. echo $this->IMresizedData;
  555. } else {
  556. $this->DebugMessage('imageinterlace($this->gdimg_output, '. (int) $this->config_output_interlace .')', __FILE__, __LINE__);
  557. imageinterlace($this->gdimg_output, (int) $this->config_output_interlace);
  558. switch ($this->thumbnailFormat) {
  559. case 'jpeg':
  560. header('Content-Type: '.phpthumb_functions::ImageTypeToMIMEtype($this->thumbnailFormat));
  561. $ImageOutFunction = 'image'.$this->thumbnailFormat;
  562. @$ImageOutFunction($this->gdimg_output, null, $this->thumbnailQuality);
  563. break;
  564. case 'png':
  565. case 'gif':
  566. header('Content-Type: '.phpthumb_functions::ImageTypeToMIMEtype($this->thumbnailFormat));
  567. $ImageOutFunction = 'image'.$this->thumbnailFormat;
  568. @$ImageOutFunction($this->gdimg_output);
  569. break;
  570. case 'bmp':
  571. if (!@include_once __DIR__ .'/phpthumb.bmp.php' ) {
  572. $this->DebugMessage('Error including "'. __DIR__ .'/phpthumb.bmp.php" which is required for BMP format output', __FILE__, __LINE__);
  573. return false;
  574. }
  575. $phpthumb_bmp = new phpthumb_bmp();
  576. if (is_object($phpthumb_bmp)) {
  577. $bmp_data = $phpthumb_bmp->GD2BMPstring($this->gdimg_output);
  578. unset($phpthumb_bmp);
  579. if (!$bmp_data) {
  580. $this->DebugMessage('$phpthumb_bmp->GD2BMPstring() failed', __FILE__, __LINE__);
  581. return false;
  582. }
  583. header('Content-Type: '.phpthumb_functions::ImageTypeToMIMEtype($this->thumbnailFormat));
  584. echo $bmp_data;
  585. } else {
  586. $this->DebugMessage('new phpthumb_bmp() failed', __FILE__, __LINE__);
  587. return false;
  588. }
  589. break;
  590. case 'ico':
  591. if (!@include_once __DIR__ .'/phpthumb.ico.php' ) {
  592. $this->DebugMessage('Error including "'. __DIR__ .'/phpthumb.ico.php" which is required for ICO format output', __FILE__, __LINE__);
  593. return false;
  594. }
  595. $phpthumb_ico = new phpthumb_ico();
  596. if (is_object($phpthumb_ico)) {
  597. $arrayOfOutputImages = array($this->gdimg_output);
  598. $ico_data = $phpthumb_ico->GD2ICOstring($arrayOfOutputImages);
  599. unset($phpthumb_ico);
  600. if (!$ico_data) {
  601. $this->DebugMessage('$phpthumb_ico->GD2ICOstring() failed', __FILE__, __LINE__);
  602. return false;
  603. }
  604. header('Content-Type: '.phpthumb_functions::ImageTypeToMIMEtype($this->thumbnailFormat));
  605. echo $ico_data;
  606. } else {
  607. $this->DebugMessage('new phpthumb_ico() failed', __FILE__, __LINE__);
  608. return false;
  609. }
  610. break;
  611. default:
  612. $this->DebugMessage('OutputThumbnail failed because $this->thumbnailFormat "'.$this->thumbnailFormat.'" is not valid', __FILE__, __LINE__);
  613. return false;
  614. break;
  615. }
  616. }
  617. return true;
  618. }
  619. // public:
  620. public function CleanUpCacheDirectory() {
  621. $this->DebugMessage('CleanUpCacheDirectory() set to purge ('.(null === $this->config_cache_maxage ? 'NULL' : number_format($this->config_cache_maxage / 86400, 1)).' days; '.(null === $this->config_cache_maxsize ? 'NULL' : number_format($this->config_cache_maxsize / 1048576, 2)).' MB; '.(null === $this->config_cache_maxfiles ? 'NULL' : number_format($this->config_cache_maxfiles)).' files)', __FILE__, __LINE__);
  622. if (!is_writable($this->config_cache_directory)) {
  623. $this->DebugMessage('CleanUpCacheDirectory() skipped because "'.$this->config_cache_directory.'" is not writable', __FILE__, __LINE__);
  624. return true;
  625. }
  626. // cache status of cache directory for 1 hour to avoid hammering the filesystem functions
  627. $phpThumbCacheStats_filename = $this->config_cache_directory.DIRECTORY_SEPARATOR.'phpThumbCacheStats.txt';
  628. if (file_exists($phpThumbCacheStats_filename) && is_readable($phpThumbCacheStats_filename) && (filemtime($phpThumbCacheStats_filename) >= (time() - 3600))) {
  629. $this->DebugMessage('CleanUpCacheDirectory() skipped because "'.$phpThumbCacheStats_filename.'" is recently modified', __FILE__, __LINE__);
  630. return true;
  631. }
  632. if (!@touch($phpThumbCacheStats_filename)) {
  633. $this->DebugMessage('touch('.$phpThumbCacheStats_filename.') failed', __FILE__, __LINE__);
  634. }
  635. $DeletedKeys = array();
  636. $AllFilesInCacheDirectory = array();
  637. if (($this->config_cache_maxage > 0) || ($this->config_cache_maxsize > 0) || ($this->config_cache_maxfiles > 0)) {
  638. $CacheDirOldFilesAge = array();
  639. $CacheDirOldFilesSize = array();
  640. $AllFilesInCacheDirectory = phpthumb_functions::GetAllFilesInSubfolders($this->config_cache_directory);
  641. foreach ($AllFilesInCacheDirectory as $fullfilename) {
  642. if (preg_match('#'.preg_quote($this->config_cache_prefix).'#i', $fullfilename) && file_exists($fullfilename)) {
  643. $CacheDirOldFilesAge[$fullfilename] = @fileatime($fullfilename);
  644. if ($CacheDirOldFilesAge[$fullfilename] == 0) {
  645. $CacheDirOldFilesAge[$fullfilename] = @filemtime($fullfilename);
  646. }
  647. $CacheDirOldFilesSize[$fullfilename] = @filesize($fullfilename);
  648. }
  649. }
  650. if (empty($CacheDirOldFilesSize)) {
  651. $this->DebugMessage('CleanUpCacheDirectory() skipped because $CacheDirOldFilesSize is empty (phpthumb_functions::GetAllFilesInSubfolders('.$this->config_cache_directory.') found no files)', __FILE__, __LINE__);
  652. return true;
  653. }
  654. $DeletedKeys['zerobyte'] = array();
  655. foreach ($CacheDirOldFilesSize as $fullfilename => $filesize) {
  656. // purge all zero-size files more than an hour old (to prevent trying to delete just-created and/or in-use files)
  657. $cutofftime = time() - 3600;
  658. if (($filesize == 0) && ($CacheDirOldFilesAge[$fullfilename] < $cutofftime)) {
  659. $this->DebugMessage('deleting "'.$fullfilename.'"', __FILE__, __LINE__);
  660. if (@unlink($fullfilename)) {
  661. $DeletedKeys['zerobyte'][] = $fullfilename;
  662. unset($CacheDirOldFilesSize[$fullfilename]);
  663. unset($CacheDirOldFilesAge[$fullfilename]);
  664. }
  665. }
  666. }
  667. $this->DebugMessage('CleanUpCacheDirectory() purged '.count($DeletedKeys['zerobyte']).' zero-byte files', __FILE__, __LINE__);
  668. asort($CacheDirOldFilesAge);
  669. if ($this->config_cache_maxfiles > 0) {
  670. $TotalCachedFiles = count($CacheDirOldFilesAge);
  671. $DeletedKeys['maxfiles'] = array();
  672. foreach ($CacheDirOldFilesAge as $fullfilename => $filedate) {
  673. if ($TotalCachedFiles > $this->config_cache_maxfiles) {
  674. $this->DebugMessage('deleting "'.$fullfilename.'"', __FILE__, __LINE__);
  675. if (@unlink($fullfilename)) {
  676. $TotalCachedFiles--;
  677. $DeletedKeys['maxfiles'][] = $fullfilename;
  678. }
  679. } else {
  680. // there are few enough files to keep the rest
  681. break;
  682. }
  683. }
  684. $this->DebugMessage('CleanUpCacheDirectory() purged '.count($DeletedKeys['maxfiles']).' files based on (config_cache_maxfiles='.$this->config_cache_maxfiles.')', __FILE__, __LINE__);
  685. foreach ($DeletedKeys['maxfiles'] as $fullfilename) {
  686. unset($CacheDirOldFilesAge[$fullfilename]);
  687. unset($CacheDirOldFilesSize[$fullfilename]);
  688. }
  689. }
  690. if ($this->config_cache_maxage > 0) {
  691. $mindate = time() - $this->config_cache_maxage;
  692. $DeletedKeys['maxage'] = array();
  693. foreach ($CacheDirOldFilesAge as $fullfilename => $filedate) {
  694. if ($filedate > 0) {
  695. if ($filedate < $mindate) {
  696. $this->DebugMessage('deleting "'.$fullfilename.'"', __FILE__, __LINE__);
  697. if (@unlink($fullfilename)) {
  698. $DeletedKeys['maxage'][] = $fullfilename;
  699. }
  700. } else {
  701. // the rest of the files are new enough to keep
  702. break;
  703. }
  704. }
  705. }
  706. $this->DebugMessage('CleanUpCacheDirectory() purged '.count($DeletedKeys['maxage']).' files based on (config_cache_maxage='.$this->config_cache_maxage.')', __FILE__, __LINE__);
  707. foreach ($DeletedKeys['maxage'] as $fullfilename) {
  708. unset($CacheDirOldFilesAge[$fullfilename]);
  709. unset($CacheDirOldFilesSize[$fullfilename]);
  710. }
  711. }
  712. if ($this->config_cache_maxsize > 0) {
  713. $TotalCachedFileSize = array_sum($CacheDirOldFilesSize);
  714. $DeletedKeys['maxsize'] = array();
  715. foreach ($CacheDirOldFilesAge as $fullfilename => $filedate) {
  716. if ($TotalCachedFileSize > $this->config_cache_maxsize) {
  717. $this->DebugMessage('deleting "'.$fullfilename.'"', __FILE__, __LINE__);
  718. if (@unlink($fullfilename)) {
  719. $TotalCachedFileSize -= $CacheDirOldFilesSize[$fullfilename];
  720. $DeletedKeys['maxsize'][] = $fullfilename;
  721. }
  722. } else {
  723. // the total filesizes are small enough to keep the rest of the files
  724. break;
  725. }
  726. }
  727. $this->DebugMessage('CleanUpCacheDirectory() purged '.count($DeletedKeys['maxsize']).' files based on (config_cache_maxsize='.$this->config_cache_maxsize.')', __FILE__, __LINE__);
  728. foreach ($DeletedKeys['maxsize'] as $fullfilename) {
  729. unset($CacheDirOldFilesAge[$fullfilename]);
  730. unset($CacheDirOldFilesSize[$fullfilename]);
  731. }
  732. }
  733. } else {
  734. $this->DebugMessage('skipping CleanUpCacheDirectory() because config set to not use it', __FILE__, __LINE__);
  735. }
  736. $totalpurged = 0;
  737. foreach ($DeletedKeys as $key => $value) {
  738. $totalpurged += count($value);
  739. }
  740. $this->DebugMessage('CleanUpCacheDirectory() purged '.$totalpurged.' files (from '.count($AllFilesInCacheDirectory).') based on config settings', __FILE__, __LINE__);
  741. if ($totalpurged > 0) {
  742. $empty_dirs = array();
  743. foreach ($AllFilesInCacheDirectory as $fullfilename) {
  744. if (is_dir($fullfilename)) {
  745. $empty_dirs[$this->realPathSafe($fullfilename)] = 1;
  746. } else {
  747. unset($empty_dirs[$this->realPathSafe(dirname($fullfilename))]);
  748. }
  749. }
  750. krsort($empty_dirs);
  751. $totalpurgeddirs = 0;
  752. foreach ($empty_dirs as $empty_dir => $dummy) {
  753. if ($empty_dir == $this->config_cache_directory) {
  754. // shouldn't happen, but just in case, don't let it delete actual cache directory
  755. continue;
  756. } elseif (@rmdir($empty_dir)) {
  757. $totalpurgeddirs++;
  758. } else {
  759. $this->DebugMessage('failed to rmdir('.$empty_dir.')', __FILE__, __LINE__);
  760. }
  761. }
  762. $this->DebugMessage('purged '.$totalpurgeddirs.' empty directories', __FILE__, __LINE__);
  763. }
  764. return true;
  765. }
  766. //////////////////////////////////////////////////////////////////////
  767. // private: re-initializator (call between rendering multiple images with one object)
  768. public function resetObject() {
  769. $class_vars = get_class_vars(get_class($this));
  770. foreach ($class_vars as $key => $value) {
  771. // do not clobber debug or config info
  772. if (!preg_match('#^(config_|debug|fatalerror)#i', $key)) {
  773. $this->$key = $value;
  774. }
  775. }
  776. $this->phpThumb(); // re-initialize some class variables
  777. return true;
  778. }
  779. //////////////////////////////////////////////////////////////////////
  780. public function ResolveSource() {
  781. if (is_resource($this->gdimg_source)) {
  782. $this->DebugMessage('ResolveSource() exiting because is_resource($this->gdimg_source)', __FILE__, __LINE__);
  783. return true;
  784. }
  785. if ($this->rawImageData) {
  786. $this->sourceFilename = null;
  787. $this->DebugMessage('ResolveSource() exiting because $this->rawImageData is set ('.number_format(strlen($this->rawImageData)).' bytes)', __FILE__, __LINE__);
  788. return true;
  789. }
  790. if ($this->sourceFilename) {
  791. $this->sourceFilename = $this->ResolveFilenameToAbsolute($this->sourceFilename);
  792. $this->DebugMessage('$this->sourceFilename set to "'.$this->sourceFilename.'"', __FILE__, __LINE__);
  793. } elseif ($this->src) {
  794. $this->sourceFilename = $this->ResolveFilenameToAbsolute($this->src);
  795. $this->DebugMessage('$this->sourceFilename set to "'.$this->sourceFilename.'" from $this->src ('.$this->src.')', __FILE__, __LINE__);
  796. } else {
  797. return $this->ErrorImage('$this->sourceFilename and $this->src are both empty');
  798. }
  799. if ($this->iswindows && ((substr($this->sourceFilename, 0, 2) == '//') || (substr($this->sourceFilename, 0, 2) == '\\\\'))) {
  800. // Windows \\share\filename.ext
  801. } elseif (preg_match('#^[a-z0-9]+://#i', $this->sourceFilename, $protocol_matches)) {
  802. if (preg_match('#^(f|ht)tps?\://#i', $this->sourceFilename)) {
  803. // URL
  804. if ($this->config_http_user_agent) {
  805. ini_set('user_agent', $this->config_http_user_agent);
  806. }
  807. } else {
  808. return $this->ErrorImage('only FTP and HTTP/HTTPS protocols are allowed, "'.$protocol_matches[1].'" is not');
  809. }
  810. } elseif (!@file_exists($this->sourceFilename)) {
  811. return $this->ErrorImage('"'.$this->sourceFilename.'" does not exist');
  812. } elseif (!@is_file($this->sourceFilename)) {
  813. return $this->ErrorImage('"'.$this->sourceFilename.'" is not a file');
  814. }
  815. return true;
  816. }
  817. public function setOutputFormat() {
  818. static $alreadyCalled = false;
  819. if ($this->thumbnailFormat && $alreadyCalled) {
  820. return true;
  821. }
  822. $alreadyCalled = true;
  823. $AvailableImageOutputFormats = array();
  824. $AvailableImageOutputFormats[] = 'text';
  825. if (@is_readable( __DIR__ .'/phpthumb.ico.php')) {
  826. $AvailableImageOutputFormats[] = 'ico';
  827. }
  828. if (@is_readable( __DIR__ .'/phpthumb.bmp.php')) {
  829. $AvailableImageOutputFormats[] = 'bmp';
  830. }
  831. $this->thumbnailFormat = 'ico';
  832. // Set default output format based on what image types are available
  833. if (function_exists('imagetypes')) {
  834. $imagetypes = imagetypes();
  835. if ($imagetypes & IMG_WBMP) {
  836. $this->thumbnailFormat = 'wbmp';
  837. $AvailableImageOutputFormats[] = 'wbmp';
  838. }
  839. if ($imagetypes & IMG_GIF) {
  840. $this->thumbnailFormat = 'gif';
  841. $AvailableImageOutputFormats[] = 'gif';
  842. }
  843. if ($imagetypes & IMG_PNG) {
  844. $this->thumbnailFormat = 'png';
  845. $AvailableImageOutputFormats[] = 'png';
  846. }
  847. if ($imagetypes & IMG_JPG) {
  848. $this->thumbnailFormat = 'jpeg';
  849. $AvailableImageOutputFormats[] = 'jpeg';
  850. }
  851. } else {
  852. $this->DebugMessage('imagetypes() does not exist - GD support might not be enabled?', __FILE__, __LINE__);
  853. }
  854. if ($this->ImageMagickVersion()) {
  855. $IMformats = array('jpeg', 'png', 'gif', 'bmp', 'ico', 'wbmp');
  856. $this->DebugMessage('Addding ImageMagick formats to $AvailableImageOutputFormats ('.implode(';', $AvailableImageOutputFormats).')', __FILE__, __LINE__);
  857. foreach ($IMformats as $key => $format) {
  858. $AvailableImageOutputFormats[] = $format;
  859. }
  860. }
  861. $AvailableImageOutputFormats = array_unique($AvailableImageOutputFormats);
  862. $this->DebugMessage('$AvailableImageOutputFormats = array('.implode(';', $AvailableImageOutputFormats).')', __FILE__, __LINE__);
  863. $this->f = preg_replace('#[^a-z]#', '', strtolower($this->f));
  864. if (strtolower($this->config_output_format) == 'jpg') {
  865. $this->config_output_format = 'jpeg';
  866. }
  867. if (strtolower($this->f) == 'jpg') {
  868. $this->f = 'jpeg';
  869. }
  870. if (phpthumb_functions::CaseInsensitiveInArray($this->config_output_format, $AvailableImageOutputFormats)) {
  871. // set output format to config default if that format is available
  872. $this->DebugMessage('$this->thumbnailFormat set to $this->config_output_format "'.strtolower($this->config_output_format).'"', __FILE__, __LINE__);
  873. $this->thumbnailFormat = strtolower($this->config_output_format);
  874. } elseif ($this->config_output_format) {
  875. $this->DebugMessage('$this->thumbnailFormat staying as "'.$this->thumbnailFormat.'" because $this->config_output_format ('.strtolower($this->config_output_format).') is not in $AvailableImageOutputFormats', __FILE__, __LINE__);
  876. }
  877. if ($this->f && phpthumb_functions::CaseInsensitiveInArray($this->f, $AvailableImageOutputFormats) ) {
  878. // override output format if $this->f is set and that format is available
  879. $this->DebugMessage('$this->thumbnailFormat set to $this->f "'.strtolower($this->f).'"', __FILE__, __LINE__);
  880. $this->thumbnailFormat = strtolower($this->f);
  881. } elseif ($this->f) {
  882. $this->DebugMessage('$this->thumbnailFormat staying as "'.$this->thumbnailFormat.'" because $this->f ('.strtolower($this->f).') is not in $AvailableImageOutputFormats', __FILE__, __LINE__);
  883. }
  884. // for JPEG images, quality 1 (worst) to 99 (best)
  885. // quality < 25 is nasty, with not much size savings - not recommended
  886. // problems with 100 - invalid JPEG?
  887. $this->thumbnailQuality = max(1, min(99, ($this->q ? (int) $this->q : 75)));
  888. $this->DebugMessage('$this->thumbnailQuality set to "'.$this->thumbnailQuality.'"', __FILE__, __LINE__);
  889. return true;
  890. }
  891. public function setCacheDirectory() {
  892. // resolve cache directory to absolute pathname
  893. $this->DebugMessage('setCacheDirectory() starting with config_cache_directory = "'.$this->config_cache_directory.'"', __FILE__, __LINE__);
  894. if ($this->config_cache_directory[ 0 ] == '.') {
  895. if (preg_match('#^(f|ht)tps?\://#i', $this->src)) {
  896. if (!$this->config_cache_disable_warning) {
  897. $this->ErrorImage('$this->config_cache_directory ('.$this->config_cache_directory.') cannot be used for remote images. Adjust "cache_directory" or "cache_disable_warning" in phpThumb.config.php');
  898. }
  899. } elseif ($this->src) {
  900. // resolve relative cache directory to source image
  901. $this->config_cache_directory = dirname($this->ResolveFilenameToAbsolute($this->src)).DIRECTORY_SEPARATOR.$this->config_cache_directory;
  902. } else {
  903. // $this->new is probably set
  904. }
  905. }
  906. if (substr($this->config_cache_directory, -1) == '/') {
  907. $this->config_cache_directory = substr($this->config_cache_directory, 0, -1);
  908. }
  909. if ($this->iswindows) {
  910. $this->config_cache_directory = str_replace('/', DIRECTORY_SEPARATOR, $this->config_cache_directory);
  911. }
  912. if ($this->config_cache_directory) {
  913. $real_cache_path = $this->realPathSafe($this->config_cache_directory);
  914. if (!$real_cache_path) {
  915. $this->DebugMessage('$this->realPathSafe($this->config_cache_directory) failed for "'.$this->config_cache_directory.'"', __FILE__, __LINE__);
  916. if (!is_dir($this->config_cache_directory)) {
  917. $this->DebugMessage('!is_dir('.$this->config_cache_directory.')', __FILE__, __LINE__);
  918. }
  919. }
  920. if ($real_cache_path) {
  921. $this->DebugMessage('setting config_cache_directory to $this->realPathSafe('.$this->config_cache_directory.') = "'.$real_cache_path.'"', __FILE__, __LINE__);
  922. $this->config_cache_directory = $real_cache_path;
  923. }
  924. }
  925. if (!is_dir($this->config_cache_directory)) {
  926. if (!$this->config_cache_disable_warning) {
  927. $this->ErrorImage('$this->config_cache_directory ('.$this->config_cache_directory.') does not exist. Adjust "cache_directory" or "cache_disable_warning" in phpThumb.config.php');
  928. }
  929. $this->DebugMessage('$this->config_cache_directory ('.$this->config_cache_directory.') is not a directory', __FILE__, __LINE__);
  930. $this->config_cache_directory = null;
  931. } elseif (!@is_writable($this->config_cache_directory)) {
  932. $this->DebugMessage('$this->config_cache_directory is not writable ('.$this->config_cache_directory.')', __FILE__, __LINE__);
  933. }
  934. $this->InitializeTempDirSetting();
  935. if (!@is_dir($this->config_temp_directory) && !@is_writable($this->config_temp_directory) && @is_dir($this->config_cache_directory) && @is_writable($this->config_cache_directory)) {
  936. $this->DebugMessage('setting $this->config_temp_directory = $this->config_cache_directory ('.$this->config_cache_directory.')', __FILE__, __LINE__);
  937. $this->config_temp_directory = $this->config_cache_directory;
  938. }
  939. return true;
  940. }
  941. /* Takes the array of path segments up to now, and the next segment (maybe a modifier: empty, . or ..)
  942. Applies it, adding or removing from $segments as a result. Returns nothing. */
  943. // http://support.silisoftware.com/phpBB3/viewtopic.php?t=961
  944. public function applyPathSegment(&$segments, $segment) {
  945. if ($segment == '.') {
  946. return; // always remove
  947. }
  948. if ($segment == '') {
  949. $test = array_pop($segments);
  950. if (null === $test) {
  951. $segments[] = $segment; // keep the first empty block
  952. } elseif ($test == '') {
  953. $test = array_pop($segments);
  954. if (null === $test) {
  955. $segments[] = $test;
  956. $segments[] = $segment; // keep the second one too
  957. } else { // put both back and ignore segment
  958. $segments[] = $test;
  959. $segments[] = $test;
  960. }
  961. } else {
  962. $segments[] = $test; // ignore empty blocks
  963. }
  964. } else {
  965. if ($segment == '..') {
  966. $test = array_pop($segments);
  967. if (null === $test) {
  968. $segments[] = $segment;
  969. } elseif ($test == '..') {
  970. $segments[] = $test;
  971. $segments[] = $segment;
  972. } else {
  973. if ($test == '') {
  974. $segments[] = $test;
  975. } // else nothing, remove both
  976. }
  977. } else {
  978. $segments[] = $segment;
  979. }
  980. }
  981. }
  982. /* Takes array of path components, normalizes it: removes empty slots and '.', collapses '..' and folder names. Returns array. */
  983. // http://support.silisoftware.com/phpBB3/viewtopic.php?t=961
  984. public function normalizePath($segments) {
  985. $parts = array();
  986. foreach ($segments as $segment) {
  987. $this->applyPathSegment($parts, $segment);
  988. }
  989. return $parts;
  990. }
  991. /* True if the provided path points (without resolving symbolic links) into one of the allowed directories. */
  992. // http://support.silisoftware.com/phpBB3/viewtopic.php?t=961
  993. public function matchPath($path, $allowed_dirs) {
  994. if (!empty($allowed_dirs)) {
  995. foreach ($allowed_dirs as $one_dir) {
  996. if (preg_match('#^'.preg_quote(str_replace(DIRECTORY_SEPARATOR, '/', $this->realPathSafe($one_dir))).'#', $path)) {
  997. return true;
  998. }
  999. }
  1000. }
  1001. return false;
  1002. }
  1003. /* True if the provided path points inside one of open_basedirs (or if open_basedirs are disabled) */
  1004. // http://support.silisoftware.com/phpBB3/viewtopic.php?t=961
  1005. public function isInOpenBasedir($path) {
  1006. static $open_basedirs = null;
  1007. if (null === $open_basedirs) {
  1008. $ini_text = ini_get('open_basedir');
  1009. $this->DebugMessage('open_basedir: "'.$ini_text.'"', __FILE__, __LINE__);
  1010. $open_basedirs = array();
  1011. if (strlen($ini_text) > 0) {
  1012. foreach (preg_split('#[;:]#', $ini_text) as $key => $value) {
  1013. $open_basedirs[$key] = $this->realPathSafe($value);
  1014. }
  1015. }
  1016. }
  1017. return (empty($open_basedirs) || $this->matchPath($path, $open_basedirs));
  1018. }
  1019. /* Resolves all symlinks in $path, checking that each continuous part ends in an allowed zone. Returns null, if any component leads outside of allowed zone. */
  1020. // http://support.silisoftware.com/phpBB3/viewtopic.php?t=961
  1021. public function resolvePath($path, $allowed_dirs) {
  1022. $this->DebugMessage('resolvePath: '.$path.' (allowed_dirs: '.print_r($allowed_dirs, true).')', __FILE__, __LINE__);
  1023. // add base path to the top of the list
  1024. if (!$this->config_allow_src_above_docroot) {
  1025. array_unshift($allowed_dirs, $this->realPathSafe($this->config_document_root));
  1026. } else {
  1027. if (!$this->config_allow_src_above_phpthumb) {
  1028. array_unshift($allowed_dirs, $this->realPathSafe( __DIR__ ));
  1029. } else {
  1030. // no checks are needed, offload the work to realpath and forget about it
  1031. $this->DebugMessage('resolvePath: checks disabled, returning '.$this->realPathSafe($path), __FILE__, __LINE__);
  1032. return $this->realPathSafe($path);
  1033. }
  1034. }
  1035. if ($path == '') {
  1036. return null; // save us trouble
  1037. }
  1038. do {
  1039. $this->DebugMessage('resolvePath: iteration, path='.$path.', base path = '.$allowed_dirs[0], __FILE__, __LINE__);
  1040. $parts = array();
  1041. // do not use "cleaner" foreach version of this loop as later code relies on both $segments and $i
  1042. // http://support.silisoftware.com/phpBB3/viewtopic.php?t=964
  1043. $segments = explode(DIRECTORY_SEPARATOR, $path);
  1044. for ($i = 0, $iMax = count($segments); $i < $iMax; $i++) {
  1045. $this->applyPathSegment($parts, $segments[$i]);
  1046. $thispart = implode(DIRECTORY_SEPARATOR, $parts);
  1047. if ($this->isInOpenBasedir($thispart)) {
  1048. if (is_link($thispart)) {
  1049. break;
  1050. }
  1051. }
  1052. }
  1053. $this->DebugMessage('resolvePath: stop at component '.$i, __FILE__, __LINE__);
  1054. // test the part up to here
  1055. $path = implode(DIRECTORY_SEPARATOR, $parts);
  1056. $this->DebugMessage('resolvePath: stop at path='.$path, __FILE__, __LINE__);
  1057. if (!$this->matchPath($path, $all

Large files files are truncated, but you can click here to view the full file