├── .github └── FUNDING.yml ├── README.md ├── images ├── Chrysanthemum.jpg ├── Desert.jpg ├── Hydrangeas.jpg ├── Jellyfish.jpg ├── Koala.jpg ├── Lighthouse.jpg ├── Penguins.jpg └── Tulips.jpg ├── img.php └── index.php /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [dcblogdev] 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Create a gallery from a folder with PHP 2 | ============= 3 | 4 | These files acompany the tutorial: [Creating an image gallery from a folder of images automatically](http://daveismyname.com/creating-an-image-gallery-from-a-folder-of-images-automatically-bp) -------------------------------------------------------------------------------- /images/Chrysanthemum.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dcblogdev/gallery-from-folder/7c54470fa84ca492e5ea50affb08b9b74ba02d04/images/Chrysanthemum.jpg -------------------------------------------------------------------------------- /images/Desert.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dcblogdev/gallery-from-folder/7c54470fa84ca492e5ea50affb08b9b74ba02d04/images/Desert.jpg -------------------------------------------------------------------------------- /images/Hydrangeas.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dcblogdev/gallery-from-folder/7c54470fa84ca492e5ea50affb08b9b74ba02d04/images/Hydrangeas.jpg -------------------------------------------------------------------------------- /images/Jellyfish.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dcblogdev/gallery-from-folder/7c54470fa84ca492e5ea50affb08b9b74ba02d04/images/Jellyfish.jpg -------------------------------------------------------------------------------- /images/Koala.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dcblogdev/gallery-from-folder/7c54470fa84ca492e5ea50affb08b9b74ba02d04/images/Koala.jpg -------------------------------------------------------------------------------- /images/Lighthouse.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dcblogdev/gallery-from-folder/7c54470fa84ca492e5ea50affb08b9b74ba02d04/images/Lighthouse.jpg -------------------------------------------------------------------------------- /images/Penguins.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dcblogdev/gallery-from-folder/7c54470fa84ca492e5ea50affb08b9b74ba02d04/images/Penguins.jpg -------------------------------------------------------------------------------- /images/Tulips.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dcblogdev/gallery-from-folder/7c54470fa84ca492e5ea50affb08b9b74ba02d04/images/Tulips.jpg -------------------------------------------------------------------------------- /img.php: -------------------------------------------------------------------------------- 1 | handleErrors(); 390 | 391 | if ($mthumb->tryBrowserCache()) { 392 | exit(0); 393 | } 394 | $mthumb->handleErrors(); 395 | if (FILE_CACHE_ENABLED && $mthumb->tryServerCache()) { 396 | exit(0); 397 | } 398 | $mthumb->handleErrors(); 399 | $mthumb->run(); 400 | $mthumb->handleErrors(); 401 | exit(0); 402 | } 403 | 404 | /** 405 | * 406 | */ 407 | public function __construct() { 408 | global $ALLOWED_SITES; 409 | $this->startTime = microtime(TRUE); 410 | date_default_timezone_set('UTC'); 411 | $this->debug(1, "Starting new request from " . $this->getIP() . " to " . $_SERVER[ 'REQUEST_URI' ]); 412 | $this->calcDocRoot(); 413 | //On windows systems I'm assuming fileinode returns an empty string or a number that doesn't change. Check this. 414 | $this->salt = @filemtime(__FILE__) . '-' . @fileinode(__FILE__); 415 | $this->debug(3, "Salt is: " . $this->salt); 416 | if (FILE_CACHE_DIRECTORY) { 417 | if (!is_dir(FILE_CACHE_DIRECTORY)) { 418 | @mkdir(FILE_CACHE_DIRECTORY); 419 | if (!is_dir(FILE_CACHE_DIRECTORY)) { 420 | $this->error("Could not create the file cache directory."); 421 | 422 | return FALSE; 423 | } 424 | } 425 | $this->cacheDirectory = FILE_CACHE_DIRECTORY; 426 | if (!touch($this->cacheDirectory . '/index.html')) { 427 | $this->error("Could not create the index.html file - to fix this create an empty file named index.html file in the cache directory."); 428 | } 429 | } else { 430 | $this->cacheDirectory = sys_get_temp_dir(); 431 | } 432 | // 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. 433 | $this->cleanCache(); 434 | 435 | $this->myHost = preg_replace('/^www\./i', '', $_SERVER[ 'HTTP_HOST' ]); 436 | 437 | // start mindshare fix for tilde's, check if tilde is found in src 438 | if (strstr($this->param('src'), '~')) { 439 | $url_parts = explode('/', $this->param('src')); 440 | foreach ($url_parts as $url_part) { 441 | //do not include any part with a ~ when building new url 442 | if (!strstr($url_part, '~')) { 443 | $new_dev_url .= $url_part . '/'; 444 | } 445 | } 446 | //remove trailing slash 447 | $new_dev_url = substr($new_dev_url, 0, -1); 448 | $this->src = $new_dev_url; 449 | } else { 450 | $this->src = $this->param('src'); 451 | } 452 | // end mindshare fix for tilde's 453 | $this->url = parse_url($this->src); 454 | $this->src = preg_replace('/https?:\/\/(?:www\.)?' . $this->myHost . '/i', '', $this->src); 455 | 456 | if (strlen($this->src) <= 3) { 457 | $this->error("No image specified"); 458 | 459 | return FALSE; 460 | } 461 | 462 | // Always block external sites from using this script 463 | if (array_key_exists('HTTP_REFERER', $_SERVER) && (!preg_match('/^https?:\/\/(?:www\.)?' . $this->myHost . '(?:$|\/)/i', $_SERVER[ 'HTTP_REFERER' ]))) { 464 | // base64 encoded red image that says 'no hotlinkers' nothing to worry about! :) 465 | $imgData = base64_decode("R0lGODlhUAAMAIAAAP8AAP///yH5BAAHAP8ALAAAAABQAAwAAAJpjI+py+0Po5y0OgAMjjv01YUZ\nOGplhWXfNa6JCLnWkXplrcBmW+spbwvaVr/cDyg7IoFC2KbYVC2NQ5MQ4ZNao9Ynzjl9ScNYpneb\nDULB3RP6JuPuaGfuuV4fumf8PuvqFyhYtjdoeFgAADs="); 466 | header('Content-Type: image/gif'); 467 | header('Content-Length: ' . strlen($imgData)); 468 | header('Cache-Control: no-store, no-cache, must-revalidate, max-age=0'); 469 | header("Pragma: no-cache"); 470 | header('Expires: ' . gmdate('D, d M Y H:i:s', time())); 471 | echo $imgData; 472 | 473 | return FALSE; 474 | exit(0); 475 | } 476 | if (preg_match('/^https?:\/\/[^\/]+/i', $this->src)) { 477 | $this->debug(2, "Is a request for an external URL: " . $this->src); 478 | $this->isURL = TRUE; 479 | } else { 480 | $this->debug(2, "Is a request for an internal file: " . $this->src); 481 | } 482 | if ($this->isURL && (!ALLOW_EXTERNAL)) { 483 | $this->error("You are not allowed to fetch images from an external website."); 484 | 485 | return FALSE; 486 | } 487 | if ($this->isURL) { 488 | 489 | $this->debug(2, "Fetching only from selected external sites is enabled."); 490 | $allowed = FALSE; 491 | foreach ($ALLOWED_SITES as $site) { 492 | if ((strtolower(substr($this->url[ 'host' ], -strlen($site) - 1)) === strtolower(".$site")) || (strtolower($this->url[ 'host' ]) === strtolower($site))) { 493 | $this->debug(3, "URL hostname {$this->url['host']} matches $site so allowing."); 494 | $allowed = TRUE; 495 | } 496 | } 497 | if (!$allowed) { 498 | return $this->error("You may not fetch images from that site. To enable this site in mthumb, you can either add it to \$ALLOWED_SITES and set ALLOW_EXTERNAL=true."); 499 | } 500 | } 501 | 502 | $cachePrefix = ($this->isURL ? '_ext_' : '_int_'); 503 | if ($this->isURL) { 504 | $arr = explode('&', $_SERVER [ 'QUERY_STRING' ]); 505 | asort($arr); 506 | $this->cachefile = $this->cacheDirectory . '/' . FILE_CACHE_PREFIX . $cachePrefix . md5($this->salt . implode('', $arr) . $this->fileCacheVersion) . FILE_CACHE_SUFFIX; 507 | } else { 508 | $this->localImage = $this->getLocalImagePath($this->src); 509 | 510 | if (!$this->localImage) { 511 | $this->debug(1, "Could not find the local image: {$this->localImage}"); 512 | $this->error("Could not find the internal image you specified."); 513 | $this->set404(); 514 | 515 | return FALSE; 516 | } 517 | $this->debug(1, "Local image path is {$this->localImage}"); 518 | $this->localImageMTime = @filemtime($this->localImage); 519 | //We include the mtime of the local file in case in changes on disk. 520 | $this->cachefile = $this->cacheDirectory . '/' . FILE_CACHE_PREFIX . $cachePrefix . md5($this->salt . $this->localImageMTime . $_SERVER [ 'QUERY_STRING' ] . $this->fileCacheVersion) . FILE_CACHE_SUFFIX; 521 | } 522 | $this->debug(2, "Cache file is: " . $this->cachefile); 523 | 524 | return TRUE; 525 | } 526 | 527 | /** 528 | * 529 | */ 530 | public function __destruct() { 531 | foreach ($this->toDeletes as $del) { 532 | $this->debug(2, "Deleting temp file $del"); 533 | @unlink($del); 534 | } 535 | } 536 | 537 | /** 538 | * @return bool 539 | */ 540 | public function run() { 541 | if ($this->isURL) { 542 | if (!ALLOW_EXTERNAL) { 543 | $this->debug(1, "Got a request for an external image but ALLOW_EXTERNAL is disabled so returning error msg."); 544 | $this->error("You are not allowed to fetch images from an external website."); 545 | 546 | return FALSE; 547 | } 548 | $this->debug(3, "Got request for external image. Starting serveExternalImage."); 549 | $this->serveExternalImage(); 550 | } else { 551 | $this->debug(3, "Got request for internal image. Starting serveInternalImage"); 552 | $this->serveInternalImage(); 553 | } 554 | 555 | return TRUE; 556 | } 557 | 558 | /** 559 | * @return bool 560 | */ 561 | protected function handleErrors() { 562 | if ($this->haveErrors()) { 563 | $this->serveErrors(); 564 | exit(0); 565 | } 566 | 567 | return FALSE; 568 | } 569 | 570 | /** 571 | * @return bool 572 | */ 573 | protected function tryBrowserCache() { 574 | if (BROWSER_CACHE_DISABLE) { 575 | $this->debug(3, "Browser caching is disabled"); 576 | 577 | return FALSE; 578 | } 579 | if (!empty($_SERVER[ 'HTTP_IF_MODIFIED_SINCE' ])) { 580 | $this->debug(3, "Got a conditional get"); 581 | $mtime = FALSE; 582 | //We've already checked if the real file exists in the constructor 583 | if (!is_file($this->cachefile)) { 584 | //If we don't have something cached, regenerate the cached image. 585 | return FALSE; 586 | } 587 | if ($this->localImageMTime) { 588 | $mtime = $this->localImageMTime; 589 | $this->debug(3, "Local real file's modification time is $mtime"); 590 | } else { 591 | if (is_file($this->cachefile)) { //If it's not a local request then use the mtime of the cached file to determine the 304 592 | $mtime = @filemtime($this->cachefile); 593 | $this->debug(3, "Cached file's modification time is $mtime"); 594 | } 595 | } 596 | if (!$mtime) { 597 | return FALSE; 598 | } 599 | 600 | $iftime = strtotime($_SERVER[ 'HTTP_IF_MODIFIED_SINCE' ]); 601 | $this->debug(3, "The conditional get's if-modified-since unixtime is $iftime"); 602 | if ($iftime < 1) { 603 | $this->debug(3, "Got an invalid conditional get modified since time. Returning false."); 604 | 605 | return FALSE; 606 | } 607 | // Real file or cache file has been modified since last request, so force refetch. 608 | if ($iftime < $mtime) { 609 | $this->debug(3, "File has been modified since last fetch."); 610 | 611 | return FALSE; 612 | } else { //Otherwise serve a 304 613 | $this->debug(3, "File has not been modified since last get, so serving a 304."); 614 | header($_SERVER[ 'SERVER_PROTOCOL' ] . ' 304 Not Modified'); 615 | $this->debug(1, "Returning 304 not modified"); 616 | 617 | return TRUE; 618 | } 619 | } 620 | 621 | return FALSE; 622 | } 623 | 624 | /** 625 | * @return bool 626 | */ 627 | protected function tryServerCache() { 628 | $this->debug(3, "Trying server cache"); 629 | if (file_exists($this->cachefile)) { 630 | $this->debug(3, "Cachefile {$this->cachefile} exists"); 631 | if ($this->isURL) { 632 | $this->debug(3, "This is an external request, so checking if the cachefile is empty which means the request failed previously."); 633 | if (filesize($this->cachefile) < 1) { 634 | $this->debug(3, "Found an empty cachefile indicating a failed earlier request. Checking how old it is."); 635 | //Fetching error occured previously 636 | if (time() - @filemtime($this->cachefile) > WAIT_BETWEEN_FETCH_ERRORS) { 637 | $this->debug(3, "File is older than " . WAIT_BETWEEN_FETCH_ERRORS . " seconds. Deleting and returning false so app can try and load file."); 638 | @unlink($this->cachefile); 639 | 640 | return FALSE; //to indicate we didn't serve from cache and app should try and load 641 | } else { 642 | $this->debug(3, "Empty cachefile is still fresh so returning message saying we had an error fetching this image from remote host."); 643 | $this->set404(); 644 | $this->error("An error occured fetching image."); 645 | 646 | return FALSE; 647 | } 648 | } 649 | } else { 650 | $this->debug(3, "Trying to serve cachefile {$this->cachefile}"); 651 | } 652 | if ($this->serveCacheFile()) { 653 | $this->debug(3, "Succesfully served cachefile {$this->cachefile}"); 654 | 655 | return TRUE; 656 | } else { 657 | $this->debug(3, "Failed to serve cachefile {$this->cachefile} - Deleting it from cache."); 658 | //Image serving failed. We can't retry at this point, but lets remove it from cache so the next request recreates it 659 | @unlink($this->cachefile); 660 | 661 | return TRUE; 662 | } 663 | } 664 | } 665 | 666 | /** 667 | * @param $err 668 | * 669 | * @return bool 670 | */ 671 | protected function error($err) { 672 | $this->debug(3, "Adding error message: $err"); 673 | $this->errors[] = $err; 674 | 675 | return FALSE; 676 | } 677 | 678 | /** 679 | * @return bool 680 | */ 681 | protected function haveErrors() { 682 | if (sizeof($this->errors) > 0) { 683 | return TRUE; 684 | } 685 | 686 | return FALSE; 687 | } 688 | 689 | /** 690 | * 691 | */ 692 | protected function serveErrors() { 693 | if (!DISPLAY_ERROR_MESSAGES) { 694 | return; 695 | } 696 | header($_SERVER[ 'SERVER_PROTOCOL' ] . ' 400 Bad Request'); 697 | $html = ''; 702 | echo '

An error has occured

The following error(s) occured:
' . $html . '
'; 703 | echo '
Query String: ' . htmlentities($_SERVER[ 'QUERY_STRING' ], ENT_QUOTES); 704 | } 705 | 706 | /** 707 | * @return bool 708 | */ 709 | protected function serveInternalImage() { 710 | $this->debug(3, "Local image path is $this->localImage"); 711 | if (!$this->localImage) { 712 | $this->sanityFail("localImage not set after verifying it earlier in the code."); 713 | 714 | return FALSE; 715 | } 716 | $fileSize = filesize($this->localImage); 717 | if ($fileSize > MAX_FILE_SIZE) { 718 | $this->error("The file you specified is greater than the maximum allowed file size."); 719 | 720 | return FALSE; 721 | } 722 | if ($fileSize <= 0) { 723 | $this->error("The file you specified is <= 0 bytes."); 724 | 725 | return FALSE; 726 | } 727 | $this->debug(3, "Calling processImageAndWriteToCache() for local image."); 728 | if ($this->processImageAndWriteToCache($this->localImage)) { 729 | $this->serveCacheFile(); 730 | 731 | return TRUE; 732 | } else { 733 | return FALSE; 734 | } 735 | } 736 | 737 | /** 738 | * @return bool 739 | */ 740 | protected function serveExternalImage() { 741 | if (!preg_match('/^https?:\/\/[a-zA-Z0-9\-\.]+/i', $this->src)) { 742 | $this->error("Invalid URL supplied."); 743 | 744 | return FALSE; 745 | } 746 | $tempfile = tempnam($this->cacheDirectory, 'mthumb'); 747 | $this->debug(3, "Fetching external image into temporary file $tempfile"); 748 | $this->toDelete($tempfile); 749 | // fetch file here 750 | if (!$this->getURL($this->src, $tempfile)) { 751 | @unlink($this->cachefile); 752 | touch($this->cachefile); 753 | $this->debug(3, "Error fetching URL: " . $this->lastURLError); 754 | $this->error("Error reading the URL you specified from remote host." . $this->lastURLError); 755 | 756 | return FALSE; 757 | } 758 | 759 | $mimeType = $this->getMimeType($tempfile); 760 | if (!preg_match("/^image\/(?:jpg|jpeg|gif|png)$/i", $mimeType)) { 761 | $this->debug(3, "Remote file has invalid mime type: $mimeType"); 762 | @unlink($this->cachefile); 763 | touch($this->cachefile); 764 | $this->error("The remote file is not a valid image. Mimetype = '" . $mimeType . "'" . $tempfile); 765 | 766 | return FALSE; 767 | } 768 | if ($this->processImageAndWriteToCache($tempfile)) { 769 | $this->debug(3, "Image processed succesfully. Serving from cache"); 770 | 771 | return $this->serveCacheFile(); 772 | } else { 773 | return FALSE; 774 | } 775 | } 776 | 777 | /** 778 | * @return bool 779 | */ 780 | protected function cleanCache() { 781 | if (FILE_CACHE_TIME_BETWEEN_CLEANS < 0) { 782 | return; 783 | } 784 | $this->debug(3, "cleanCache() called"); 785 | $lastCleanFile = $this->cacheDirectory . '/mthumb_cacheLastCleanTime.touch'; 786 | 787 | // If the cache dir isn't writable, exit 788 | if (!is_writable($lastCleanFile)) { 789 | return; 790 | } 791 | //If this is a new mthumb installation we need to create the file 792 | if (!is_file($lastCleanFile)) { 793 | $this->debug(1, "File tracking last clean doesn't exist. Creating $lastCleanFile"); 794 | if (!touch($lastCleanFile)) { 795 | $this->error("Could not create cache clean timestamp file."); 796 | } 797 | 798 | return; 799 | } 800 | if (@filemtime($lastCleanFile) < (time() - FILE_CACHE_TIME_BETWEEN_CLEANS)) { //Cache was last cleaned more than 1 day ago 801 | $this->debug(1, "Cache was last cleaned more than " . FILE_CACHE_TIME_BETWEEN_CLEANS . " seconds ago. Cleaning now."); 802 | // Very slight race condition here, but worst case we'll have 2 or 3 servers cleaning the cache simultaneously once a day. 803 | if (!touch($lastCleanFile)) { 804 | $this->error("Could not create cache clean timestamp file."); 805 | } 806 | $files = glob($this->cacheDirectory . '/*' . FILE_CACHE_SUFFIX); 807 | if ($files) { 808 | $timeAgo = time() - FILE_CACHE_MAX_FILE_AGE; 809 | foreach ($files as $file) { 810 | if (@filemtime($file) < $timeAgo) { 811 | $this->debug(3, "Deleting cache file $file older than max age: " . FILE_CACHE_MAX_FILE_AGE . " seconds"); 812 | @unlink($file); 813 | } 814 | } 815 | } 816 | 817 | return TRUE; 818 | } else { 819 | $this->debug(3, "Cache was cleaned less than " . FILE_CACHE_TIME_BETWEEN_CLEANS . " seconds ago so no cleaning needed."); 820 | } 821 | 822 | return FALSE; 823 | } 824 | 825 | /** 826 | * @param $localImage 827 | * 828 | * @return bool 829 | */ 830 | protected function processImageAndWriteToCache($localImage) { 831 | $sData = getimagesize($localImage); 832 | $origType = $sData[ 2 ]; 833 | $mimeType = $sData[ 'mime' ]; 834 | 835 | $this->debug(3, "Mime type of image is $mimeType"); 836 | if (!preg_match('/^image\/(?:gif|jpg|jpeg|png)$/i', $mimeType)) { 837 | return $this->error("The image being resized is not a valid gif, jpg or png."); 838 | } 839 | 840 | if (!function_exists('imagecreatetruecolor')) { 841 | return $this->error('GD Library Error: imagecreatetruecolor does not exist - please contact your webhost and ask them to install the GD library'); 842 | } 843 | 844 | if (function_exists('imagefilter') && defined('IMG_FILTER_NEGATE')) { 845 | $imageFilters = array( 846 | 1 => array(IMG_FILTER_NEGATE, 0), 847 | 2 => array(IMG_FILTER_GRAYSCALE, 0), 848 | 3 => array(IMG_FILTER_BRIGHTNESS, 1), 849 | 4 => array(IMG_FILTER_CONTRAST, 1), 850 | 5 => array(IMG_FILTER_COLORIZE, 4), 851 | 6 => array(IMG_FILTER_EDGEDETECT, 0), 852 | 7 => array(IMG_FILTER_EMBOSS, 0), 853 | 8 => array(IMG_FILTER_GAUSSIAN_BLUR, 0), 854 | 9 => array(IMG_FILTER_SELECTIVE_BLUR, 0), 855 | 10 => array(IMG_FILTER_MEAN_REMOVAL, 0), 856 | 11 => array(IMG_FILTER_SMOOTH, 0), 857 | ); 858 | } 859 | 860 | // get standard input properties 861 | $new_width = (int) abs($this->param('w', 0)); 862 | $new_height = (int) abs($this->param('h', 0)); 863 | $zoom_crop = (int) $this->param('zc', DEFAULT_ZC); 864 | $quality = (int) abs($this->param('q', DEFAULT_Q)); 865 | $align = $this->cropTop ? 't' : $this->param('a', 'c'); 866 | $filters = $this->param('f', DEFAULT_F); 867 | $sharpen = (bool) $this->param('s', DEFAULT_S); 868 | $canvas_color = $this->param('cc', DEFAULT_CC); 869 | $canvas_trans = (bool) $this->param('ct', '1'); 870 | 871 | // set default width and height if neither are set already 872 | if ($new_width == 0 && $new_height == 0) { 873 | $new_width = (int) DEFAULT_WIDTH; 874 | $new_height = (int) DEFAULT_HEIGHT; 875 | } 876 | 877 | // ensure size limits can not be abused 878 | $new_width = min($new_width, MAX_WIDTH); 879 | $new_height = min($new_height, MAX_HEIGHT); 880 | 881 | // open the existing image 882 | $image = $this->openImage($mimeType, $localImage); 883 | if ($image === FALSE) { 884 | return $this->error('Unable to open image.'); 885 | } 886 | 887 | // Get original width and height 888 | $width = imagesx($image); 889 | $height = imagesy($image); 890 | $origin_x = 0; 891 | $origin_y = 0; 892 | 893 | // generate new w/h if not provided 894 | if ($new_width && !$new_height) { 895 | $new_height = floor($height * ($new_width / $width)); 896 | } else { 897 | if ($new_height && !$new_width) { 898 | $new_width = floor($width * ($new_height / $height)); 899 | } 900 | } 901 | 902 | // scale down and add borders 903 | if ($zoom_crop == 3) { 904 | 905 | $final_height = $height * ($new_width / $width); 906 | 907 | if ($final_height > $new_height) { 908 | $new_width = $width * ($new_height / $height); 909 | } else { 910 | $new_height = $final_height; 911 | } 912 | } 913 | 914 | // create a new true color image 915 | $canvas = imagecreatetruecolor($new_width, $new_height); 916 | imagealphablending($canvas, FALSE); 917 | 918 | if (strlen($canvas_color) == 3) { //if is 3-char notation, edit string into 6-char notation 919 | $canvas_color = str_repeat(substr($canvas_color, 0, 1), 2) . str_repeat(substr($canvas_color, 1, 1), 2) . str_repeat(substr($canvas_color, 2, 1), 2); 920 | } else { 921 | if (strlen($canvas_color) != 6) { 922 | $canvas_color = DEFAULT_CC; // on error return default canvas color 923 | } 924 | } 925 | 926 | $canvas_color_R = hexdec(substr($canvas_color, 0, 2)); 927 | $canvas_color_G = hexdec(substr($canvas_color, 2, 2)); 928 | $canvas_color_B = hexdec(substr($canvas_color, 4, 2)); 929 | 930 | // Create a new transparent color for image 931 | // If is a png and PNG_IS_TRANSPARENT is false then remove the alpha transparency 932 | // (and if is set a canvas color show it in the background) 933 | if (preg_match('/^image\/png$/i', $mimeType) && !PNG_IS_TRANSPARENT && $canvas_trans) { 934 | $color = imagecolorallocatealpha($canvas, $canvas_color_R, $canvas_color_G, $canvas_color_B, 127); 935 | } else { 936 | $color = imagecolorallocatealpha($canvas, $canvas_color_R, $canvas_color_G, $canvas_color_B, 0); 937 | } 938 | 939 | // Completely fill the background of the new image with allocated color. 940 | imagefill($canvas, 0, 0, $color); 941 | 942 | // scale down and add borders 943 | if ($zoom_crop == 2) { 944 | 945 | $final_height = $height * ($new_width / $width); 946 | 947 | if ($final_height > $new_height) { 948 | 949 | $origin_x = $new_width / 2; 950 | $new_width = $width * ($new_height / $height); 951 | $origin_x = round($origin_x - ($new_width / 2)); 952 | } else { 953 | 954 | $origin_y = $new_height / 2; 955 | $new_height = $final_height; 956 | $origin_y = round($origin_y - ($new_height / 2)); 957 | } 958 | } 959 | 960 | // Restore transparency blending 961 | imagesavealpha($canvas, TRUE); 962 | 963 | if ($zoom_crop > 0) { 964 | 965 | $src_x = $src_y = 0; 966 | $src_w = $width; 967 | $src_h = $height; 968 | 969 | $cmp_x = $width / $new_width; 970 | $cmp_y = $height / $new_height; 971 | 972 | // calculate x or y coordinate and width or height of source 973 | if ($cmp_x > $cmp_y) { 974 | 975 | $src_w = round($width / $cmp_x * $cmp_y); 976 | $src_x = round(($width - ($width / $cmp_x * $cmp_y)) / 2); 977 | } else { 978 | if ($cmp_y > $cmp_x) { 979 | 980 | $src_h = round($height / $cmp_y * $cmp_x); 981 | $src_y = round(($height - ($height / $cmp_y * $cmp_x)) / 2); 982 | } 983 | } 984 | 985 | // positional cropping! 986 | if ($align) { 987 | if (strpos($align, 't') !== FALSE) { 988 | $src_y = 0; 989 | } 990 | if (strpos($align, 'b') !== FALSE) { 991 | $src_y = $height - $src_h; 992 | } 993 | if (strpos($align, 'l') !== FALSE) { 994 | $src_x = 0; 995 | } 996 | if (strpos($align, 'r') !== FALSE) { 997 | $src_x = $width - $src_w; 998 | } 999 | } 1000 | 1001 | imagecopyresampled($canvas, $image, $origin_x, $origin_y, $src_x, $src_y, $new_width, $new_height, $src_w, $src_h); 1002 | } else { 1003 | 1004 | // copy and resize part of an image with resampling 1005 | imagecopyresampled($canvas, $image, 0, 0, 0, 0, $new_width, $new_height, $width, $height); 1006 | } 1007 | 1008 | if ($filters != '' && function_exists('imagefilter') && defined('IMG_FILTER_NEGATE')) { 1009 | // apply filters to image 1010 | $filterList = explode('|', $filters); 1011 | foreach ($filterList as $fl) { 1012 | 1013 | $filterSettings = explode(',', $fl); 1014 | if (isset ($imageFilters[ $filterSettings[ 0 ] ])) { 1015 | 1016 | for ($i = 0; $i < 4; $i++) { 1017 | if (!isset ($filterSettings[ $i ])) { 1018 | $filterSettings[ $i ] = NULL; 1019 | } else { 1020 | $filterSettings[ $i ] = (int) $filterSettings[ $i ]; 1021 | } 1022 | } 1023 | 1024 | switch ($imageFilters[ $filterSettings[ 0 ] ][ 1 ]) { 1025 | 1026 | case 1: 1027 | 1028 | imagefilter($canvas, $imageFilters[ $filterSettings[ 0 ] ][ 0 ], $filterSettings[ 1 ]); 1029 | break; 1030 | 1031 | case 2: 1032 | 1033 | imagefilter($canvas, $imageFilters[ $filterSettings[ 0 ] ][ 0 ], $filterSettings[ 1 ], $filterSettings[ 2 ]); 1034 | break; 1035 | 1036 | case 3: 1037 | 1038 | imagefilter($canvas, $imageFilters[ $filterSettings[ 0 ] ][ 0 ], $filterSettings[ 1 ], $filterSettings[ 2 ], $filterSettings[ 3 ]); 1039 | break; 1040 | 1041 | case 4: 1042 | 1043 | imagefilter($canvas, $imageFilters[ $filterSettings[ 0 ] ][ 0 ], $filterSettings[ 1 ], $filterSettings[ 2 ], $filterSettings[ 3 ], $filterSettings[ 4 ]); 1044 | break; 1045 | 1046 | default: 1047 | 1048 | imagefilter($canvas, $imageFilters[ $filterSettings[ 0 ] ][ 0 ]); 1049 | break; 1050 | } 1051 | } 1052 | } 1053 | } 1054 | 1055 | // sharpen image 1056 | if ($sharpen && function_exists('imageconvolution')) { 1057 | 1058 | $sharpenMatrix = array( 1059 | array(-1, -1, -1), 1060 | array(-1, 16, -1), 1061 | array(-1, -1, -1), 1062 | ); 1063 | 1064 | $divisor = 8; 1065 | $offset = 0; 1066 | 1067 | imageconvolution($canvas, $sharpenMatrix, $divisor, $offset); 1068 | } 1069 | //Straight from Wordpress core code. Reduces filesize by up to 70% for PNG's 1070 | if ((IMAGETYPE_PNG == $origType || IMAGETYPE_GIF == $origType) && function_exists('imageistruecolor') && !imageistruecolor($image) && imagecolortransparent($image) > 0) { 1071 | imagetruecolortopalette($canvas, FALSE, imagecolorstotal($image)); 1072 | } 1073 | 1074 | $tempfile = tempnam($this->cacheDirectory, 'mthumb_tmpimg_'); 1075 | if (preg_match('/^image\/(?:jpg|jpeg)$/i', $mimeType)) { 1076 | $imgType = 'jpg'; 1077 | imagejpeg($canvas, $tempfile, $quality); 1078 | } else { 1079 | if (preg_match('/^image\/png$/i', $mimeType)) { 1080 | $imgType = 'png'; 1081 | imagepng($canvas, $tempfile, floor($quality * 0.09)); 1082 | } else { 1083 | if (preg_match('/^image\/gif$/i', $mimeType)) { 1084 | $imgType = 'gif'; 1085 | imagegif($canvas, $tempfile); 1086 | } else { 1087 | return $this->sanityFail("Could not match mime type after verifying it previously."); 1088 | } 1089 | } 1090 | } 1091 | 1092 | if ($imgType == 'png' && OPTIPNG_ENABLED && OPTIPNG_PATH && @is_file(OPTIPNG_PATH)) { 1093 | $exec = OPTIPNG_PATH; 1094 | $this->debug(3, "optipng'ing $tempfile"); 1095 | $presize = filesize($tempfile); 1096 | $out = `$exec -o1 $tempfile`; //you can use up to -o7 but it really slows things down 1097 | clearstatcache(); 1098 | $aftersize = filesize($tempfile); 1099 | $sizeDrop = $presize - $aftersize; 1100 | if ($sizeDrop > 0) { 1101 | $this->debug(1, "optipng reduced size by $sizeDrop"); 1102 | } else { 1103 | if ($sizeDrop < 0) { 1104 | $this->debug(1, "optipng increased size! Difference was: $sizeDrop"); 1105 | } else { 1106 | $this->debug(1, "optipng did not change image size."); 1107 | } 1108 | } 1109 | } else { 1110 | if ($imgType == 'png' && PNGCRUSH_ENABLED && PNGCRUSH_PATH && @is_file(PNGCRUSH_PATH)) { 1111 | $exec = PNGCRUSH_PATH; 1112 | $tempfile2 = tempnam($this->cacheDirectory, 'mthumb_tmpimg_'); 1113 | $this->debug(3, "pngcrush'ing $tempfile to $tempfile2"); 1114 | $out = `$exec $tempfile $tempfile2`; 1115 | 1116 | if (is_file($tempfile2)) { 1117 | $sizeDrop = filesize($tempfile) - filesize($tempfile2); 1118 | if ($sizeDrop > 0) { 1119 | $this->debug(1, "pngcrush was succesful and gave a $sizeDrop byte size reduction"); 1120 | $todel = $tempfile; 1121 | $tempfile = $tempfile2; 1122 | } else { 1123 | $this->debug(1, "pngcrush did not reduce file size. Difference was $sizeDrop bytes."); 1124 | $todel = $tempfile2; 1125 | } 1126 | } else { 1127 | $this->debug(3, "pngcrush failed with output: $out"); 1128 | $todel = $tempfile2; 1129 | } 1130 | @unlink($todel); 1131 | } 1132 | } 1133 | 1134 | $this->debug(3, "Rewriting image with security header."); 1135 | $tempfile4 = tempnam($this->cacheDirectory, 'mthumb_tmpimg_'); 1136 | $context = stream_context_create(); 1137 | $fp = fopen($tempfile, 'r', 0, $context); 1138 | file_put_contents($tempfile4, $this->filePrependSecurityBlock . $imgType . ' ?' . '>'); //6 extra bytes, first 3 being image type 1139 | file_put_contents($tempfile4, $fp, FILE_APPEND); 1140 | fclose($fp); 1141 | @unlink($tempfile); 1142 | $this->debug(3, "Locking and replacing cache file."); 1143 | $lockFile = $this->cachefile . '.lock'; 1144 | $fh = fopen($lockFile, 'w'); 1145 | if (!$fh) { 1146 | return $this->error("Could not open the lockfile for writing an image."); 1147 | } 1148 | if (flock($fh, LOCK_EX)) { 1149 | @unlink($this->cachefile); //rename generally overwrites, but doing this in case of platform specific quirks. File might not exist yet. 1150 | rename($tempfile4, $this->cachefile); 1151 | flock($fh, LOCK_UN); 1152 | fclose($fh); 1153 | @unlink($lockFile); 1154 | } else { 1155 | fclose($fh); 1156 | @unlink($lockFile); 1157 | @unlink($tempfile4); 1158 | 1159 | return $this->error("Could not get a lock for writing."); 1160 | } 1161 | $this->debug(3, "Done image replace with security header. Cleaning up and running cleanCache()"); 1162 | imagedestroy($canvas); 1163 | imagedestroy($image); 1164 | 1165 | return TRUE; 1166 | } 1167 | 1168 | /** 1169 | * 1170 | */ 1171 | protected function calcDocRoot() { 1172 | $docRoot = @$_SERVER[ 'DOCUMENT_ROOT' ]; 1173 | if (defined('LOCAL_FILE_BASE_DIRECTORY')) { 1174 | $docRoot = LOCAL_FILE_BASE_DIRECTORY; 1175 | } 1176 | if (!isset($docRoot)) { 1177 | $this->debug(3, "DOCUMENT_ROOT is not set. This is probably windows. Starting search 1."); 1178 | if (isset($_SERVER[ 'SCRIPT_FILENAME' ])) { 1179 | $docRoot = str_replace('\\', '/', substr($_SERVER[ 'SCRIPT_FILENAME' ], 0, 0 - strlen($_SERVER[ 'PHP_SELF' ]))); 1180 | $this->debug(3, "Generated docRoot using SCRIPT_FILENAME and PHP_SELF as: $docRoot"); 1181 | } 1182 | } 1183 | if (!isset($docRoot)) { 1184 | $this->debug(3, "DOCUMENT_ROOT still is not set. Starting search 2."); 1185 | if (isset($_SERVER[ 'PATH_TRANSLATED' ])) { 1186 | $docRoot = str_replace('\\', '/', substr(str_replace('\\\\', '\\', $_SERVER[ 'PATH_TRANSLATED' ]), 0, 0 - strlen($_SERVER[ 'PHP_SELF' ]))); 1187 | $this->debug(3, "Generated docRoot using PATH_TRANSLATED and PHP_SELF as: $docRoot"); 1188 | } 1189 | } 1190 | if ($docRoot && $_SERVER[ 'DOCUMENT_ROOT' ] != '/') { 1191 | $docRoot = preg_replace('/\/$/', '', $docRoot); 1192 | } 1193 | $this->debug(3, "Doc root is: " . $docRoot); 1194 | $this->docRoot = $docRoot; 1195 | } 1196 | 1197 | /** 1198 | * @param $src 1199 | * 1200 | * @return bool|string 1201 | */ 1202 | protected function getLocalImagePath($src) { 1203 | $src = ltrim($src, '/'); //strip off the leading '/' 1204 | if (!$this->docRoot) { 1205 | $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."); 1206 | //We don't support serving images outside the current dir if we don't have a doc root for security reasons. 1207 | $file = preg_replace('/^.*?([^\/\\\\]+)$/', '$1', $src); //strip off any path info and just leave the filename. 1208 | if (is_file($file)) { 1209 | return $this->realpath($file); 1210 | } 1211 | 1212 | return $this->error("Could not find your website document root and the file specified doesn't exist in mThumb's directory. We don't support serving files outside mThumb's directory without a document root for security reasons."); 1213 | } else { 1214 | if (!is_dir($this->docRoot)) { 1215 | $this->error("Server path does not exist. Ensure variable \$_SERVER['DOCUMENT_ROOT'] is set correctly"); 1216 | } 1217 | } 1218 | 1219 | //Do not go past this point without docRoot set 1220 | 1221 | //Try src under docRoot 1222 | if (file_exists($this->docRoot . '/' . $src)) { 1223 | $this->debug(3, "Found file as " . $this->docRoot . '/' . $src); 1224 | $real = $this->realpath($this->docRoot . '/' . $src); 1225 | if (stripos($real, $this->docRoot) === 0) { 1226 | return $real; 1227 | } else { 1228 | $this->debug(1, "Security block: The file specified occurs outside the document root."); 1229 | //allow search to continue 1230 | } 1231 | } 1232 | //Check absolute paths and then verify the real path is under doc root 1233 | $absolute = $this->realpath('/' . $src); 1234 | if ($absolute && file_exists($absolute)) { //realpath does file_exists check, so can probably skip the exists check here 1235 | $this->debug(3, "Found absolute path: $absolute"); 1236 | if (!$this->docRoot) { 1237 | $this->sanityFail("docRoot not set when checking absolute path."); 1238 | } 1239 | if (stripos($absolute, $this->docRoot) === 0) { 1240 | return $absolute; 1241 | } else { 1242 | $this->debug(1, "Security block: The file specified occurs outside the document root."); 1243 | //and continue search 1244 | } 1245 | } 1246 | 1247 | $base = $this->docRoot; 1248 | 1249 | // account for Windows directory structure 1250 | if (strstr($_SERVER[ 'SCRIPT_FILENAME' ], ':')) { 1251 | $sub_directories = explode('\\', str_replace($this->docRoot, '', $_SERVER[ 'SCRIPT_FILENAME' ])); 1252 | } else { 1253 | $sub_directories = explode('/', str_replace($this->docRoot, '', $_SERVER[ 'SCRIPT_FILENAME' ])); 1254 | } 1255 | 1256 | foreach ($sub_directories as $sub) { 1257 | $base .= $sub . '/'; 1258 | $this->debug(3, "Trying file as: " . $base . $src); 1259 | if (file_exists($base . $src)) { 1260 | $this->debug(3, "Found file as: " . $base . $src); 1261 | $real = $this->realpath($base . $src); 1262 | if (stripos($real, $this->realpath($this->docRoot)) === 0) { 1263 | return $real; 1264 | } else { 1265 | $this->debug(1, "Security block: The file specified occurs outside the document root."); 1266 | //And continue search 1267 | } 1268 | } 1269 | } 1270 | 1271 | return FALSE; 1272 | } 1273 | 1274 | /** 1275 | * @param $path 1276 | * 1277 | * @return string 1278 | */ 1279 | protected function realpath($path) { 1280 | // try to remove any relative paths 1281 | $remove_relatives = '/\w+\/\.\.\//'; 1282 | while (preg_match($remove_relatives, $path)) { 1283 | $path = preg_replace($remove_relatives, '', $path); 1284 | } 1285 | // if any remain use PHP realpath to strip them out, otherwise return $path 1286 | // if using realpath, any symlinks will also be resolved 1287 | return preg_match('#^\.\./|/\.\./#', $path) ? realpath($path) : $path; 1288 | } 1289 | 1290 | /** 1291 | * @param $name 1292 | */ 1293 | protected function toDelete($name) { 1294 | $this->debug(3, "Scheduling file $name to delete on destruct."); 1295 | $this->toDeletes[] = $name; 1296 | } 1297 | 1298 | /** 1299 | * @param $h 1300 | * @param $d 1301 | * 1302 | * @return int 1303 | */ 1304 | public static function curlWrite($h, $d) { 1305 | fwrite(self::$curlFH, $d); 1306 | self::$curlDataWritten += strlen($d); 1307 | if (self::$curlDataWritten > MAX_FILE_SIZE) { 1308 | return 0; 1309 | } else { 1310 | return strlen($d); 1311 | } 1312 | } 1313 | 1314 | /** 1315 | * @return bool 1316 | */ 1317 | protected function serveCacheFile() { 1318 | $this->debug(3, "Serving {$this->cachefile}"); 1319 | if (!is_file($this->cachefile)) { 1320 | $this->error("serveCacheFile called in mthumb but we couldn't find the cached file."); 1321 | 1322 | return FALSE; 1323 | } 1324 | $fp = fopen($this->cachefile, 'rb'); 1325 | if (!$fp) { 1326 | return $this->error("Could not open cachefile."); 1327 | } 1328 | fseek($fp, strlen($this->filePrependSecurityBlock), SEEK_SET); 1329 | $imgType = fread($fp, 3); 1330 | fseek($fp, 3, SEEK_CUR); 1331 | if (ftell($fp) != strlen($this->filePrependSecurityBlock) + 6) { 1332 | @unlink($this->cachefile); 1333 | 1334 | return $this->error("The cached image file seems to be corrupt."); 1335 | } 1336 | $imageDataSize = filesize($this->cachefile) - (strlen($this->filePrependSecurityBlock) + 6); 1337 | $this->sendImageHeaders($imgType, $imageDataSize); 1338 | $bytesSent = @fpassthru($fp); 1339 | fclose($fp); 1340 | if ($bytesSent > 0) { 1341 | return TRUE; 1342 | } 1343 | $content = file_get_contents($this->cachefile); 1344 | if ($content != FALSE) { 1345 | $content = substr($content, strlen($this->filePrependSecurityBlock) + 6); 1346 | echo $content; 1347 | $this->debug(3, "Served using file_get_contents and echo"); 1348 | 1349 | return TRUE; 1350 | } else { 1351 | $this->error("Cache file could not be loaded."); 1352 | 1353 | return FALSE; 1354 | } 1355 | } 1356 | 1357 | /** 1358 | * @param $mimeType 1359 | * @param $dataSize 1360 | * 1361 | * @return bool 1362 | */ 1363 | protected function sendImageHeaders($mimeType, $dataSize) { 1364 | if (!preg_match('/^image\//i', $mimeType)) { 1365 | $mimeType = 'image/' . $mimeType; 1366 | } 1367 | if (strtolower($mimeType) == 'image/jpg') { 1368 | $mimeType = 'image/jpeg'; 1369 | } 1370 | $gmdate_expires = gmdate('D, d M Y H:i:s', strtotime('now +10 days')) . ' GMT'; 1371 | $gmdate_modified = gmdate('D, d M Y H:i:s') . ' GMT'; 1372 | // send content headers then display image 1373 | header('Content-Type: ' . $mimeType); 1374 | header('Accept-Ranges: none'); //Changed this because we don't accept range requests 1375 | header('Last-Modified: ' . $gmdate_modified); 1376 | header('Content-Length: ' . $dataSize); 1377 | if (BROWSER_CACHE_DISABLE) { 1378 | $this->debug(3, "Browser cache is disabled so setting non-caching headers."); 1379 | header('Cache-Control: no-store, no-cache, must-revalidate, max-age=0'); 1380 | header("Pragma: no-cache"); 1381 | header('Expires: ' . gmdate('D, d M Y H:i:s', time())); 1382 | } else { 1383 | $this->debug(3, "Browser caching is enabled"); 1384 | header('Cache-Control: max-age=' . BROWSER_CACHE_MAX_AGE . ', must-revalidate'); 1385 | header('Expires: ' . $gmdate_expires); 1386 | } 1387 | 1388 | return TRUE; 1389 | } 1390 | 1391 | /** 1392 | * @param $property 1393 | * @param string $default 1394 | * 1395 | * @return string 1396 | */ 1397 | protected function param($property, $default = '') { 1398 | if (isset ($_GET[ $property ])) { 1399 | return $_GET[ $property ]; 1400 | } else { 1401 | return $default; 1402 | } 1403 | } 1404 | 1405 | /** 1406 | * @param $mimeType 1407 | * @param $src 1408 | * 1409 | * @return resource 1410 | */ 1411 | protected function openImage($mimeType, $src) { 1412 | switch ($mimeType) { 1413 | case 'image/jpeg': 1414 | $image = imagecreatefromjpeg($src); 1415 | break; 1416 | 1417 | case 'image/png': 1418 | $image = imagecreatefrompng($src); 1419 | imagealphablending($image, TRUE); 1420 | imagesavealpha($image, TRUE); 1421 | break; 1422 | 1423 | case 'image/gif': 1424 | $image = imagecreatefromgif($src); 1425 | break; 1426 | 1427 | default: 1428 | $this->error("Unrecognised mimeType"); 1429 | } 1430 | 1431 | return $image; 1432 | } 1433 | 1434 | /** 1435 | * @return string 1436 | */ 1437 | protected function getIP() { 1438 | $rem = @$_SERVER[ "REMOTE_ADDR" ]; 1439 | $ff = @$_SERVER[ "HTTP_X_FORWARDED_FOR" ]; 1440 | $ci = @$_SERVER[ "HTTP_CLIENT_IP" ]; 1441 | if (preg_match('/^(?:192\.168|172\.16|10\.|127\.)/', $rem)) { 1442 | if ($ff) { 1443 | return $ff; 1444 | } 1445 | if ($ci) { 1446 | return $ci; 1447 | } 1448 | 1449 | return $rem; 1450 | } else { 1451 | if ($rem) { 1452 | return $rem; 1453 | } 1454 | if ($ff) { 1455 | return $ff; 1456 | } 1457 | if ($ci) { 1458 | return $ci; 1459 | } 1460 | 1461 | return "UNKNOWN"; 1462 | } 1463 | } 1464 | 1465 | /** 1466 | * @param $level 1467 | * @param $msg 1468 | */ 1469 | protected function debug($level, $msg) { 1470 | if (DEBUG_ON && $level <= DEBUG_LEVEL) { 1471 | $execTime = sprintf('%.6f', microtime(TRUE) - $this->startTime); 1472 | $tick = sprintf('%.6f', 0); 1473 | if ($this->lastBenchTime > 0) { 1474 | $tick = sprintf('%.6f', microtime(TRUE) - $this->lastBenchTime); 1475 | } 1476 | $this->lastBenchTime = microtime(TRUE); 1477 | error_log("mThumb Debug line " . __LINE__ . " [$execTime : $tick]: $msg"); 1478 | } 1479 | } 1480 | 1481 | /** 1482 | * @param $msg 1483 | * 1484 | * @return bool 1485 | */ 1486 | protected function sanityFail($msg) { 1487 | return $this->error("There is a problem in the mThumb code. Message: Please report this error at mThumb's issue tracking page: $msg"); 1488 | } 1489 | 1490 | /** 1491 | * @param $file 1492 | * 1493 | * @return string 1494 | */ 1495 | protected function getMimeType($file) { 1496 | $info = getimagesize($file); 1497 | if (is_array($info) && $info[ 'mime' ]) { 1498 | return $info[ 'mime' ]; 1499 | } 1500 | 1501 | return ''; 1502 | } 1503 | 1504 | /** 1505 | * @param $size_str 1506 | * 1507 | * @return int 1508 | */ 1509 | protected static function returnBytes($size_str) { 1510 | switch (substr($size_str, -1)) { 1511 | case 'M': 1512 | case 'm': 1513 | return (int) $size_str * 1048576; 1514 | case 'K': 1515 | case 'k': 1516 | return (int) $size_str * 1024; 1517 | case 'G': 1518 | case 'g': 1519 | return (int) $size_str * 1073741824; 1520 | default: 1521 | return $size_str; 1522 | } 1523 | } 1524 | 1525 | /** 1526 | * @param $url 1527 | * @param $tempfile 1528 | * 1529 | * @return bool 1530 | */ 1531 | protected function getURL($url, $tempfile) { 1532 | $this->lastURLError = FALSE; 1533 | $url = preg_replace('/ /', '%20', $url); 1534 | if (function_exists('curl_init')) { 1535 | $this->debug(3, "Curl is installed so using it to fetch URL."); 1536 | self::$curlFH = fopen($tempfile, 'w'); 1537 | if (!self::$curlFH) { 1538 | $this->error("Could not open $tempfile for writing."); 1539 | 1540 | return FALSE; 1541 | } 1542 | self::$curlDataWritten = 0; 1543 | $this->debug(3, "Fetching url with curl: $url"); 1544 | $curl = curl_init($url); 1545 | curl_setopt($curl, CURLOPT_TIMEOUT, CURL_TIMEOUT); 1546 | 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"); 1547 | curl_setopt($curl, CURLOPT_RETURNTRANSFER, TRUE); 1548 | curl_setopt($curl, CURLOPT_HEADER, 0); 1549 | curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE); 1550 | curl_setopt($curl, CURLOPT_WRITEFUNCTION, 'mthumb::curlWrite'); 1551 | @curl_setopt($curl, CURLOPT_FOLLOWLOCATION, TRUE); 1552 | @curl_setopt($curl, CURLOPT_MAXREDIRS, 10); 1553 | 1554 | $curlResult = curl_exec($curl); 1555 | fclose(self::$curlFH); 1556 | $httpStatus = curl_getinfo($curl, CURLINFO_HTTP_CODE); 1557 | if ($httpStatus == 404) { 1558 | $this->set404(); 1559 | } 1560 | if ($httpStatus == 302) { 1561 | $this->error("External Image is Redirecting. Try alternate image URL."); 1562 | 1563 | return FALSE; 1564 | } 1565 | if ($curlResult) { 1566 | curl_close($curl); 1567 | 1568 | return TRUE; 1569 | } else { 1570 | $this->lastURLError = curl_error($curl); 1571 | curl_close($curl); 1572 | 1573 | return FALSE; 1574 | } 1575 | } else { 1576 | $img = @file_get_contents($url); 1577 | if ($img === FALSE) { 1578 | $err = error_get_last(); 1579 | if (is_array($err) && $err[ 'message' ]) { 1580 | $this->lastURLError = $err[ 'message' ]; 1581 | } else { 1582 | $this->lastURLError = $err; 1583 | } 1584 | if (preg_match('/404/', $this->lastURLError)) { 1585 | $this->set404(); 1586 | } 1587 | 1588 | return FALSE; 1589 | } 1590 | if (!file_put_contents($tempfile, $img)) { 1591 | $this->error("Could not write to $tempfile."); 1592 | 1593 | return FALSE; 1594 | } 1595 | 1596 | return TRUE; 1597 | } 1598 | } 1599 | 1600 | /** 1601 | * @param $file 1602 | * 1603 | * @return bool 1604 | */ 1605 | protected function serveImg($file) { 1606 | $s = getimagesize($file); 1607 | if (!($s && $s[ 'mime' ])) { 1608 | return FALSE; 1609 | } 1610 | header('Content-Type: ' . $s[ 'mime' ]); 1611 | header('Content-Length: ' . filesize($file)); 1612 | header('Cache-Control: no-store, no-cache, must-revalidate, max-age=0'); 1613 | header("Pragma: no-cache"); 1614 | $bytes = @readfile($file); 1615 | if ($bytes > 0) { 1616 | return TRUE; 1617 | } 1618 | $content = @file_get_contents($file); 1619 | if ($content != FALSE) { 1620 | echo $content; 1621 | 1622 | return TRUE; 1623 | } 1624 | 1625 | return FALSE; 1626 | } 1627 | 1628 | /** 1629 | * 1630 | */ 1631 | protected function set404() { 1632 | $this->is404 = TRUE; 1633 | } 1634 | 1635 | /** 1636 | * @return bool 1637 | */ 1638 | protected function is404() { 1639 | return $this->is404; 1640 | } 1641 | } 1642 | } 1643 | endif; 1644 | 1645 | mthumb::start(); 1646 | -------------------------------------------------------------------------------- /index.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Gallery from Folder Demo 6 | 17 | 18 | 19 | 20 | 33 | 34 | 35 | 36 | --------------------------------------------------------------------------------