<?php
/**
 *---------------------------------------------------------------------------------------
 * @package       VirtuePlanet Framework for Joomla!
 *---------------------------------------------------------------------------------------
 * @copyright     Copyright (C) 2012-2021 VirtuePlanet Services LLP. All rights reserved.
 * @license       GNU General Public License version 2 or later; see LICENSE.txt
 * @authors       Abhishek Das
 * @email         info@virtueplanet.com
 * @link          https://www.virtueplanet.com
 *---------------------------------------------------------------------------------------
 */
defined('_JEXEC') or die;

include JPATH_BASE . '/plugins/system/vpframework/define.php';

class VPFrameworkOptimizerOutput
{
	public static function execute()
	{
		if(!class_exists('VPFrameworkOptimizer'))
		{
			require VPF_PLUGINPATH . '/helpers/optimizer.php';
		}
		
		// Get inputs
		$type       = self::get('type', 'WORD');
		$gzip       = self::get('gzip', 'WORD');
		$key        = self::get('key', 'ALNUM');
		$lifetime   = self::get('lt', 'INT');
		$validTypes = array('css', 'js', 'gif');
		
		if(!in_array($type, $validTypes))
		{
			die('Invalid file type.');
		}
		
		if($type == 'css' && $key == 'test')
		{
			return self::printTestReport();
		}
		
		if($type == 'gif' && !empty($key) && !empty($lifetime))
		{
			return self::runMaintenance($key, $lifetime);
		}
		
		if(!VPFrameworkOptimizer::cacheFileExists($key))
		{
			die('Cached file not found.');
		}
		
		$modified_time = VPFrameworkOptimizer::getCacheModifiedTime($key);
		$filename = VPFrameworkOptimizer::getCacheFilename($key);
		
		$user_agent = isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : null;
		$server_accepted_encodings = isset($_SERVER['HTTP_ACCEPT_ENCODING']) ? $_SERVER['HTTP_ACCEPT_ENCODING'] : null;
		
		list($filemtime, $expirytime) = self::getDates($modified_time, '1 year', ' GMT');
		
		$if_modified_since = null;
		$if_none_match = null;
		$ETag = md5($filename . $filemtime . $expirytime);
		
		if(function_exists('apache_request_headers'))
		{
			$headers = apache_request_headers();

			if(isset($headers['If-Modified-Since']))
			{
				$if_modified_since = strtotime($headers['If-Modified-Since']);
			}

			if(isset($headers['If-None-Match']))
			{
				$if_none_match = $headers['If-None-Match'];
			}
		}
		
		if (!$if_modified_since && isset($_SERVER['HTTP_IF_MODIFIED_SINCE']))
		{
			$if_modified_since = strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']);
		}

		if ($if_none_match && isset($_SERVER['HTTP_IF_NONE_MATCH']))
		{
			$if_none_match = $_SERVER['HTTP_IF_NONE_MATCH'];
		}
		
		if($type == 'css')
		{
			header("Content-type: text/css; charset=UTF-8");
		}
		elseif($type == 'js')
		{
			//header('Content-type: application/javascript');
			header("Content-type: application/javascript; charset=UTF-8");
		}
		
		header('Last-Modified: ' . $filemtime);
		header('Expires: ' . $expirytime);
		header('Accept-Ranges: bytes');
		header('Cache-Control: Public');
		header('Vary: Accept-Encoding');
		header('Etag: ' . $ETag);
		
		if($if_modified_since == strtotime($filemtime) || $if_none_match == $ETag)
		{
			// Browser has a valid cached copy.
			// Therefore we just respond '304 Not Modified'.
			header('HTTP/1.1 304 Not Modified');
			header('Content-Length: 0');

			return;
		}
		
		// Get output from cache
		$output = VPFrameworkOptimizer::getOutput($key, $lifetime);
		
		if(is_numeric($gzip))
		{
			$gzip = (int) $gzip;
		}
		elseif(strtolower($gzip) == 'n' || strtolower($gzip) == 'normal')
		{
			$gzip = false;
		}
		else
		{
			$gzip = true;
		}
		
		if($gzip && $user_agent)
		{
			$pattern = strtolower('/facebookexternalhit|LinkedInBot/x');
			
			if (strpos(strtolower($user_agent), $pattern) !== false)
			{
				$gzip = false;
			}
		}
		
		if($gzip)
		{
			$supported_encodings = array(
				'x-gzip'  => 'gz',
				'gzip'    => 'gz',
				'deflate' => 'deflate'
			);
			
			$accepted_encodings = array('gzip');
			
			if($server_accepted_encodings)
			{
				$server_accepted_encodings = array_map('trim', explode(',', $server_accepted_encodings));
				$accepted_encodings = array_intersect($server_accepted_encodings, array_keys($supported_encodings));
			}
			
			if(!empty($accepted_encodings))
			{
				foreach ($accepted_encodings as $encoding)
				{
					$encoding_key = isset($supported_encodings[$encoding]) ? $supported_encodings[$encoding] : null;
					
					if ($encoding_key == 'gz' || $encoding_key == 'deflate')
					{
						$encoding_constant = ($encoding_key == 'gz') ? FORCE_GZIP : FORCE_DEFLATE;
						$encoded_output    = gzencode($output, 4, $encoding_constant);

						if ($encoded_output !== false)
						{
							header('Content-Encoding: ' . $encoding);
							$output = $encoded_output;
							break;
						}
					}
				}
			}
		}
		
		// Print the output
		echo $output;
	}
	
