<?php
/**
 * @package    VP Advanced User
 * @author     Abhishek Das <info@virtueplanet.com>
 * @copyright  Copyright (C) 2012-2019 VirtuePlanet Services LLP. All rights reserved.
 * @license    GNU General Public License version 2 or later; see LICENSE.txt
 * @link       https://www.virtueplanet.com
 */

defined('_JEXEC') or die;

jimport('joomla.filesystem.file');
jimport('joomla.filesystem.folder');
jimport('cms.helper.media');

/**
 * Avatar  helper.
 *
 * @since  1.0
 */
class VPAdvancedUserHelperAvatar extends JObject
{
	public $user;
	public $file;
	public $destination;
	public $present_file;
	
	public static $instance;
	
	protected $app;
	protected $input;
	protected $params;
	protected $encryption_method;
	
	public function __construct($config = array()) 
	{
		$this->app    = JFactory::getApplication();
		$this->input  = $this->app->input;
		$this->params = JComponentHelper::getParams('com_vpadvanceduser');
		
		if(isset($config['user']) && is_array($config['user']))
		{
			$this->user = $config['user'];
		}
		elseif(isset($config['user_id']) && $config['user_id'] > 0)
		{
			$this->user = JUser::getInstance($config['user_id'])->getProperties();
		}
		
		if(isset($config['destination']))
		{
			$this->destination = $config['destination'];
		}
		else
		{
			$this->destination = JPATH_ROOT . '/media/com_vpadvanceduser/avatars';
		}
		
		if(isset($config['encryption_method']))
		{
			$this->encryption_method = $config['encryption_method'];
		}
		else
		{
			$this->encryption_method = 'aes-128-cbc';
		}
	}
	
	public static function getInstance($config = array())
	{
		if(!is_object(self::$instance))
		{
			self::$instance = new self($config);
		}
		
		return self::$instance;
	}
	
	public function setPresentFile($file)
	{
		$file = (string) $file;
		
		if(!empty($file))
		{
			$this->present_file = basename($file);
		}
	}
	
	public function validate($file)
	{
		$file = (array) $file;
		
		if(!count($file) || empty($file['name']))
		{
			$this->setError(JText::_('COM_VPADVANCEDUSER_ERROR_NO_FILES_TO_UPLOAD'));
			return false;
		}

		// Check valid image file extension
		if (!preg_match('/\.(gif|jpe?g|png)$/i', $file['name']))
		{
			$this->setError(JText::_('COM_VPADVANCEDUSER_ERROR_INVALID_IMAGE_FORMAT'));
			return false;
		}
		
		// Make double sure if exif_imagetype is enabled
		if(function_exists('exif_imagetype'))
		{
			$detectedType = exif_imagetype($file['tmp_name']);
			$allowedTypes = array(IMAGETYPE_PNG, IMAGETYPE_JPEG, IMAGETYPE_GIF);
			
			if(!in_array($detectedType, $allowedTypes))
			{
				$this->setError(JText::_('COM_VPADVANCEDUSER_ERROR_INVALID_IMAGE_FORMAT2'));
				return false;
			}
		}

		// Total length of post back data in bytes.
		$contentLength = (int) $_SERVER['CONTENT_LENGTH'];
		
		// Instantiate the media helper
		$mediaHelper = new JHelperMedia;
		
		// Initiate some required variables
		$uploadMaxFileSize   = 0;
		$avatarMaxSize       = $this->params->get('avatar_maxsize', 1000);
		
		if(function_exists('ini_get'))
		{
			// Maximum allowed size of post back data in MB.
			$postMaxSize = $mediaHelper->toBytes(ini_get('post_max_size'));

			// Maximum allowed size of script execution in MB.
			$memoryLimit = $mediaHelper->toBytes(ini_get('memory_limit'));

			// Check for the total size of post back data.
			if (($postMaxSize > 0 && $contentLength > $postMaxSize) || ($memoryLimit != -1 && $contentLength > $memoryLimit))
			{
				$this->setError(JText::_('COM_VPADVANCEDUSER_ERROR_WARNUPLOADTOOLARGE_SERVER'));
				return false;
			}
			
			$uploadMaxFileSize = $mediaHelper->toBytes(ini_get('upload_max_filesize'));
		}
		
		// Perform basic checks on file info before attempting anything
		if (($file['error'] == 1) || ($uploadMaxFileSize > 0 && $file['size'] > $uploadMaxFileSize))
		{
			// File size exceed either 'upload_max_filesize' or 'upload_maxsize'.
			$this->setError(JText::_('COM_VPADVANCEDUSER_ERROR_WARNUPLOADTOOLARGE_SERVER'));
			return false;
		}
		
		if ($avatarMaxSize > 0 && $file['size'] > ($avatarMaxSize * 1024))
		{
			// File size exceed either 'upload_max_filesize' or 'upload_maxsize'.
			$this->setError(JText::sprintf('COM_VPADVANCEDUSER_ERROR_WARNUPLOADTOOLARGE', $avatarMaxSize));
			return false;
		}
		
		// Make safe file name
		$file['name'] = JFile::makeSafe($file['name']);
		
		// If file name is empty after File::makeSafe
		if (empty($file['name']))
		{
			$this->setError(JText::_('COM_VPADVANCEDUSER_ERROR_INVALID_REQUEST'));
			return false;
		}

		// Convert array to JObject
		$file = new JObject($file);

		if(!function_exists('getimagesize'))
		{
			$this->setError(JText::_('COM_VPADVANCEDUSER_ERROR_GETIMAGESIZE'));
			return false;
		}

		// Get image size
		list($width, $height) = getimagesize($file->tmp_name);
		
		$file->width  = 0;
		$file->height = 0;
		
		if (is_int($width) && is_int($height)) 
		{
			$minWidth  = (int) $this->params->get('avatar_width');
			$minHeight = (int) $this->params->get('avatar_height');
			
			if(($minWidth && $width < $minWidth) || ($minHeight && $height < $minHeight))
			{
				$this->setError(JText::sprintf('COM_VPADVANCEDUSER_ERROR_IMAGE_DIMENSION', $minWidth, $minHeight));
				return false;
			}
			
			$file->width  = $width;
			$file->height = $height;
		}
		
		if(!$file->width || !$file->height)
		{
			$this->setError(JText::_('COM_VPADVANCEDUSER_ERROR_INVALID_IMAGE_FORMAT2'));
			return false;
		}
		
		// Get the extension of the image
		$file->extension = strtolower(substr(strrchr($file->name, '.'), 1));
		
		// Save to internal cache
		$this->file = $file;
		
		return true;
	}
	
