| Total Complexity | 243 | 
| Total Lines | 1342 | 
| Duplicated Lines | 0 % | 
| Changes | 7 | ||
| Bugs | 0 | Features | 0 | 
Complex classes like Timthumb often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use Timthumb, and based on these observations, apply Extract Interface, too.
| 1 | <?php declare(strict_types=1);  | 
            ||
| 255 | class Timthumb  | 
            ||
| 256 | { | 
            ||
| 257 | protected $src = '';  | 
            ||
| 258 | protected $is404 = false;  | 
            ||
| 259 | protected $docRoot = '';  | 
            ||
| 260 | protected $lastURLError = false;  | 
            ||
| 261 | protected $localImage = '';  | 
            ||
| 262 | protected $localImageMTime = 0.0;  | 
            ||
| 263 | protected $url = false;  | 
            ||
| 264 | protected $myHost = '';  | 
            ||
| 265 | protected $isURL = false;  | 
            ||
| 266 | protected $cachefile = '';  | 
            ||
| 267 | protected $errors = [];  | 
            ||
| 268 | protected $toDeletes = [];  | 
            ||
| 269 | protected $cacheDirectory = '';  | 
            ||
| 270 | protected $startTime = 0.0;  | 
            ||
| 271 | protected $lastBenchTime = 0.0;  | 
            ||
| 272 | protected $cropTop = false;  | 
            ||
| 273 | protected $salt = '';  | 
            ||
| 274 | 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.  | 
            ||
| 275 |     protected        $filePrependSecurityBlock = "<?php exit('Execution denied!'); //"; //Designed to have three letter mime type, space, question mark and greater than symbol appended. 6 bytes total. | 
            ||
| 276 | protected static $curlDataWritten = 0;  | 
            ||
| 277 | protected static $curlFH = false;  | 
            ||
| 278 | |||
| 279 | public static function start()  | 
            ||
| 295 | }  | 
            ||
| 296 | |||
| 297 | public function __construct()  | 
            ||
| 298 |     { | 
            ||
| 299 | global $allowedSites;  | 
            ||
| 300 | $this->startTime = microtime(true);  | 
            ||
| 301 |         date_default_timezone_set('UTC'); | 
            ||
| 302 |         $this->debug(1, 'Starting new request from ' . $this->getIP() . ' to ' . Request::getString('REQUEST_URI', '', 'SERVER')); | 
            ||
| 303 | $this->calcDocRoot();  | 
            ||
| 304 | //On windows systems I'm assuming fileinode returns an empty string or a number that doesn't change. Check this.  | 
            ||
| 305 | $this->salt = @filemtime(__FILE__) . '-' . @fileinode(__FILE__);  | 
            ||
| 306 | $this->debug(3, 'Salt is: ' . $this->salt);  | 
            ||
| 307 |         if (FILE_CACHE_DIRECTORY) { | 
            ||
| 308 |             if (!is_dir(FILE_CACHE_DIRECTORY)) { | 
            ||
| 309 |                 if (!mkdir($concurrentDirectory = FILE_CACHE_DIRECTORY) && !is_dir($concurrentDirectory)) { | 
            ||
| 310 |                     throw new \RuntimeException(sprintf('Directory "%s" was not created', $concurrentDirectory)); | 
            ||
| 311 | }  | 
            ||
| 312 |                 if (!is_dir(FILE_CACHE_DIRECTORY)) { | 
            ||
| 313 |                     $this->error('Could not create the file cache directory.'); | 
            ||
| 314 | |||
| 315 | return false;  | 
            ||
| 316 | }  | 
            ||
| 317 | }  | 
            ||
| 318 | $this->cacheDirectory = FILE_CACHE_DIRECTORY;  | 
            ||
| 319 |             if (!touch($this->cacheDirectory . '/index.html')) { | 
            ||
| 320 |                 $this->error('Could not create the index.html file - to fix this create an empty file named index.html file in the cache directory.'); | 
            ||
| 321 | }  | 
            ||
| 322 |         } else { | 
            ||
| 323 | $this->cacheDirectory = sys_get_temp_dir();  | 
            ||
| 324 | }  | 
            ||
| 325 | //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.  | 
            ||
| 326 | $this->cleanCache();  | 
            ||
| 327 | |||
| 328 |         $this->myHost = preg_replace('/^www\./i', '', \Xmf\Request::getString('HTTP_HOST', '', 'SERVER')); | 
            ||
| 329 |         $this->src    = $this->param('src'); | 
            ||
| 330 | $this->url = parse_url($this->src);  | 
            ||
| 331 |         $this->src    = preg_replace('/https?:\/\/(?:www\.)?' . $this->myHost . '/i', '', $this->src); | 
            ||
| 332 | |||
| 333 |         if (mb_strlen($this->src) <= 3) { | 
            ||
| 334 |             $this->error('No image specified'); | 
            ||
| 335 | |||
| 336 | return false;  | 
            ||
| 337 | }  | 
            ||
| 338 |         if (BLOCK_EXTERNAL_LEECHERS && array_key_exists('HTTP_REFERER', $_SERVER) && (!preg_match('/^https?:\/\/(?:www\.)?' . $this->myHost . '(?:$|\/)/i', \Xmf\Request::getString('HTTP_REFERER', '', 'SERVER')))) { | 
            ||
| 339 | // base64 encoded red image that says 'no hotlinkers'  | 
            ||
| 340 | // nothing to worry about! :)  | 
            ||
| 341 |             $imgData = base64_decode("R0lGODlhUAAMAIAAAP8AAP///yH5BAAHAP8ALAAAAABQAAwAAAJpjI+py+0Po5y0OgAMjjv01YUZ\nOGplhWXfNa6JCLnWkXplrcBmW+spbwvaVr/cDyg7IoFC2KbYVC2NQ5MQ4ZNao9Ynzjl9ScNYpneb\nDULB3RP6JuPuaGfuuV4fumf8PuvqFyhYtjdoeFgAADs=", true); | 
            ||
| 342 |             header('Content-Type: image/gif'); | 
            ||
| 343 |             header('Content-Length: ' . mb_strlen($imgData)); | 
            ||
| 344 |             header('Cache-Control: no-store, no-cache, must-revalidate, max-age=0'); | 
            ||
| 345 |             header('Pragma: no-cache'); | 
            ||
| 346 |             header('Expires: ' . gmdate('D, d M Y H:i:s', time())); | 
            ||
| 347 | echo $imgData;  | 
            ||
| 348 | |||
| 349 | return false;  | 
            ||
| 350 | }  | 
            ||
| 351 |         if (preg_match('/^https?:\/\/[^\/]+/i', $this->src)) { | 
            ||
| 352 | $this->debug(2, 'Is a request for an external URL: ' . $this->src);  | 
            ||
| 353 | $this->isURL = true;  | 
            ||
| 354 |         } else { | 
            ||
| 355 | $this->debug(2, 'Is a request for an internal file: ' . $this->src);  | 
            ||
| 356 | }  | 
            ||
| 357 |         if ($this->isURL && (!ALLOW_EXTERNAL)) { | 
            ||
| 358 |             $this->error('You are not allowed to fetch images from an external website.'); | 
            ||
| 359 | |||
| 360 | return false;  | 
            ||
| 361 | }  | 
            ||
| 362 |         if ($this->isURL) { | 
            ||
| 363 |             if (ALLOW_ALL_EXTERNAL_SITES) { | 
            ||
| 364 | $this->debug(2, 'Fetching from all external sites is enabled.');  | 
            ||
| 365 |             } else { | 
            ||
| 366 | $this->debug(2, 'Fetching only from selected external sites is enabled.');  | 
            ||
| 367 | $allowed = false;  | 
            ||
| 368 |                 foreach ($allowedSites as $site) { | 
            ||
| 369 |                     if ((mb_strtolower($this->url['host']) === \mb_strtolower($site)) || (mb_strtolower(mb_substr($this->url['host'], -mb_strlen($site) - 1)) === \mb_strtolower(".$site"))) { | 
            ||
| 370 |                         $this->debug(3, "URL hostname {$this->url['host']} matches $site so allowing."); | 
            ||
| 371 | $allowed = true;  | 
            ||
| 372 | }  | 
            ||
| 373 | }  | 
            ||
| 374 |                 if (!$allowed) { | 
            ||
| 375 |                     return $this->error('You may not fetch images from that site. To enable this site in timthumb, you can either add it to $allowedSites and set ALLOW_EXTERNAL=true. Or you can set ALLOW_ALL_EXTERNAL_SITES=true, depending on your security needs.'); | 
            ||
| 376 | }  | 
            ||
| 377 | }  | 
            ||
| 378 | }  | 
            ||
| 379 | |||
| 380 | $cachePrefix = ($this->isURL ? '_ext_' : '_int_');  | 
            ||
| 381 |         if ($this->isURL) { | 
            ||
| 382 |             $arr = explode('&', $_SERVER['QUERY_STRING']); | 
            ||
| 383 | asort($arr);  | 
            ||
| 384 |             $this->cachefile = $this->cacheDirectory . '/' . FILE_CACHE_PREFIX . $cachePrefix . md5($this->salt . implode('', $arr) . $this->fileCacheVersion) . FILE_CACHE_SUFFIX; | 
            ||
| 385 |         } else { | 
            ||
| 386 | $this->localImage = $this->getLocalImagePath($this->src);  | 
            ||
| 387 |             if (!$this->localImage) { | 
            ||
| 388 |                 $this->debug(1, "Could not find the local image: {$this->localImage}"); | 
            ||
| 389 |                 $this->error('Could not find the internal image you specified.'); | 
            ||
| 390 | $this->set404();  | 
            ||
| 391 | |||
| 392 | return false;  | 
            ||
| 393 | }  | 
            ||
| 394 |             $this->debug(1, "Local image path is {$this->localImage}"); | 
            ||
| 395 | $this->localImageMTime = @filemtime($this->localImage);  | 
            ||
| 396 | //We include the mtime of the local file in case in changes on disk.  | 
            ||
| 397 | $this->cachefile = $this->cacheDirectory . '/' . FILE_CACHE_PREFIX . $cachePrefix . md5($this->salt . $this->localImageMTime . $_SERVER['QUERY_STRING'] . $this->fileCacheVersion) . FILE_CACHE_SUFFIX;  | 
            ||
| 398 | }  | 
            ||
| 399 | $this->debug(2, 'Cache file is: ' . $this->cachefile);  | 
            ||
| 400 | |||
| 401 | return true;  | 
            ||
| 402 | }  | 
            ||
