Your IP : 216.73.217.68


Current Path : /home/w/u/e/wuectly/www/03cbe/
Upload File :
Current File : /home/w/u/e/wuectly/www/03cbe/Download.tar

Download.php000060400000026301152455614130007030 0ustar00<?php
/**
 * @package   FOF
 * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd
 * @license   GNU General Public License version 3, or later
 */

namespace FOF40\Download;

defined('_JEXEC') || die;

use FOF40\Container\Container;
use FOF40\Download\Exception\DownloadError;
use FOF40\Timer\Timer;
use Joomla\CMS\Language\Text;

class Download
{
	/**
	 * The component container object
	 *
	 * @var  Container
	 */
	protected $container;

	/**
	 * Parameters passed from the GUI when importing from URL
	 *
	 * @var  array
	 */
	private $params = [];

	/**
	 * The download adapter which will be used by this class
	 *
	 * @var  DownloadInterface
	 */
	private $adapter;

	/**
	 * Additional params that will be passed to the adapter while performing the download
	 *
	 * @var  array
	 */
	private $adapterOptions = [];

	/**
	 * Public constructor
	 *
	 * @param   Container  $c  The component container
	 */
	public function __construct(Container $c)
	{
		$this->container = $c;

		// Find the best fitting adapter
		$allAdapters = self::getFiles(__DIR__ . '/Adapter', [], ['AbstractAdapter.php']);
		$priority    = 0;

		foreach ($allAdapters as $adapterInfo)
		{
			/** @var Adapter\AbstractAdapter $adapter */
			$adapter = new $adapterInfo['classname'];

			if (!$adapter->isSupported())
			{
				continue;
			}

			if ($adapter->priority > $priority)
			{
				$this->adapter = $adapter;
				$priority      = $adapter->priority;
			}
		}

		// Load the language strings
		$c->platform->loadTranslations('lib_fof40');
	}

	/**
	 * This method will crawl a starting directory and get all the valid files
	 * that will be analyzed by __construct. Then it organizes them into an
	 * associative array.
	 *
	 * @param   string  $path           Folder where we should start looking
	 * @param   array   $ignoreFolders  Folder ignore list
	 * @param   array   $ignoreFiles    File ignore list
	 *
	 * @return  array   Associative array, where the `fullpath` key contains the path to the file,
	 *                  and the `classname` key contains the name of the class
	 */
	protected static function getFiles(string $path, array $ignoreFolders = [], array $ignoreFiles = []): array
	{
		$return = [];

		$files = self::scanDirectory($path, $ignoreFolders, $ignoreFiles);

		// Ok, I got the files, now I have to organize them
		foreach ($files as $file)
		{
			$clean = str_replace($path, '', $file);
			$clean = trim(str_replace('\\', '/', $clean), '/');

			$parts = explode('/', $clean);

			$return[] = [
				'fullpath'  => $file,
				'classname' => '\\FOF40\\Download\\Adapter\\' . ucfirst(basename($parts[0], '.php')),
			];
		}

		return $return;
	}

	/**
	 * Recursive function that will scan every directory unless it's in the
	 * ignore list. Files that aren't in the ignore list are returned.
	 *
	 * @param   string  $path           Folder where we should start looking
	 * @param   array   $ignoreFolders  Folder ignore list
	 * @param   array   $ignoreFiles    File ignore list
	 *
	 * @return  array   List of all the files
	 */
	protected static function scanDirectory(string $path, array $ignoreFolders = [], array $ignoreFiles = []): array
	{
		$return = [];

		$handle = @opendir($path);

		if (!$handle)
		{
			return $return;
		}

		while (($file = readdir($handle)) !== false)
		{
			if ($file == '.' || $file == '..')
			{
				continue;
			}

			$fullpath = $path . '/' . $file;

			if ((is_dir($fullpath) && in_array($file, $ignoreFolders)) || (is_file($fullpath) && in_array($file, $ignoreFiles)))
			{
				continue;
			}

			if (is_dir($fullpath))
			{
				$return = array_merge(self::scanDirectory($fullpath, $ignoreFolders, $ignoreFiles), $return);
			}
			else
			{
				$return[] = $path . '/' . $file;
			}
		}

		return $return;
	}