	/**
	* Method to get url queries
	* A smart way to protect the $ _GET input from malicious injection
	* 
	* @param  string $name   Query name
	* @param  mixed  $value  Default value if query not found
	* 
	* @return mixed  Query value
	*/
	protected static function get($key = null, $filter = 'WORD')
	{
		$value  = isset($_GET[$key]) ? $_GET[$key] : null;
		$filter = strtolower($filter);
		
		switch($filter)
		{
			case 'WORD':
				$value = $value ? preg_replace('@([^a-zA-Z])@Ui', '', $value) : null;
				break;
			case 'INT':
				$value = $value ? preg_replace('@([^0-9])@Ui', '', $value) : null;
				break;
			case 'ALNUM':
				$value = $value ? preg_replace('@([^0-9a-f])@Ui', '', $value) : null;
				break;
			case 'FLOAT':
				$value = $value ? preg_replace('@([^0-9\,\.\+\-])@Ui', '', $value) : null;
				break;
		}
		
		return $value;
	}
	
	protected static function getDates($filemtime, $period, $suffix = '')
	{
		$dates = array();

		$date = new DateTime();
		$date->setTimestamp($filemtime);

		$dates[] = $date->format('D, d M Y H:i:s') . $suffix;

		$date->add(DateInterval::createFromDateString($period));
		$dates[] = $date->format('D, d M Y H:i:s') . $suffix;

		return $dates;
	}
	
	protected static function printTestReport()
	{
		header('Content-type: application/json');
		header('Cache-Control: public,max-age=1,must-revalidate');
		header('Expires: ' . gmdate('D, d M Y H:i:s', ($_SERVER['REQUEST_TIME'] + 1)) . ' GMT');
		header('Last-modified: ' . gmdate('D, d M Y H:i:s', $_SERVER['REQUEST_TIME']) . ' GMT');

		if(function_exists('header_remove')) 
		{
			header_remove('Pragma');
		}
		
		$output = array('success' => true);
		
		echo json_encode($output);
	}
	
	protected static function runMaintenance($key, $lifetime)
	{
		if($key == 'gc')
		{
			// Final stage
			VPFrameworkOptimizer::gc($lifetime);
		}
		else
		{
			$gc_url = VPFrameworkOptimizer::url_decode($key);
			
			if(!empty($gc_url))
			{
				if(!class_exists('VPFrameworkUtility'))
				{
					require VPF_PLUGINPATH . '/helpers/utility.php';
				}
				
				$result = VPFrameworkUtility::getRemoteData($gc_url);
			}
			
			header('Content-Type: image/gif');
			header('Content-Length: 43');
			header('Cache-Control: public,max-age=1,must-revalidate');
			header('Expires: ' . gmdate('D, d M Y H:i:s', ($_SERVER['REQUEST_TIME'] + 1)) . ' GMT');
			header('Last-modified: ' . gmdate('D, d M Y H:i:s', $_SERVER['REQUEST_TIME']) . ' GMT');

			if(function_exists('header_remove')) 
			{
				header_remove('Pragma');
			}

			// 1 x 1 gif
			echo chr(0x47).chr(0x49).chr(0x46).chr(0x38).chr(0x39).chr(0x61).chr(0x01).chr(0x00).
			     chr(0x01).chr(0x00).chr(0x80).chr(0x00).chr(0x00).chr(0x04).chr(0x02).chr(0x04).
			     chr(0x00).chr(0x00).chr(0x00).chr(0x21).chr(0xF9).chr(0x04).chr(0x01).chr(0x00).
			     chr(0x00).chr(0x00).chr(0x00).chr(0x2C).chr(0x00).chr(0x00).chr(0x00).chr(0x00).
			     chr(0x01).chr(0x00).chr(0x01).chr(0x00).chr(0x00).chr(0x02).chr(0x02).chr(0x44).
			     chr(0x01).chr(0x00).chr(0x3B);
			
			die();
		}
	}
}