| 403 | |||
| 404 | public function __destruct()  | 
            ||
| 405 |     { | 
            ||
| 406 |         foreach ($this->toDeletes as $del) { | 
            ||
| 407 | $this->debug(2, "Deleting temp file $del");  | 
            ||
| 408 | @unlink($del);  | 
            ||
| 409 | }  | 
            ||
| 410 | }  | 
            ||
| 411 | |||
| 412 | /**  | 
            ||
| 413 | * @return bool  | 
            ||
| 414 | */  | 
            ||
| 415 | public function run()  | 
            ||
| 416 |     { | 
            ||
| 417 |         if ($this->isURL) { | 
            ||
| 418 |             if (!ALLOW_EXTERNAL) { | 
            ||
| 419 | $this->debug(1, 'Got a request for an external image but ALLOW_EXTERNAL is disabled so returning error msg.');  | 
            ||
| 420 |                 $this->error('You are not allowed to fetch images from an external website.'); | 
            ||
| 421 | |||
| 422 | return false;  | 
            ||
| 423 | }  | 
            ||
| 424 | $this->debug(3, 'Got request for external image. Starting serveExternalImage.');  | 
            ||
| 425 |             if ($this->param('webshot')) { | 
            ||
| 426 |                 if (WEBSHOT_ENABLED) { | 
            ||
| 427 | $this->debug(3, 'webshot param is set, so we\'re going to take a webshot.');  | 
            ||
| 428 | $this->serveWebshot();  | 
            ||
| 429 |                 } else { | 
            ||
| 430 |                     $this->error('You added the webshot parameter but webshots are disabled on this server. You need to set WEBSHOT_ENABLED === true to enable webshots.'); | 
            ||
| 431 | }  | 
            ||
| 432 |             } else { | 
            ||
| 433 | $this->debug(3, 'webshot is NOT set so we\'re going to try to fetch a regular image.');  | 
            ||
| 434 | $this->serveExternalImage();  | 
            ||
| 435 | }  | 
            ||
| 436 |         } else { | 
            ||
| 437 | $this->debug(3, 'Got request for internal image. Starting serveInternalImage()');  | 
            ||
| 438 | $this->serveInternalImage();  | 
            ||
| 439 | }  | 
            ||
| 440 | |||
| 441 | return true;  | 
            ||
| 442 | }  | 
            ||
| 443 | |||
| 444 | /**  | 
            ||
| 445 | * @return bool  | 
            ||
| 446 | */  | 
            ||
| 447 | protected function handleErrors()  | 
            ||
| 448 |     { | 
            ||
| 449 |         if ($this->haveErrors()) { | 
            ||
| 450 |             if (NOT_FOUND_IMAGE && $this->is404()) { | 
            ||
| 451 |                 if ($this->serveImg(NOT_FOUND_IMAGE)) { | 
            ||
| 452 | exit(0);  | 
            ||
| 453 | }  | 
            ||
| 454 |                 $this->error('Additionally, the 404 image that is configured could not be found or there was an error serving it.'); | 
            ||
| 455 | }  | 
            ||
| 456 |             if (ERROR_IMAGE) { | 
            ||
| 457 |                 if ($this->serveImg(ERROR_IMAGE)) { | 
            ||
| 458 | exit(0);  | 
            ||
| 459 | }  | 
            ||
| 460 |                 $this->error('Additionally, the error image that is configured could not be found or there was an error serving it.'); | 
            ||
| 461 | }  | 
            ||
| 462 | $this->serveErrors();  | 
            ||
| 463 | exit(0);  | 
            ||
| 464 | }  | 
            ||
| 465 | |||
| 466 | return false;  | 
            ||
| 467 | }  | 
            ||
| 468 | |||
| 469 | /**  | 
            ||
| 470 | * @return bool  | 
            ||
| 471 | */  | 
            ||
| 472 | protected function tryBrowserCache()  | 
            ||
| 473 |     { | 
            ||
| 474 |         if (BROWSER_CACHE_DISABLE) { | 
            ||
| 475 | $this->debug(3, 'Browser caching is disabled');  | 
            ||
| 476 | |||
| 477 | return false;  | 
            ||
| 478 | }  | 
            ||
| 479 |         if (\Xmf\Request::hasVar('HTTP_IF_MODIFIED_SINCE', 'SERVER')) { | 
            ||
| 480 | $this->debug(3, 'Got a conditional get');  | 
            ||
| 481 | $mtime = false;  | 
            ||
| 482 | //We've already checked if the real file exists in the constructor  | 
            ||
| 483 |             if (!is_file($this->cachefile)) { | 
            ||
| 484 | //If we don't have something cached, regenerate the cached image.  | 
            ||
| 485 | return false;  | 
            ||
| 486 | }  | 
            ||
| 487 |             if ($this->localImageMTime) { | 
            ||
| 488 | $mtime = $this->localImageMTime;  | 
            ||
| 489 | $this->debug(3, "Local real file's modification time is $mtime");  | 
            ||
| 490 |             } elseif (\is_file($this->cachefile)) { | 
            ||
| 491 | //If it's not a local request then use the mtime of the cached file to determine the 304  | 
            ||
| 492 | $mtime = @filemtime($this->cachefile);  | 
            ||
| 493 | $this->debug(3, "Cached file's modification time is $mtime");  | 
            ||
| 494 | }  | 
            ||
| 495 |             if (false === $mtime) { | 
            ||
| 496 | return false;  | 
            ||
| 497 | }  | 
            ||
| 498 | |||
| 499 | $iftime = strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']);  | 
            ||
| 500 | $this->debug(3, "The conditional get's if-modified-since unixtime is $iftime");  | 
            ||
| 501 |             if ($iftime < 1) { | 
            ||
| 502 | $this->debug(3, 'Got an invalid conditional get modified since time. Returning false.');  | 
            ||
| 503 | |||
| 504 | return false;  | 
            ||
| 505 | }  | 
            ||
| 506 |             if ($iftime < $mtime) { | 
            ||
| 507 | //Real file or cache file has been modified since last request, so force refetch.  | 
            ||
| 508 | $this->debug(3, 'File has been modified since last fetch.');  | 
            ||
| 509 | |||
| 510 | return false;  | 
            ||
| 511 | }  | 
            ||
| 512 | //Otherwise serve a 304  | 
            ||
| 513 | $this->debug(3, 'File has not been modified since last get, so serving a 304.');  | 
            ||
| 514 | header($_SERVER['SERVER_PROTOCOL'] . ' 304 Not Modified');  | 
            ||
| 515 | $this->debug(1, 'Returning 304 not modified');  | 
            ||
| 516 | |||
| 517 | return true;  | 
            ||
| 518 | }  | 
            ||
| 519 | |||
| 520 | return false;  | 
            ||
| 521 | }  | 
            ||
| 522 | |||
| 523 | /**  | 
            ||
| 524 | * @return bool  | 
            ||
| 525 | */  | 
            ||
| 526 | protected function tryServerCache()  | 
            ||
