primo commit
This commit is contained in:
256
libraries/vendor/joomla/archive/src/Tar.php
vendored
Normal file
256
libraries/vendor/joomla/archive/src/Tar.php
vendored
Normal file
@ -0,0 +1,256 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Part of the Joomla Framework Archive 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\Archive;
|
||||
|
||||
use Joomla\Filesystem\File;
|
||||
use Joomla\Filesystem\Folder;
|
||||
use Joomla\Filesystem\Path;
|
||||
|
||||
/**
|
||||
* Tar format adapter for the Archive package
|
||||
*
|
||||
* This class is inspired from and draws heavily in code and concept from the Compress package of
|
||||
* The Horde Project <http://www.horde.org>
|
||||
*
|
||||
* @contributor Michael Slusarz <slusarz@horde.org>
|
||||
* @contributor Michael Cochrane <mike@graftonhall.co.nz>
|
||||
*
|
||||
* @since 1.0
|
||||
*/
|
||||
class Tar implements ExtractableInterface
|
||||
{
|
||||
/**
|
||||
* Tar file types.
|
||||
*
|
||||
* @var array
|
||||
* @since 1.0
|
||||
*/
|
||||
private const TYPES = [
|
||||
0x0 => 'Unix file',
|
||||
0x30 => 'File',
|
||||
0x31 => 'Link',
|
||||
0x32 => 'Symbolic link',
|
||||
0x33 => 'Character special file',
|
||||
0x34 => 'Block special file',
|
||||
0x35 => 'Directory',
|
||||
0x36 => 'FIFO special file',
|
||||
0x37 => 'Contiguous file',
|
||||
];
|
||||
|
||||
/**
|
||||
* Tar file data buffer
|
||||
*
|
||||
* @var string
|
||||
* @since 1.0
|
||||
*/
|
||||
private $data;
|
||||
|
||||
/**
|
||||
* Tar file metadata array
|
||||
*
|
||||
* @var array
|
||||
* @since 1.0
|
||||
*/
|
||||
private $metadata;
|
||||
|
||||
/**
|
||||
* Holds the options array.
|
||||
*
|
||||
* @var array|\ArrayAccess
|
||||
* @since 1.0
|
||||
*/
|
||||
protected $options = [];
|
||||
|
||||
/**
|
||||
* Create a new Archive object.
|
||||
*
|
||||
* @param array|\ArrayAccess $options An array of options or an object that implements \ArrayAccess
|
||||
*
|
||||
* @since 1.0
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function __construct($options = [])
|
||||
{
|
||||
if (!\is_array($options) && !($options instanceof \ArrayAccess)) {
|
||||
throw new \InvalidArgumentException(
|
||||
'The options param must be an array or implement the ArrayAccess interface.'
|
||||
);
|
||||
}
|
||||
|
||||
$this->options = $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract a ZIP compressed file to a given path
|
||||
*
|
||||
* @param string $archive Path to ZIP archive to extract
|
||||
* @param string $destination Path to extract archive into
|
||||
*
|
||||
* @return boolean True if successful
|
||||
*
|
||||
* @since 1.0
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
public function extract($archive, $destination)
|
||||
{
|
||||
$this->metadata = [];
|
||||
$this->data = file_get_contents($archive);
|
||||
|
||||
if (!$this->data) {
|
||||
throw new \RuntimeException('Unable to read archive');
|
||||
}
|
||||
|
||||
$this->getTarInfo($this->data);
|
||||
|
||||
for ($i = 0, $n = \count($this->metadata); $i < $n; $i++) {
|
||||
$type = strtolower($this->metadata[$i]['type']);
|
||||
|
||||
if ($type == 'file' || $type == 'unix file') {
|
||||
$buffer = $this->metadata[$i]['data'];
|
||||
$path = Path::clean($destination . '/' . $this->metadata[$i]['name']);
|
||||
|
||||
if (!$this->isBelow($destination, $destination . '/' . $this->metadata[$i]['name'])) {
|
||||
throw new \OutOfBoundsException('Unable to write outside of destination path', 100);
|
||||
}
|
||||
|
||||
// Make sure the destination folder exists
|
||||
if (!Folder::create(\dirname($path))) {
|
||||
throw new \RuntimeException('Unable to create destination folder ' . \dirname($path));
|
||||
}
|
||||
|
||||
if (!File::write($path, $buffer)) {
|
||||
throw new \RuntimeException('Unable to write entry to file ' . $path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests whether this adapter can unpack files on this computer.
|
||||
*
|
||||
* @return boolean True if supported
|
||||
*
|
||||
* @since 1.0
|
||||
*/
|
||||
public static function isSupported()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of files/data from a Tar archive buffer and builds a metadata array.
|
||||
*
|
||||
* Array structure:
|
||||
* <pre>
|
||||
* KEY: Position in the array
|
||||
* VALUES: 'attr' -- File attributes
|
||||
* 'data' -- Raw file contents
|
||||
* 'date' -- File modification time
|
||||
* 'name' -- Filename
|
||||
* 'size' -- Original file size
|
||||
* 'type' -- File type
|
||||
* </pre>
|
||||
*
|
||||
* @param string $data The Tar archive buffer.
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @since 1.0
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
protected function getTarInfo(&$data)
|
||||
{
|
||||
$position = 0;
|
||||
$returnArray = [];
|
||||
|
||||
while ($position < \strlen($data)) {
|
||||
$info = @unpack(
|
||||
'Z100filename/Z8mode/Z8uid/Z8gid/Z12size/Z12mtime/Z8checksum/Ctypeflag/Z100link/Z6magic/Z2version/Z32uname/Z32gname/Z8devmajor/Z8devminor',
|
||||
$data,
|
||||
$position
|
||||
);
|
||||
|
||||
/*
|
||||
* This variable has been set in the previous loop, meaning that the filename was present in the previous block
|
||||
* to allow more than 100 characters - see below
|
||||
*/
|
||||
if (isset($longlinkfilename)) {
|
||||
$info['filename'] = $longlinkfilename;
|
||||
unset($longlinkfilename);
|
||||
}
|
||||
|
||||
if (!$info) {
|
||||
throw new \RuntimeException('Unable to decompress data');
|
||||
}
|
||||
|
||||
$position += 512;
|
||||
$contents = substr($data, $position, octdec($info['size']));
|
||||
$position += ceil(octdec($info['size']) / 512) * 512;
|
||||
|
||||
if ($info['filename']) {
|
||||
$file = [
|
||||
'attr' => null,
|
||||
'data' => null,
|
||||
'date' => octdec($info['mtime']),
|
||||
'name' => trim($info['filename']),
|
||||
'size' => octdec($info['size']),
|
||||
'type' => self::TYPES[$info['typeflag']] ?? null,
|
||||
];
|
||||
|
||||
if (($info['typeflag'] == 0) || ($info['typeflag'] == 0x30) || ($info['typeflag'] == 0x35)) {
|
||||
// File or folder.
|
||||
$file['data'] = $contents;
|
||||
|
||||
$mode = hexdec(substr($info['mode'], 4, 3));
|
||||
$file['attr'] = (($info['typeflag'] == 0x35) ? 'd' : '-')
|
||||
. (($mode & 0x400) ? 'r' : '-')
|
||||
. (($mode & 0x200) ? 'w' : '-')
|
||||
. (($mode & 0x100) ? 'x' : '-')
|
||||
. (($mode & 0x040) ? 'r' : '-')
|
||||
. (($mode & 0x020) ? 'w' : '-')
|
||||
. (($mode & 0x010) ? 'x' : '-')
|
||||
. (($mode & 0x004) ? 'r' : '-')
|
||||
. (($mode & 0x002) ? 'w' : '-')
|
||||
. (($mode & 0x001) ? 'x' : '-');
|
||||
} elseif (\chr($info['typeflag']) == 'L' && $info['filename'] == '././@LongLink') {
|
||||
// GNU tar ././@LongLink support - the filename is actually in the contents, set a variable here so we can test in the next loop
|
||||
$longlinkfilename = $contents;
|
||||
|
||||
// And the file contents are in the next block so we'll need to skip this
|
||||
continue;
|
||||
}
|
||||
|
||||
$returnArray[] = $file;
|
||||
}
|
||||
}
|
||||
|
||||
$this->metadata = $returnArray;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a path is below a given destination path
|
||||
*
|
||||
* @param string $destination The destination path
|
||||
* @param string $path The path to be checked
|
||||
*
|
||||
* @return boolean
|
||||
*
|
||||
* @since 2.0.1
|
||||
*/
|
||||
private function isBelow($destination, $path): bool
|
||||
{
|
||||
$absoluteRoot = Path::clean(Path::resolve($destination));
|
||||
$absolutePath = Path::clean(Path::resolve($path));
|
||||
|
||||
return strpos($absolutePath, $absoluteRoot) === 0;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user