	/**
	 * Forces the use of a specific adapter
	 *
	 * @param   string  $className  The name of the class or the name of the adapter
	 */
	public function setAdapter(?string $className = null): void
	{
		if (is_null($className))
		{
			return;
		}

		$adapter = null;

		if (class_exists($className, true))
		{
			$adapter = new $className;
		}
		elseif (class_exists('\\FOF40\\Download\\Adapter\\' . ucfirst($className)))
		{
			$className = '\\FOF40\\Download\\Adapter\\' . ucfirst($className);
			$adapter   = new $className;
		}

		if (!is_object($adapter))
		{
			return;
		}

		if (!$adapter instanceof DownloadInterface)
		{
			return;
		}

		$this->adapter = $adapter;
	}

	/**
	 * Returns the name of the current adapter
	 *
	 * @return string
	 */
	public function getAdapterName(): string
	{
		if (is_object($this->adapter))
		{
			$class = get_class($this->adapter);

			return strtolower(str_ireplace('FOF40\\Download\\Adapter\\', '', $class));
		}

		return '';
	}

	/**
	 * Returns the additional options for the adapter
	 *
	 * @return array
	 *
	 * @codeCoverageIgnore
	 */
	public function getAdapterOptions(): array
	{
		return $this->adapterOptions;
	}

	/**
	 * Sets the additional options for the adapter
	 *
	 * @param   array  $options
	 *
	 * @codeCoverageIgnore
	 */
	public function setAdapterOptions(array $options): void
	{
		$this->adapterOptions = $options;
	}

	/**
	 * Download data from a URL and return it.
	 *
	 * Important note about ranges: byte ranges start at 0. This means that the first 500 bytes of a file are from 0
	 * to 499, NOT from 1 to 500. If you ask more bytes than there are in the file or a range which is invalid or does
	 * not exist this method will return false.
	 *
	 * @param   string  $url   The URL to download from
	 * @param   int     $from  Byte range to start downloading from. Use null (default) for start of file.
	 * @param   int     $to    Byte range to stop downloading. Use null to download the entire file ($from will be
	 *                         ignored!)
	 *
	 * @return  string  The downloaded data or null on failure
	 */
	public function getFromURL(string $url, ?int $from = null, ?int $to = null): ?string
	{
		try
		{
			return $this->adapter->downloadAndReturn($url, $from, $to, $this->adapterOptions);
		}
		catch (DownloadError $e)
		{
			return null;
		}
	}