| 527 |     { | 
            ||
| 528 | $this->debug(3, 'Trying server cache');  | 
            ||
| 529 |         if (\is_file($this->cachefile)) { | 
            ||
| 530 |             $this->debug(3, "Cachefile {$this->cachefile} exists"); | 
            ||
| 531 |             if ($this->isURL) { | 
            ||
| 532 | $this->debug(3, 'This is an external request, so checking if the cachefile is empty which means the request failed previously.');  | 
            ||
| 533 |                 if (filesize($this->cachefile) < 1) { | 
            ||
| 534 | $this->debug(3, 'Found an empty cachefile indicating a failed earlier request. Checking how old it is.');  | 
            ||
| 535 | //Fetching error occured previously  | 
            ||
| 536 |                     if (time() - @filemtime($this->cachefile) > WAIT_BETWEEN_FETCH_ERRORS) { | 
            ||
| 537 | $this->debug(3, 'File is older than ' . WAIT_BETWEEN_FETCH_ERRORS . ' seconds. Deleting and returning false so app can try and load file.');  | 
            ||
| 538 | @unlink($this->cachefile);  | 
            ||
| 539 | |||
| 540 | return false; //to indicate we didn't serve from cache and app should try and load  | 
            ||
| 541 | }  | 
            ||
| 542 | $this->debug(3, 'Empty cachefile is still fresh so returning message saying we had an error fetching this image from remote host.');  | 
            ||
| 543 | $this->set404();  | 
            ||
| 544 |                     $this->error('An error occured fetching image.'); | 
            ||
| 545 | |||
| 546 | return false;  | 
            ||
| 547 | }  | 
            ||
| 548 |             } else { | 
            ||
| 549 |                 $this->debug(3, "Trying to serve cachefile {$this->cachefile}"); | 
            ||
| 550 | }  | 
            ||
| 551 |             if ($this->serveCacheFile()) { | 
            ||
| 552 |                 $this->debug(3, "Succesfully served cachefile {$this->cachefile}"); | 
            ||
| 553 | |||
| 554 | return true;  | 
            ||
| 555 | }  | 
            ||
| 556 |             $this->debug(3, "Failed to serve cachefile {$this->cachefile} - Deleting it from cache."); | 
            ||
| 557 | //Image serving failed. We can't retry at this point, but lets remove it from cache so the next request recreates it  | 
            ||
| 558 | @unlink($this->cachefile);  | 
            ||
| 559 | |||
| 560 | return true;  | 
            ||
| 561 | }  | 
            ||
| 562 | |||
| 563 | return null;  | 
            ||
| 564 | }  | 
            ||
| 565 | |||
| 566 | /**  | 
            ||
| 567 | * @param $err  | 
            ||
| 568 | *  | 
            ||
| 569 | * @return bool  | 
            ||
| 570 | */  | 
            ||
| 571 | protected function error($err)  | 
            ||
| 572 |     { | 
            ||
| 573 | $this->debug(3, "Adding error message: $err");  | 
            ||
| 574 | $this->errors[] = $err;  | 
            ||
| 575 | |||
| 576 | return false;  | 
            ||
| 577 | }  | 
            ||
| 578 | |||
| 579 | /**  | 
            ||
| 580 | * @return bool  | 
            ||
| 581 | */  | 
            ||
| 582 | protected function haveErrors()  | 
            ||
| 585 | }  | 
            ||
| 586 | |||
| 587 | protected function serveErrors()  | 
            ||
| 588 |     { | 
            ||
| 589 | header($_SERVER['SERVER_PROTOCOL'] . ' 400 Bad Request');  | 
            ||
| 590 |         if (!DISPLAY_ERROR_MESSAGES) { | 
            ||
| 591 | return;  | 
            ||
| 592 | }  | 
            ||
| 593 | $html = '<ul>';  | 
            ||
| 594 |         foreach ($this->errors as $err) { | 
            ||
| 595 | $html .= '<li>' . htmlentities($err, ENT_QUOTES | ENT_HTML5) . '</li>';  | 
            ||
| 596 | }  | 
            ||
| 597 | $html .= '</ul>';  | 
            ||
| 598 | echo '<h1>A TimThumb error has occured</h1>The following error(s) occured:<br>' . $html . '<br>';  | 
            ||
| 599 | echo '<br>Query String : ' . htmlentities($_SERVER['QUERY_STRING'], ENT_QUOTES | ENT_HTML5);  | 
            ||
| 600 | echo '<br>TimThumb version : ' . VERSION . '</pre>';  | 
            ||
| 601 | }  | 
            ||
| 602 | |||
| 603 | /**  | 
            ||
| 604 | * @return bool  | 
            ||
| 605 | */  | 
            ||
| 606 | protected function serveInternalImage()  | 
            ||
| 607 |     { | 
            ||
| 608 | $this->debug(3, "Local image path is $this->localImage");  | 
            ||
| 609 |         if (!$this->localImage) { | 
            ||
| 610 |             $this->sanityFail('localImage not set after verifying it earlier in the code.'); | 
            ||
| 611 | |||
| 612 | return false;  | 
            ||
| 613 | }  | 
            ||
| 614 | $fileSize = filesize($this->localImage);  | 
            ||
| 615 |         if ($fileSize > MAX_FILE_SIZE) { | 
            ||
| 616 |             $this->error('The file you specified is greater than the maximum allowed file size.'); | 
            ||
| 617 | |||
| 618 | return false;  | 
            ||
| 619 | }  | 
            ||
| 620 |         if ($fileSize <= 0) { | 
            ||
| 621 |             $this->error('The file you specified is <= 0 bytes.'); | 
            ||
| 622 | |||
| 623 | return false;  | 
            ||
| 624 | }  | 
            ||
| 625 | $this->debug(3, 'Calling processImageAndWriteToCache() for local image.');  | 
            ||
| 626 |         if ($this->processImageAndWriteToCache($this->localImage)) { | 
            ||
| 627 | $this->serveCacheFile();  | 
            ||
| 628 | |||
| 629 | return true;  | 
            ||
| 630 | }  | 
            ||
| 631 | |||
| 632 | return false;  | 
            ||
| 633 | }  | 
            ||
| 634 | |||
| 635 | /**  | 
            ||
| 636 | * @return bool|void  | 
            ||
| 637 | */  | 
            ||
| 638 | protected function cleanCache()  | 
            ||
| 678 | }  | 
            ||
| 679 | |||
| 680 | /**  | 
            ||
| 681 | * @param $localImage  | 
            ||
| 682 | *  | 
            ||
| 683 | * @return bool  | 
            ||
| 684 | */  | 
            ||
| 685 | protected function processImageAndWriteToCache($localImage)  | 
            ||