	public function upload()
	{
		if(!is_object($this->file) || empty($this->file->name))
		{
			$this->setError(JText::_('COM_VPADVANCEDUSER_ERROR_NO_FILES_TO_UPLOAD'));
			return false;
		}
		
		if(!$this->user || empty($this->user['id']))
		{
			$this->setError(JText::_('COM_VPADVANCEDUSER_ERROR_INVALID_REQUEST'));
			return false;
		}
		
		// Get path info
		$parts = pathinfo($this->file->name);
		
		if(!isset($parts['extension']))
		{
			$this->setError(JText::_('COM_VPADVANCEDUSER_ERROR_INVALID_REQUEST'));
			return false;
		}
		
		// Build destination filename
		$date     = JFactory::getDate();
		$filename = $date->toUnix() . '.' . $parts['extension'];
		
		// Build destination path
		$folder =  $this->destination . '/' . $this->user['id'];
		
		// Build destination file
		$dest = $folder . '/' . $filename;
		
		// Ensure that the path is valid and clean
		$pathObject = new JFilesystemWrapperPath;
		$dest = $pathObject->clean($dest);
		
		// Create the destination directory if it does not exist
		$baseDir = dirname($dest);

		if (!file_exists($baseDir))
		{
			$folderObject = new JFilesystemWrapperFolder;
			$folderObject->create($baseDir);
		}
		
		// Get list of old files
		$oldFiles = JFolder::files($baseDir, '.', false, true, array('.svn', 'CVS', '.DS_Store', '__MACOSX', '.html'));
		
		if(is_array($oldFiles) && count($oldFiles) > 1)
		{
			foreach($oldFiles as $oldFile)
			{
				// Delete alll old files except the last one
				if($this->present_file && strpos($oldFile, $this->present_file) === false)
				{
					JFile::delete($oldFile);
				}
			}
		}
		
		// Delete if destination file exists
		if(is_file($dest))
		{
			JFile::delete($dest);
		}
		
		// Try to resize and upload the image
		$result = $this->resize($this->file, $dest);
		$ret = false;
		
		if($result === false)
		{
			$this->setError($this->getError());
		}
		elseif(is_string($result))
		{
			// Get FTP options
			$FTPOptions = JClientHelper::getCredentials('ftp');

			if ($FTPOptions['enabled'] == 1)
			{
				// Connect the FTP client
				$ftp = JClientFtp::getInstance($FTPOptions['host'], $FTPOptions['port'], array(), $FTPOptions['user'], $FTPOptions['pass']);

				// Translate path for the FTP account
				$dest = $pathObject->clean(str_replace(JPATH_ROOT, $FTPOptions['root'], $dest), '/');

				// Copy the file to the destination directory
				if (is_uploaded_file($result) && $ftp->store($result, $dest))
				{
					unlink($result);
					$ret = $this->user['id'] . '/' . $filename;
				}
				else
				{
					$this->setError(JText::_('JLIB_FILESYSTEM_ERROR_WARNFS_ERR02'));
				}
			}
			else
			{
				if (is_writeable($baseDir) && move_uploaded_file($result, $dest))
				{
					// Short circuit to prevent file permission errors
					if ($pathObject->setPermissions($dest))
					{
						$ret = $this->user['id'] . '/' . $filename;
					}
					else
					{
						$this->setError(JText::_('JLIB_FILESYSTEM_ERROR_WARNFS_ERR01'));
					}
				}
				else
				{
					$this->setError(JText::_('JLIB_FILESYSTEM_ERROR_WARNFS_ERR02'));
				}
			}
		}
		else
		{
			unlink($this->file->tmp_name);
			
			$ret = $this->user['id'] . '/' . $filename;
		}

		return $ret;
	}
	
