<?php
/*
* CKFinder
* ========
* http://ckfinder.com
* Copyright (C) 2007-2011, CKSource - Frederico Knabben. All rights reserved.
*
* The software, this file and its contents are subject to the CKFinder
* License. Please read the license.txt file before using, installing, copying,
* modifying or distribute this file or part of its contents. The contents of
* this file is part of the Source Code of CKFinder.
*/
if (!defined('IN_CKFINDER')) exit;

/**
 * @package CKFinder
 * @subpackage CommandHandlers
 * @copyright CKSource - Frederico Knabben
 */

/**
 * Handle Thumbnail command (create thumbnail if doesn't exist)
 *
 * @package CKFinder
 * @subpackage CommandHandlers
 * @copyright CKSource - Frederico Knabben
 */
class CKFinder_Connector_CommandHandler_Thumbnail extends CKFinder_Connector_CommandHandler_CommandHandlerBase
{
        /**
         * Command name
         *
         * @access private
         * @var string
         */
        private $command = "Thumbnail";

        /**
         * handle request and send response
         * @access public
         *
         */
        public function sendResponse()
        {
                // Get rid of BOM markers
                if (ob_get_level()) {
                        while (@ob_end_clean() && ob_get_level());
                }
                header("Content-Encoding: none");

                $this->checkConnector();
                $this->checkRequest();

                $_config =& CKFinder_Connector_Core_Factory::getInstance("Core_Config");

                $_thumbnails = $_config->getThumbnailsConfig();
                if (!$_thumbnails->getIsEnabled()) {
                        $this->_errorHandler->throwError(CKFINDER_CONNECTOR_ERROR_THUMBNAILS_DISABLED);
                }

                if (!$this->_currentFolder->checkAcl(CKFINDER_CONNECTOR_ACL_FILE_VIEW)) {
                        $this->_errorHandler->throwError(CKFINDER_CONNECTOR_ERROR_UNAUTHORIZED);
                }

                if (!isset($_GET["FileName"])) {
                        $this->_errorHandler->throwError(CKFINDER_CONNECTOR_ERROR_INVALID_REQUEST);
                }

                $fileName = CKFinder_Connector_Utils_FileSystem::convertToFilesystemEncoding($_GET["FileName"]);
                $_resourceTypeInfo = $this->_currentFolder->getResourceTypeConfig();

                if (!CKFinder_Connector_Utils_FileSystem::checkFileName($fileName)) {
                        $this->_errorHandler->throwError(CKFINDER_CONNECTOR_ERROR_INVALID_REQUEST);
                }

                $sourceFilePath = CKFinder_Connector_Utils_FileSystem::combinePaths($this->_currentFolder->getServerPath(), $fileName);

                if ($_resourceTypeInfo->checkIsHiddenFile($fileName) || !file_exists($sourceFilePath)) {
                        $this->_errorHandler->throwError(CKFINDER_CONNECTOR_ERROR_FILE_NOT_FOUND);
                }

                $thumbFilePath = CKFinder_Connector_Utils_FileSystem::combinePaths($this->_currentFolder->getThumbsServerPath(), $fileName);

                // If the thumbnail file doesn't exists, create it now.
                if (!file_exists($thumbFilePath)) {
                        if(!$this->createThumb($sourceFilePath, $thumbFilePath, $_thumbnails->getMaxWidth(), $_thumbnails->getMaxHeight(), $_thumbnails->getQuality(), true, $_thumbnails->getBmpSupported())) {
                                $this->_errorHandler->throwError(CKFINDER_CONNECTOR_ERROR_ACCESS_DENIED);
                        }
                }

                $size = filesize($thumbFilePath);
                $sourceImageAttr = getimagesize($thumbFilePath);
                $mime = $sourceImageAttr["mime"];

                $rtime = isset($_SERVER["HTTP_IF_MODIFIED_SINCE"])?@strtotime($_SERVER["HTTP_IF_MODIFIED_SINCE"]):0;
                $mtime =    filemtime($thumbFilePath);
                $etag = dechex($mtime) . "-" . dechex($size);

                $is304 = false;

                if (isset($_SERVER["HTTP_IF_NONE_MATCH"]) && $_SERVER["HTTP_IF_NONE_MATCH"] === $etag) {
                        $is304 = true;
                }
                else if($rtime == $mtime) {
                        $is304 = true;
                }

                if ($is304) {
                        header("HTTP/1.0 304 Not Modified");
                        exit();
                }

                //header("Cache-Control: cache, must-revalidate");
                //header("Pragma: public");
                //header("Expires: 0");
                header('Cache-control: public');
                header('Etag: ' . $etag);
                header("Content-type: " . $mime . "; name=\"" . CKFinder_Connector_Utils_Misc::mbBasename($thumbFilePath) . "\"");
                header("Last-Modified: ".gmdate('D, d M Y H:i:s', $mtime) . " GMT");
                //header("Content-type: application/octet-stream; name=\"{$file}\"");
                //header("Content-Disposition: attachment; filename=\"{$file}\"");
                header("Content-Length: ".$size);
                readfile($thumbFilePath);
                exit;
        }