| 686 |     { | 
            ||
| 687 | $sData = getimagesize($localImage);  | 
            ||
| 688 | $origType = $sData[2];  | 
            ||
| 689 | $mimeType = $sData['mime'];  | 
            ||
| 690 | |||
| 691 | $this->debug(3, "Mime type of image is $mimeType");  | 
            ||
| 692 |         if (!preg_match('/^image\/(?:gif|jpg|jpeg|png)$/i', $mimeType)) { | 
            ||
| 693 |             return $this->error('The image being resized is not a valid gif, jpg or png.'); | 
            ||
| 694 | }  | 
            ||
| 695 | |||
| 696 |         if (!function_exists('imagecreatetruecolor')) { | 
            ||
| 697 |             return $this->error('GD Library Error: imagecreatetruecolor does not exist - please contact your webhost and ask them to install the GD library'); | 
            ||
| 698 | }  | 
            ||
| 699 | |||
| 700 |         if (defined('IMG_FILTER_NEGATE') && function_exists('imagefilter')) { | 
            ||
| 701 | $imageFilters = [  | 
            ||
| 702 | 1 => [IMG_FILTER_NEGATE, 0],  | 
            ||
| 703 | 2 => [IMG_FILTER_GRAYSCALE, 0],  | 
            ||
| 704 | 3 => [IMG_FILTER_BRIGHTNESS, 1],  | 
            ||
| 705 | 4 => [IMG_FILTER_CONTRAST, 1],  | 
            ||
| 706 | 5 => [IMG_FILTER_COLORIZE, 4],  | 
            ||
| 707 | 6 => [IMG_FILTER_EDGEDETECT, 0],  | 
            ||
| 708 | 7 => [IMG_FILTER_EMBOSS, 0],  | 
            ||
| 709 | 8 => [IMG_FILTER_GAUSSIAN_BLUR, 0],  | 
            ||
| 710 | 9 => [IMG_FILTER_SELECTIVE_BLUR, 0],  | 
            ||
| 711 | 10 => [IMG_FILTER_MEAN_REMOVAL, 0],  | 
            ||
| 712 | 11 => [IMG_FILTER_SMOOTH, 0],  | 
            ||
| 713 | ];  | 
            ||
| 714 | }  | 
            ||
| 715 | |||
| 716 | // get standard input properties  | 
            ||
| 717 |         $newWidth     = (int)abs((int)$this->param('w', 0)); | 
            ||
| 718 |         $newHeight    = (int)abs((int)$this->param('h', 0)); | 
            ||
| 719 |         $zoomCrop     = (int)$this->param('zc', DEFAULT_ZC); | 
            ||
| 720 |         $quality      = (int)abs((int)$this->param('q', DEFAULT_Q)); | 
            ||
| 721 |         $align        = $this->cropTop ? 't' : $this->param('a', 'c'); | 
            ||
| 722 |         $filters      = $this->param('f', DEFAULT_F); | 
            ||
| 723 |         $sharpen      = (bool)$this->param('s', DEFAULT_S); | 
            ||
| 724 |         $canvas_color = $this->param('cc', DEFAULT_CC); | 
            ||
| 725 |         $canvas_trans = (bool)$this->param('ct', '1'); | 
            ||
| 726 | |||
| 727 | // set default width and height if neither are set already  | 
            ||
| 728 |         if (0 == $newWidth && 0 == $newHeight) { | 
            ||
| 729 | $newWidth = DEFAULT_WIDTH;  | 
            ||
| 730 | $newHeight = DEFAULT_HEIGHT;  | 
            ||
| 731 | }  | 
            ||
| 732 | |||
| 733 | // ensure size limits can not be abused  | 
            ||
| 734 | $newWidth = min($newWidth, MAX_WIDTH);  | 
            ||
| 735 | $newHeight = min($newHeight, MAX_HEIGHT);  | 
            ||
| 736 | |||
| 737 | // set memory limit to be able to have enough space to resize larger images  | 
            ||
| 738 | $this->setMemoryLimit();  | 
            ||
| 739 | |||
| 740 | // open the existing image  | 
            ||
| 741 | $image = $this->openImage($mimeType, $localImage);  | 
            ||
| 742 |         if (false === $image) { | 
            ||
| 743 |             return $this->error('Unable to open image.'); | 
            ||
| 744 | }  | 
            ||
| 745 | |||
| 746 | // Get original width and height  | 
            ||
| 747 | $width = imagesx($image);  | 
            ||
| 748 | $height = imagesy($image);  | 
            ||
| 749 | $origin_x = 0;  | 
            ||
| 750 | $origin_y = 0;  | 
            ||
| 751 | |||
| 752 | // generate new w/h if not provided  | 
            ||
| 753 |         if ($newWidth && !$newHeight) { | 
            ||
| 754 | $newHeight = floor($height * ($newWidth / $width));  | 
            ||
| 755 |         } elseif ($newHeight && !$newWidth) { | 
            ||
| 756 | $newWidth = floor($width * ($newHeight / $height));  | 
            ||
| 757 | }  | 
            ||
| 758 | |||
| 759 | // scale down and add borders  | 
            ||
| 760 |         if (3 == $zoomCrop) { | 
            ||
| 761 | $final_height = $height * ($newWidth / $width);  | 
            ||
| 762 | |||
| 763 |             if ($final_height > $newHeight) { | 
            ||
| 764 | $newWidth = $width * ($newHeight / $height);  | 
            ||
| 765 |             } else { | 
            ||
| 766 | $newHeight = $final_height;  | 
            ||
| 767 | }  | 
            ||
| 768 | }  | 
            ||
| 769 | |||
| 770 | // create a new true color image  | 
            ||
| 771 | $canvas = imagecreatetruecolor((int)$newWidth, (int)$newHeight);  | 
            ||
| 772 | imagealphablending($canvas, false);  | 
            ||
| 773 | |||
| 774 |         if (3 == \mb_strlen($canvas_color)) { | 
            ||
| 775 | //if is 3-char notation, edit string into 6-char notation  | 
            ||
| 776 | $canvas_color = str_repeat(mb_substr($canvas_color, 0, 1), 2) . str_repeat(mb_substr($canvas_color, 1, 1), 2) . str_repeat(mb_substr($canvas_color, 2, 1), 2);  | 
            ||
| 777 |         } elseif (6 != \mb_strlen($canvas_color)) { | 
            ||
| 778 | $canvas_color = DEFAULT_CC; // on error return default canvas color  | 
            ||
| 779 | }  | 
            ||
| 780 | |||
| 781 | $canvas_color_R = hexdec(mb_substr($canvas_color, 0, 2));  | 
            ||
| 782 | $canvas_color_G = hexdec(mb_substr($canvas_color, 2, 2));  | 
            ||
| 783 | $canvas_color_B = hexdec(mb_substr($canvas_color, 4, 2));  | 
            ||
| 784 | |||
| 785 | // Create a new transparent color for image  | 
            ||
| 786 | // If is a png and PNG_IS_TRANSPARENT is false then remove the alpha transparency  | 
            ||
| 787 | // (and if is set a canvas color show it in the background)  | 
            ||
| 788 |         if (!PNG_IS_TRANSPARENT && $canvas_trans && preg_match('/^image\/png$/i', $mimeType)) { | 
            ||
| 789 | $color = imagecolorallocatealpha($canvas, $canvas_color_R, $canvas_color_G, $canvas_color_B, 127);  | 
            ||
| 790 |         } else { | 
            ||
| 791 | $color = imagecolorallocatealpha($canvas, $canvas_color_R, $canvas_color_G, $canvas_color_B, 0);  | 
            ||
| 792 | }  | 
            ||
| 793 | |||
| 794 | // Completely fill the background of the new image with allocated color.  | 
            ||
| 795 | imagefill($canvas, 0, 0, $color);  | 
            ||
| 796 | // scale down and add borders  | 
            ||
| 797 |         if (2 == $zoomCrop) { | 
            ||
| 798 | $final_height = $height * ($newWidth / $width);  | 
            ||
| 799 |             if ($final_height > $newHeight) { | 
            ||
| 800 | $origin_x = $newWidth / 2;  | 
            ||
| 801 | $newWidth = $width * ($newHeight / $height);  | 
            ||
| 802 | $origin_x = round($origin_x - ($newWidth / 2));  | 
            ||
| 803 |             } else { | 
            ||
| 804 | $origin_y = $newHeight / 2;  | 
            ||
| 805 | $newHeight = $final_height;  | 
            ||
| 806 | $origin_y = round($origin_y - ($newHeight / 2));  | 
            ||
| 807 | }  | 
            ||
| 808 | }  | 
            ||
| 809 | |||
| 810 | // Restore transparency blending  | 
            ||
| 811 | imagesavealpha($canvas, true);  | 
            ||
| 812 | |||
| 813 |         if ($zoomCrop > 0) { | 
            ||
| 814 | $src_x = $src_y = 0;  | 
            ||
| 815 | $src_w = $width;  | 
            ||
| 816 | $src_h = $height;  | 
            ||
| 817 | |||
| 818 | $cmp_x = $width / $newWidth;  | 
            ||
| 819 | $cmp_y = $height / $newHeight;  | 
            ||
| 820 | |||
| 821 | // calculate x or y coordinate and width or height of source  | 
            ||
| 822 |             if ($cmp_x > $cmp_y) { | 
            ||
| 823 | $src_w = round($width / $cmp_x * $cmp_y);  | 
            ||
| 824 | $src_x = round(($width - ($width / $cmp_x * $cmp_y)) / 2);  | 
            ||
| 825 |             } elseif ($cmp_y > $cmp_x) { | 
            ||
| 826 | $src_h = round($height / $cmp_y * $cmp_x);  | 
            ||
| 827 | $src_y = round(($height - ($height / $cmp_y * $cmp_x)) / 2);  | 
            ||
| 828 | }  | 
            ||
| 829 | |||
| 830 | // positional cropping!  | 
            ||
| 831 |             if ($align) { | 
            ||
| 832 |                 if (false !== mb_strpos($align, 't')) { | 
            ||
| 833 | $src_y = 0;  | 
            ||
| 834 | }  | 
            ||
| 835 |                 if (false !== mb_strpos($align, 'b')) { | 
            ||
| 836 | $src_y = $height - $src_h;  | 
            ||
| 837 | }  | 
            ||
| 838 |                 if (false !== mb_strpos($align, 'l')) { | 
            ||
| 839 | $src_x = 0;  | 
            ||
| 840 | }  | 
            ||
| 841 |                 if (false !== mb_strpos($align, 'r')) { | 
            ||
| 842 | $src_x = $width - $src_w;  | 
            ||
| 843 | }  | 
            ||
| 844 | }  | 
            ||
| 845 | |||
| 846 | imagecopyresampled($canvas, $image, (int)$origin_x, (int)$origin_y, (int)$src_x, (int)$src_y, (int)$newWidth, (int)$newHeight, (int)$src_w, (int)$src_h);  | 
            ||
| 847 |         } else { | 
            ||
| 848 | // copy and resize part of an image with resampling  | 
            ||
| 849 | imagecopyresampled($canvas, $image, 0, 0, 0, 0, (int)$newWidth, (int)$newHeight, (int)$width, (int)$height);  | 
            ||
| 850 | }  | 
            ||
| 851 | |||
| 852 |         if (defined('IMG_FILTER_NEGATE') && '' != $filters && function_exists('imagefilter')) { | 
            ||
| 853 | // apply filters to image  | 
            ||
| 854 |             $filterList = explode('|', $filters); | 
            ||
| 855 |             foreach ($filterList as $fl) { | 
            ||
| 856 |                 $filterSettings = explode(',', $fl); | 
            ||
| 857 |                 if (isset($imageFilters[$filterSettings[0]])) { | 
            ||
| 858 |                     for ($i = 0; $i < 4; ++$i) { | 
            ||
| 859 |                         if (isset($filterSettings[$i])) { | 
            ||
| 860 | $filterSettings[$i] = (int)$filterSettings[$i];  | 
            ||
| 861 |                         } else { | 
            ||
| 862 | $filterSettings[$i] = null;  | 
            ||
| 863 | }  | 
            ||
| 864 | }  | 
            ||
| 865 | |||
| 866 |                     switch ($imageFilters[$filterSettings[0]][1]) { | 
            ||
| 867 | case 1:  | 
            ||
| 868 | |||
| 869 | imagefilter($canvas, $imageFilters[$filterSettings[0]][0], $filterSettings[1]);  | 
            ||
| 870 | |||
| 871 | break;  | 
            ||
| 872 | case 2:  | 
            ||
| 873 | imagefilter($canvas, $imageFilters[$filterSettings[0]][0], $filterSettings[1], $filterSettings[2]);  | 
            ||
| 874 | |||
| 875 | break;  | 
            ||
| 876 | case 3:  | 
            ||
| 877 | imagefilter($canvas, $imageFilters[$filterSettings[0]][0], $filterSettings[1], $filterSettings[2], $filterSettings[3]);  | 
            ||
| 878 | |||
| 879 | break;  | 
            ||
| 880 | case 4:  | 
            ||
| 881 | imagefilter($canvas, $imageFilters[$filterSettings[0]][0], $filterSettings[1], $filterSettings[2], $filterSettings[3], $filterSettings[4]);  | 
            ||
| 882 | |||
| 883 | break;  | 
            ||
| 884 | default:  | 
            ||
| 885 | imagefilter($canvas, $imageFilters[$filterSettings[0]][0]);  | 
            ||
| 886 | break;  | 
            ||
| 887 | }  | 
            ||
| 888 | }  | 
            ||
| 889 | }  | 
            ||
| 890 | }  | 
            ||
| 891 | |||
| 892 | // sharpen image  | 
            ||
| 893 |         if ($sharpen && function_exists('imageconvolution')) { | 
            ||
| 894 | $sharpenMatrix = [  | 
            ||
| 895 | [-1, -1, -1],  | 
            ||
| 896 | [-1, 16, -1],  | 
            ||
| 897 | [-1, -1, -1],  | 
            ||
| 898 | ];  | 
            ||
| 899 | |||
| 900 | $divisor = 8;  | 
            ||
| 901 | $offset = 0;  | 
            ||
| 902 | |||
| 903 | imageconvolution($canvas, $sharpenMatrix, $divisor, $offset);  | 
            ||
| 904 | }  | 
            ||
| 905 | //Straight from Wordpress core code. Reduces filesize by up to 70% for PNG's  | 
            ||
| 906 |         if ((IMAGETYPE_PNG == $origType || IMAGETYPE_GIF == $origType) && function_exists('imageistruecolor') && !imageistruecolor($image) && imagecolortransparent($image) > 0) { | 
            ||
| 907 | imagetruecolortopalette($canvas, false, imagecolorstotal($image));  | 
            ||
| 908 | }  | 
            ||
| 909 | |||
| 910 | $imgType = '';  | 
            ||
| 911 | $tempfile = tempnam($this->cacheDirectory, 'timthumb_tmpimg_');  | 
            ||
| 912 |         if (preg_match('/^image\/(?:jpg|jpeg)$/i', $mimeType)) { | 
            ||
| 913 | $imgType = 'jpg';  | 
            ||
| 914 | imagejpeg($canvas, $tempfile, $quality);  | 
            ||
| 915 |         } elseif (preg_match('/^image\/png$/i', $mimeType)) { | 
            ||
| 916 | $imgType = 'png';  | 
            ||
| 917 | imagepng($canvas, $tempfile, (int)floor($quality * 0.09));  | 
            ||
| 918 |         } elseif (preg_match('/^image\/gif$/i', $mimeType)) { | 
            ||
| 919 | $imgType = 'gif';  | 
            ||
| 920 | imagegif($canvas, $tempfile);  | 
            ||
| 921 |         } else { | 
            ||
| 922 |             return $this->sanityFail('Could not match mime type after verifying it previously.'); | 
            ||
| 923 | }  | 
            ||
| 924 | |||
| 925 |         if ('png' === $imgType && OPTIPNG_ENABLED && OPTIPNG_PATH && @is_file(OPTIPNG_PATH)) { | 
            ||
| 926 | $exec = OPTIPNG_PATH;  | 
            ||
| 927 | $this->debug(3, "optipng'ing $tempfile");  | 
            ||
| 928 | $presize = filesize($tempfile);  | 
            ||
| 929 |             $out     = shell_exec('$exec -o1 $tempfile'); //you can use up to -o7 but it really slows things down | 
            ||
| 930 | clearstatcache();  | 
            ||
| 931 | $aftersize = filesize($tempfile);  | 
            ||
| 932 | $sizeDrop = $presize - $aftersize;  | 
            ||
| 933 |             if ($sizeDrop > 0) { | 
            ||
| 934 | $this->debug(1, "optipng reduced size by $sizeDrop");  | 
            ||
| 935 |             } elseif ($sizeDrop < 0) { | 
            ||
| 936 | $this->debug(1, "optipng increased size! Difference was: $sizeDrop");  | 
            ||
| 937 |             } else { | 
            ||
| 938 | $this->debug(1, 'optipng did not change image size.');  | 
            ||
| 939 | }  | 
            ||
| 940 |         } elseif ('png' === $imgType && PNGCRUSH_ENABLED && PNGCRUSH_PATH && @is_file(PNGCRUSH_PATH)) { | 
            ||
| 941 | $exec = PNGCRUSH_PATH;  | 
            ||
| 942 | $tempfile2 = tempnam($this->cacheDirectory, 'timthumb_tmpimg_');  | 
            ||
| 943 | $this->debug(3, "pngcrush'ing $tempfile to $tempfile2");  | 
            ||
| 944 |             $out   = shell_exec('$exec $tempfile $tempfile2'); | 
            ||
| 945 | $todel = '';  | 
            ||
| 946 |             if (\is_file($tempfile2)) { | 
            ||
| 947 | $sizeDrop = filesize($tempfile) - filesize($tempfile2);  | 
            ||
| 948 |                 if ($sizeDrop > 0) { | 
            ||
| 949 | $this->debug(1, "pngcrush was succesful and gave a $sizeDrop byte size reduction");  | 
            ||
| 950 | $todel = $tempfile;  | 
            ||
| 951 | $tempfile = $tempfile2;  | 
            ||
| 952 |                 } else { | 
            ||
| 953 | $this->debug(1, "pngcrush did not reduce file size. Difference was $sizeDrop bytes.");  | 
            ||
| 954 | $todel = $tempfile2;  | 
            ||
| 955 | }  | 
            ||
| 956 |             } else { | 
            ||
| 957 | $this->debug(3, "pngcrush failed with output: $out");  | 
            ||
| 958 | $todel = $tempfile2;  | 
            ||
| 959 | }  | 
            ||
| 960 | @unlink($todel);  | 
            ||
| 961 | }  | 
            ||
| 962 | |||
| 963 | $this->debug(3, 'Rewriting image with security header.');  | 
            ||
| 964 | $tempfile4 = tempnam($this->cacheDirectory, 'timthumb_tmpimg_');  | 
            ||
| 965 | $context = stream_context_create();  | 
            ||
| 966 | $fp = fopen($tempfile, 'rb', false, $context);  | 
            ||
| 967 | file_put_contents($tempfile4, $this->filePrependSecurityBlock . $imgType . ' ?' . '>'); //6 extra bytes, first 3 being image type  | 
            ||
| 968 | file_put_contents($tempfile4, $fp, FILE_APPEND);  | 
            ||
| 969 | fclose($fp);  | 
            ||
| 970 | @unlink($tempfile);  | 
            ||
| 971 | $this->debug(3, 'Locking and replacing cache file.');  | 
            ||
| 972 | $lockFile = $this->cachefile . '.lock';  | 
            ||
| 973 | $fh = fopen($lockFile, 'wb');  | 
            ||
| 974 |         if (!$fh) { | 
            ||
| 975 |             return $this->error('Could not open the lockfile for writing an image.'); | 
            ||
| 976 | }  | 
            ||
| 977 |         if (flock($fh, LOCK_EX)) { | 
            ||
| 978 | @unlink($this->cachefile); //rename generally overwrites, but doing this in case of platform specific quirks. File might not exist yet.  | 
            ||
| 979 | rename($tempfile4, $this->cachefile);  | 
            ||
| 980 | flock($fh, LOCK_UN);  | 
            ||
| 981 | fclose($fh);  | 
            ||
| 982 | @unlink($lockFile);  | 
            ||
| 983 |         } else { | 
            ||
| 984 | fclose($fh);  | 
            ||
| 985 | @unlink($lockFile);  | 
            ||
| 986 | @unlink($tempfile4);  | 
            ||
| 987 | |||
| 988 |             return $this->error('Could not get a lock for writing.'); | 
            ||
| 989 | }  | 
            ||
| 990 | $this->debug(3, 'Done image replace with security header. Cleaning up and running cleanCache()');  | 
            ||
| 991 | imagedestroy($canvas);  | 
            ||
| 992 | imagedestroy($image);  | 
            ||
| 993 | |||
| 994 | return true;  | 
            ||
| 995 | }  | 
            ||