	protected function resize($image, $destination)
	{
		$max_width  = (int) $this->params->get('avatar_width');
		$max_height = (int) $this->params->get('avatar_height');
		
		if(!$max_width || !$max_height)
		{
			return $image->tmp_name;
		}
		
		// Get the scale for the image resizing
		$scale = min($max_width / $image->width, $max_height / $image->height);
		
		if ($scale >= 1) 
		{
			return $image->tmp_name;
		}
		
		$new_width  = $image->width * $scale;
		$new_height = $image->height * $scale;
		
		// Lets check if we have all functions available
		$required_functions = array(
		    'imagecreatetruecolor', 'imagecreatefromjpeg', 'imagecolortransparent', 'imagecolorallocate',
		    'imagecreatefromgif', 'imagealphablending', 'imagesavealpha', 'imagecreatefrompng',
		    'imagecopyresampled', 'imagedestroy'
		);
		
		foreach($required_functions as $required_function)
		{
			if(!function_exists($required_function))
			{
				$this->setError(JText::sprintf('COM_VPADVANCEDUSER_ERROR_RESIZE_FUNCTION_NOT_AVAILABLE', $required_function));
				return false;
			}
		}
		
		// Create a new image canvas
		$new_img = imagecreatetruecolor($new_width, $new_height);
		
		switch ($image->extension) 
		{
			case 'jpg':
			case 'jpeg':
				$src_img       = imagecreatefromjpeg($image->tmp_name);
				$write_image   = 'imagejpeg';
				$image_quality = 100;
				break;

			case 'gif':
				imagecolortransparent($new_img, imagecolorallocate($new_img, 0, 0, 0));
				$src_img       = imagecreatefromgif($image->tmp_name);
				$write_image   = 'imagegif';
				$image_quality = null;
				break;

			case 'png':
				imagecolortransparent($new_img, imagecolorallocate($new_img, 0, 0, 0));
				imagealphablending($new_img, false);
				imagesavealpha($new_img, true);
				$src_img       = imagecreatefrompng($image->tmp_name);
				$write_image   = 'imagepng';
				$image_quality = 0;
				break;

			default:
				$src_img = null;
		}
		
		// Try to resample and upload the new image
		$success = $src_img && imagecopyresampled(
		    $new_img,
		    $src_img,
		    0, 0, 0, 0,
		    $new_width,
		    $new_height,
		    $image->width,
		    $image->height
		) && $write_image($new_img, $destination, $image_quality);
		
		// Free up memory (imagedestroy does not delete files):
		imagedestroy($src_img);
		imagedestroy($new_img);
		
		if(!$success)
		{
			$this->setError(JText::_('COM_VPADVANCEDUSER_ERROR_RESIZE_FAILED'));
			return false;
		}
		
		return true;
	}
	
	public function delete()
	{
		if(empty($this->user['id']))
		{
			return true;
		}
		
		$dir = JPath::clean($this->destination . '/' . $this->user['id']);
		
		if(is_dir($dir))
		{
			if(!JFolder::delete($dir))
			{
				jimport('joomla.log.log');
				
				// Log usage of deprecated function
				JLog::add('Failed to delete profile picture directory for user id: ' . $this->user['id'], JLog::WARNING, 'com_vpadvanceduser');
			}
		}
		
		return true;
	}
	
	public function getUrl($profile_picture, $force_no_encode = false)
	{
		$profile_picture = (string) $profile_picture;
		
		if(empty($profile_picture))
		{
			$profile_picture = 'no-image.jpg';
		}
		
		// Build destination url from path
		$dest_url = JPATH_ROOT ? str_replace(JPATH_ROOT, '', $this->destination) : $this->destination;
		$dest_url = str_replace(DIRECTORY_SEPARATOR, '/', $dest_url);

		// Build the full path of the image file
		$path = JPath::clean($this->destination . '/' . $profile_picture);
		
		if(is_file($path))
		{
			$url = JUri::root(true) . $dest_url . '/' . $profile_picture;
		}
		else
		{
			$path = JPath::clean($this->destination . '/no-image.jpg');
			$url  = JUri::root(true) . $dest_url . '/no-image.jpg';
		}
		
		if(!is_file($path))
		{
			return '';
		}
		
		if($this->params->get('encode_avatar', 0) && !$force_no_encode)
		{
			if(function_exists('mime_content_type'))
			{
				$type = mime_content_type($path);
			}
			else
			{
				$type = pathinfo($path, PATHINFO_EXTENSION);
				$type = $type ? ('image/' . $type) : $type;
			}
			
			// Get file contents
			$data = file_get_contents($path);
			
			if($data && $type)
			{
				if(!class_exists('VPAdvancedUserHelperUtility'))
				{
					require JPATH_ADVANCEDUSER_SITE . '/helpers/utility.php';
				}
				
				$url = 'data:' . $type . ';base' . '64,' . VPAdvancedUserHelperUtility::encode($data);
			}
		}
		
		return $url;
	}
}