        /**
         * Create thumbnail
         *
         * @param string $sourceFile
         * @param string $targetFile
         * @param int $maxWidth
         * @param int $maxHeight
         * @param boolean $preserverAspectRatio
         * @param boolean $bmpSupported
         * @return boolean
         * @static
         * @access public
         */
        public static function createThumb($sourceFile, $targetFile, $maxWidth, $maxHeight, $quality, $preserverAspectRatio, $bmpSupported = false)
        {
                $sourceImageAttr = @getimagesize($sourceFile);
                if ($sourceImageAttr === false) {
                        return false;
                }
                $sourceImageWidth = isset($sourceImageAttr[0]) ? $sourceImageAttr[0] : 0;
                $sourceImageHeight = isset($sourceImageAttr[1]) ? $sourceImageAttr[1] : 0;
                $sourceImageMime = isset($sourceImageAttr["mime"]) ? $sourceImageAttr["mime"] : "";
                $sourceImageBits = isset($sourceImageAttr["bits"]) ? $sourceImageAttr["bits"] : 8;
                $sourceImageChannels = isset($sourceImageAttr["channels"]) ? $sourceImageAttr["channels"] : 3;

                if (!$sourceImageWidth || !$sourceImageHeight || !$sourceImageMime) {
                        return false;
                }

                $iFinalWidth = $maxWidth == 0 ? $sourceImageWidth : $maxWidth;
                $iFinalHeight = $maxHeight == 0 ? $sourceImageHeight : $maxHeight;

                if ($sourceImageWidth <= $iFinalWidth && $sourceImageHeight <= $iFinalHeight) {
                        if ($sourceFile != $targetFile) {
                                copy($sourceFile, $targetFile);
                        }
                        return true;
                }

                if ($preserverAspectRatio)
                {
                        // Gets the best size for aspect ratio resampling
                        $oSize = CKFinder_Connector_CommandHandler_Thumbnail::GetAspectRatioSize($iFinalWidth, $iFinalHeight, $sourceImageWidth, $sourceImageHeight );
                }
                else {
                        $oSize = array('Width' => $iFinalWidth, 'Height' => $iFinalHeight);
                }

                CKFinder_Connector_Utils_Misc::setMemoryForImage($sourceImageWidth, $sourceImageHeight, $sourceImageBits, $sourceImageChannels);

                switch ($sourceImageAttr['mime'])
                {
                        case 'image/gif':
                                {
                                        if (@imagetypes() & IMG_GIF) {
                                                $oImage = @imagecreatefromgif($sourceFile);
                                        } else {
                                                $ermsg = 'GIF images are not supported';
                                        }
                                }
                                break;
                        case 'image/jpeg':
                                {
                                        if (@imagetypes() & IMG_JPG) {
                                                $oImage = @imagecreatefromjpeg($sourceFile) ;
                                        } else {
                                                $ermsg = 'JPEG images are not supported';
                                        }
                                }
                                break;
                        case 'image/png':
                                {
                                        if (@imagetypes() & IMG_PNG) {
                                                $oImage = @imagecreatefrompng($sourceFile) ;
                                        } else {
                                                $ermsg = 'PNG images are not supported';
                                        }
                                }
                                break;
                        case 'image/wbmp':
                                {
                                        if (@imagetypes() & IMG_WBMP) {
                                                $oImage = @imagecreatefromwbmp($sourceFile);
                                        } else {
                                                $ermsg = 'WBMP images are not supported';
                                        }
                                }
                                break;
                        case 'image/bmp':
                                {
                                        /*
                                        * This is sad that PHP doesn't support bitmaps.
                                        * Anyway, we will use our custom function at least to display thumbnails.
                                        * We'll not resize images this way (if $sourceFile === $targetFile),
                                        * because user defined imagecreatefrombmp and imagecreatebmp are horribly slow
                                        */
                                        if ($bmpSupported && (@imagetypes() & IMG_JPG) && $sourceFile != $targetFile) {
                                                $oImage = CKFinder_Connector_Utils_Misc::imageCreateFromBmp($sourceFile);
                                        } else {
                                                $ermsg = 'BMP/JPG images are not supported';
                                        }
                                }
                                break;
                        default:
                                $ermsg = $sourceImageAttr['mime'].' images are not supported';
                                break;
                }

                if (isset($ermsg) || false === $oImage) {
                        return false;
                }


                $oThumbImage = imagecreatetruecolor($oSize["Width"], $oSize["Height"]);

                if ($sourceImageAttr['mime'] == 'image/png')
                {
                        $bg = imagecolorallocatealpha($oThumbImage, 255, 255, 255, 127); // (PHP 4 >= 4.3.2, PHP 5)
                        imagefill($oThumbImage, 0, 0 , $bg);
                        imagealphablending($oThumbImage, false);
                        imagesavealpha($oThumbImage, true);
                }

                //imagecopyresampled($oThumbImage, $oImage, 0, 0, 0, 0, $oSize["Width"], $oSize["Height"], $sourceImageWidth, $sourceImageHeight);
                CKFinder_Connector_Utils_Misc::fastImageCopyResampled($oThumbImage, $oImage, 0, 0, 0, 0, $oSize["Width"], $oSize["Height"], $sourceImageWidth, $sourceImageHeight, (int)max(floor($quality/20), 6));

                switch ($sourceImageAttr['mime'])
                {
                        case 'image/gif':
                                imagegif($oThumbImage, $targetFile);
                                break;
                        case 'image/jpeg':
                        case 'image/bmp':
                                imagejpeg($oThumbImage, $targetFile, $quality);
                                break;
                        case 'image/png':
                                imagepng($oThumbImage, $targetFile);
                                break;
                        case 'image/wbmp':
                                imagewbmp($oThumbImage, $targetFile);
                                break;
                }

                $_config =& CKFinder_Connector_Core_Factory::getInstance("Core_Config");
                if (file_exists($targetFile) && ($perms = $_config->getChmodFiles())) {
                        $oldUmask = umask(0);
                        chmod($targetFile, $perms);
                        umask($oldUmask);
                }

                imageDestroy($oImage);
                imageDestroy($oThumbImage);

                return true;
        }