| 996 | |||
| 997 | protected function calcDocRoot()  | 
            ||
| 998 |     { | 
            ||
| 999 | $docRoot = @$_SERVER['DOCUMENT_ROOT'];  | 
            ||
| 1000 |         if (defined('LOCAL_FILE_BASE_DIRECTORY')) { | 
            ||
| 1001 | $docRoot = LOCAL_FILE_BASE_DIRECTORY;  | 
            ||
| 1002 | }  | 
            ||
| 1003 |         if (!isset($docRoot)) { | 
            ||
| 1004 | $this->debug(3, 'DOCUMENT_ROOT is not set. This is probably windows. Starting search 1.');  | 
            ||
| 1005 |             if (\Xmf\Request::hasVar('SCRIPT_FILENAME', 'SERVER')) { | 
            ||
| 1006 |                 $docRoot = str_replace('\\', '/', mb_substr($_SERVER['SCRIPT_FILENAME'], 0, 0 - mb_strlen($_SERVER['SCRIPT_NAME']))); | 
            ||
| 1007 | $this->debug(3, "Generated docRoot using SCRIPT_FILENAME and SCRIPT_NAME as: $docRoot");  | 
            ||
| 1008 | }  | 
            ||
| 1009 | }  | 
            ||
| 1010 |         if (!isset($docRoot)) { | 
            ||
| 1011 | $this->debug(3, 'DOCUMENT_ROOT still is not set. Starting search 2.');  | 
            ||
| 1012 |             if (\Xmf\Request::hasVar('PATH_TRANSLATED', 'SERVER')) { | 
            ||
| 1013 |                 $docRoot = str_replace('\\', '/', mb_substr(str_replace('\\\\', '\\', $_SERVER['PATH_TRANSLATED']), 0, 0 - mb_strlen($_SERVER['SCRIPT_NAME']))); | 
            ||
| 1014 | $this->debug(3, "Generated docRoot using PATH_TRANSLATED and SCRIPT_NAME as: $docRoot");  | 
            ||
| 1015 | }  | 
            ||
| 1016 | }  | 
            ||
| 1017 |         if ($docRoot && '/' !== $_SERVER['DOCUMENT_ROOT']) { | 
            ||
| 1018 |             $docRoot = preg_replace('/\/$/', '', $docRoot); | 
            ||
| 1019 | }  | 
            ||
| 1020 | $this->debug(3, 'Doc root is: ' . $docRoot);  | 
            ||
| 1021 | $this->docRoot = $docRoot;  | 
            ||
| 1022 | }  | 
            ||
