| 
<?php/**
 * Wave Framework <http://github.com/kristovaher/Wave-Framework>
 * File Handler
 *
 * File Handler is used for returning a file from web server. It is loaded by Index Controller
 * when a request is made to a file that has an extension that has not already been served by
 * another Handler (such as Resource and Image handlers). File Handler is usually considered
 * for returning documents, videos and audio from the web server. It also allows to return a
 * file within a byte range, like in the case of file streams.
 *
 * @package    Index Gateway
 * @author     Kristo Vaher <[email protected]>
 * @copyright  Copyright (c) 2012, Kristo Vaher
 * @license    GNU Lesser General Public License Version 3
 * @tutorial   /doc/pages/handler_file.htm
 * @since      1.5.0
 * @version    3.6.4
 */
 // INITIALIZATION
 // Stopping all requests that did not come from Index Gateway
 if(!isset($resourceAddress)){
 header('HTTP/1.1 403 Forbidden');
 die();
 }
 
 // If access control header is set in configuration
 if(isset($config['access-control'])){
 header('Access-Control-Allow-Origin: '.$config['access-control']);
 }
 // If filename includes & symbol, then system assumes it should be dynamically generated
 $parameters=array_unique(explode('&',$resourceFile));
 // Getting the downloadable file name
 $resourceFile=array_pop($parameters);
 // The amount of non-filenames in the request
 $parameterCount=count($parameters);
 
 // Range of bytes to return
 // This allows user agent to request only part of the file
 if(isset($_SERVER['HTTP_RANGE'])){
 $tmp=explode('=',$_SERVER['HTTP_RANGE']);
 $bytesData=explode('-',array_pop($tmp));
 if(isset($bytesData) && is_numeric($bytesData[0]) && is_numeric($bytesData[1])){
 $bytesFrom=$bytesData[0];
 $bytesTo=$bytesData[1];
 }
 }
 // No cache flag
 $noCache=array_search('nocache',$parameters);
 if($noCache!==false){
 // Unsetting the key for nocache parameter
 unset($parameters[$noCache]);
 }
 
 // Web root is the subfolder on public site
 $webRoot=str_replace('index.php','',$_SERVER['SCRIPT_NAME']);
 // Checking if the file might be loaded from overrides folder
 if(preg_match('/^'.str_replace('/','\/',$webRoot).'resources\//',$_SERVER['REQUEST_URI'])){
 // Solving possible overrides folder
 $overridesFolder=str_replace($webRoot.'resources'.DIRECTORY_SEPARATOR,$webRoot.'overrides'.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR,$resourceFolder);
 if(file_exists($overridesFolder.$resourceFile)){
 $resourceFolder=$overridesFolder;
 }
 }
 // Default cache timeout of one month, unless timeout is set
 if(!isset($config['resource-cache-timeout'])){
 $config['resource-cache-timeout']=31536000; // A year
 }
 // CHECK FOR PARAMETER SUPPORT
 // If more than one parameter is set, it returns 404
 // 404 is also returned if file does not actually exist
 if($parameterCount>1 || ($parameterCount==1 && !$noCache) || !file_exists($resourceFolder.$resourceFile)){
 // Adding log entry
 if(isset($logger)){
 // Assigning custom log data to logger
 $logger->setCustomLogData(array('category'=>'file','response-code'=>'404'));
 // Writing log entry
 $logger->writeLog();
 }
 // Returning 404 header
 header('HTTP/1.1 404 Not Found');
 die();
 }
 // Last-modified date
 $lastModified=filemtime($resourceFolder.$resourceFile);
 // NOT MODIFIED CHECK
 // Checking if file has been modified or not
 if(!$noCache){
 // If the request timestamp is exactly the same, then we let the browser know of this
 if((isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) && strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE'])==$lastModified) || (isset($_SERVER['HTTP_IF_RANGE']) && strtotime($_SERVER['HTTP_IF_RANGE'])==$lastModified)){
 // Adding log entry
 if(isset($logger)){
 // Assigning custom log data to logger
 $logger->setCustomLogData(array('cache-used'=>true,'category'=>'image','response-code'=>'304'));
 // Writing log entry
 $logger->writeLog();
 }
 // Cache headers (Last modified is never sent with 304 header)
 header('Cache-Control: public,max-age='.$config['resource-cache-timeout']);
 header('Expires: '.gmdate('D, d M Y H:i:s',($_SERVER['REQUEST_TIME']+$config['resource-cache-timeout'])).' GMT');
 // Returning 304 header
 header('HTTP/1.1 304 Not Modified');
 die();
 }
 }
 // DETECTING MIME TYPE
 // Currently assumed MIME type
 $mimeType='';
 // Finding the proper MIME type
 if(extension_loaded('fileinfo')){
 // This opens MIME type 'magic' resource for use
 if($fileInfo=finfo_open(FILEINFO_MIME_TYPE)){
 // Finding MIME type with magic resource
 $mimeType=finfo_file($fileInfo,$resourceFolder.$resourceFile);
 // Resourse is not needed further, so it is closed
 finfo_close($fileInfo);
 }
 } else {
 // Since Fileinfo was not available, we use extension-based detection as fallback
 if(isset($resourceExtension)){
 switch($resourceExtension){
 case 'ico':
 $mimeType='image/vnd.microsoft.icon;';
 break;
 case 'zip':
 $mimeType='application/zip';
 break;
 case 'pdf':
 $mimeType='application/pdf';
 break;
 case 'mp3':
 $mimeType='audio/mpeg';
 break;
 case 'gif':
 $mimeType='image/gif';
 break;
 case 'tif':
 $mimeType='image/tiff';
 break;
 }
 }
 }
 
 // HEADERS
 // Assigning MIME type if it was found
 if($mimeType && $mimeType!=''){
 // Detected mime type is set as content-type header
 header('Content-Type: '.$mimeType.';');
 } else {
 // Octet stream is a general-use unknown resource, and browsers will often attempt to 'download' such a file
 header('Content-Type: application/octet-stream;');
 header('Content-Disposition: attachment; filename='.$resourceFile);
 }
 
 // If cache is used, then proper headers will be sent
 if($noCache){
 // User agent is told to cache these results for set duration
 header('Cache-Control: no-cache,no-store');
 header('Expires: '.gmdate('D, d M Y H:i:s',$_SERVER['REQUEST_TIME']).' GMT');
 header('Last-Modified: '.gmdate('D, d M Y H:i:s',$lastModified).' GMT');
 } else {
 // User agent is told to cache these results for set duration
 header('Cache-Control: public,max-age='.$config['resource-cache-timeout']);
 header('Expires: '.gmdate('D, d M Y H:i:s',($_SERVER['REQUEST_TIME']+$config['resource-cache-timeout'])).' GMT');
 header('Last-Modified: '.gmdate('D, d M Y H:i:s',$lastModified).' GMT');
 }
 // Robots header
 if(isset($config['file-robots'])){
 // If file-specific robots setting is defined
 header('Robots-Tag: '.$config['file-robots'],true);
 } elseif(isset($config['robots'])){
 // This sets general robots setting, if it is defined in configuration file
 header('Robots-Tag: '.$config['robots'],true);
 } else {
 // If robots setting is not configured, system tells user agent not to cache the file
 header('Robots-Tag: noindex,nocache,nofollow,noarchive,noimageindex,nosnippet',true);
 }
 
 // OUTPUT
 // If user agent only requested part of the file to be returned
 if(isset($bytesFrom,$bytesTo)){
 // Getting current output length
 $contentLength=filesize($resourceFolder.$resourceFile);
 if($bytesTo<=$contentLength){
 // Required for range response
 header('HTTP/1.1 206 Partial Content');
 header('Content-Range: bytes '.$bytesFrom.'-'.$bytesTo.'/'.$contentLength);
 // Content length is defined that can speed up website requests, letting user agent to determine file size
 header('Content-Length: '.($bytesTo-$bytesFrom));
 // Returning part of the file
 $fileHandle=fopen($resourceFolder.$resourceFile,'r');
 fseek($fileHandle,$bytesFrom);
 // Returning the data to user agent
 echo fread($fileHandle,($bytesTo-$bytesFrom));
 } else {
 header('HTTP/1.1 416 Requested Range Not Satisfiable');
 }
 } else {
 // Getting current output length
 $contentLength=filesize($resourceFolder.$resourceFile);
 // Content length is defined that can speed up website requests, letting user agent to determine file size
 header('Content-Length: '.$contentLength);
 // Returning the file to user agent
 readfile($resourceFolder.$resourceFile);
 }
 
 // WRITING TO LOG
 // If Logger is defined then request is logged and can be used for performance review later
 if(isset($logger)){
 // Assigning custom log data to logger
 $logger->setCustomLogData(array('cache-used'=>false,'category'=>'file','content-length-used'=>$contentLength));
 // Writing log entry
 $logger->writeLog();
 }
 ?>
 |