        /**
         * Return aspect ratio size, returns associative array:
         * <pre>
         * Array
         * (
         *            [Width] => 80
         *            [Heigth] => 120
         * )
         * </pre>
         *
         * @param int $maxWidth
         * @param int $maxHeight
         * @param int $actualWidth
         * @param int $actualHeight
         * @return array
         * @static
         * @access public
         */
        public static function getAspectRatioSize($maxWidth, $maxHeight, $actualWidth, $actualHeight)
        {
                $oSize = array("Width"=>$maxWidth, "Height"=>$maxHeight);

                // Calculates the X and Y resize factors
                $iFactorX = (float)$maxWidth / (float)$actualWidth;
                $iFactorY = (float)$maxHeight / (float)$actualHeight;

                // If some dimension have to be scaled
                if ($iFactorX != 1 || $iFactorY != 1)
                {
                        // Uses the lower Factor to scale the oposite size
                        if ($iFactorX < $iFactorY) {
                                $oSize["Height"] = (int)round($actualHeight * $iFactorX);
                        }
                        else if ($iFactorX > $iFactorY) {
                                $oSize["Width"] = (int)round($actualWidth * $iFactorY);
                        }
                }

                if ($oSize["Height"] <= 0) {
                        $oSize["Height"] = 1;
                }
                if ($oSize["Width"] <= 0) {
                        $oSize["Width"] = 1;
                }

                // Returns the Size
                return $oSize;
        }
}