| 1023 | |||
| 1024 | /**  | 
            ||
| 1025 | * @param $src  | 
            ||
| 1026 | *  | 
            ||
| 1027 | * @return bool|string  | 
            ||
| 1028 | */  | 
            ||
| 1029 | protected function getLocalImagePath($src)  | 
            ||
| 1030 |     { | 
            ||
| 1031 | $src = ltrim($src, '/'); //strip off the leading '/'  | 
            ||
| 1032 |         if (!$this->docRoot) { | 
            ||
| 1033 | $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.');  | 
            ||
| 1034 | //We don't support serving images outside the current dir if we don't have a doc root for security reasons.  | 
            ||
| 1035 |             $file = preg_replace('/^.*?([^\/\\\\]+)$/', '$1', $src); //strip off any path info and just leave the filename. | 
            ||
| 1036 |             if (\is_file($file)) { | 
            ||
| 1037 | return $this->realpath($file);  | 
            ||
| 1038 | }  | 
            ||
| 1039 | |||
| 1040 |             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."); | 
            ||
| 1041 | }  | 
            ||
| 1042 |         if (!is_dir($this->docRoot)) { | 
            ||
| 1043 |             $this->error("Server path does not exist. Ensure variable \$_SERVER['DOCUMENT_ROOT'] is set correctly"); | 
            ||
| 1044 | }  | 
            ||
| 1045 | |||
| 1046 | //Do not go past this point without docRoot set  | 
            ||
| 1047 | |||
| 1048 | //Try src under docRoot  | 
            ||
| 1049 |         if (file_exists($this->docRoot . '/' . $src)) { | 
            ||
| 1050 | $this->debug(3, 'Found file as ' . $this->docRoot . '/' . $src);  | 
            ||
| 1051 | $real = $this->realpath($this->docRoot . '/' . $src);  | 
            ||
| 1052 |             if (0 === mb_stripos($real, $this->docRoot)) { | 
            ||
| 1053 | return $real;  | 
            ||
| 1054 | }  | 
            ||
| 1055 | $this->debug(1, 'Security block: The file specified occurs outside the document root.');  | 
            ||
| 1056 | //allow search to continue  | 
            ||
| 1057 | }  | 
            ||
| 1058 | //Check absolute paths and then verify the real path is under doc root  | 
            ||
| 1059 |         $absolute = $this->realpath('/' . $src); | 
            ||
| 1060 |         if ($absolute && file_exists($absolute)) { | 
            ||
| 1061 | //realpath does file_exists check, so can probably skip the exists check here  | 
            ||
| 1062 | $this->debug(3, "Found absolute path: $absolute");  | 
            ||
| 1063 |             if (!$this->docRoot) { | 
            ||
| 1064 |                 $this->sanityFail('docRoot not set when checking absolute path.'); | 
            ||
| 1065 | }  | 
            ||
| 1066 |             if (0 === mb_stripos($absolute, $this->docRoot)) { | 
            ||
| 1067 | return $absolute;  | 
            ||
| 1068 | }  | 
            ||
| 1069 | $this->debug(1, 'Security block: The file specified occurs outside the document root.');  | 
            ||
| 1070 | //and continue search  | 
            ||
| 1071 | }  | 
            ||
| 1072 | |||
| 1073 | $base = $this->docRoot;  | 
            ||
| 1074 | |||
| 1075 | // account for Windows directory structure  | 
            ||
| 1076 |         if (false !== mb_strpos($_SERVER['SCRIPT_FILENAME'], ':')) { | 
            ||
| 1077 |             $subDirectories = explode('\\', str_replace($this->docRoot, '', $_SERVER['SCRIPT_FILENAME'])); | 
            ||
| 1078 |         } else { | 
            ||
| 1079 |             $subDirectories = explode('/', str_replace($this->docRoot, '', $_SERVER['SCRIPT_FILENAME'])); | 
            ||
| 1080 | }  | 
            ||
| 1081 | |||
| 1082 |         foreach ($subDirectories as $sub) { | 
            ||
| 1083 | $base .= $sub . '/';  | 
            ||
| 1084 | $this->debug(3, 'Trying file as: ' . $base . $src);  | 
            ||
| 1085 |             if (file_exists($base . $src)) { | 
            ||
| 1086 | $this->debug(3, 'Found file as: ' . $base . $src);  | 
            ||
| 1087 | $real = $this->realpath($base . $src);  | 
            ||
| 1088 |                 if (0 === mb_stripos($real, $this->realpath($this->docRoot))) { | 
            ||
| 1089 | return $real;  | 
            ||
| 1090 | }  | 
            ||
| 1091 | $this->debug(1, 'Security block: The file specified occurs outside the document root.');  | 
            ||
| 1092 | //And continue search  | 
            ||
| 1093 | }  | 
            ||
| 1094 | }  | 
            ||
| 1095 | |||
| 1096 | return false;  | 
            ||
| 1097 | }  | 
            ||
| 1098 | |||
| 1099 | /**  | 
            ||
| 1100 | * @param $path  | 
            ||
| 1101 | *  | 
            ||
| 1102 | * @return string  | 
            ||
| 1103 | */  | 
            ||
| 1104 | protected function realpath($path)  | 
            ||
| 1105 |     { | 
            ||
| 1106 | //try to remove any relative paths  | 
            ||
| 1107 | $removeRelatives = '/\w+\/\.\.\//';  | 
            ||
| 1108 |         while (preg_match($removeRelatives, $path)) { | 
            ||
| 1109 | $path = preg_replace($removeRelatives, '', $path);  | 
            ||
| 1110 | }  | 
            ||
| 1111 | //if any remain use PHP realpath to strip them out, otherwise return $path  | 
            ||
| 1112 | //if using realpath, any symlinks will also be resolved  | 
            ||
| 1113 |         return preg_match('#^\.\./|/\.\./#', $path) ? realpath($path) : $path; | 
            ||
| 1114 | }  | 
            ||
| 1115 | |||
| 1116 | /**  | 
            ||
| 1117 | * @param $name  | 
            ||
| 1118 | */  | 
            ||
| 1119 | protected function toDelete($name)  | 
            ||
| 1123 | }  | 
            ||
| 1124 | |||
| 1125 | /**  | 
            ||
| 1126 | * @return bool  | 
            ||
| 1127 | */  | 
            ||
| 1128 | protected function serveWebshot()  | 
            ||
| 1183 | }  | 
            ||
| 1184 | |||
| 1185 | /**  | 
            ||
| 1186 | * @return bool  | 
            ||
| 1187 | */  | 
            ||
| 1188 | protected function serveExternalImage()  | 
            ||
| 1224 | }  | 
            ||
| 1225 | |||
| 1226 | /**  | 
            ||
| 1227 | * @param $h  | 
            ||
| 1228 | * @param $d  | 
            ||
| 1229 | *  | 
            ||
| 1230 | * @return int  | 
            ||
| 1231 | */  | 
            ||
| 1232 | public static function curlWrite($h, $d)  | 
            ||
| 1233 |     { | 
            ||
| 1234 | fwrite(self::$curlFH, $d);  | 
            ||
| 1235 | self::$curlDataWritten += \mb_strlen($d);  | 
            ||
| 1236 |         if (self::$curlDataWritten > MAX_FILE_SIZE) { | 
            ||
| 1237 | return 0;  | 
            ||
| 1238 | }  | 
            ||
| 1239 | |||
| 1240 | return mb_strlen($d);  | 
            ||
| 1241 | }  | 
            ||
| 1242 | |||
| 1243 | /**  | 
            ||
| 1244 | * @return bool  | 
            ||
| 1245 | */  | 
            ||
| 1246 | protected function serveCacheFile()  | 
            ||
