<?
/*********************************/
/* Copyright (c) Igor Nikolaev   */
/*********************************/

if (!defined("DOWNLOADER_CLASS_PHP"))
{
define("DOWNLOADER_CLASS_PHP",1);	

	define("kb", 1024);
	define("mb", 1024 * kb);
	define("mb10", 10 * mb);

	//
	// Класс для обеспечения скачивания файла скриптом PHP с поддержкой многопоточности
	//
	// Пример использования:
	//
	// $downloader = new Downloader("путь-к-файлу-с-именем-файла");
	// $downloader->setName("имя файла, под которым он будет скачиваться");
	// $downloader->download();
	//
	// Можно использовать для трансляции медиа-файла по сети:
	//
	// $downloader = new Downloader("video.avi");
	// $downloader->setContentType("video/x-msvideo");
	// $downloader->download();
	//

	class Downloader
	{
		var $log;
		var $filepath;
		var $blocksize = mb10;

		var $name = null;
		var $contentType = null;

		function Downloader($filepath)
		{
			global $_SERVER;

			$this->filepath = $filepath;
		}

		function download()
		{
			global $_SERVER;

			if (!file_exists($this->filepath))
			{
				$this->header("HTTP/1.1 404 Not Found");
				return;
			}

			$fh = fopen($this->filepath, "rb");

			if (!$fh)
			{
				$this->header("HTTP/1.1 500 Internal Server Error");
				return;
			}

			$requestRange = $this->getRequestRange();

			fseek($fh, $requestRange->rangeStart, SEEK_SET);
			$readLength = $requestRange->rangeLength;

			if (!is_null($this->getName()))
			{
				$this->header("Content-Disposition: attachment; filename=\"".$this->getName()."\"");
			}			

			if ($requestRange->isPartial)
			{
				$this->header("HTTP/1.1 206 Partial content");
				$this->header("Content-Range: bytes ".$requestRange->rangeStart."-".$requestRange->rangeEnd."/".$requestRange->fileLength);
			}			

			$this->header("Accept-Ranges: bytes");
			$this->header("ETag: ".$this->getETag());
			$this->header("Content-Length: ".$requestRange->rangeLength);
			$this->header("Content-Type: ".$this->getContentType());
			$this->header("Content-Transfer-Encoding: binary");
			$this->header("Last-Modified: ".$this->getLastModified());

			while ($readLength > 0)
			{
				$readsize = $this->blocksize;

				if ($readLength < $this->blocksize)
					$readsize = $readLength;

				$data = fread($fh, $readsize);

				$readLength -= $readsize;

				echo $data;
			}

			fclose($fh);
		}

		function setName($name)
		{
			global $_SERVER;

			$this->name = $name;

			if (strstr($_SERVER['HTTP_USER_AGENT'], "MSIE"))
			{
        			$this->name = preg_replace('/\./', '%2e', $this->name, substr_count($this->name, '.') - 1);
				$this->name = preg_replace('/ /', '%20', $this->name);
			}
		}

		function getName()
		{
			return $this->name;
		}

		function setContentType($contentType)
		{
			$this->contentType = $contentType;
		}

		function getContentType()
		{
			if (is_null($this->contentType))
                        	$this->contentType = "application/octet-stream";

			return $this->contentType;
		}

		function getETag()
		{
			$etag = md5_file($this->filepath);
			$etag = substr($etag, 0, 8) . '-' . substr($etag, 8, 7) . '-' . substr($etag, 15, 8);

			return $etag;
		}

		function getLastModified()
		{
			return date("D, d M Y H:i:s T", filemtime($this->filepath));
		}

		function getRequestRange()
		{
			global $_SERVER;

			$requestRange->rangeLength = $requestRange->fileLength = $this->getFilesize($this->filepath);
			$requestRange->rangeStart = 0;
                        $requestRange->rangeEnd = $requestRange->rangeLength - 1;
			$requestRange->isPartial = isset($_SERVER["HTTP_RANGE"]) ? true:false;

			$httpRange = $_SERVER["HTTP_RANGE"];

			if ($requestRange->isPartial)
			{
				list($type, $ranges) = explode("=", $httpRange);

                                $ranges = explode(",", $ranges);
				$range = trim($ranges[0]);

				list($start, $end) = explode("-", $range);

                                if (strlen($start) != 0)
					$requestRange->rangeStart = doubleval($start);
				else if (strlen($start) == 0 && strlen($end) != 0)
					$requestRange->rangeStart = $requestRange->rangeEnd - doubleval($end);

				if (strlen($end) != 0)
					$requestRange->rangeEnd = doubleval($end);
 			}

			$requestRange->rangeLength = $requestRange->rangeEnd - $requestRange->rangeStart + 1;

			return $requestRange;
		}

		function header($value)
		{
			header($value);
		}

		function getFilesize($path)
		{
			return doubleval(sprintf("%u", filesize($path)));
		}
	}
}
?>