	/**
	 * Performs the staggered download of file.
	 *
	 * @param   array  $params  A parameters array, as sent by the user interface
	 *
	 * @return  array  A return status array
	 */
	public function importFromURL(array $params): array
	{
		$this->params = $params;

		// Fetch data
		$url           = $this->getParam('url');
		$localFilename = $this->getParam('localFilename');
		$frag          = $this->getParam('frag', -1);
		$totalSize     = $this->getParam('totalSize', -1);
		$doneSize      = $this->getParam('doneSize', -1);
		$maxExecTime   = $this->getParam('maxExecTime', 5);
		$runTimeBias   = $this->getParam('runTimeBias', 75);
		$length        = $this->getParam('length', 1048576);

		if (empty($localFilename))
		{
			$localFilename = basename($url);

			if (strpos($localFilename, '?') !== false)
			{
				$paramsPos     = strpos($localFilename, '?');
				$localFilename = substr($localFilename, 0, $paramsPos - 1);

				$platformBaseDirectories = $this->container->platform->getPlatformBaseDirs();
				$tmpDir                  = $platformBaseDirectories['tmp'];
				$tmpDir                  = rtrim($tmpDir, '/\\');

				$localFilename = $tmpDir . '/' . $localFilename;
			}
		}

		// Init retArray
		$retArray = [
			"status"    => true,
			"error"     => '',
			"frag"      => $frag,
			"totalSize" => $totalSize,
			"doneSize"  => $doneSize,
			"percent"   => 0,
			"localfile" => $localFilename,
		];

		try
		{
			$timer = new Timer($maxExecTime, $runTimeBias);
			$start = $timer->getRunningTime(); // Mark the start of this download
			$break = false; // Don't break the step

			do
			{
				// Do we have to initialize the file?
				if ($frag == -1)
				{
					// Currently downloaded size
					$doneSize = 0;

					if (@file_exists($localFilename))
					{
						@unlink($localFilename);
					}

					// Delete and touch the output file
					$fp = @fopen($localFilename, 'w');

					if ($fp !== false)
					{
						@fclose($fp);
					}

					// Init
					$frag = 0;

					$retArray['totalSize'] = $this->adapter->getFileSize($url);

					if ($retArray['totalSize'] <= 0)
					{
						$retArray['totalSize'] = 0;
					}

					$totalSize = $retArray['totalSize'];
				}

				// Calculate from and length
				$from = $frag * $length;
				$to   = $length + $from - 1;

				// Try to download the first frag
				$required_time = 1.0;

				$error = '';

				try
				{
					$result = $this->adapter->downloadAndReturn($url, $from, $to, $this->adapterOptions);
				}
				catch (DownloadError $e)
				{
					$result = false;
					$error  = $e->getMessage();
				}

				if ($result === false)
				{
					// Failed download
					if ($frag == 0)
					{
						// Failure to download first frag = failure to download. Period.
						$retArray['status'] = false;
						$retArray['error']  = $error;

						return $retArray;
					}
					else
					{
						// Since this is a staggered download, consider this normal and finish
						$frag      = -1;
						$totalSize = $doneSize;
						$break     = true;
					}
				}

				// Add the currently downloaded frag to the total size of downloaded files
				if ($result !== false)
				{
					$fileSize = strlen($result);
					$doneSize += $fileSize;

					// Append the file
					$fp = @fopen($localFilename, 'a');

					if ($fp === false)
					{
						// Can't open the file for writing
						$retArray['status'] = false;
						$retArray['error']  = Text::sprintf('LIB_FOF40_DOWNLOAD_ERR_COULDNOTWRITELOCALFILE', $localFilename);

						return $retArray;
					}

					fwrite($fp, $result);
					fclose($fp);

					$frag++;

					if (($fileSize < $length) || ($fileSize > $length)
						|| (($totalSize == $doneSize) && ($totalSize > 0))
					)
					{
						// A partial download or a download larger than the frag size means we are done
						$frag = -1;
						//debugMsg("-- Import complete (partial download of last frag)");
						$totalSize = $doneSize;
						$break     = true;
					}
				}

				// Advance the frag pointer and mark the end
				$end = $timer->getRunningTime();

				// Do we predict that we have enough time?
				$required_time = max(1.1 * ($end - $start), $required_time);

				if ($required_time > (10 - $end + $start))
				{
					$break = true;
				}

				$start = $end;

			} while (($timer->getTimeLeft() > 0) && !$break);

			if ($frag == -1)
			{
				$percent = 100;
			}
			elseif ($doneSize <= 0)
			{
				$percent = 0;
			}
			elseif ($totalSize > 0)
			{
				$percent = 100 * ($doneSize / $totalSize);
			}
			else
			{
				$percent = 0;
			}

			// Update $retArray
			$retArray = [
				"status"    => true,
				"error"     => '',
				"frag"      => $frag,
				"totalSize" => $totalSize,
				"doneSize"  => $doneSize,
				"percent"   => $percent,
			];
		}
		catch (DownloadError $e)
		{
			$retArray['status'] = false;
			$retArray['error']  = $e->getMessage();
		}

		return $retArray;
	}

	/**
	 * Used to decode the $params array
	 *
	 * @param   string  $key      The parameter key you want to retrieve the value for
	 * @param   mixed   $default  The default value, if none is specified
	 *
	 * @return  mixed  The value for this parameter key
	 */
	private function getParam(string $key, $default = null)
	{
		if (array_key_exists($key, $this->params))
		{
			return $this->params[$key];
		}
		else
		{
			return $default;
		}
	}
}
Exception/DownloadError.php000060400000000464152455614130012002 0ustar00<?php
/**
 * @package   FOF
 * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd
 * @license   GNU General Public License version 3, or later
 */

namespace  FOF40\Download\Exception;

defined('_JEXEC') || die;

use RuntimeException;

class DownloadError extends RuntimeException
{

}
Adapter/Curl.php000060400000016207152455614130007552 0ustar00<?php
/**
 * @package   FOF
 * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd
 * @license   GNU General Public License version 3, or later
 */

namespace FOF40\Download\Adapter;

defined('_JEXEC') || die;

use FOF40\Download\DownloadInterface;
use FOF40\Download\Exception\DownloadError;
use Joomla\CMS\Language\Text;

/**
 * A download adapter using the cURL PHP integration
 */
class Curl extends AbstractAdapter implements DownloadInterface
{
	protected $headers = [];

	public function __construct()
	{
		$this->priority              = 110;
		$this->supportsFileSize      = true;
		$this->supportsChunkDownload = true;
		$this->name                  = 'curl';
		$this->isSupported           = function_exists('curl_init') && function_exists('curl_exec') && function_exists('curl_close');
	}

	/**
	 * Download a part (or the whole) of a remote URL and return the downloaded
	 * data. You are supposed to check the size of the returned data. If it's
	 * smaller than what you expected you've reached end of file. If it's empty
	 * you have tried reading past EOF. If it's larger than what you expected
	 * the server doesn't support chunk downloads.
	 *
	 * If this class' supportsChunkDownload returns false you should assume
	 * that the $from and $to parameters will be ignored.
	 *
	 * @param   string   $url     The remote file's URL
	 * @param   integer  $from    Byte range to start downloading from. Use null for start of file.
	 * @param   integer  $to      Byte range to stop downloading. Use null to download the entire file ($from is
	 *                            ignored)
	 * @param   array    $params  Additional params that will be added before performing the download
	 *
	 * @return  string  The raw file data retrieved from the remote URL.
	 *
	 * @throws  DownloadError  A generic exception is thrown on error
	 */
	public function downloadAndReturn(string $url, ?int $from = null, ?int $to = null, array $params = []): string
	{
		$ch = curl_init();

		if (empty($from))
		{
			$from = 0;
		}

		if (empty($to))
		{
			$to = 0;
		}

		if ($to < $from)
		{
			$temp = $to;
			$to   = $from;
			$from = $temp;
			unset($temp);
		}

		$caCertPath = class_exists('\\Composer\\CaBundle\\CaBundle')
			? \Composer\CaBundle\CaBundle::getBundledCaBundlePath()
			: JPATH_LIBRARIES . '/src/Http/Transport/cacert.pem';

		curl_setopt($ch, CURLOPT_URL, $url);
		curl_setopt($ch, CURLOPT_AUTOREFERER, 1);
		curl_setopt($ch, CURLOPT_BINARYTRANSFER, 1);
		curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
		@curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
		curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 1);
		curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
		curl_setopt($ch, CURLOPT_SSLVERSION, 0);
		curl_setopt($ch, CURLOPT_CAINFO, $caCertPath);
		curl_setopt($ch, CURLOPT_HEADERFUNCTION, [$this, 'reponseHeaderCallback']);

		if (!(empty($from) && empty($to)))
		{
			curl_setopt($ch, CURLOPT_RANGE, "$from-$to");
		}

		if (!is_array($params))
		{
			$params = [];
		}

		$patched_accept_encoding = false;

		// Work around LiteSpeed sending compressed output under HTTP/2 when no encoding was requested
		// See https://github.com/joomla/joomla-cms/issues/21423#issuecomment-410941000
		if (defined('CURLOPT_ACCEPT_ENCODING'))
		{
			if (!array_key_exists(CURLOPT_ACCEPT_ENCODING, $params))
			{
				$params[CURLOPT_ACCEPT_ENCODING] = 'identity';
			}

			$patched_accept_encoding = true;
		}

		foreach ($params as $k => $v)
		{
			// I couldn't patch the accept encoding header (missing constant), so I'll check if we manually set it
			if (!$patched_accept_encoding && $k == CURLOPT_HTTPHEADER)
			{
				foreach ($v as $custom_header)
				{
					// Ok, we explicitly set the Accept-Encoding header, so we consider it patched
					if (stripos($custom_header, 'Accept-Encoding') !== false)
					{
						$patched_accept_encoding = true;
					}
				}
			}

			@curl_setopt($ch, $k, $v);
		}

		// Accept encoding wasn't patched, let's manually do that
		if (!$patched_accept_encoding)
		{
			@curl_setopt($ch, CURLOPT_HTTPHEADER, ['Accept-Encoding: identity']);

			$patched_accept_encoding = true;
		}

		$result = curl_exec($ch);

		$errno       = curl_errno($ch);
		$errmsg      = curl_error($ch);
		$error       = '';
		$http_status = curl_getinfo($ch, CURLINFO_HTTP_CODE);

		if ($result === false)
		{
			$error = Text::sprintf('LIB_FOF40_DOWNLOAD_ERR_CURL_ERROR', $errno, $errmsg);
		}
		elseif (($http_status >= 300) && ($http_status <= 399) && isset($this->headers['location']) && !empty($this->headers['location']))
		{
			return $this->downloadAndReturn($this->headers['location'], $from, $to, $params);
		}
		elseif ($http_status > 399)
		{
			$result = false;
			$errno  = $http_status;
			$error  = Text::sprintf('LIB_FOF40_DOWNLOAD_ERR_HTTPERROR', $http_status);
		}

		curl_close($ch);

		if ($result === false)
		{
			throw new DownloadError($error, $errno);
		}
		else
		{
			return $result;
		}
	}

	/**
	 * Get the size of a remote file in bytes
	 *
	 * @param   string  $url  The remote file's URL
	 *
	 * @return  integer  The file size, or -1 if the remote server doesn't support this feature
	 */
	public function getFileSize(string $url): int
	{
		$result = -1;

		$ch = curl_init();

		curl_setopt($ch, CURLOPT_AUTOREFERER, 1);
		curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 1);
		curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
		curl_setopt($ch, CURLOPT_SSLVERSION, 0);

		$caCertPath = class_exists('\\Composer\\CaBundle\\CaBundle')
			? \Composer\CaBundle\CaBundle::getBundledCaBundlePath()
			: JPATH_LIBRARIES . '/src/Http/Transport/cacert.pem';;

		curl_setopt($ch, CURLOPT_URL, $url);
		curl_setopt($ch, CURLOPT_NOBODY, true);
		curl_setopt($ch, CURLOPT_HEADER, true);
		curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
		@curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
		curl_setopt($ch, CURLOPT_CAINFO, $caCertPath);

		$data = curl_exec($ch);
		curl_close($ch);

		if ($data)
		{
			$content_length = "unknown";
			$status         = "unknown";
			$redirection    = null;

			if (preg_match("/^HTTP\/1\.[01] (\d\d\d)/i", $data, $matches))
			{
				$status = (int) $matches[1];
			}

			if (preg_match("/Content-Length: (\d+)/i", $data, $matches))
			{
				$content_length = (int) $matches[1];
			}

			if (preg_match("/Location: (.*)/i", $data, $matches))
			{
				$redirection = (int) $matches[1];
			}

			if ($status == 200 || ($status > 300 && $status <= 308))
			{
				$result = $content_length;
			}

			if (($status > 300) && ($status <= 308))
			{
				if (!empty($redirection))
				{
					return $this->getFileSize($redirection);
				}

				return -1;
			}
		}

		return (int) $result;
	}

	/**
	 * Handles the HTTP headers returned by cURL
	 *
	 * @param   resource  $ch    cURL resource handle (unused)
	 * @param   string    $data  Each header line, as returned by the server
	 *
	 * @return  int  The length of the $data string
	 */
	protected function reponseHeaderCallback($ch, string $data): int
	{
		$strlen = strlen($data);

		if (($strlen) <= 2)
		{
			return $strlen;
		}

		if (substr($data, 0, 4) == 'HTTP')
		{
			return $strlen;
		}

		if (strpos($data, ':') === false)
		{
			return $strlen;
		}

		[$header, $value] = explode(': ', trim($data), 2);

		$this->headers[strtolower($header)] = $value;

		return $strlen;
	}
}
Adapter/AbstractAdapter.php000060400000006611152455614130011707 0ustar00<?php
/**
 * @package   FOF
 * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd
 * @license   GNU General Public License version 3, or later
 */

namespace  FOF40\Download\Adapter;

defined('_JEXEC') || die;

use FOF40\Download\DownloadInterface;
use FOF40\Download\Exception\DownloadError;

abstract class AbstractAdapter implements DownloadInterface
{
	/**
	 * Load order priority
	 *
	 * @var  int
	 */
	public $priority = 100;

	/**
	 * Name of the adapter (identical to filename)
	 *
	 * @var  string
	 */
	public $name = '';

	/**
	 * Is this adapter supported in the current execution environment?
	 *
	 * @var  bool
	 */
	public $isSupported = false;

	/**
	 * Does this adapter support chunked downloads?
	 *
	 * @var  bool
	 */
	public $supportsChunkDownload = false;

	/**
	 * Does this adapter support querying the remote file's size?
	 *
	 * @var  bool
	 */
	public $supportsFileSize = false;

	/**
	 * Does this download adapter support downloading files in chunks?
	 *
	 * @return  boolean  True if chunk download is supported
	 */
	public function supportsChunkDownload(): bool
	{
		return $this->supportsChunkDownload;
	}

	/**
	 * Does this download adapter support reading the size of a remote file?
	 *
	 * @return  boolean  True if remote file size determination is supported
	 */
	public function supportsFileSize(): bool
	{
		return $this->supportsFileSize;
	}

	/**
	 * Is this download class supported in the current server environment?
	 *
	 * @return  boolean  True if this server environment supports this download class
	 */
	public function isSupported(): bool
	{
		return $this->isSupported;
	}

	/**
	 * Get the priority of this adapter. If multiple download adapters are
	 * supported on a site, the one with the highest priority will be
	 * used.
	 *
	 * @return  int
	 */
	public function getPriority(): int
	{
		return $this->priority;
	}

	/**
	 * Returns the name of this download adapter in use
	 *
	 * @return  string
	 */
	public function getName(): string
	{
		return $this->name;
	}

	/**
	 * Download a part (or the whole) of a remote URL and return the downloaded
	 * data. You are supposed to check the size of the returned data. If it's
	 * smaller than what you expected you've reached end of file. If it's empty
	 * you have tried reading past EOF. If it's larger than what you expected
	 * the server doesn't support chunk downloads.
	 *
	 * If this class' supportsChunkDownload returns false you should assume
	 * that the $from and $to parameters will be ignored.
	 *
	 * @param   string   $url     The remote file's URL
	 * @param   integer  $from    Byte range to start downloading from. Use null for start of file.
	 * @param   integer  $to      Byte range to stop downloading. Use null to download the entire file ($from is ignored)
	 * @param   array    $params  Additional params that will be added before performing the download
	 *
	 * @return  string  The raw file data retrieved from the remote URL.
	 *
	 * @throws  DownloadError  A generic exception is thrown on error
	 */
	public function downloadAndReturn(string $url, ?int $from = null, ?int $to = null, array $params = []): string
	{
		return '';
	}

	/**
	 * Get the size of a remote file in bytes
	 *
	 * @param   string  $url  The remote file's URL
	 *
	 * @return  integer  The file size, or -1 if the remote server doesn't support this feature
	 */
	public function getFileSize(string $url): int
	{
		return -1;
	}
}
Adapter/Fopen.php000060400000010303152455614130007703 0ustar00<?php
/**
 * @package   FOF
 * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd
 * @license   GNU General Public License version 3, or later
 */

namespace FOF40\Download\Adapter;

defined('_JEXEC') || die;

use FOF40\Download\DownloadInterface;
use FOF40\Download\Exception\DownloadError;
use Joomla\CMS\Language\Text;

/**
 * A download adapter using URL fopen() wrappers
 */
class Fopen extends AbstractAdapter implements DownloadInterface
{
	public function __construct()
	{
		$this->priority              = 100;
		$this->supportsFileSize      = false;
		$this->supportsChunkDownload = true;
		$this->name                  = 'fopen';

		$this->isSupported = !function_exists('ini_get') ? false : ini_get('allow_url_fopen');
	}

	/**
	 * Download a part (or the whole) of a remote URL and return the downloaded
	 * data. You are supposed to check the size of the returned data. If it's
	 * smaller than what you expected you've reached end of file. If it's empty
	 * you have tried reading past EOF. If it's larger than what you expected
	 * the server doesn't support chunk downloads.
	 *
	 * If this class' supportsChunkDownload returns false you should assume
	 * that the $from and $to parameters will be ignored.
	 *
	 * @param   string   $url     The remote file's URL
	 * @param   integer  $from    Byte range to start downloading from. Use null for start of file.
	 * @param   integer  $to      Byte range to stop downloading. Use null to download the entire file ($from is
	 *                            ignored)
	 * @param   array    $params  Additional params that will be added before performing the download
	 *
	 * @return  string  The raw file data retrieved from the remote URL.
	 *
	 * @throws  DownloadError  A generic exception is thrown on error
	 */
	public function downloadAndReturn(string $url, ?int $from = null, ?int $to = null, array $params = []): string
	{
		if (empty($from))
		{
			$from = 0;
		}

		if (empty($to))
		{
			$to = 0;
		}

		if ($to < $from)
		{
			$temp = $to;
			$to   = $from;
			$from = $temp;
			unset($temp);
		}


		if (!(empty($from) && empty($to)))
		{
			$caCertPath = class_exists('\\Composer\\CaBundle\\CaBundle')
				? \Composer\CaBundle\CaBundle::getBundledCaBundlePath()
				: JPATH_LIBRARIES . '/src/Http/Transport/cacert.pem';

			$options = [
				'http' => [
					'method' => 'GET',
					'header' => "Range: bytes=$from-$to\r\n",
				],
				'ssl'  => [
					'verify_peer'  => true,
					'cafile'       => $caCertPath,
					'verify_depth' => 5,
				],
			];

			$options = array_merge($options, $params);

			$context = stream_context_create($options);
			$result  = @file_get_contents($url, false, $context, $from - $to + 1);
		}
		else
		{
			$caCertPath = class_exists('\\Composer\\CaBundle\\CaBundle')
				? \Composer\CaBundle\CaBundle::getBundledCaBundlePath()
				: JPATH_LIBRARIES . '/src/Http/Transport/cacert.pem';

			$options = [
				'http' => [
					'method' => 'GET',
				],
				'ssl'  => [
					'verify_peer'  => true,
					'cafile'       => $caCertPath,
					'verify_depth' => 5,
				],
			];

			$options = array_merge($options, $params);

			$context = stream_context_create($options);
			$result  = @file_get_contents($url, false, $context);
		}

		global $http_response_header_test;

		if (!isset($http_response_header) && empty($http_response_header_test))
		{
			$error = Text::_('LIB_FOF40_DOWNLOAD_ERR_FOPEN_ERROR');
			throw new DownloadError($error, 404);
		}
		else
		{
			// Used for testing
			if (!isset($http_response_header) && !empty($http_response_header_test))
			{
				$http_response_header = $http_response_header_test;
			}

			$http_code = 200;
			$nLines    = count($http_response_header);

			for ($i = $nLines - 1; $i >= 0; $i--)
			{
				$line = $http_response_header[$i];
				if (strncasecmp("HTTP", $line, 4) == 0)
				{
					$response  = explode(' ', $line);
					$http_code = $response[1];
					break;
				}
			}

			if ($http_code >= 299)
			{
				$error = Text::sprintf('LIB_FOF40_DOWNLOAD_ERR_HTTPERROR', $http_code);
				throw new DownloadError($error, $http_code);
			}
		}

		if ($result === false)
		{
			$error = Text::sprintf('LIB_FOF40_DOWNLOAD_ERR_FOPEN_ERROR');
			throw new DownloadError($error, 1);
		}
		else
		{
			return $result;
		}
	}
}
DownloadInterface.php000060400000005157152455614130010657 0ustar00<?php
/**
 * @package   FOF
 * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd
 * @license   GNU General Public License version 3, or later
 */

namespace FOF40\Download;

defined('_JEXEC') || die;

use FOF40\Download\Exception\DownloadError;

/**
 * Interface DownloadInterface
 *
 * @codeCoverageIgnore
 */
interface DownloadInterface
{
	/**
	 * Does this download adapter support downloading files in chunks?
	 *
	 * @return  boolean  True if chunk download is supported
	 */
	public function supportsChunkDownload(): bool;

	/**
	 * Does this download adapter support reading the size of a remote file?
	 *
	 * @return  boolean  True if remote file size determination is supported
	 */
	public function supportsFileSize(): bool;

	/**
	 * Is this download class supported in the current server environment?
	 *
	 * @return  boolean  True if this server environment supports this download class
	 */
	public function isSupported(): bool;

	/**
	 * Get the priority of this adapter. If multiple download adapters are
	 * supported on a site, the one with the highest priority will be
	 * used.
	 *
	 * @return  int
	 */
	public function getPriority(): int;

	/**
	 * Returns the name of this download adapter in use
	 *
	 * @return  string
	 */
	public function getName(): string;

	/**
	 * Download a part (or the whole) of a remote URL and return the downloaded
	 * data. You are supposed to check the size of the returned data. If it's
	 * smaller than what you expected you've reached end of file. If it's empty
	 * you have tried reading past EOF. If it's larger than what you expected
	 * the server doesn't support chunk downloads.
	 *
	 * If this class' supportsChunkDownload returns false you should assume
	 * that the $from and $to parameters will be ignored.
	 *
	 * @param string   $url    The remote file's URL
	 * @param int|null $from   Byte range to start downloading from. Use null for start of file.
	 * @param int|null $to     Byte range to stop downloading. Use null to download the entire file ($from is ignored)
	 * @param array    $params Additional params that will be added before performing the download
	 *
	 * @return  string  The raw file data retrieved from the remote URL.
	 *
	 * @throws  DownloadError  A generic exception is thrown on error
	 */
	public function downloadAndReturn(string $url, ?int $from = null, ?int $to = null, array $params = []): string;

	/**
	 * Get the size of a remote file in bytes
	 *
	 * @param string $url The remote file's URL
	 *
	 * @return  integer  The file size, or -1 if the remote server doesn't support this feature
	 */
	public function getFileSize(string $url): int;
}