| 1247 |     { | 
            ||
| 1248 |         $this->debug(3, "Serving {$this->cachefile}"); | 
            ||
| 1249 |         if (!is_file($this->cachefile)) { | 
            ||
| 1250 |             $this->error("serveCacheFile called in timthumb but we couldn't find the cached file."); | 
            ||
| 1251 | |||
| 1252 | return false;  | 
            ||
| 1253 | }  | 
            ||
| 1254 | $fp = fopen($this->cachefile, 'rb');  | 
            ||
| 1255 |         if (!$fp) { | 
            ||
| 1256 |             return $this->error('Could not open cachefile.'); | 
            ||
| 1257 | }  | 
            ||
| 1258 | fseek($fp, mb_strlen($this->filePrependSecurityBlock), SEEK_SET);  | 
            ||
| 1259 | $imgType = fread($fp, 3);  | 
            ||
| 1260 | fseek($fp, 3, SEEK_CUR);  | 
            ||
| 1261 |         if (ftell($fp) != \mb_strlen($this->filePrependSecurityBlock) + 6) { | 
            ||
| 1262 | @unlink($this->cachefile);  | 
            ||
| 1263 | |||
| 1264 |             return $this->error('The cached image file seems to be corrupt.'); | 
            ||
| 1265 | }  | 
            ||
| 1266 | $imageDataSize = filesize($this->cachefile) - (mb_strlen($this->filePrependSecurityBlock) + 6);  | 
            ||
| 1267 | $this->sendImageHeaders($imgType, $imageDataSize);  | 
            ||
| 1268 | $bytesSent = @fpassthru($fp);  | 
            ||
| 1269 | fclose($fp);  | 
            ||
| 1270 |         if ($bytesSent > 0) { | 
            ||
| 1271 | return true;  | 
            ||
| 1272 | }  | 
            ||
| 1273 | $content = file_get_contents($this->cachefile);  | 
            ||
| 1274 |         if (false !== $content) { | 
            ||
| 1275 | $content = mb_substr($content, mb_strlen($this->filePrependSecurityBlock) + 6);  | 
            ||
| 1276 | echo $content;  | 
            ||
| 1277 | $this->debug(3, 'Served using file_get_contents and echo');  | 
            ||
| 1278 | |||
| 1279 | return true;  | 
            ||
| 1280 | }  | 
            ||
| 1281 |         $this->error('Cache file could not be loaded.'); | 
            ||
| 1282 | |||
| 1283 | return false;  | 
            ||
| 1284 | }  | 
            ||
| 1285 | |||
| 1286 | /**  | 
            ||
| 1287 | * @param $mimeType  | 
            ||
| 1288 | * @param $dataSize  | 
            ||
| 1289 | *  | 
            ||
| 1290 | * @return bool  | 
            ||
| 1291 | */  | 
            ||
| 1292 | protected function sendImageHeaders($mimeType, $dataSize)  | 
            ||
| 1293 |     { | 
            ||
| 1294 |         if (!preg_match('/^image\//i', $mimeType)) { | 
            ||
| 1295 | $mimeType = 'image/' . $mimeType;  | 
            ||
| 1296 | }  | 
            ||
| 1297 |         if ('image/jpg' === \mb_strtolower($mimeType)) { | 
            ||
| 1298 | $mimeType = 'image/jpeg';  | 
            ||
| 1299 | }  | 
            ||
| 1300 |         $gmdateExpires   = gmdate('D, d M Y H:i:s', strtotime('now +10 days')) . ' GMT'; | 
            ||
| 1301 |         $gmdate_modified = gmdate('D, d M Y H:i:s') . ' GMT'; | 
            ||
| 1302 | // send content headers then display image  | 
            ||
| 1303 |         header('Content-Type: ' . $mimeType); | 
            ||
| 1304 |         header('Accept-Ranges: none'); //Changed this because we don't accept range requests | 
            ||
| 1305 |         header('Last-Modified: ' . $gmdate_modified); | 
            ||
| 1306 |         header('Content-Length: ' . $dataSize); | 
            ||
| 1307 |         if (BROWSER_CACHE_DISABLE) { | 
            ||
| 1308 | $this->debug(3, 'Browser cache is disabled so setting non-caching headers.');  | 
            ||
| 1309 |             header('Cache-Control: no-store, no-cache, must-revalidate, max-age=0'); | 
            ||
| 1310 |             header('Pragma: no-cache'); | 
            ||
| 1311 |             header('Expires: ' . gmdate('D, d M Y H:i:s', time())); | 
            ||
| 1312 |         } else { | 
            ||
| 1313 | $this->debug(3, 'Browser caching is enabled');  | 
            ||
| 1314 |             header('Cache-Control: max-age=' . BROWSER_CACHE_MAX_AGE . ', must-revalidate'); | 
            ||
| 1315 |             header('Expires: ' . $gmdateExpires); | 
            ||
| 1316 | }  | 
            ||
| 1317 | |||
| 1318 | return true;  | 
            ||
| 1319 | }  | 
            ||
| 1320 | |||
| 1321 | protected function securityChecks()  | 
            ||
| 1322 |     { | 
            ||
| 1323 | }  | 
            ||
| 1324 | |||
| 1325 | /**  | 
            ||
| 1326 | * @param $property  | 
            ||
| 1327 | * @param string $default  | 
            ||
| 1328 | *  | 
            ||
| 1329 | * @return string  | 
            ||
| 1330 | */  | 
            ||
| 1331 | protected function param($property, $default = '')  | 
            ||
| 1332 |     { | 
            ||
| 1333 |         if (isset($_GET[$property])) { | 
            ||
| 1334 | return Request::getString($property, '', 'GET');  | 
            ||
| 1335 | }  | 
            ||
| 1336 | |||
| 1337 | return $default;  | 
            ||
| 1338 | }  | 
            ||
| 1339 | |||
| 1340 | /**  | 
            ||
| 1341 | * @param $mimeType  | 
            ||
| 1342 | * @param $src  | 
            ||
| 1343 | *  | 
            ||
| 1344 | * @return resource  | 
            ||
| 1345 | */  | 
            ||
| 1346 | protected function openImage($mimeType, $src)  | 
            ||
| 1347 |     { | 
            ||
| 1348 | $image = '';  | 
            ||
| 1349 |         switch ($mimeType) { | 
            ||
| 1350 | case 'image/jpeg':  | 
            ||
| 1351 | |||
| 1352 | $image = imagecreatefromjpeg($src);  | 
            ||
| 1353 | |||
| 1354 | break;  | 
            ||
| 1355 | case 'image/png':  | 
            ||
| 1356 | $image = imagecreatefrompng($src);  | 
            ||
| 1357 | imagealphablending($image, true);  | 
            ||
| 1358 | imagesavealpha($image, true);  | 
            ||
| 1359 | |||
| 1360 | break;  | 
            ||
| 1361 | case 'image/gif':  | 
            ||
| 1362 | $image = imagecreatefromgif($src);  | 
            ||
| 1363 | |||
| 1364 | break;  | 
            ||
| 1365 | default:  | 
            ||
| 1366 |                 $this->error('Unrecognised mimeType'); | 
            ||
| 1367 | }  | 
            ||
| 1368 | |||
| 1369 | return $image;  | 
            ||
| 1370 | }  | 
            ||
| 1371 | |||
| 1372 | /**  | 
            ||
| 1373 | * @return string  | 
            ||
| 1374 | */  | 
            ||
| 1375 | protected function getIP()  | 
            ||
| 1376 |     { | 
            ||
| 1377 | $rem = @$_SERVER['REMOTE_ADDR'];  | 
            ||
| 1378 | $ff = @$_SERVER['HTTP_X_FORWARDED_FOR'];  | 
            ||
| 1379 | $ci = @$_SERVER['HTTP_CLIENT_IP'];  | 
            ||
| 1380 |         if (preg_match('/^(?:192\.168|172\.16|10\.|127\.)/', $rem)) { | 
            ||
| 1381 |             if ($ff) { | 
            ||
| 1382 | return $ff;  | 
            ||
| 1383 | }  | 
            ||
| 1384 |             if ($ci) { | 
            ||
| 1385 | return $ci;  | 
            ||
| 1386 | }  | 
            ||
| 1387 | |||
| 1388 | return $rem;  | 
            ||
| 1389 | }  | 
            ||
| 1390 |         if ($rem) { | 
            ||
| 1391 | return $rem;  | 
            ||
| 1392 | }  | 
            ||
| 1393 |         if ($ff) { | 
            ||
| 1394 | return $ff;  | 
            ||
| 1395 | }  | 
            ||
| 1396 |         if ($ci) { | 
            ||
| 1397 | return $ci;  | 
            ||
| 1398 | }  | 
            ||
| 1399 | |||
| 1400 | return 'UNKNOWN';  | 
            ||
| 1401 | }  | 
            ||
| 1402 | |||
| 1403 | /**  | 
            ||
| 1404 | * @param $level  | 
            ||
| 1405 | * @param $msg  | 
            ||
| 1406 | */  | 
            ||
| 1407 | protected function debug($level, $msg)  | 
            ||
| 1408 |     { | 
            ||
| 1409 |         if (DEBUG_ON && $level <= DEBUG_LEVEL) { | 
            ||
| 1410 |             $execTime = sprintf('%.6f', microtime(true) - $this->startTime); | 
            ||
| 1411 |             $tick     = sprintf('%.6f', 0); | 
            ||
| 1412 |             if ($this->lastBenchTime > 0) { | 
            ||
| 1413 |                 $tick = sprintf('%.6f', microtime(true) - $this->lastBenchTime); | 
            ||
| 1414 | }  | 
            ||
| 1415 | $this->lastBenchTime = microtime(true);  | 
            ||
| 1416 |             error_log('TimThumb Debug line ' . __LINE__ . " [$execTime : $tick]: $msg"); | 
            ||
| 1417 | }  | 
            ||
| 1418 | }  | 
            ||
