primo commit
This commit is contained in:
186
libraries/vendor/joomla/filesystem/src/Buffer.php
vendored
Normal file
186
libraries/vendor/joomla/filesystem/src/Buffer.php
vendored
Normal file
@ -0,0 +1,186 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Part of the Joomla Framework Filesystem Package
|
||||
*
|
||||
* @copyright Copyright (C) 2005 - 2021 Open Source Matters, Inc. All rights reserved.
|
||||
* @license GNU General Public License version 2 or later; see LICENSE
|
||||
*/
|
||||
|
||||
namespace Joomla\Filesystem;
|
||||
|
||||
/**
|
||||
* Generic Buffer stream handler
|
||||
*
|
||||
* This class provides a generic buffer stream. It can be used to store/retrieve/manipulate
|
||||
* string buffers with the standard PHP filesystem I/O methods.
|
||||
*
|
||||
* @since 1.0
|
||||
*/
|
||||
class Buffer
|
||||
{
|
||||
/**
|
||||
* Stream position
|
||||
*
|
||||
* @var integer
|
||||
* @since 1.0
|
||||
*/
|
||||
public $position = 0;
|
||||
|
||||
/**
|
||||
* Buffer name
|
||||
*
|
||||
* @var string
|
||||
* @since 1.0
|
||||
*/
|
||||
public $name;
|
||||
|
||||
/**
|
||||
* Buffer hash
|
||||
*
|
||||
* @var array
|
||||
* @since 1.0
|
||||
*/
|
||||
public $buffers = [];
|
||||
|
||||
/**
|
||||
* Function to open file or url
|
||||
*
|
||||
* @param string $path The URL that was passed
|
||||
* @param string $mode Mode used to open the file @see fopen
|
||||
* @param integer $options Flags used by the API, may be STREAM_USE_PATH and STREAM_REPORT_ERRORS
|
||||
* @param string $openedPath Full path of the resource. Used with STREAN_USE_PATH option
|
||||
*
|
||||
* @return boolean
|
||||
*
|
||||
* @since 1.0
|
||||
* @see streamWrapper::stream_open
|
||||
*/
|
||||
public function stream_open($path, $mode, $options, &$openedPath)
|
||||
{
|
||||
$url = parse_url($path);
|
||||
$this->name = $url['host'];
|
||||
$this->buffers[$this->name] = null;
|
||||
$this->position = 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read stream
|
||||
*
|
||||
* @param integer $count How many bytes of data from the current position should be returned.
|
||||
*
|
||||
* @return mixed The data from the stream up to the specified number of bytes (all data if
|
||||
* the total number of bytes in the stream is less than $count. Null if
|
||||
* the stream is empty.
|
||||
*
|
||||
* @see streamWrapper::stream_read
|
||||
* @since 1.0
|
||||
*/
|
||||
public function stream_read($count)
|
||||
{
|
||||
$ret = substr($this->buffers[$this->name], $this->position, $count);
|
||||
$this->position += \strlen($ret);
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write stream
|
||||
*
|
||||
* @param string $data The data to write to the stream.
|
||||
*
|
||||
* @return integer
|
||||
*
|
||||
* @see streamWrapper::stream_write
|
||||
* @since 1.0
|
||||
*/
|
||||
public function stream_write($data)
|
||||
{
|
||||
$left = substr($this->buffers[$this->name], 0, $this->position);
|
||||
$right = substr($this->buffers[$this->name], $this->position + \strlen($data));
|
||||
$this->buffers[$this->name] = $left . $data . $right;
|
||||
$this->position += \strlen($data);
|
||||
|
||||
return \strlen($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Function to get the current position of the stream
|
||||
*
|
||||
* @return integer
|
||||
*
|
||||
* @see streamWrapper::stream_tell
|
||||
* @since 1.0
|
||||
*/
|
||||
public function stream_tell()
|
||||
{
|
||||
return $this->position;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function to test for end of file pointer
|
||||
*
|
||||
* @return boolean True if the pointer is at the end of the stream
|
||||
*
|
||||
* @see streamWrapper::stream_eof
|
||||
* @since 1.0
|
||||
*/
|
||||
public function stream_eof()
|
||||
{
|
||||
return $this->position >= \strlen($this->buffers[$this->name]);
|
||||
}
|
||||
|
||||
/**
|
||||
* The read write position updates in response to $offset and $whence
|
||||
*
|
||||
* @param integer $offset The offset in bytes
|
||||
* @param integer $whence Position the offset is added to
|
||||
* Options are SEEK_SET, SEEK_CUR, and SEEK_END
|
||||
*
|
||||
* @return boolean True if updated
|
||||
*
|
||||
* @see streamWrapper::stream_seek
|
||||
* @since 1.0
|
||||
*/
|
||||
public function stream_seek($offset, $whence)
|
||||
{
|
||||
switch ($whence) {
|
||||
case \SEEK_SET:
|
||||
if ($offset < \strlen($this->buffers[$this->name]) && $offset >= 0) {
|
||||
$this->position = $offset;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
case \SEEK_CUR:
|
||||
if ($offset >= 0) {
|
||||
$this->position += $offset;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
case \SEEK_END:
|
||||
if (\strlen($this->buffers[$this->name]) + $offset >= 0) {
|
||||
$this->position = \strlen($this->buffers[$this->name]) + $offset;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Register the stream
|
||||
// phpcs:disable PSR1.Files.SideEffects
|
||||
stream_wrapper_register('buffer', 'Joomla\\Filesystem\\Buffer');
|
||||
// phpcs:enable PSR1.Files.SideEffects
|
||||
1635
libraries/vendor/joomla/filesystem/src/Clients/FtpClient.php
vendored
Normal file
1635
libraries/vendor/joomla/filesystem/src/Clients/FtpClient.php
vendored
Normal file
File diff suppressed because it is too large
Load Diff
38
libraries/vendor/joomla/filesystem/src/Exception/FilesystemException.php
vendored
Normal file
38
libraries/vendor/joomla/filesystem/src/Exception/FilesystemException.php
vendored
Normal file
@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Part of the Joomla Framework Filesystem Package
|
||||
*
|
||||
* @copyright Copyright (C) 2005 - 2021 Open Source Matters, Inc. All rights reserved.
|
||||
* @license GNU General Public License version 2 or later; see LICENSE
|
||||
*/
|
||||
|
||||
namespace Joomla\Filesystem\Exception;
|
||||
|
||||
use Joomla\Filesystem\Path;
|
||||
|
||||
/**
|
||||
* Exception class for handling errors in the Filesystem package
|
||||
*
|
||||
* @since 1.2.0
|
||||
* @change 2.0.1 If the message contains a full path, the root path (JPATH_ROOT) is removed from it
|
||||
* to avoid any full path disclosure. Before 2.0.1, the path was propagated as provided.
|
||||
*/
|
||||
class FilesystemException extends \RuntimeException
|
||||
{
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param string $message The message
|
||||
* @param integer $code The code
|
||||
* @param \Throwable|null $previous A previous exception
|
||||
*/
|
||||
public function __construct($message = "", $code = 0, ?\Throwable $previous = null)
|
||||
{
|
||||
parent::__construct(
|
||||
Path::removeRoot($message),
|
||||
$code,
|
||||
$previous
|
||||
);
|
||||
}
|
||||
}
|
||||
341
libraries/vendor/joomla/filesystem/src/File.php
vendored
Normal file
341
libraries/vendor/joomla/filesystem/src/File.php
vendored
Normal file
@ -0,0 +1,341 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Part of the Joomla Framework Filesystem Package
|
||||
*
|
||||
* @copyright Copyright (C) 2005 - 2021 Open Source Matters, Inc. All rights reserved.
|
||||
* @license GNU General Public License version 2 or later; see LICENSE
|
||||
*/
|
||||
|
||||
namespace Joomla\Filesystem;
|
||||
|
||||
use Joomla\Filesystem\Exception\FilesystemException;
|
||||
|
||||
/**
|
||||
* A File handling class
|
||||
*
|
||||
* @since 1.0
|
||||
*/
|
||||
class File
|
||||
{
|
||||
/**
|
||||
* Gets the extension of a file name
|
||||
*
|
||||
* @param string $file The file name
|
||||
*
|
||||
* @return string The file extension
|
||||
*
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public static function getExt($file)
|
||||
{
|
||||
// String manipulation should be faster than pathinfo() on newer PHP versions.
|
||||
$dot = strrpos($file, '.');
|
||||
|
||||
if ($dot === false) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$ext = substr($file, $dot + 1);
|
||||
|
||||
// Extension cannot contain slashes.
|
||||
if (strpos($ext, '/') !== false || (DIRECTORY_SEPARATOR === '\\' && strpos($ext, '\\') !== false)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return $ext;
|
||||
}
|
||||
|
||||
/**
|
||||
* Strips the last extension off of a file name
|
||||
*
|
||||
* @param string $file The file name
|
||||
*
|
||||
* @return string The file name without the extension
|
||||
*
|
||||
* @since 1.0
|
||||
*/
|
||||
public static function stripExt($file)
|
||||
{
|
||||
return preg_replace('#\.[^.]*$#', '', $file);
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes the file name safe to use
|
||||
*
|
||||
* @param string $file The name of the file [not full path]
|
||||
* @param array $stripChars Array of regex (by default will remove any leading periods)
|
||||
*
|
||||
* @return string The sanitised string
|
||||
*
|
||||
* @since 1.0
|
||||
*/
|
||||
public static function makeSafe($file, array $stripChars = ['#^\.#'])
|
||||
{
|
||||
// Try transliterating the file name using the native php function
|
||||
if (function_exists('transliterator_transliterate') && function_exists('iconv')) {
|
||||
// Using iconv to ignore characters that can't be transliterated
|
||||
$file = iconv("UTF-8", "ASCII//TRANSLIT//IGNORE", transliterator_transliterate('Any-Latin; Latin-ASCII', $file));
|
||||
}
|
||||
|
||||
$regex = array_merge(['#(\.){2,}#', '#[^A-Za-z0-9\.\_\- ]#'], $stripChars);
|
||||
$file = preg_replace($regex, '', $file);
|
||||
|
||||
// Remove any trailing dots, as those aren't ever valid file names.
|
||||
$file = rtrim($file, '.');
|
||||
|
||||
return trim($file);
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies a file
|
||||
*
|
||||
* @param string $src The path to the source file
|
||||
* @param string $dest The path to the destination file
|
||||
* @param string $path An optional base path to prefix to the file names
|
||||
* @param boolean $useStreams True to use streams
|
||||
*
|
||||
* @return boolean True on success
|
||||
*
|
||||
* @since 1.0
|
||||
* @throws FilesystemException
|
||||
* @throws \UnexpectedValueException
|
||||
*/
|
||||
public static function copy($src, $dest, $path = null, $useStreams = false)
|
||||
{
|
||||
// Prepend a base path if it exists
|
||||
if ($path) {
|
||||
$src = Path::clean($path . '/' . $src);
|
||||
$dest = Path::clean($path . '/' . $dest);
|
||||
}
|
||||
|
||||
// Check src path
|
||||
if (!is_readable($src)) {
|
||||
throw new \UnexpectedValueException(
|
||||
sprintf(
|
||||
"%s: Cannot find or read file: %s",
|
||||
__METHOD__,
|
||||
Path::removeRoot($src)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if ($useStreams) {
|
||||
$stream = Stream::getStream();
|
||||
|
||||
if (!$stream->copy($src, $dest, null, false)) {
|
||||
throw new FilesystemException(sprintf('%1$s(%2$s, %3$s): %4$s', __METHOD__, $src, $dest, $stream->getError()));
|
||||
}
|
||||
|
||||
self::invalidateFileCache($dest);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!@ copy($src, $dest)) {
|
||||
throw new FilesystemException(__METHOD__ . ': Copy failed.');
|
||||
}
|
||||
|
||||
self::invalidateFileCache($dest);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a file or array of files
|
||||
*
|
||||
* @param mixed $file The file name or an array of file names
|
||||
*
|
||||
* @return boolean True on success
|
||||
*
|
||||
* @since 1.0
|
||||
* @throws FilesystemException
|
||||
*/
|
||||
public static function delete($file)
|
||||
{
|
||||
$files = (array) $file;
|
||||
|
||||
foreach ($files as $file) {
|
||||
$file = Path::clean($file);
|
||||
$filename = basename($file);
|
||||
|
||||
// Try making the file writable first. If it's read-only, it can't be deleted
|
||||
// on Windows, even if the parent folder is writable
|
||||
@chmod($file, 0777);
|
||||
|
||||
// In case of restricted permissions we zap it one way or the other
|
||||
// as long as the owner is either the webserver or the ftp
|
||||
if (!@ unlink($file)) {
|
||||
throw new FilesystemException(__METHOD__ . ': Failed deleting ' . $filename);
|
||||
}
|
||||
|
||||
self::invalidateFileCache($file);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves a file
|
||||
*
|
||||
* @param string $src The path to the source file
|
||||
* @param string $dest The path to the destination file
|
||||
* @param string $path An optional base path to prefix to the file names
|
||||
* @param boolean $useStreams True to use streams
|
||||
*
|
||||
* @return boolean True on success
|
||||
*
|
||||
* @since 1.0
|
||||
* @throws FilesystemException
|
||||
*/
|
||||
public static function move($src, $dest, $path = '', $useStreams = false)
|
||||
{
|
||||
if ($path) {
|
||||
$src = Path::clean($path . '/' . $src);
|
||||
$dest = Path::clean($path . '/' . $dest);
|
||||
}
|
||||
|
||||
// Check src path
|
||||
if (!is_readable($src)) {
|
||||
return 'Cannot find source file.';
|
||||
}
|
||||
|
||||
if ($useStreams) {
|
||||
$stream = Stream::getStream();
|
||||
|
||||
if (!$stream->move($src, $dest, null, false)) {
|
||||
throw new FilesystemException(__METHOD__ . ': ' . $stream->getError());
|
||||
}
|
||||
|
||||
self::invalidateFileCache($dest);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!@ rename($src, $dest)) {
|
||||
throw new FilesystemException(__METHOD__ . ': Rename failed.');
|
||||
}
|
||||
|
||||
self::invalidateFileCache($dest);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write contents to a file
|
||||
*
|
||||
* @param string $file The full file path
|
||||
* @param string $buffer The buffer to write
|
||||
* @param boolean $useStreams Use streams
|
||||
* @param boolean $appendToFile Append to the file and not overwrite it.
|
||||
*
|
||||
* @return boolean True on success
|
||||
*
|
||||
* @since 1.0
|
||||
*/
|
||||
public static function write($file, $buffer, $useStreams = false, $appendToFile = false)
|
||||
{
|
||||
if (\function_exists('set_time_limit')) {
|
||||
set_time_limit(ini_get('max_execution_time'));
|
||||
}
|
||||
|
||||
// If the destination directory doesn't exist we need to create it
|
||||
if (!file_exists(\dirname($file))) {
|
||||
Folder::create(\dirname($file));
|
||||
}
|
||||
|
||||
if ($useStreams) {
|
||||
$stream = Stream::getStream();
|
||||
|
||||
// Beef up the chunk size to a meg
|
||||
$stream->set('chunksize', (1024 * 1024));
|
||||
$stream->writeFile($file, $buffer, $appendToFile);
|
||||
|
||||
self::invalidateFileCache($file);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
$file = Path::clean($file);
|
||||
|
||||
// Set the required flag to only append to the file and not overwrite it
|
||||
if ($appendToFile === true) {
|
||||
$res = \is_int(file_put_contents($file, $buffer, \FILE_APPEND));
|
||||
} else {
|
||||
$res = \is_int(file_put_contents($file, $buffer));
|
||||
}
|
||||
|
||||
self::invalidateFileCache($file);
|
||||
|
||||
return $res;
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves an uploaded file to a destination folder
|
||||
*
|
||||
* @param string $src The name of the php (temporary) uploaded file
|
||||
* @param string $dest The path (including filename) to move the uploaded file to
|
||||
* @param boolean $useStreams True to use streams
|
||||
*
|
||||
* @return boolean True on success
|
||||
*
|
||||
* @since 1.0
|
||||
* @throws FilesystemException
|
||||
*/
|
||||
public static function upload($src, $dest, $useStreams = false)
|
||||
{
|
||||
// Ensure that the path is valid and clean
|
||||
$dest = Path::clean($dest);
|
||||
|
||||
// Create the destination directory if it does not exist
|
||||
$baseDir = \dirname($dest);
|
||||
|
||||
if (!is_dir($baseDir)) {
|
||||
Folder::create($baseDir);
|
||||
}
|
||||
|
||||
if ($useStreams) {
|
||||
$stream = Stream::getStream();
|
||||
|
||||
if (!$stream->upload($src, $dest, null, false)) {
|
||||
throw new FilesystemException(sprintf('%1$s(%2$s, %3$s): %4$s', __METHOD__, $src, $dest, $stream->getError()));
|
||||
}
|
||||
|
||||
self::invalidateFileCache($dest);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (is_writable($baseDir) && move_uploaded_file($src, $dest)) {
|
||||
// Short circuit to prevent file permission errors
|
||||
if (Path::setPermissions($dest)) {
|
||||
self::invalidateFileCache($dest);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
throw new FilesystemException(__METHOD__ . ': Failed to change file permissions.');
|
||||
}
|
||||
|
||||
throw new FilesystemException(__METHOD__ . ': Failed to move file.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Invalidate any opcache for a newly written file immediately, if opcache* functions exist and if this was a PHP file.
|
||||
*
|
||||
* @param string $file The path to the file just written to, to flush from opcache
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function invalidateFileCache($file)
|
||||
{
|
||||
if (function_exists('opcache_invalidate')) {
|
||||
$info = pathinfo($file);
|
||||
|
||||
if (isset($info['extension']) && $info['extension'] === 'php') {
|
||||
// Force invalidation to be absolutely sure the opcache is cleared for this file.
|
||||
opcache_invalidate($file, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
548
libraries/vendor/joomla/filesystem/src/Folder.php
vendored
Normal file
548
libraries/vendor/joomla/filesystem/src/Folder.php
vendored
Normal file
@ -0,0 +1,548 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Part of the Joomla Framework Filesystem Package
|
||||
*
|
||||
* @copyright Copyright (C) 2005 - 2021 Open Source Matters, Inc. All rights reserved.
|
||||
* @license GNU General Public License version 2 or later; see LICENSE
|
||||
*/
|
||||
|
||||
namespace Joomla\Filesystem;
|
||||
|
||||
use Joomla\Filesystem\Exception\FilesystemException;
|
||||
|
||||
/**
|
||||
* A Folder handling class
|
||||
*
|
||||
* @since 1.0
|
||||
*/
|
||||
abstract class Folder
|
||||
{
|
||||
/**
|
||||
* Copy a folder.
|
||||
*
|
||||
* @param string $src The path to the source folder.
|
||||
* @param string $dest The path to the destination folder.
|
||||
* @param string $path An optional base path to prefix to the file names.
|
||||
* @param boolean $force Force copy.
|
||||
* @param boolean $useStreams Optionally force folder/file overwrites.
|
||||
*
|
||||
* @return boolean True on success.
|
||||
*
|
||||
* @since 1.0
|
||||
* @throws FilesystemException
|
||||
*/
|
||||
public static function copy($src, $dest, $path = '', $force = false, $useStreams = false)
|
||||
{
|
||||
if (\function_exists('set_time_limit')) {
|
||||
set_time_limit(ini_get('max_execution_time'));
|
||||
}
|
||||
|
||||
if ($path) {
|
||||
$src = Path::clean($path . '/' . $src);
|
||||
$dest = Path::clean($path . '/' . $dest);
|
||||
}
|
||||
|
||||
// Eliminate trailing directory separators, if any
|
||||
$src = rtrim($src, \DIRECTORY_SEPARATOR);
|
||||
$dest = rtrim($dest, \DIRECTORY_SEPARATOR);
|
||||
|
||||
if (!is_dir(Path::clean($src))) {
|
||||
throw new FilesystemException('Source folder not found', -1);
|
||||
}
|
||||
|
||||
if (is_dir(Path::clean($dest)) && !$force) {
|
||||
throw new FilesystemException('Destination folder not found', -1);
|
||||
}
|
||||
|
||||
// Make sure the destination exists
|
||||
if (!self::create($dest)) {
|
||||
throw new FilesystemException('Cannot create destination folder', -1);
|
||||
}
|
||||
|
||||
if (!($dh = @opendir($src))) {
|
||||
throw new FilesystemException('Cannot open source folder', -1);
|
||||
}
|
||||
|
||||
// Walk through the directory copying files and recursing into folders.
|
||||
while (($file = readdir($dh)) !== false) {
|
||||
$sfid = $src . '/' . $file;
|
||||
$dfid = $dest . '/' . $file;
|
||||
|
||||
switch (filetype($sfid)) {
|
||||
case 'dir':
|
||||
if ($file != '.' && $file != '..') {
|
||||
$ret = self::copy($sfid, $dfid, null, $force, $useStreams);
|
||||
|
||||
if ($ret !== true) {
|
||||
return $ret;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 'file':
|
||||
if ($useStreams) {
|
||||
Stream::getStream()->copy($sfid, $dfid);
|
||||
} else {
|
||||
if (!@copy($sfid, $dfid)) {
|
||||
throw new FilesystemException('Copy file failed', -1);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a folder -- and all necessary parent folders.
|
||||
*
|
||||
* @param string $path A path to create from the base path.
|
||||
* @param integer $mode Directory permissions to set for folders created. 0755 by default.
|
||||
*
|
||||
* @return boolean True if successful.
|
||||
*
|
||||
* @since 1.0
|
||||
* @throws FilesystemException
|
||||
*/
|
||||
public static function create($path = '', $mode = 0755)
|
||||
{
|
||||
static $nested = 0;
|
||||
|
||||
// Check to make sure the path valid and clean
|
||||
$path = Path::clean($path);
|
||||
|
||||
// Check if parent dir exists
|
||||
$parent = \dirname($path);
|
||||
|
||||
if (!is_dir(Path::clean($parent))) {
|
||||
// Prevent infinite loops!
|
||||
$nested++;
|
||||
|
||||
if (($nested > 20) || ($parent == $path)) {
|
||||
throw new FilesystemException(__METHOD__ . ': Infinite loop detected');
|
||||
}
|
||||
|
||||
try {
|
||||
// Create the parent directory
|
||||
if (self::create($parent, $mode) !== true) {
|
||||
// Folder::create throws an error
|
||||
$nested--;
|
||||
|
||||
return false;
|
||||
}
|
||||
} catch (FilesystemException $exception) {
|
||||
$nested--;
|
||||
|
||||
throw $exception;
|
||||
}
|
||||
|
||||
// OK, parent directory has been created
|
||||
$nested--;
|
||||
}
|
||||
|
||||
// Check if dir already exists
|
||||
if (is_dir(Path::clean($path))) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// We need to get and explode the open_basedir paths
|
||||
$obd = ini_get('open_basedir');
|
||||
|
||||
// If open_basedir is set we need to get the open_basedir that the path is in
|
||||
if ($obd != null) {
|
||||
if (\defined('PHP_WINDOWS_VERSION_MAJOR')) {
|
||||
$obdSeparator = ';';
|
||||
} else {
|
||||
$obdSeparator = ':';
|
||||
}
|
||||
|
||||
// Create the array of open_basedir paths
|
||||
$obdArray = explode($obdSeparator, $obd);
|
||||
$inBaseDir = false;
|
||||
|
||||
// Iterate through open_basedir paths looking for a match
|
||||
foreach ($obdArray as $test) {
|
||||
$test = Path::clean($test);
|
||||
|
||||
if (strpos($path, $test) === 0 || strpos($path, realpath($test)) === 0) {
|
||||
$inBaseDir = true;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ($inBaseDir == false) {
|
||||
// Throw a FilesystemException because the path to be created is not in open_basedir
|
||||
throw new FilesystemException(__METHOD__ . ': Path not in open_basedir paths');
|
||||
}
|
||||
}
|
||||
|
||||
// First set umask
|
||||
$origmask = @umask(0);
|
||||
|
||||
// Create the path
|
||||
if (!$ret = @mkdir($path, $mode)) {
|
||||
@umask($origmask);
|
||||
|
||||
throw new FilesystemException(__METHOD__ . ': Could not create directory. Path: ' . $path);
|
||||
}
|
||||
|
||||
// Reset umask
|
||||
@umask($origmask);
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a folder.
|
||||
*
|
||||
* @param string $path The path to the folder to delete.
|
||||
*
|
||||
* @return boolean True on success.
|
||||
*
|
||||
* @since 1.0
|
||||
* @throws FilesystemException
|
||||
* @throws \UnexpectedValueException
|
||||
*/
|
||||
public static function delete($path)
|
||||
{
|
||||
if (\function_exists('set_time_limit')) {
|
||||
set_time_limit(ini_get('max_execution_time'));
|
||||
}
|
||||
|
||||
// Sanity check
|
||||
if (!$path) {
|
||||
// Bad programmer! Bad Bad programmer!
|
||||
throw new FilesystemException(__METHOD__ . ': You can not delete a base directory.');
|
||||
}
|
||||
|
||||
// Check to make sure the path valid and clean
|
||||
$path = Path::clean($path);
|
||||
|
||||
// Is this really a folder?
|
||||
if (!is_dir($path)) {
|
||||
throw new \UnexpectedValueException(
|
||||
sprintf(
|
||||
'%1$s: Path is not a folder. Path: %2$s',
|
||||
__METHOD__,
|
||||
Path::removeRoot($path)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// Remove all the files in folder if they exist; disable all filtering
|
||||
$files = self::files($path, '.', false, true, [], []);
|
||||
|
||||
if (!empty($files)) {
|
||||
if (File::delete($files) !== true) {
|
||||
// File::delete throws an error
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Remove sub-folders of folder; disable all filtering
|
||||
$folders = self::folders($path, '.', false, true, [], []);
|
||||
|
||||
foreach ($folders as $folder) {
|
||||
if (is_link($folder)) {
|
||||
// Don't descend into linked directories, just delete the link.
|
||||
if (File::delete($folder) !== true) {
|
||||
// File::delete throws an error
|
||||
return false;
|
||||
}
|
||||
} elseif (self::delete($folder) !== true) {
|
||||
// Folder::delete throws an error
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// In case of restricted permissions we zap it one way or the other as long as the owner is either the webserver or the ftp.
|
||||
if (@rmdir($path)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
throw new FilesystemException(sprintf('%1$s: Could not delete folder. Path: %2$s', __METHOD__, $path));
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves a folder.
|
||||
*
|
||||
* @param string $src The path to the source folder.
|
||||
* @param string $dest The path to the destination folder.
|
||||
* @param string $path An optional base path to prefix to the file names.
|
||||
* @param boolean $useStreams Optionally use streams.
|
||||
*
|
||||
* @return string|boolean Error message on false or boolean true on success.
|
||||
*
|
||||
* @since 1.0
|
||||
*/
|
||||
public static function move($src, $dest, $path = '', $useStreams = false)
|
||||
{
|
||||
if ($path) {
|
||||
$src = Path::clean($path . '/' . $src);
|
||||
$dest = Path::clean($path . '/' . $dest);
|
||||
}
|
||||
|
||||
if (!is_dir(Path::clean($src))) {
|
||||
return 'Cannot find source folder';
|
||||
}
|
||||
|
||||
if (is_dir(Path::clean($dest))) {
|
||||
return 'Folder already exists';
|
||||
}
|
||||
|
||||
if ($useStreams) {
|
||||
Stream::getStream()->move($src, $dest);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!@rename($src, $dest)) {
|
||||
return 'Rename failed';
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility function to read the files in a folder.
|
||||
*
|
||||
* @param string $path The path of the folder to read.
|
||||
* @param string $filter A filter for file names.
|
||||
* @param mixed $recurse True to recursively search into sub-folders, or an integer to specify the maximum depth.
|
||||
* @param boolean $full True to return the full path to the file.
|
||||
* @param array $exclude Array with names of files which should not be shown in the result.
|
||||
* @param array $excludeFilter Array of filter to exclude
|
||||
* @param boolean $naturalSort False for asort, true for natsort
|
||||
*
|
||||
* @return array Files in the given folder.
|
||||
*
|
||||
* @since 1.0
|
||||
* @throws \UnexpectedValueException
|
||||
*/
|
||||
public static function files(
|
||||
$path,
|
||||
$filter = '.',
|
||||
$recurse = false,
|
||||
$full = false,
|
||||
$exclude = ['.svn', 'CVS', '.DS_Store', '__MACOSX'],
|
||||
$excludeFilter = ['^\..*', '.*~'],
|
||||
$naturalSort = false
|
||||
) {
|
||||
// Check to make sure the path valid and clean
|
||||
$path = Path::clean($path);
|
||||
|
||||
// Is the path a folder?
|
||||
if (!is_dir($path)) {
|
||||
throw new \UnexpectedValueException(
|
||||
sprintf(
|
||||
'%1$s: Path is not a folder. Path: %2$s',
|
||||
__METHOD__,
|
||||
Path::removeRoot($path)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// Compute the excludefilter string
|
||||
if (\count($excludeFilter)) {
|
||||
$excludeFilterString = '/(' . implode('|', $excludeFilter) . ')/';
|
||||
} else {
|
||||
$excludeFilterString = '';
|
||||
}
|
||||
|
||||
// Get the files
|
||||
$arr = self::_items($path, $filter, $recurse, $full, $exclude, $excludeFilterString, true);
|
||||
|
||||
// Sort the files based on either natural or alpha method
|
||||
if ($naturalSort) {
|
||||
natsort($arr);
|
||||
} else {
|
||||
asort($arr);
|
||||
}
|
||||
|
||||
return array_values($arr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility function to read the folders in a folder.
|
||||
*
|
||||
* @param string $path The path of the folder to read.
|
||||
* @param string $filter A filter for folder names.
|
||||
* @param mixed $recurse True to recursively search into sub-folders, or an integer to specify the maximum depth.
|
||||
* @param boolean $full True to return the full path to the folders.
|
||||
* @param array $exclude Array with names of folders which should not be shown in the result.
|
||||
* @param array $excludeFilter Array with regular expressions matching folders which should not be shown in the result.
|
||||
*
|
||||
* @return array Folders in the given folder.
|
||||
*
|
||||
* @since 1.0
|
||||
* @throws \UnexpectedValueException
|
||||
*/
|
||||
public static function folders(
|
||||
$path,
|
||||
$filter = '.',
|
||||
$recurse = false,
|
||||
$full = false,
|
||||
$exclude = ['.svn', 'CVS', '.DS_Store', '__MACOSX'],
|
||||
$excludeFilter = ['^\..*']
|
||||
) {
|
||||
// Check to make sure the path valid and clean
|
||||
$path = Path::clean($path);
|
||||
|
||||
// Is the path a folder?
|
||||
if (!is_dir($path)) {
|
||||
throw new \UnexpectedValueException(
|
||||
sprintf(
|
||||
'%1$s: Path is not a folder. Path: %2$s',
|
||||
__METHOD__,
|
||||
Path::removeRoot($path)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// Compute the excludefilter string
|
||||
if (\count($excludeFilter)) {
|
||||
$excludeFilterString = '/(' . implode('|', $excludeFilter) . ')/';
|
||||
} else {
|
||||
$excludeFilterString = '';
|
||||
}
|
||||
|
||||
// Get the folders
|
||||
$arr = self::_items($path, $filter, $recurse, $full, $exclude, $excludeFilterString, false);
|
||||
|
||||
// Sort the folders
|
||||
asort($arr);
|
||||
|
||||
return array_values($arr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Function to read the files/folders in a folder.
|
||||
*
|
||||
* @param string $path The path of the folder to read.
|
||||
* @param string $filter A filter for file names.
|
||||
* @param mixed $recurse True to recursively search into sub-folders, or an integer to specify the maximum depth.
|
||||
* @param boolean $full True to return the full path to the file.
|
||||
* @param array $exclude Array with names of files which should not be shown in the result.
|
||||
* @param string $excludeFilterString Regexp of files to exclude
|
||||
* @param boolean $findfiles True to read the files, false to read the folders
|
||||
*
|
||||
* @return array Files.
|
||||
*
|
||||
* @since 1.0
|
||||
*/
|
||||
protected static function _items($path, $filter, $recurse, $full, $exclude, $excludeFilterString, $findfiles)
|
||||
{
|
||||
if (\function_exists('set_time_limit')) {
|
||||
set_time_limit(ini_get('max_execution_time'));
|
||||
}
|
||||
|
||||
$arr = [];
|
||||
|
||||
// Read the source directory
|
||||
if (!($handle = @opendir($path))) {
|
||||
return $arr;
|
||||
}
|
||||
|
||||
while (($file = readdir($handle)) !== false) {
|
||||
if (
|
||||
$file != '.' && $file != '..' && !\in_array($file, $exclude)
|
||||
&& (empty($excludeFilterString) || !preg_match($excludeFilterString, $file))
|
||||
) {
|
||||
// Compute the fullpath
|
||||
$fullpath = Path::clean($path . '/' . $file);
|
||||
|
||||
// Compute the isDir flag
|
||||
$isDir = is_dir($fullpath);
|
||||
|
||||
if (($isDir xor $findfiles) && preg_match("/$filter/", $file)) {
|
||||
// (fullpath is dir and folders are searched or fullpath is not dir and files are searched) and file matches the filter
|
||||
if ($full) {
|
||||
// Full path is requested
|
||||
$arr[] = $fullpath;
|
||||
} else {
|
||||
// Filename is requested
|
||||
$arr[] = $file;
|
||||
}
|
||||
}
|
||||
|
||||
if ($isDir && $recurse) {
|
||||
// Search recursively
|
||||
if (\is_int($recurse)) {
|
||||
// Until depth 0 is reached
|
||||
$arr = array_merge($arr, self::_items($fullpath, $filter, $recurse - 1, $full, $exclude, $excludeFilterString, $findfiles));
|
||||
} else {
|
||||
$arr = array_merge($arr, self::_items($fullpath, $filter, $recurse, $full, $exclude, $excludeFilterString, $findfiles));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
closedir($handle);
|
||||
|
||||
return $arr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lists folder in format suitable for tree display.
|
||||
*
|
||||
* @param string $path The path of the folder to read.
|
||||
* @param string $filter A filter for folder names.
|
||||
* @param integer $maxLevel The maximum number of levels to recursively read, defaults to three.
|
||||
* @param integer $level The current level, optional.
|
||||
* @param integer $parent Unique identifier of the parent folder, if any.
|
||||
*
|
||||
* @return array Folders in the given folder.
|
||||
*
|
||||
* @since 1.0
|
||||
*/
|
||||
public static function listFolderTree($path, $filter, $maxLevel = 3, $level = 0, $parent = 0)
|
||||
{
|
||||
$dirs = [];
|
||||
|
||||
if ($level == 0) {
|
||||
$GLOBALS['_JFolder_folder_tree_index'] = 0;
|
||||
}
|
||||
|
||||
if ($level < $maxLevel) {
|
||||
$folders = self::folders($path, $filter);
|
||||
|
||||
// First path, index foldernames
|
||||
foreach ($folders as $name) {
|
||||
$id = ++$GLOBALS['_JFolder_folder_tree_index'];
|
||||
$fullName = Path::clean($path . '/' . $name);
|
||||
$dirs[] = [
|
||||
'id' => $id,
|
||||
'parent' => $parent,
|
||||
'name' => $name,
|
||||
'fullname' => $fullName,
|
||||
'relname' => str_replace(JPATH_ROOT, '', $fullName),
|
||||
];
|
||||
$dirs2 = self::listFolderTree($fullName, $filter, $maxLevel, $level + 1, $id);
|
||||
$dirs = array_merge($dirs, $dirs2);
|
||||
}
|
||||
}
|
||||
|
||||
return $dirs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes path name safe to use.
|
||||
*
|
||||
* @param string $path The full path to sanitise.
|
||||
*
|
||||
* @return string The sanitised string.
|
||||
*
|
||||
* @since 1.0
|
||||
*/
|
||||
public static function makeSafe($path)
|
||||
{
|
||||
$regex = ['#[^A-Za-z0-9_\\\/\(\)\[\]\{\}\#\$\^\+\.\'~`!@&=;,-]#'];
|
||||
|
||||
return preg_replace($regex, '', $path);
|
||||
}
|
||||
}
|
||||
280
libraries/vendor/joomla/filesystem/src/Helper.php
vendored
Normal file
280
libraries/vendor/joomla/filesystem/src/Helper.php
vendored
Normal file
@ -0,0 +1,280 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Part of the Joomla Framework Filesystem Package
|
||||
*
|
||||
* @copyright Copyright (C) 2005 - 2021 Open Source Matters, Inc. All rights reserved.
|
||||
* @license GNU General Public License version 2 or later; see LICENSE
|
||||
*/
|
||||
|
||||
namespace Joomla\Filesystem;
|
||||
|
||||
/**
|
||||
* File system helper
|
||||
*
|
||||
* Holds support functions for the filesystem, particularly the stream
|
||||
*
|
||||
* @since 1.0
|
||||
*/
|
||||
class Helper
|
||||
{
|
||||
/**
|
||||
* Remote file size function for streams that don't support it
|
||||
*
|
||||
* @param string $url TODO Add text
|
||||
*
|
||||
* @return mixed
|
||||
*
|
||||
* @link https://www.php.net/manual/en/function.filesize.php#71098
|
||||
* @since 1.0
|
||||
*/
|
||||
public static function remotefsize($url)
|
||||
{
|
||||
$sch = parse_url($url, \PHP_URL_SCHEME);
|
||||
|
||||
if (!\in_array($sch, ['http', 'https', 'ftp', 'ftps'], true)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (\in_array($sch, ['http', 'https'], true)) {
|
||||
$headers = @ get_headers($url, 1);
|
||||
|
||||
if (!$headers || (!\array_key_exists('Content-Length', $headers))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $headers['Content-Length'];
|
||||
}
|
||||
|
||||
if (\in_array($sch, ['ftp', 'ftps'], true)) {
|
||||
$server = parse_url($url, \PHP_URL_HOST);
|
||||
$port = parse_url($url, \PHP_URL_PORT);
|
||||
$path = parse_url($url, \PHP_URL_PATH);
|
||||
$user = parse_url($url, \PHP_URL_USER);
|
||||
$pass = parse_url($url, \PHP_URL_PASS);
|
||||
|
||||
if ((!$server) || (!$path)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!$port) {
|
||||
$port = 21;
|
||||
}
|
||||
|
||||
if (!$user) {
|
||||
$user = 'anonymous';
|
||||
}
|
||||
|
||||
if (!$pass) {
|
||||
$pass = '';
|
||||
}
|
||||
|
||||
$ftpid = null;
|
||||
|
||||
switch ($sch) {
|
||||
case 'ftp':
|
||||
$ftpid = @ftp_connect($server, $port);
|
||||
|
||||
break;
|
||||
|
||||
case 'ftps':
|
||||
$ftpid = @ftp_ssl_connect($server, $port);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (!$ftpid) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$login = @ftp_login($ftpid, $user, $pass);
|
||||
|
||||
if (!$login) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$ftpsize = ftp_size($ftpid, $path);
|
||||
ftp_close($ftpid);
|
||||
|
||||
if ($ftpsize == -1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $ftpsize;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Quick FTP chmod
|
||||
*
|
||||
* @param string $url Link identifier
|
||||
* @param integer $mode The new permissions, given as an octal value.
|
||||
*
|
||||
* @return integer|boolean
|
||||
*
|
||||
* @link https://www.php.net/manual/en/function.ftp-chmod.php
|
||||
* @since 1.0
|
||||
*/
|
||||
public static function ftpChmod($url, $mode)
|
||||
{
|
||||
$sch = parse_url($url, \PHP_URL_SCHEME);
|
||||
|
||||
if (($sch != 'ftp') && ($sch != 'ftps')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$server = parse_url($url, \PHP_URL_HOST);
|
||||
$port = parse_url($url, \PHP_URL_PORT);
|
||||
$path = parse_url($url, \PHP_URL_PATH);
|
||||
$user = parse_url($url, \PHP_URL_USER);
|
||||
$pass = parse_url($url, \PHP_URL_PASS);
|
||||
|
||||
if ((!$server) || (!$path)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!$port) {
|
||||
$port = 21;
|
||||
}
|
||||
|
||||
if (!$user) {
|
||||
$user = 'anonymous';
|
||||
}
|
||||
|
||||
if (!$pass) {
|
||||
$pass = '';
|
||||
}
|
||||
|
||||
$ftpid = null;
|
||||
|
||||
switch ($sch) {
|
||||
case 'ftp':
|
||||
$ftpid = @ftp_connect($server, $port);
|
||||
|
||||
break;
|
||||
|
||||
case 'ftps':
|
||||
$ftpid = @ftp_ssl_connect($server, $port);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (!$ftpid) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$login = @ftp_login($ftpid, $user, $pass);
|
||||
|
||||
if (!$login) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$res = @ftp_chmod($ftpid, $mode, $path);
|
||||
ftp_close($ftpid);
|
||||
|
||||
return $res;
|
||||
}
|
||||
|
||||
/**
|
||||
* Modes that require a write operation
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
* @since 1.0
|
||||
*/
|
||||
public static function getWriteModes()
|
||||
{
|
||||
return ['w', 'w+', 'a', 'a+', 'r+', 'x', 'x+'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Stream and Filter Support Operations
|
||||
*
|
||||
* Returns the supported streams, in addition to direct file access
|
||||
* Also includes Joomla! streams as well as PHP streams
|
||||
*
|
||||
* @return array Streams
|
||||
*
|
||||
* @since 1.0
|
||||
*/
|
||||
public static function getSupported()
|
||||
{
|
||||
// Really quite cool what php can do with arrays when you let it...
|
||||
static $streams;
|
||||
|
||||
if (!$streams) {
|
||||
$streams = array_merge(stream_get_wrappers(), self::getJStreams());
|
||||
}
|
||||
|
||||
return $streams;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of transports
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
* @since 1.0
|
||||
*/
|
||||
public static function getTransports()
|
||||
{
|
||||
// Is this overkill?
|
||||
return stream_get_transports();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of filters
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
* @since 1.0
|
||||
*/
|
||||
public static function getFilters()
|
||||
{
|
||||
// Note: This will look like the getSupported() function with J! filters.
|
||||
// TODO: add user space filter loading like user space stream loading
|
||||
return stream_get_filters();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of J! streams
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
* @since 1.0
|
||||
*/
|
||||
public static function getJStreams()
|
||||
{
|
||||
static $streams = [];
|
||||
|
||||
if (!$streams) {
|
||||
$files = new \DirectoryIterator(__DIR__ . '/Stream');
|
||||
|
||||
/** @var \DirectoryIterator $file */
|
||||
foreach ($files as $file) {
|
||||
// Only load for php files.
|
||||
if (!$file->isFile() || $file->getExtension() != 'php') {
|
||||
continue;
|
||||
}
|
||||
|
||||
$streams[] = $file->getBasename('.php');
|
||||
}
|
||||
}
|
||||
|
||||
return $streams;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if a stream is a Joomla stream.
|
||||
*
|
||||
* @param string $streamname The name of a stream
|
||||
*
|
||||
* @return boolean True for a Joomla Stream
|
||||
*
|
||||
* @since 1.0
|
||||
*/
|
||||
public static function isJoomlaStream($streamname)
|
||||
{
|
||||
return \in_array($streamname, self::getJStreams());
|
||||
}
|
||||
}
|
||||
516
libraries/vendor/joomla/filesystem/src/Patcher.php
vendored
Normal file
516
libraries/vendor/joomla/filesystem/src/Patcher.php
vendored
Normal file
@ -0,0 +1,516 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Part of the Joomla Framework Filesystem Package
|
||||
*
|
||||
* @copyright Copyright (C) 2005 - 2021 Open Source Matters, Inc. All rights reserved.
|
||||
* @license GNU General Public License version 2 or later; see LICENSE
|
||||
*/
|
||||
|
||||
namespace Joomla\Filesystem;
|
||||
|
||||
/**
|
||||
* A Unified Diff Format Patcher class
|
||||
*
|
||||
* @link http://sourceforge.net/projects/phppatcher/ This has been derived from the PhpPatcher version 0.1.1 written by Giuseppe Mazzotta
|
||||
* @since 1.0
|
||||
*/
|
||||
class Patcher
|
||||
{
|
||||
/**
|
||||
* Regular expression for searching source files
|
||||
*
|
||||
* @var string
|
||||
* @since 1.0
|
||||
*/
|
||||
public const SRC_FILE = '/^---\\s+(\\S+)\s+\\d{1,4}-\\d{1,2}-\\d{1,2}\\s+\\d{1,2}:\\d{1,2}:\\d{1,2}(\\.\\d+)?\\s+(\+|-)\\d{4}/A';
|
||||
|
||||
/**
|
||||
* Regular expression for searching destination files
|
||||
*
|
||||
* @var string
|
||||
* @since 1.0
|
||||
*/
|
||||
public const DST_FILE = '/^\\+\\+\\+\\s+(\\S+)\s+\\d{1,4}-\\d{1,2}-\\d{1,2}\\s+\\d{1,2}:\\d{1,2}:\\d{1,2}(\\.\\d+)?\\s+(\+|-)\\d{4}/A';
|
||||
|
||||
/**
|
||||
* Regular expression for searching hunks of differences
|
||||
*
|
||||
* @var string
|
||||
* @since 1.0
|
||||
*/
|
||||
public const HUNK = '/@@ -(\\d+)(,(\\d+))?\\s+\\+(\\d+)(,(\\d+))?\\s+@@($)/A';
|
||||
|
||||
/**
|
||||
* Regular expression for splitting lines
|
||||
*
|
||||
* @var string
|
||||
* @since 1.0
|
||||
*/
|
||||
public const SPLIT = '/(\r\n)|(\r)|(\n)/';
|
||||
|
||||
/**
|
||||
* Source files
|
||||
*
|
||||
* @var array
|
||||
* @since 1.0
|
||||
*/
|
||||
protected $sources = [];
|
||||
|
||||
/**
|
||||
* Destination files
|
||||
*
|
||||
* @var array
|
||||
* @since 1.0
|
||||
*/
|
||||
protected $destinations = [];
|
||||
|
||||
/**
|
||||
* Removal files
|
||||
*
|
||||
* @var array
|
||||
* @since 1.0
|
||||
*/
|
||||
protected $removals = [];
|
||||
|
||||
/**
|
||||
* Patches
|
||||
*
|
||||
* @var array
|
||||
* @since 1.0
|
||||
*/
|
||||
protected $patches = [];
|
||||
|
||||
/**
|
||||
* Singleton instance of this class
|
||||
*
|
||||
* @var Patcher
|
||||
* @since 1.0
|
||||
*/
|
||||
protected static $instance;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* The constructor is protected to force the use of Patcher::getInstance()
|
||||
*
|
||||
* @since 1.0
|
||||
*/
|
||||
protected function __construct()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to get a patcher
|
||||
*
|
||||
* @return Patcher an instance of the patcher
|
||||
*
|
||||
* @since 1.0
|
||||
*/
|
||||
public static function getInstance()
|
||||
{
|
||||
if (!isset(static::$instance)) {
|
||||
static::$instance = new static();
|
||||
}
|
||||
|
||||
return static::$instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the pacher
|
||||
*
|
||||
* @return Patcher This object for chaining
|
||||
*
|
||||
* @since 1.0
|
||||
*/
|
||||
public function reset()
|
||||
{
|
||||
$this->sources = [];
|
||||
$this->destinations = [];
|
||||
$this->removals = [];
|
||||
$this->patches = [];
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply the patches
|
||||
*
|
||||
* @return integer The number of files patched
|
||||
*
|
||||
* @since 1.0
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
public function apply()
|
||||
{
|
||||
foreach ($this->patches as $patch) {
|
||||
// Separate the input into lines
|
||||
$lines = self::splitLines($patch['udiff']);
|
||||
|
||||
// Loop for each header
|
||||
while (self::findHeader($lines, $src, $dst)) {
|
||||
$done = false;
|
||||
|
||||
if ($patch['strip'] === null) {
|
||||
$src = $patch['root'] . preg_replace('#^([^/]*/)*#', '', $src);
|
||||
$dst = $patch['root'] . preg_replace('#^([^/]*/)*#', '', $dst);
|
||||
} else {
|
||||
$src = $patch['root'] . preg_replace('#^([^/]*/){' . (int) $patch['strip'] . '}#', '', $src);
|
||||
$dst = $patch['root'] . preg_replace('#^([^/]*/){' . (int) $patch['strip'] . '}#', '', $dst);
|
||||
}
|
||||
|
||||
// Loop for each hunk of differences
|
||||
while (self::findHunk($lines, $srcLine, $srcSize, $dstLine, $dstSize)) {
|
||||
$done = true;
|
||||
|
||||
// Apply the hunk of differences
|
||||
$this->applyHunk($lines, $src, $dst, $srcLine, $srcSize, $dstLine, $dstSize);
|
||||
}
|
||||
|
||||
// If no modifications were found, throw an exception
|
||||
if (!$done) {
|
||||
throw new \RuntimeException('Invalid Diff');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize the counter
|
||||
$done = 0;
|
||||
|
||||
// Patch each destination file
|
||||
foreach ($this->destinations as $file => $content) {
|
||||
$content = implode("\n", $content);
|
||||
|
||||
if (File::write($file, $content)) {
|
||||
if (isset($this->sources[$file])) {
|
||||
$this->sources[$file] = $content;
|
||||
}
|
||||
|
||||
$done++;
|
||||
}
|
||||
}
|
||||
|
||||
// Remove each removed file
|
||||
foreach ($this->removals as $file) {
|
||||
if (File::delete($file)) {
|
||||
if (isset($this->sources[$file])) {
|
||||
unset($this->sources[$file]);
|
||||
}
|
||||
|
||||
$done++;
|
||||
}
|
||||
}
|
||||
|
||||
// Clear the destinations cache
|
||||
$this->destinations = [];
|
||||
|
||||
// Clear the removals
|
||||
$this->removals = [];
|
||||
|
||||
// Clear the patches
|
||||
$this->patches = [];
|
||||
|
||||
return $done;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a unified diff file to the patcher
|
||||
*
|
||||
* @param string $filename Path to the unified diff file
|
||||
* @param string $root The files root path
|
||||
* @param integer $strip The number of '/' to strip
|
||||
*
|
||||
* @return Patcher $this for chaining
|
||||
*
|
||||
* @since 1.0
|
||||
*/
|
||||
public function addFile($filename, $root, $strip = 0)
|
||||
{
|
||||
return $this->add(file_get_contents($filename), $root, $strip);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a unified diff string to the patcher
|
||||
*
|
||||
* @param string $udiff Unified diff input string
|
||||
* @param string $root The files root path
|
||||
* @param integer $strip The number of '/' to strip
|
||||
*
|
||||
* @return Patcher $this for chaining
|
||||
*
|
||||
* @since 1.0
|
||||
*/
|
||||
public function add($udiff, $root, $strip = 0)
|
||||
{
|
||||
$this->patches[] = [
|
||||
'udiff' => $udiff,
|
||||
'root' => isset($root) ? rtrim($root, \DIRECTORY_SEPARATOR) . \DIRECTORY_SEPARATOR : '',
|
||||
'strip' => $strip,
|
||||
];
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Separate CR or CRLF lines
|
||||
*
|
||||
* @param string $data Input string
|
||||
*
|
||||
* @return array The lines of the input destination file
|
||||
*
|
||||
* @since 1.0
|
||||
*/
|
||||
protected static function splitLines($data)
|
||||
{
|
||||
return preg_split(self::SPLIT, $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the diff header
|
||||
*
|
||||
* The internal array pointer of $lines is on the next line after the finding
|
||||
*
|
||||
* @param array $lines The udiff array of lines
|
||||
* @param string $src The source file
|
||||
* @param string $dst The destination file
|
||||
*
|
||||
* @return boolean TRUE in case of success, FALSE in case of failure
|
||||
*
|
||||
* @since 1.0
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
protected static function findHeader(&$lines, &$src, &$dst)
|
||||
{
|
||||
// Get the current line
|
||||
$line = current($lines);
|
||||
|
||||
// Search for the header
|
||||
while ($line !== false && !preg_match(self::SRC_FILE, $line, $m)) {
|
||||
$line = next($lines);
|
||||
}
|
||||
|
||||
if ($line === false) {
|
||||
// No header found, return false
|
||||
return false;
|
||||
}
|
||||
|
||||
// Set the source file
|
||||
$src = $m[1];
|
||||
|
||||
// Advance to the next line
|
||||
$line = next($lines);
|
||||
|
||||
if ($line === false) {
|
||||
throw new \RuntimeException('Unexpected EOF');
|
||||
}
|
||||
|
||||
// Search the destination file
|
||||
if (!preg_match(self::DST_FILE, $line, $m)) {
|
||||
throw new \RuntimeException('Invalid Diff file');
|
||||
}
|
||||
|
||||
// Set the destination file
|
||||
$dst = $m[1];
|
||||
|
||||
// Advance to the next line
|
||||
if (next($lines) === false) {
|
||||
throw new \RuntimeException('Unexpected EOF');
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the next hunk of difference
|
||||
*
|
||||
* The internal array pointer of $lines is on the next line after the finding
|
||||
*
|
||||
* @param array $lines The udiff array of lines
|
||||
* @param string $srcLine The beginning of the patch for the source file
|
||||
* @param string $srcSize The size of the patch for the source file
|
||||
* @param string $dstLine The beginning of the patch for the destination file
|
||||
* @param string $dstSize The size of the patch for the destination file
|
||||
*
|
||||
* @return boolean TRUE in case of success, false in case of failure
|
||||
*
|
||||
* @since 1.0
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
protected static function findHunk(&$lines, &$srcLine, &$srcSize, &$dstLine, &$dstSize)
|
||||
{
|
||||
$line = current($lines);
|
||||
|
||||
if (preg_match(self::HUNK, $line, $m)) {
|
||||
$srcLine = (int) $m[1];
|
||||
|
||||
if ($m[3] === '') {
|
||||
$srcSize = 1;
|
||||
} else {
|
||||
$srcSize = (int) $m[3];
|
||||
}
|
||||
|
||||
$dstLine = (int) $m[4];
|
||||
|
||||
if ($m[6] === '') {
|
||||
$dstSize = 1;
|
||||
} else {
|
||||
$dstSize = (int) $m[6];
|
||||
}
|
||||
|
||||
if (next($lines) === false) {
|
||||
throw new \RuntimeException('Unexpected EOF');
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply the patch
|
||||
*
|
||||
* @param array $lines The udiff array of lines
|
||||
* @param string $src The source file
|
||||
* @param string $dst The destination file
|
||||
* @param string $srcLine The beginning of the patch for the source file
|
||||
* @param string $srcSize The size of the patch for the source file
|
||||
* @param string $dstLine The beginning of the patch for the destination file
|
||||
* @param string $dstSize The size of the patch for the destination file
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @since 1.0
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
protected function applyHunk(&$lines, $src, $dst, $srcLine, $srcSize, $dstLine, $dstSize)
|
||||
{
|
||||
$srcLine--;
|
||||
$dstLine--;
|
||||
$line = current($lines);
|
||||
|
||||
// Source lines (old file)
|
||||
$source = [];
|
||||
|
||||
// New lines (new file)
|
||||
$destin = [];
|
||||
$srcLeft = $srcSize;
|
||||
$dstLeft = $dstSize;
|
||||
|
||||
do {
|
||||
if (!isset($line[0])) {
|
||||
$source[] = '';
|
||||
$destin[] = '';
|
||||
$srcLeft--;
|
||||
$dstLeft--;
|
||||
} elseif ($line[0] == '-') {
|
||||
if ($srcLeft == 0) {
|
||||
throw new \RuntimeException('Unexpected remove line at line ' . key($lines));
|
||||
}
|
||||
|
||||
$source[] = substr($line, 1);
|
||||
$srcLeft--;
|
||||
} elseif ($line[0] == '+') {
|
||||
if ($dstLeft == 0) {
|
||||
throw new \RuntimeException('Unexpected add line at line ' . key($lines));
|
||||
}
|
||||
|
||||
$destin[] = substr($line, 1);
|
||||
$dstLeft--;
|
||||
} elseif ($line != '\\ No newline at end of file') {
|
||||
$line = substr($line, 1);
|
||||
$source[] = $line;
|
||||
$destin[] = $line;
|
||||
$srcLeft--;
|
||||
$dstLeft--;
|
||||
}
|
||||
|
||||
if ($srcLeft == 0 && $dstLeft == 0) {
|
||||
// Now apply the patch, finally!
|
||||
if ($srcSize > 0) {
|
||||
$srcLines = & $this->getSource($src);
|
||||
|
||||
if (!isset($srcLines)) {
|
||||
throw new \RuntimeException(
|
||||
'Unexisting source file: ' . Path::removeRoot($src)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if ($dstSize > 0) {
|
||||
if ($srcSize > 0) {
|
||||
$dstLines = & $this->getDestination($dst, $src);
|
||||
$srcBottom = $srcLine + \count($source);
|
||||
|
||||
for ($l = $srcLine; $l < $srcBottom; $l++) {
|
||||
if ($srcLines[$l] != $source[$l - $srcLine]) {
|
||||
throw new \RuntimeException(
|
||||
sprintf(
|
||||
'Failed source verification of file %1$s at line %2$s',
|
||||
Path::removeRoot($src),
|
||||
$l
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
array_splice($dstLines, $dstLine, \count($source), $destin);
|
||||
} else {
|
||||
$this->destinations[$dst] = $destin;
|
||||
}
|
||||
} else {
|
||||
$this->removals[] = $src;
|
||||
}
|
||||
|
||||
next($lines);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$line = next($lines);
|
||||
} while ($line !== false);
|
||||
|
||||
throw new \RuntimeException('Unexpected EOF');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the lines of a source file
|
||||
*
|
||||
* @param string $src The path of a file
|
||||
*
|
||||
* @return array The lines of the source file
|
||||
*
|
||||
* @since 1.0
|
||||
*/
|
||||
protected function &getSource($src)
|
||||
{
|
||||
if (!isset($this->sources[$src])) {
|
||||
if (is_readable($src)) {
|
||||
$this->sources[$src] = self::splitLines(file_get_contents($src));
|
||||
} else {
|
||||
$this->sources[$src] = null;
|
||||
}
|
||||
}
|
||||
|
||||
return $this->sources[$src];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the lines of a destination file
|
||||
*
|
||||
* @param string $dst The path of a destination file
|
||||
* @param string $src The path of a source file
|
||||
*
|
||||
* @return array The lines of the destination file
|
||||
*
|
||||
* @since 1.0
|
||||
*/
|
||||
protected function &getDestination($dst, $src)
|
||||
{
|
||||
if (!isset($this->destinations[$dst])) {
|
||||
$this->destinations[$dst] = $this->getSource($src);
|
||||
}
|
||||
|
||||
return $this->destinations[$dst];
|
||||
}
|
||||
}
|
||||
376
libraries/vendor/joomla/filesystem/src/Path.php
vendored
Normal file
376
libraries/vendor/joomla/filesystem/src/Path.php
vendored
Normal file
@ -0,0 +1,376 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Part of the Joomla Framework Filesystem Package
|
||||
*
|
||||
* @copyright Copyright (C) 2005 - 2021 Open Source Matters, Inc. All rights reserved.
|
||||
* @license GNU General Public License version 2 or later; see LICENSE
|
||||
*/
|
||||
|
||||
namespace Joomla\Filesystem;
|
||||
|
||||
use Joomla\Filesystem\Exception\FilesystemException;
|
||||
|
||||
/**
|
||||
* A Path handling class
|
||||
*
|
||||
* @since 1.0
|
||||
*/
|
||||
class Path
|
||||
{
|
||||
/**
|
||||
* Checks if a path's permissions can be changed.
|
||||
*
|
||||
* @param string $path Path to check.
|
||||
*
|
||||
* @return boolean True if path can have mode changed.
|
||||
*
|
||||
* @since 1.0
|
||||
*/
|
||||
public static function canChmod($path)
|
||||
{
|
||||
if (!file_exists($path)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$perms = @fileperms($path);
|
||||
|
||||
if ($perms !== false) {
|
||||
if (@chmod($path, $perms ^ 0001)) {
|
||||
@chmod($path, $perms);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Chmods files and directories recursively to given permissions.
|
||||
*
|
||||
* @param string $path Root path to begin changing mode [without trailing slash].
|
||||
* @param string $filemode Octal representation of the value to change file mode to [null = no change].
|
||||
* @param string $foldermode Octal representation of the value to change folder mode to [null = no change].
|
||||
*
|
||||
* @return boolean True if successful [one fail means the whole operation failed].
|
||||
*
|
||||
* @since 1.0
|
||||
*/
|
||||
public static function setPermissions($path, $filemode = '0644', $foldermode = '0755')
|
||||
{
|
||||
// Initialise return value
|
||||
$ret = true;
|
||||
|
||||
if (is_dir($path)) {
|
||||
$dh = @opendir($path);
|
||||
|
||||
if ($dh) {
|
||||
while ($file = readdir($dh)) {
|
||||
if ($file != '.' && $file != '..') {
|
||||
$fullpath = $path . '/' . $file;
|
||||
|
||||
if (is_dir($fullpath)) {
|
||||
if (!static::setPermissions($fullpath, $filemode, $foldermode)) {
|
||||
$ret = false;
|
||||
}
|
||||
} else {
|
||||
if (isset($filemode)) {
|
||||
if (!static::canChmod($fullpath) || !@ chmod($fullpath, octdec($filemode))) {
|
||||
$ret = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
closedir($dh);
|
||||
}
|
||||
|
||||
if (isset($foldermode)) {
|
||||
if (!static::canChmod($path) || !@ chmod($path, octdec($foldermode))) {
|
||||
$ret = false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (isset($filemode)) {
|
||||
if (!static::canChmod($path) || !@ chmod($path, octdec($filemode))) {
|
||||
$ret = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the permissions of the file/folder at a give path.
|
||||
*
|
||||
* @param string $path The path of a file/folder.
|
||||
*
|
||||
* @return string Filesystem permissions.
|
||||
*
|
||||
* @since 1.0
|
||||
*/
|
||||
public static function getPermissions($path)
|
||||
{
|
||||
$path = self::clean($path);
|
||||
$mode = @ decoct(@ fileperms($path) & 0777);
|
||||
|
||||
if (\strlen($mode) < 3) {
|
||||
return '---------';
|
||||
}
|
||||
|
||||
$parsedMode = '';
|
||||
|
||||
for ($i = 0; $i < 3; $i++) {
|
||||
// Read
|
||||
$parsedMode .= ($mode[$i] & 04) ? 'r' : '-';
|
||||
|
||||
// Write
|
||||
$parsedMode .= ($mode[$i] & 02) ? 'w' : '-';
|
||||
|
||||
// Execute
|
||||
$parsedMode .= ($mode[$i] & 01) ? 'x' : '-';
|
||||
}
|
||||
|
||||
return $parsedMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks for snooping outside of the file system root.
|
||||
*
|
||||
* @param string $path A file system path to check.
|
||||
* @param string $basePath The base path of the system
|
||||
*
|
||||
* @return string A cleaned version of the path or exit on error.
|
||||
*
|
||||
* @since 1.0
|
||||
* @throws FilesystemException
|
||||
*/
|
||||
public static function check($path, $basePath = '')
|
||||
{
|
||||
if (strpos($path, '..') !== false) {
|
||||
throw new FilesystemException(
|
||||
sprintf(
|
||||
'%s() - Use of relative paths not permitted',
|
||||
__METHOD__
|
||||
),
|
||||
20
|
||||
);
|
||||
}
|
||||
|
||||
$path = static::clean($path);
|
||||
|
||||
// If a base path is defined then check the cleaned path is not outside of root
|
||||
if (($basePath != '') && strpos($path, static::clean($basePath)) !== 0) {
|
||||
throw new FilesystemException(
|
||||
sprintf(
|
||||
'%1$s() - Snooping out of bounds @ %2$s',
|
||||
__METHOD__,
|
||||
$path
|
||||
),
|
||||
20
|
||||
);
|
||||
}
|
||||
|
||||
return $path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function to strip additional / or \ in a path name.
|
||||
*
|
||||
* @param string $path The path to clean.
|
||||
* @param string $ds Directory separator (optional).
|
||||
*
|
||||
* @return string The cleaned path.
|
||||
*
|
||||
* @since 1.0
|
||||
* @throws \UnexpectedValueException If $path is not a string.
|
||||
*/
|
||||
public static function clean($path, $ds = \DIRECTORY_SEPARATOR)
|
||||
{
|
||||
if ($path === '') {
|
||||
return '';
|
||||
}
|
||||
|
||||
if (!\is_string($path)) {
|
||||
throw new \InvalidArgumentException('You must specify a non-empty path to clean');
|
||||
}
|
||||
|
||||
$stream = explode('://', $path, 2);
|
||||
$scheme = '';
|
||||
$path = $stream[0];
|
||||
|
||||
if (\count($stream) >= 2) {
|
||||
$scheme = $stream[0] . '://';
|
||||
$path = $stream[1];
|
||||
}
|
||||
|
||||
$path = trim($path);
|
||||
|
||||
// Remove double slashes and backslashes and convert all slashes and backslashes to DIRECTORY_SEPARATOR
|
||||
// If dealing with a UNC path don't forget to prepend the path with a backslash.
|
||||
if (($ds == '\\') && ($path[0] == '\\') && ($path[1] == '\\')) {
|
||||
$path = '\\' . preg_replace('#[/\\\\]+#', $ds, $path);
|
||||
} else {
|
||||
$path = preg_replace('#[/\\\\]+#', $ds, $path);
|
||||
}
|
||||
|
||||
return $scheme . $path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to determine if script owns the path.
|
||||
*
|
||||
* @param string $path Path to check ownership.
|
||||
*
|
||||
* @return boolean True if the php script owns the path passed.
|
||||
*
|
||||
* @since 1.0
|
||||
*/
|
||||
public static function isOwner($path)
|
||||
{
|
||||
$tmp = md5(random_bytes(16));
|
||||
$ssp = ini_get('session.save_path');
|
||||
|
||||
// Try to find a writable directory
|
||||
$dir = is_writable('/tmp') ? '/tmp' : false;
|
||||
$dir = !$dir && is_writable('.') ? '.' : $dir;
|
||||
$dir = !$dir && is_writable($ssp) ? $ssp : $dir;
|
||||
|
||||
if ($dir) {
|
||||
$test = $dir . '/' . $tmp;
|
||||
|
||||
// Create the test file
|
||||
$blank = '';
|
||||
File::write($test, $blank, false);
|
||||
|
||||
// Test ownership
|
||||
$return = fileowner($test) === fileowner($path);
|
||||
|
||||
// Delete the test file
|
||||
File::delete($test);
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches the directory paths for a given file.
|
||||
*
|
||||
* @param mixed $paths A path string or array of path strings to search in
|
||||
* @param string $file The file name to look for.
|
||||
*
|
||||
* @return string|boolean The full path and file name for the target file, or boolean false if the file is not found in any of the paths.
|
||||
*
|
||||
* @since 1.0
|
||||
*/
|
||||
public static function find($paths, $file)
|
||||
{
|
||||
// Force to array
|
||||
if (!\is_array($paths) && !($paths instanceof \Iterator)) {
|
||||
settype($paths, 'array');
|
||||
}
|
||||
|
||||
// Start looping through the path set
|
||||
foreach ($paths as $path) {
|
||||
// Get the path to the file
|
||||
$fullname = $path . '/' . $file;
|
||||
|
||||
// Is the path based on a stream?
|
||||
if (strpos($path, '://') === false) {
|
||||
// Not a stream, so do a realpath() to avoid directory
|
||||
// traversal attempts on the local file system.
|
||||
|
||||
// Needed for substr() later
|
||||
$path = realpath($path);
|
||||
$fullname = realpath($fullname);
|
||||
}
|
||||
|
||||
/*
|
||||
* The substr() check added to make sure that the realpath()
|
||||
* results in a directory registered so that
|
||||
* non-registered directories are not accessible via directory
|
||||
* traversal attempts.
|
||||
*/
|
||||
if (file_exists($fullname) && substr($fullname, 0, \strlen($path)) == $path) {
|
||||
return $fullname;
|
||||
}
|
||||
}
|
||||
|
||||
// Could not find the file in the set of paths
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves /./, /../ and multiple / in a string and returns the resulting absolute path, inspired by Flysystem
|
||||
* Removes trailing slashes
|
||||
*
|
||||
* @param string $path A path to resolve
|
||||
*
|
||||
* @return string The resolved path
|
||||
*
|
||||
* @since 1.6.0
|
||||
*/
|
||||
public static function resolve($path)
|
||||
{
|
||||
$path = static::clean($path);
|
||||
|
||||
// Save start character for absolute path
|
||||
$startCharacter = ($path[0] === DIRECTORY_SEPARATOR) ? DIRECTORY_SEPARATOR : '';
|
||||
|
||||
$parts = [];
|
||||
|
||||
foreach (explode(DIRECTORY_SEPARATOR, $path) as $part) {
|
||||
switch ($part) {
|
||||
case '':
|
||||
case '.':
|
||||
break;
|
||||
|
||||
case '..':
|
||||
if (empty($parts)) {
|
||||
throw new FilesystemException('Path is outside of the defined root');
|
||||
}
|
||||
|
||||
array_pop($parts);
|
||||
break;
|
||||
|
||||
default:
|
||||
$parts[] = $part;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $startCharacter . implode(DIRECTORY_SEPARATOR, $parts);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove all references to root directory path and the system tmp path from a message
|
||||
*
|
||||
* @param string $message The message to be cleaned
|
||||
* @param string $rootDirectory Optional root directory, defaults to JPATH_ROOT
|
||||
*
|
||||
* @return string
|
||||
* @since 2.0.1
|
||||
*/
|
||||
public static function removeRoot($message, $rootDirectory = null)
|
||||
{
|
||||
if (empty($rootDirectory)) {
|
||||
$rootDirectory = JPATH_ROOT;
|
||||
}
|
||||
|
||||
$makePattern = static function ($dir) {
|
||||
return '~' . str_replace('~', '\\~', preg_replace('~[/\\\\]+~', '.', $dir)) . '~';
|
||||
};
|
||||
|
||||
$replacements = [
|
||||
$makePattern(static::clean($rootDirectory)) => '[ROOT]',
|
||||
$makePattern(sys_get_temp_dir()) => '[TMP]',
|
||||
];
|
||||
|
||||
return preg_replace(array_keys($replacements), array_values($replacements), $message);
|
||||
}
|
||||
}
|
||||
1418
libraries/vendor/joomla/filesystem/src/Stream.php
vendored
Normal file
1418
libraries/vendor/joomla/filesystem/src/Stream.php
vendored
Normal file
File diff suppressed because it is too large
Load Diff
290
libraries/vendor/joomla/filesystem/src/Stream/StringWrapper.php
vendored
Normal file
290
libraries/vendor/joomla/filesystem/src/Stream/StringWrapper.php
vendored
Normal file
@ -0,0 +1,290 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Part of the Joomla Framework Filesystem Package
|
||||
*
|
||||
* @copyright Copyright (C) 2005 - 2021 Open Source Matters, Inc. All rights reserved.
|
||||
* @license GNU General Public License version 2 or later; see LICENSE
|
||||
*/
|
||||
|
||||
namespace Joomla\Filesystem\Stream;
|
||||
|
||||
use Joomla\Filesystem\Support\StringController;
|
||||
|
||||
/**
|
||||
* String Stream Wrapper
|
||||
*
|
||||
* This class allows you to use a PHP string in the same way that you would normally use a regular stream wrapper
|
||||
*
|
||||
* @since 1.3.0
|
||||
*/
|
||||
class StringWrapper
|
||||
{
|
||||
/**
|
||||
* The current string
|
||||
*
|
||||
* @var string
|
||||
* @since 1.3.0
|
||||
*/
|
||||
protected $currentString;
|
||||
|
||||
/**
|
||||
* The path
|
||||
*
|
||||
* @var string
|
||||
* @since 1.3.0
|
||||
*/
|
||||
protected $path;
|
||||
|
||||
/**
|
||||
* The mode
|
||||
*
|
||||
* @var string
|
||||
* @since 1.3.0
|
||||
*/
|
||||
protected $mode;
|
||||
|
||||
/**
|
||||
* Enter description here ...
|
||||
*
|
||||
* @var string
|
||||
* @since 1.3.0
|
||||
*/
|
||||
protected $options;
|
||||
|
||||
/**
|
||||
* Enter description here ...
|
||||
*
|
||||
* @var string
|
||||
* @since 1.3.0
|
||||
*/
|
||||
protected $openedPath;
|
||||
|
||||
/**
|
||||
* Current position
|
||||
*
|
||||
* @var integer
|
||||
* @since 1.3.0
|
||||
*/
|
||||
protected $pos;
|
||||
|
||||
/**
|
||||
* Length of the string
|
||||
*
|
||||
* @var string
|
||||
* @since 1.3.0
|
||||
*/
|
||||
protected $len;
|
||||
|
||||
/**
|
||||
* Statistics for a file
|
||||
*
|
||||
* @var array
|
||||
* @since 1.3.0
|
||||
* @link https://www.php.net/manual/en/function.stat.php
|
||||
*/
|
||||
protected $stat;
|
||||
|
||||
/**
|
||||
* Method to open a file or URL.
|
||||
*
|
||||
* @param string $path The stream path.
|
||||
* @param string $mode Not used.
|
||||
* @param integer $options Not used.
|
||||
* @param string $openedPath Not used.
|
||||
*
|
||||
* @return boolean
|
||||
*
|
||||
* @since 1.3.0
|
||||
*/
|
||||
public function stream_open($path, $mode, $options, &$openedPath)
|
||||
{
|
||||
$refPath = StringController::getRef(str_replace('string://', '', $path));
|
||||
|
||||
$this->currentString = &$refPath;
|
||||
|
||||
if ($this->currentString) {
|
||||
$this->len = \strlen($this->currentString);
|
||||
$this->pos = 0;
|
||||
$this->stat = $this->url_stat($path, 0);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to retrieve information from a file resource
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
* @link https://www.php.net/manual/en/streamwrapper.stream-stat.php
|
||||
* @since 1.3.0
|
||||
*/
|
||||
public function stream_stat()
|
||||
{
|
||||
return $this->stat;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to retrieve information about a file.
|
||||
*
|
||||
* @param string $path File path or URL to stat
|
||||
* @param integer $flags Additional flags set by the streams API
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
* @link https://www.php.net/manual/en/streamwrapper.url-stat.php
|
||||
* @since 1.3.0
|
||||
*/
|
||||
public function url_stat($path, $flags = 0)
|
||||
{
|
||||
$now = time();
|
||||
$refPath = StringController::getRef(str_replace('string://', '', $path));
|
||||
$string = &$refPath;
|
||||
$stat = [
|
||||
'dev' => 0,
|
||||
'ino' => 0,
|
||||
'mode' => 0,
|
||||
'nlink' => 1,
|
||||
'uid' => 0,
|
||||
'gid' => 0,
|
||||
'rdev' => 0,
|
||||
'size' => \strlen($string),
|
||||
'atime' => $now,
|
||||
'mtime' => $now,
|
||||
'ctime' => $now,
|
||||
'blksize' => '512',
|
||||
'blocks' => ceil(\strlen($string) / 512),
|
||||
];
|
||||
|
||||
return $stat;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to read a given number of bytes starting at the current position
|
||||
* and moving to the end of the string defined by the current position plus the
|
||||
* given number.
|
||||
*
|
||||
* @param integer $count Bytes of data from the current position should be returned.
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @link https://www.php.net/manual/en/streamwrapper.stream-read.php
|
||||
* @since 1.3.0
|
||||
*/
|
||||
public function stream_read($count)
|
||||
{
|
||||
$result = substr($this->currentString, $this->pos ?? 0, $count);
|
||||
$this->pos += $count;
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stream write, always returning false.
|
||||
*
|
||||
* @param string $data The data to write.
|
||||
*
|
||||
* @return boolean
|
||||
*
|
||||
* @since 1.3.0
|
||||
* @note Updating the string is not supported.
|
||||
*/
|
||||
public function stream_write($data)
|
||||
{
|
||||
// We don't support updating the string.
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to get the current position
|
||||
*
|
||||
* @return integer The position
|
||||
*
|
||||
* @since 1.3.0
|
||||
*/
|
||||
public function stream_tell()
|
||||
{
|
||||
return $this->pos;
|
||||
}
|
||||
|
||||
/**
|
||||
* End of field check
|
||||
*
|
||||
* @return boolean True if at end of field.
|
||||
*
|
||||
* @since 1.3.0
|
||||
*/
|
||||
public function stream_eof()
|
||||
{
|
||||
if ($this->pos >= $this->len) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stream offset
|
||||
*
|
||||
* @param integer $offset The starting offset.
|
||||
* @param integer $whence SEEK_SET, SEEK_CUR, SEEK_END
|
||||
*
|
||||
* @return boolean True on success.
|
||||
*
|
||||
* @since 1.3.0
|
||||
*/
|
||||
public function stream_seek($offset, $whence)
|
||||
{
|
||||
// $whence: SEEK_SET, SEEK_CUR, SEEK_END
|
||||
if ($offset > $this->len) {
|
||||
// We can't seek beyond our len.
|
||||
return false;
|
||||
}
|
||||
|
||||
switch ($whence) {
|
||||
case \SEEK_SET:
|
||||
$this->pos = $offset;
|
||||
|
||||
break;
|
||||
|
||||
case \SEEK_CUR:
|
||||
if (($this->pos + $offset) > $this->len) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->pos += $offset;
|
||||
|
||||
break;
|
||||
|
||||
case \SEEK_END:
|
||||
$this->pos = $this->len - $offset;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stream flush, always returns true.
|
||||
*
|
||||
* @return boolean
|
||||
*
|
||||
* @since 1.3.0
|
||||
* @note Data storage is not supported
|
||||
*/
|
||||
public function stream_flush()
|
||||
{
|
||||
// We don't store data.
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// phpcs:disable PSR1.Files.SideEffects
|
||||
if (!stream_wrapper_register('string', '\\Joomla\\Filesystem\\Stream\\StringWrapper')) {
|
||||
die('\\Joomla\\Filesystem\\Stream\\StringWrapper Wrapper Registration Failed');
|
||||
}
|
||||
// phpcs:enable PSR1.Files.SideEffects
|
||||
84
libraries/vendor/joomla/filesystem/src/Support/StringController.php
vendored
Normal file
84
libraries/vendor/joomla/filesystem/src/Support/StringController.php
vendored
Normal file
@ -0,0 +1,84 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Part of the Joomla Framework Filesystem Package
|
||||
*
|
||||
* @copyright Copyright (C) 2005 - 2021 Open Source Matters, Inc. All rights reserved.
|
||||
* @license GNU General Public License version 2 or later; see LICENSE
|
||||
*/
|
||||
|
||||
namespace Joomla\Filesystem\Support;
|
||||
|
||||
/**
|
||||
* String Controller
|
||||
*
|
||||
* @since 1.0
|
||||
*/
|
||||
class StringController
|
||||
{
|
||||
/**
|
||||
* Internal string references
|
||||
*
|
||||
* @var array
|
||||
* @ssince 1.4.0
|
||||
*/
|
||||
private static $strings = [];
|
||||
|
||||
/**
|
||||
* Defines a variable as an array
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
* @since 1.0
|
||||
* @deprecated 2.0 Use `getArray` instead.
|
||||
*/
|
||||
public static function _getArray()
|
||||
{
|
||||
return self::getArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines a variable as an array
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
* @since 1.4.0
|
||||
*/
|
||||
public static function getArray()
|
||||
{
|
||||
return self::$strings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a reference
|
||||
*
|
||||
* @param string $reference The key
|
||||
* @param string $string The value
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @since 1.0
|
||||
*/
|
||||
public static function createRef($reference, &$string)
|
||||
{
|
||||
self::$strings[$reference] = & $string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get reference
|
||||
*
|
||||
* @param string $reference The key for the reference.
|
||||
*
|
||||
* @return mixed False if not set, reference if it exists
|
||||
*
|
||||
* @since 1.0
|
||||
*/
|
||||
public static function getRef($reference)
|
||||
{
|
||||
if (isset(self::$strings[$reference])) {
|
||||
return self::$strings[$reference];
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user