| 1419 | |||
| 1420 | /**  | 
            ||
| 1421 | * @param $msg  | 
            ||
| 1422 | *  | 
            ||
| 1423 | * @return bool  | 
            ||
| 1424 | */  | 
            ||
| 1425 | protected function sanityFail($msg)  | 
            ||
| 1426 |     { | 
            ||
| 1427 |         return $this->error("There is a problem in the timthumb code. Message: Please report this error at <a href='https://code.google.com/p/timthumb/issues/list'>timthumb's bug tracking page</a>: $msg"); | 
            ||
| 1428 | }  | 
            ||
| 1429 | |||
| 1430 | /**  | 
            ||
| 1431 | * @param $file  | 
            ||
| 1432 | *  | 
            ||
| 1433 | * @return string  | 
            ||
| 1434 | */  | 
            ||
| 1435 | protected function getMimeType($file)  | 
            ||
| 1436 |     { | 
            ||
| 1437 | $info = getimagesize($file);  | 
            ||
| 1438 |         if (is_array($info) && $info['mime']) { | 
            ||
| 1439 | return $info['mime'];  | 
            ||
| 1440 | }  | 
            ||
| 1441 | |||
| 1442 | return '';  | 
            ||
| 1443 | }  | 
            ||
| 1444 | |||
| 1445 | protected function setMemoryLimit()  | 
            ||
| 1446 |     { | 
            ||
| 1447 |         $inimem   = ini_get('memory_limit'); | 
            ||
| 1448 | $inibytes = self::returnBytes($inimem);  | 
            ||
| 1449 | $ourbytes = self::returnBytes(MEMORY_LIMIT);  | 
            ||
| 1450 |         if ($inibytes < $ourbytes) { | 
            ||
| 1451 |             ini_set('memory_limit', MEMORY_LIMIT); | 
            ||
| 1452 | $this->debug(3, "Increased memory from $inimem to " . MEMORY_LIMIT);  | 
            ||
| 1453 |         } else { | 
            ||
| 1454 | $this->debug(3, 'Not adjusting memory size because the current setting is ' . $inimem . ' and our size of ' . MEMORY_LIMIT . ' is smaller.');  | 
            ||
| 1455 | }  | 
            ||
| 1456 | }  | 
            ||
| 1457 | |||
| 1458 | /**  | 
            ||
| 1459 | * @param $sizeString  | 
            ||
| 1460 | *  | 
            ||
| 1461 | * @return int  | 
            ||
| 1462 | */  | 
            ||
| 1463 | protected static function returnBytes($sizeString)  | 
            ||
| 1464 |     { | 
            ||
| 1465 |         switch (mb_substr($sizeString, -1)) { | 
            ||
| 1466 | case 'M':  | 
            ||
| 1467 | |||
| 1468 | case 'm':  | 
            ||
| 1469 | |||
| 1470 | return (int)$sizeString * 1048576;  | 
            ||
| 1471 | case 'K':  | 
            ||
| 1472 | case 'k':  | 
            ||
| 1473 | |||
| 1474 | return (int)$sizeString * 1024;  | 
            ||
| 1475 | case 'G':  | 
            ||
| 1476 | case 'g':  | 
            ||
| 1477 | |||
| 1478 | return (int)$sizeString * 1073741824;  | 
            ||
| 1479 | default:  | 
            ||
| 1480 | return $sizeString;  | 
            ||
| 1481 | }  | 
            ||
| 1482 | }  | 
            ||
| 1483 | |||
| 1484 | /**  | 
            ||
| 1485 | * @param $url  | 
            ||
| 1486 | * @param $tempfile  | 
            ||
| 1487 | *  | 
            ||
| 1488 | * @return bool  | 
            ||
| 1489 | */  | 
            ||
| 1490 | protected function getURL($url, $tempfile)  | 
            ||
| 1491 |     { | 
            ||
| 1492 | $this->lastURLError = false;  | 
            ||
| 1493 |         $url                = preg_replace('/ /', '%20', $url); | 
            ||
| 1494 |         if (function_exists('curl_init')) { | 
            ||
| 1495 | $this->debug(3, 'Curl is installed so using it to fetch URL.');  | 
            ||
| 1496 | self::$curlFH = fopen($tempfile, 'wb');  | 
            ||
| 1497 |             if (!self::$curlFH) { | 
            ||
| 1498 |                 $this->error("Could not open $tempfile for writing."); | 
            ||
| 1499 | |||
| 1500 | return false;  | 
            ||
| 1501 | }  | 
            ||
| 1502 | self::$curlDataWritten = 0;  | 
            ||
| 1503 | $this->debug(3, "Fetching url with curl: $url");  | 
            ||
| 1504 | $curl = curl_init($url);  | 
            ||
| 1505 | curl_setopt($curl, CURLOPT_TIMEOUT, CURL_TIMEOUT);  | 
            ||
| 1506 | 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');  | 
            ||
| 1507 | curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);  | 
            ||
| 1508 | curl_setopt($curl, CURLOPT_HEADER, 0);  | 
            ||
| 1509 | curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, true); //was false before  | 
            ||
| 1510 | curl_setopt($curl, CURLOPT_WRITEFUNCTION, 'timthumb::curlWrite');  | 
            ||
| 1511 | @curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true);  | 
            ||
| 1512 | @curl_setopt($curl, CURLOPT_MAXREDIRS, 10);  | 
            ||
| 1513 | |||
| 1514 | $curlResult = curl_exec($curl);  | 
            ||
| 1515 | fclose(self::$curlFH);  | 
            ||
| 1516 | $httpStatus = curl_getinfo($curl, CURLINFO_HTTP_CODE);  | 
            ||
| 1517 |             if (404 == $httpStatus) { | 
            ||
| 1518 | $this->set404();  | 
            ||
| 1519 | }  | 
            ||
| 1520 |             if (302 == $httpStatus) { | 
            ||
| 1521 |                 $this->error('External Image is Redirecting. Try alternate image url'); | 
            ||
| 1522 | |||
| 1523 | return false;  | 
            ||
| 1524 | }  | 
            ||
| 1525 |             if ($curlResult) { | 
            ||
| 1526 | curl_close($curl);  | 
            ||
| 1527 | |||
| 1528 | return true;  | 
            ||
| 1529 | }  | 
            ||
| 1530 | $this->lastURLError = curl_error($curl);  | 
            ||
| 1531 | curl_close($curl);  | 
            ||
| 1532 | |||
| 1533 | return false;  | 
            ||
| 1534 | }  | 
            ||
| 1535 | $img = @file_get_contents($url);  | 
            ||
| 1536 |         if (false === $img) { | 
            ||
| 1537 | $err = error_get_last();  | 
            ||
| 1538 | $this->lastURLError = $err;  | 
            ||
| 1539 |             if (is_array($err) && $err['message']) { | 
            ||
| 1540 | $this->lastURLError = $err['message'];  | 
            ||
| 1541 | }  | 
            ||
| 1542 |             if (false !== mb_strpos($this->lastURLError, '404')) { | 
            ||
| 1543 | $this->set404();  | 
            ||
| 1544 | }  | 
            ||
| 1545 | |||
| 1546 | return false;  | 
            ||
| 1547 | }  | 
            ||
| 1548 |         if (!file_put_contents($tempfile, $img)) { | 
            ||
| 1549 |             $this->error("Could not write to $tempfile."); | 
            ||
| 1550 | |||
| 1551 | return false;  | 
            ||
| 1552 | }  | 
            ||
| 1553 | |||
| 1554 | return true;  | 
            ||
| 1555 | }  | 
            ||
| 1556 | |||
| 1557 | /**  | 
            ||
| 1558 | * @param $file  | 
            ||
| 1559 | *  | 
            ||
| 1560 | * @return bool  | 
            ||
| 1561 | */  | 
            ||
| 1562 | protected function serveImg($file)  | 
            ||
| 1584 | }  | 
            ||
| 1585 | |||
| 1586 | protected function set404()  | 
            ||
| 1587 |     { | 
            ||
| 1588 | $this->is404 = true;  | 
            ||
| 1589 | }  | 
            ||
| 1590 | |||
| 1591 | /**  | 
            ||
| 1592 | * @return bool  | 
            ||
| 1593 | */  | 
            ||
| 1594 | protected function is404()  | 
            ||
| 1597 | }  | 
            ||
| 1598 | }  | 
            ||
| 1599 | 
Let?s assume that you have a directory layout like this:
. |-- OtherDir | |-- Bar.php | `-- Foo.php `-- SomeDir `-- Foo.phpand let?s assume the following content of
Bar.php:If both files
OtherDir/Foo.phpandSomeDir/Foo.phpare loaded in the same runtime, you will see a PHP error such as the following:PHP Fatal error: Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.phpHowever, as
OtherDir/Foo.phpdoes not necessarily have to be loaded and the error is only triggered if it is loaded beforeOtherDir/Bar.php, this problem might go unnoticed for a while. In order to prevent this error from surfacing, you must import the namespace with a different alias: