primo commit
This commit is contained in:
		
							
								
								
									
										210
									
								
								libraries/vendor/joomla/archive/src/Archive.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										210
									
								
								libraries/vendor/joomla/archive/src/Archive.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,210 @@ | ||||
| <?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\Archive\Exception\UnknownArchiveException; | ||||
| use Joomla\Archive\Exception\UnsupportedArchiveException; | ||||
| use Joomla\Filesystem\File; | ||||
| use Joomla\Filesystem\Folder; | ||||
|  | ||||
| /** | ||||
|  * An Archive handling class | ||||
|  * | ||||
|  * @since  1.0 | ||||
|  */ | ||||
| class Archive | ||||
| { | ||||
|     /** | ||||
|      * The array of instantiated archive adapters. | ||||
|      * | ||||
|      * @var    ExtractableInterface[] | ||||
|      * @since  1.0 | ||||
|      */ | ||||
|     protected $adapters = []; | ||||
|  | ||||
|     /** | ||||
|      * Holds the options array. | ||||
|      * | ||||
|      * @var    array|\ArrayAccess | ||||
|      * @since  1.0 | ||||
|      */ | ||||
|     public $options = []; | ||||
|  | ||||
|     /** | ||||
|      * Create a new Archive object. | ||||
|      * | ||||
|      * @param   array|\ArrayAccess  $options  An array of options | ||||
|      * | ||||
|      * @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.' | ||||
|             ); | ||||
|         } | ||||
|  | ||||
|         // Make sure we have a tmp directory. | ||||
|         isset($options['tmp_path']) || $options['tmp_path'] = realpath(sys_get_temp_dir()); | ||||
|  | ||||
|         $this->options = $options; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Extract an archive file to a directory. | ||||
|      * | ||||
|      * @param   string  $archivename  The name of the archive file | ||||
|      * @param   string  $extractdir   Directory to unpack into | ||||
|      * | ||||
|      * @return  boolean  True for success | ||||
|      * | ||||
|      * @since   1.0 | ||||
|      * @throws  UnknownArchiveException if the archive type is not supported | ||||
|      */ | ||||
|     public function extract($archivename, $extractdir) | ||||
|     { | ||||
|         $ext      = pathinfo($archivename, \PATHINFO_EXTENSION); | ||||
|         $path     = pathinfo($archivename, \PATHINFO_DIRNAME); | ||||
|         $filename = pathinfo($archivename, \PATHINFO_FILENAME); | ||||
|  | ||||
|         switch (strtolower($ext)) { | ||||
|             case 'zip': | ||||
|                 $result = $this->getAdapter('zip')->extract($archivename, $extractdir); | ||||
|  | ||||
|                 break; | ||||
|  | ||||
|             case 'tar': | ||||
|                 $result = $this->getAdapter('tar')->extract($archivename, $extractdir); | ||||
|  | ||||
|                 break; | ||||
|  | ||||
|             case 'tgz': | ||||
|             case 'gz': | ||||
|             case 'gzip': | ||||
|                 // This may just be an individual file (e.g. sql script) | ||||
|                 $tmpfname = $this->options['tmp_path'] . '/' . uniqid('gzip'); | ||||
|  | ||||
|                 try { | ||||
|                     $this->getAdapter('gzip')->extract($archivename, $tmpfname); | ||||
|                 } catch (\RuntimeException $exception) { | ||||
|                     @unlink($tmpfname); | ||||
|  | ||||
|                     return false; | ||||
|                 } | ||||
|  | ||||
|                 if ($ext === 'tgz' || stripos($filename, '.tar') !== false) { | ||||
|                     $result = $this->getAdapter('tar')->extract($tmpfname, $extractdir); | ||||
|                 } else { | ||||
|                     Folder::create($extractdir); | ||||
|                     $result = File::copy($tmpfname, $extractdir . '/' . $filename, null, false); | ||||
|                 } | ||||
|  | ||||
|                 @unlink($tmpfname); | ||||
|  | ||||
|                 break; | ||||
|  | ||||
|             case 'tbz2': | ||||
|             case 'bz2': | ||||
|             case 'bzip2': | ||||
|                 // This may just be an individual file (e.g. sql script) | ||||
|                 $tmpfname = $this->options['tmp_path'] . '/' . uniqid('bzip2'); | ||||
|  | ||||
|                 try { | ||||
|                     $this->getAdapter('bzip2')->extract($archivename, $tmpfname); | ||||
|                 } catch (\RuntimeException $exception) { | ||||
|                     @unlink($tmpfname); | ||||
|  | ||||
|                     return false; | ||||
|                 } | ||||
|  | ||||
|                 if ($ext === 'tbz2' || stripos($filename, '.tar') !== false) { | ||||
|                     $result = $this->getAdapter('tar')->extract($tmpfname, $extractdir); | ||||
|                 } else { | ||||
|                     Folder::create($extractdir); | ||||
|                     $result = File::copy($tmpfname, $extractdir . '/' . $filename, null, false); | ||||
|                 } | ||||
|  | ||||
|                 @unlink($tmpfname); | ||||
|  | ||||
|                 break; | ||||
|  | ||||
|             default: | ||||
|                 throw new UnknownArchiveException(sprintf('Unsupported archive type: %s', $ext)); | ||||
|         } | ||||
|  | ||||
|         return $result; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Method to override the provided adapter with your own implementation. | ||||
|      * | ||||
|      * @param   string   $type      Name of the adapter to set. | ||||
|      * @param   string   $class     FQCN of your class which implements ExtractableInterface. | ||||
|      * @param   boolean  $override  True to force override the adapter type. | ||||
|      * | ||||
|      * @return  $this | ||||
|      * | ||||
|      * @since   1.0 | ||||
|      * @throws  UnsupportedArchiveException if the adapter type is not supported | ||||
|      */ | ||||
|     public function setAdapter($type, $class, $override = true) | ||||
|     { | ||||
|         if ($override || !isset($this->adapters[$type])) { | ||||
|             if (!\is_object($class) && !class_exists($class)) { | ||||
|                 throw new UnsupportedArchiveException($type, sprintf('Archive adapter "%s" (class "%s") not found.', $type, $class)); | ||||
|             } | ||||
|  | ||||
|             if (!$class::isSupported()) { | ||||
|                 throw new UnsupportedArchiveException($type, sprintf('Archive adapter "%s" (class "%s") not supported.', $type, $class)); | ||||
|             } | ||||
|  | ||||
|             $object = new $class($this->options); | ||||
|  | ||||
|             if (!($object instanceof ExtractableInterface)) { | ||||
|                 throw new UnsupportedArchiveException( | ||||
|                     $type, | ||||
|                     sprintf( | ||||
|                         'The provided adapter "%s" (class "%s") must implement %s', | ||||
|                         $type, | ||||
|                         $class, | ||||
|                         ExtractableInterface::class | ||||
|                     ) | ||||
|                 ); | ||||
|             } | ||||
|  | ||||
|             $this->adapters[$type] = $object; | ||||
|         } | ||||
|  | ||||
|         return $this; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Get a file compression adapter. | ||||
|      * | ||||
|      * @param   string  $type  The type of adapter (bzip2|gzip|tar|zip). | ||||
|      * | ||||
|      * @return  ExtractableInterface  Adapter for the requested type | ||||
|      * | ||||
|      * @since   1.0 | ||||
|      */ | ||||
|     public function getAdapter($type) | ||||
|     { | ||||
|         $type = strtolower($type); | ||||
|  | ||||
|         if (!isset($this->adapters[$type])) { | ||||
|             // Try to load the adapter object | ||||
|             $this->setAdapter($type, __NAMESPACE__ . '\\' . ucfirst($type)); | ||||
|         } | ||||
|  | ||||
|         return $this->adapters[$type]; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										139
									
								
								libraries/vendor/joomla/archive/src/Bzip2.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										139
									
								
								libraries/vendor/joomla/archive/src/Bzip2.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,139 @@ | ||||
| <?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\Stream; | ||||
|  | ||||
| /** | ||||
|  * Bzip2 format adapter for the Archive package | ||||
|  * | ||||
|  * @since  1.0 | ||||
|  */ | ||||
| class Bzip2 implements ExtractableInterface | ||||
| { | ||||
|     /** | ||||
|      * Bzip2 file data buffer | ||||
|      * | ||||
|      * @var    string | ||||
|      * @since  1.0 | ||||
|      */ | ||||
|     private $data; | ||||
|  | ||||
|     /** | ||||
|      * 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 | ||||
|      * | ||||
|      * @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 Bzip2 compressed file to a given path | ||||
|      * | ||||
|      * @param   string  $archive      Path to Bzip2 archive to extract | ||||
|      * @param   string  $destination  Path to extract archive to | ||||
|      * | ||||
|      * @return  boolean  True if successful | ||||
|      * | ||||
|      * @since   1.0 | ||||
|      * @throws  \RuntimeException | ||||
|      */ | ||||
|     public function extract($archive, $destination) | ||||
|     { | ||||
|         $this->data = null; | ||||
|  | ||||
|         if (!isset($this->options['use_streams']) || $this->options['use_streams'] == false) { | ||||
|             // Old style: read the whole file and then parse it | ||||
|             $this->data = file_get_contents($archive); | ||||
|  | ||||
|             if (!$this->data) { | ||||
|                 throw new \RuntimeException('Unable to read archive'); | ||||
|             } | ||||
|  | ||||
|             $buffer = bzdecompress($this->data); | ||||
|             unset($this->data); | ||||
|  | ||||
|             if (empty($buffer)) { | ||||
|                 throw new \RuntimeException('Unable to decompress data'); | ||||
|             } | ||||
|  | ||||
|             if (!File::write($destination, $buffer)) { | ||||
|                 throw new \RuntimeException('Unable to write archive to file ' . $destination); | ||||
|             } | ||||
|         } else { | ||||
|             // New style! streams! | ||||
|             $input = Stream::getStream(); | ||||
|  | ||||
|             // Use bzip | ||||
|             $input->set('processingmethod', 'bz'); | ||||
|  | ||||
|             if (!$input->open($archive)) { | ||||
|                 throw new \RuntimeException('Unable to read archive'); | ||||
|             } | ||||
|  | ||||
|             $output = Stream::getStream(); | ||||
|  | ||||
|             if (!$output->open($destination, 'w')) { | ||||
|                 $input->close(); | ||||
|  | ||||
|                 throw new \RuntimeException('Unable to open file "' . $destination . '" for writing'); | ||||
|             } | ||||
|  | ||||
|             do { | ||||
|                 $this->data = $input->read($input->get('chunksize', 8196)); | ||||
|  | ||||
|                 if ($this->data) { | ||||
|                     if (!$output->write($this->data)) { | ||||
|                         $input->close(); | ||||
|  | ||||
|                         throw new \RuntimeException('Unable to write archive to file ' . $destination); | ||||
|                     } | ||||
|                 } | ||||
|             } while ($this->data); | ||||
|  | ||||
|             $output->close(); | ||||
|             $input->close(); | ||||
|         } | ||||
|  | ||||
|         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 \extension_loaded('bz2'); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										19
									
								
								libraries/vendor/joomla/archive/src/Exception/UnknownArchiveException.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								libraries/vendor/joomla/archive/src/Exception/UnknownArchiveException.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,19 @@ | ||||
| <?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\Exception; | ||||
|  | ||||
| /** | ||||
|  * Exception class defining an unknown archive type | ||||
|  * | ||||
|  * @since  2.0.0 | ||||
|  */ | ||||
| class UnknownArchiveException extends \InvalidArgumentException | ||||
| { | ||||
| } | ||||
							
								
								
									
										55
									
								
								libraries/vendor/joomla/archive/src/Exception/UnsupportedArchiveException.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								libraries/vendor/joomla/archive/src/Exception/UnsupportedArchiveException.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,55 @@ | ||||
| <?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\Exception; | ||||
|  | ||||
| /** | ||||
|  * Exception class defining an unsupported archive adapter | ||||
|  * | ||||
|  * @since  2.0.0 | ||||
|  */ | ||||
| class UnsupportedArchiveException extends \InvalidArgumentException | ||||
| { | ||||
|     /** | ||||
|      * The unsupported archive adapter name | ||||
|      * | ||||
|      * @var    string | ||||
|      * @since  2.0.0-beta2 | ||||
|      */ | ||||
|     protected $adapterType = ''; | ||||
|  | ||||
|     /** | ||||
|      * Constructor | ||||
|      * | ||||
|      * @param   string       $adapterType  The unsupported adapter type. | ||||
|      * @param   string       $message      The Exception message to throw. | ||||
|      * @param   int          $code         The Exception code. | ||||
|      * @param   ?\Throwable  $previous     The previous throwable used for the exception chaining. | ||||
|      * | ||||
|      * @since  2.0.0-beta2 | ||||
|      */ | ||||
|     public function __construct(string $adapterType, string $message = '', int $code = 0, ?\Throwable $previous = null) | ||||
|     { | ||||
|         $this->adapterType = $adapterType; | ||||
|  | ||||
|         parent::__construct($message, $code, $previous); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Gets the name of the adapter type that was unsupported | ||||
|      * | ||||
|      * @return  string | ||||
|      * | ||||
|      * @since  2.0.0-beta2 | ||||
|      */ | ||||
|     public function getUnsupportedAdapterType(): string | ||||
|     { | ||||
|         return $this->adapterType; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										40
									
								
								libraries/vendor/joomla/archive/src/ExtractableInterface.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								libraries/vendor/joomla/archive/src/ExtractableInterface.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,40 @@ | ||||
| <?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; | ||||
|  | ||||
| /** | ||||
|  * Archive class interface | ||||
|  * | ||||
|  * @since  1.0 | ||||
|  */ | ||||
| interface ExtractableInterface | ||||
| { | ||||
|     /** | ||||
|      * Extract a compressed file to a given path | ||||
|      * | ||||
|      * @param   string  $archive      Path to archive to extract | ||||
|      * @param   string  $destination  Path to extract archive to | ||||
|      * | ||||
|      * @return  boolean  True if successful | ||||
|      * | ||||
|      * @since   1.0 | ||||
|      * @throws  \RuntimeException | ||||
|      */ | ||||
|     public function extract($archive, $destination); | ||||
|  | ||||
|     /** | ||||
|      * Tests whether this adapter can unpack files on this computer. | ||||
|      * | ||||
|      * @return  boolean  True if supported | ||||
|      * | ||||
|      * @since   1.0 | ||||
|      */ | ||||
|     public static function isSupported(); | ||||
| } | ||||
							
								
								
									
										197
									
								
								libraries/vendor/joomla/archive/src/Gzip.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										197
									
								
								libraries/vendor/joomla/archive/src/Gzip.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,197 @@ | ||||
| <?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\Stream; | ||||
|  | ||||
| /** | ||||
|  * Gzip 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 Gzip implements ExtractableInterface | ||||
| { | ||||
|     /** | ||||
|      * Gzip file flags. | ||||
|      * | ||||
|      * @var    array | ||||
|      * @since  1.0 | ||||
|      */ | ||||
|     private const FLAGS = ['FTEXT' => 0x01, 'FHCRC' => 0x02, 'FEXTRA' => 0x04, 'FNAME' => 0x08, 'FCOMMENT' => 0x10]; | ||||
|  | ||||
|     /** | ||||
|      * Gzip file data buffer | ||||
|      * | ||||
|      * @var    string | ||||
|      * @since  1.0 | ||||
|      */ | ||||
|     private $data; | ||||
|  | ||||
|     /** | ||||
|      * 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 | ||||
|      * | ||||
|      * @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 Gzip compressed file to a given path | ||||
|      * | ||||
|      * @param   string  $archive      Path to ZIP archive to extract | ||||
|      * @param   string  $destination  Path to extract archive to | ||||
|      * | ||||
|      * @return  boolean  True if successful | ||||
|      * | ||||
|      * @since   1.0 | ||||
|      * @throws  \RuntimeException | ||||
|      */ | ||||
|     public function extract($archive, $destination) | ||||
|     { | ||||
|         $this->data = null; | ||||
|  | ||||
|         if (!isset($this->options['use_streams']) || $this->options['use_streams'] == false) { | ||||
|             $this->data = file_get_contents($archive); | ||||
|  | ||||
|             if (!$this->data) { | ||||
|                 throw new \RuntimeException('Unable to read archive'); | ||||
|             } | ||||
|  | ||||
|             $position = $this->getFilePosition(); | ||||
|             $buffer   = gzinflate(substr($this->data, $position, \strlen($this->data) - $position)); | ||||
|  | ||||
|             if (empty($buffer)) { | ||||
|                 throw new \RuntimeException('Unable to decompress data'); | ||||
|             } | ||||
|  | ||||
|             if (!File::write($destination, $buffer)) { | ||||
|                 throw new \RuntimeException('Unable to write archive to file ' . $destination); | ||||
|             } | ||||
|         } else { | ||||
|             // New style! streams! | ||||
|             $input = Stream::getStream(); | ||||
|  | ||||
|             // Use gz | ||||
|             $input->set('processingmethod', 'gz'); | ||||
|  | ||||
|             if (!$input->open($archive)) { | ||||
|                 throw new \RuntimeException('Unable to read archive'); | ||||
|             } | ||||
|  | ||||
|             $output = Stream::getStream(); | ||||
|  | ||||
|             if (!$output->open($destination, 'w')) { | ||||
|                 $input->close(); | ||||
|  | ||||
|                 throw new \RuntimeException('Unable to open file "' . $destination . '" for writing'); | ||||
|             } | ||||
|  | ||||
|             do { | ||||
|                 $this->data = $input->read($input->get('chunksize', 8196)); | ||||
|  | ||||
|                 if ($this->data) { | ||||
|                     if (!$output->write($this->data)) { | ||||
|                         $input->close(); | ||||
|  | ||||
|                         throw new \RuntimeException('Unable to write archive to file ' . $destination); | ||||
|                     } | ||||
|                 } | ||||
|             } while ($this->data); | ||||
|  | ||||
|             $output->close(); | ||||
|             $input->close(); | ||||
|         } | ||||
|  | ||||
|         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 \extension_loaded('zlib'); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Get file data offset for archive | ||||
|      * | ||||
|      * @return  integer  Data position marker for archive | ||||
|      * | ||||
|      * @since   1.0 | ||||
|      * @throws  \RuntimeException | ||||
|      */ | ||||
|     public function getFilePosition() | ||||
|     { | ||||
|         // Gzipped file... unpack it first | ||||
|         $position = 0; | ||||
|         $info     = @ unpack('CCM/CFLG/VTime/CXFL/COS', $this->data, $position + 2); | ||||
|  | ||||
|         if (!$info) { | ||||
|             throw new \RuntimeException('Unable to decompress data.'); | ||||
|         } | ||||
|  | ||||
|         $position += 10; | ||||
|  | ||||
|         if ($info['FLG'] & self::FLAGS['FEXTRA']) { | ||||
|             $XLEN = unpack('vLength', $this->data, $position); | ||||
|             $XLEN = $XLEN['Length']; | ||||
|             $position += $XLEN + 2; | ||||
|         } | ||||
|  | ||||
|         if ($info['FLG'] & self::FLAGS['FNAME']) { | ||||
|             $filenamePos = strpos($this->data, "\x0", $position); | ||||
|             $position    = $filenamePos + 1; | ||||
|         } | ||||
|  | ||||
|         if ($info['FLG'] & self::FLAGS['FCOMMENT']) { | ||||
|             $commentPos = strpos($this->data, "\x0", $position); | ||||
|             $position   = $commentPos + 1; | ||||
|         } | ||||
|  | ||||
|         if ($info['FLG'] & self::FLAGS['FHCRC']) { | ||||
|             $hcrc = unpack('vCRC', $this->data, $position); | ||||
|             $hcrc = $hcrc['CRC']; | ||||
|             $position += 2; | ||||
|         } | ||||
|  | ||||
|         return $position; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										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; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										662
									
								
								libraries/vendor/joomla/archive/src/Zip.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										662
									
								
								libraries/vendor/joomla/archive/src/Zip.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,662 @@ | ||||
| <?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; | ||||
|  | ||||
| /** | ||||
|  * ZIP format adapter for the Archive package | ||||
|  * | ||||
|  * The ZIP compression code is partially based on code from: | ||||
|  * Eric Mueller <eric@themepark.com> | ||||
|  * http://www.zend.com/codex.php?id=535&single=1 | ||||
|  * | ||||
|  * Deins125 <webmaster@atlant.ru> | ||||
|  * http://www.zend.com/codex.php?id=470&single=1 | ||||
|  * | ||||
|  * The ZIP compression date code is partially based on code from | ||||
|  * Peter Listiak <mlady@users.sourceforge.net> | ||||
|  * | ||||
|  * 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  Chuck Hagenbuch <chuck@horde.org> | ||||
|  * @contributor  Michael Slusarz <slusarz@horde.org> | ||||
|  * @contributor  Michael Cochrane <mike@graftonhall.co.nz> | ||||
|  * | ||||
|  * @since  1.0 | ||||
|  */ | ||||
| class Zip implements ExtractableInterface | ||||
| { | ||||
|     /** | ||||
|      * ZIP compression methods. | ||||
|      * | ||||
|      * @var    array | ||||
|      * @since  1.0 | ||||
|      */ | ||||
|     private const METHODS = [ | ||||
|         0x0 => 'None', | ||||
|         0x1 => 'Shrunk', | ||||
|         0x2 => 'Super Fast', | ||||
|         0x3 => 'Fast', | ||||
|         0x4 => 'Normal', | ||||
|         0x5 => 'Maximum', | ||||
|         0x6 => 'Imploded', | ||||
|         0x8 => 'Deflated', | ||||
|     ]; | ||||
|  | ||||
|     /** | ||||
|      * Beginning of central directory record. | ||||
|      * | ||||
|      * @var    string | ||||
|      * @since  1.0 | ||||
|      */ | ||||
|     private const CTRL_DIR_HEADER = "\x50\x4b\x01\x02"; | ||||
|  | ||||
|     /** | ||||
|      * End of central directory record. | ||||
|      * | ||||
|      * @var    string | ||||
|      * @since  1.0 | ||||
|      */ | ||||
|     private const CTRL_DIR_END = "\x50\x4b\x05\x06\x00\x00\x00\x00"; | ||||
|  | ||||
|     /** | ||||
|      * Beginning of file contents. | ||||
|      * | ||||
|      * @var    string | ||||
|      * @since  1.0 | ||||
|      */ | ||||
|     private const FILE_HEADER = "\x50\x4b\x03\x04"; | ||||
|  | ||||
|     /** | ||||
|      * ZIP file data buffer | ||||
|      * | ||||
|      * @var    string | ||||
|      * @since  1.0 | ||||
|      */ | ||||
|     private $data; | ||||
|  | ||||
|     /** | ||||
|      * ZIP 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; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Create a ZIP compressed file from an array of file data. | ||||
|      * | ||||
|      * @param   string  $archive  Path to save archive. | ||||
|      * @param   array   $files    Array of files to add to archive. | ||||
|      * | ||||
|      * @return  boolean  True if successful. | ||||
|      * | ||||
|      * @since   1.0 | ||||
|      * @todo    Finish Implementation | ||||
|      */ | ||||
|     public function create($archive, $files) | ||||
|     { | ||||
|         $contents = []; | ||||
|         $ctrldir  = []; | ||||
|  | ||||
|         foreach ($files as $file) { | ||||
|             $this->addToZipFile($file, $contents, $ctrldir); | ||||
|         } | ||||
|  | ||||
|         return $this->createZipFile($contents, $ctrldir, $archive); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 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) | ||||
|     { | ||||
|         if (!is_file($archive)) { | ||||
|             throw new \RuntimeException('Archive does not exist at ' . $archive); | ||||
|         } | ||||
|  | ||||
|         if (static::hasNativeSupport()) { | ||||
|             return $this->extractNative($archive, $destination); | ||||
|         } | ||||
|  | ||||
|         return $this->extractCustom($archive, $destination); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Tests whether this adapter can unpack files on this computer. | ||||
|      * | ||||
|      * @return  boolean  True if supported | ||||
|      * | ||||
|      * @since   1.0 | ||||
|      */ | ||||
|     public static function isSupported() | ||||
|     { | ||||
|         return self::hasNativeSupport() || \extension_loaded('zlib'); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Method to determine if the server has native zip support for faster handling | ||||
|      * | ||||
|      * @return  boolean  True if php has native ZIP support | ||||
|      * | ||||
|      * @since   1.0 | ||||
|      */ | ||||
|     public static function hasNativeSupport() | ||||
|     { | ||||
|         return \extension_loaded('zip'); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Checks to see if the data is a valid ZIP file. | ||||
|      * | ||||
|      * @param   string  $data  ZIP archive data buffer. | ||||
|      * | ||||
|      * @return  boolean  True if valid, false if invalid. | ||||
|      * | ||||
|      * @since   1.0 | ||||
|      */ | ||||
|     public function checkZipData($data) | ||||
|     { | ||||
|         return strpos($data, self::FILE_HEADER) !== false; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Extract a ZIP compressed file to a given path using a php based algorithm that only requires zlib support | ||||
|      * | ||||
|      * @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 | ||||
|      */ | ||||
|     protected function extractCustom($archive, $destination) | ||||
|     { | ||||
|         $this->metadata = []; | ||||
|         $this->data     = file_get_contents($archive); | ||||
|  | ||||
|         if (!$this->data) { | ||||
|             throw new \RuntimeException('Unable to read archive'); | ||||
|         } | ||||
|  | ||||
|         if (!$this->readZipInfo($this->data)) { | ||||
|             throw new \RuntimeException('Get ZIP Information failed'); | ||||
|         } | ||||
|  | ||||
|         foreach ($this->metadata as $i => $metadata) { | ||||
|             $lastPathCharacter = substr($metadata['name'], -1, 1); | ||||
|  | ||||
|             if ($lastPathCharacter !== '/' && $lastPathCharacter !== '\\') { | ||||
|                 $buffer = $this->getFileData($i); | ||||
|                 $path   = Path::clean($destination . '/' . $metadata['name']); | ||||
|  | ||||
|                 if (!$this->isBelow($destination, $destination . '/' . $metadata['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'); | ||||
|                 } | ||||
|  | ||||
|                 if (!File::write($path, $buffer)) { | ||||
|                     throw new \RuntimeException('Unable to write file'); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Extract a ZIP compressed file to a given path using native php api calls for speed | ||||
|      * | ||||
|      * @param   string  $archive      Path to ZIP archive to extract | ||||
|      * @param   string  $destination  Path to extract archive into | ||||
|      * | ||||
|      * @return  boolean  True on success | ||||
|      * | ||||
|      * @throws  \RuntimeException | ||||
|      * @since   1.0 | ||||
|      */ | ||||
|     protected function extractNative($archive, $destination) | ||||
|     { | ||||
|         $zip = new \ZipArchive(); | ||||
|  | ||||
|         if ($zip->open($archive) !== true) { | ||||
|             throw new \RuntimeException('Unable to open archive'); | ||||
|         } | ||||
|  | ||||
|         // Make sure the destination folder exists | ||||
|         if (!Folder::create($destination)) { | ||||
|             throw new \RuntimeException('Unable to create destination folder ' . \dirname($destination)); | ||||
|         } | ||||
|  | ||||
|         // Read files in the archive | ||||
|         for ($index = 0; $index < $zip->numFiles; $index++) { | ||||
|             $file = $zip->getNameIndex($index); | ||||
|  | ||||
|             if (substr($file, -1) === '/') { | ||||
|                 continue; | ||||
|             } | ||||
|  | ||||
|             $buffer = $zip->getFromIndex($index); | ||||
|  | ||||
|             if ($buffer === false) { | ||||
|                 throw new \RuntimeException('Unable to read ZIP entry'); | ||||
|             } | ||||
|  | ||||
|             if (!$this->isBelow($destination, $destination . '/' . $file)) { | ||||
|                 throw new \RuntimeException('Unable to write outside of destination path', 100); | ||||
|             } | ||||
|  | ||||
|             if (File::write($destination . '/' . $file, $buffer) === false) { | ||||
|                 throw new \RuntimeException('Unable to write ZIP entry to file ' . $destination . '/' . $file); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         $zip->close(); | ||||
|  | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Get the list of files/data from a ZIP archive buffer. | ||||
|      * | ||||
|      * <pre> | ||||
|      * KEY: Position in zipfile | ||||
|      * VALUES: 'attr'  --  File attributes | ||||
|      *         'crc'   --  CRC checksum | ||||
|      *         'csize' --  Compressed file size | ||||
|      *         'date'  --  File modification time | ||||
|      *         'name'  --  Filename | ||||
|      *         'method'--  Compression method | ||||
|      *         'size'  --  Original file size | ||||
|      *         'type'  --  File type | ||||
|      * </pre> | ||||
|      * | ||||
|      * @param   string  $data  The ZIP archive buffer. | ||||
|      * | ||||
|      * @return  boolean True on success | ||||
|      * | ||||
|      * @since   1.0 | ||||
|      * @throws  \RuntimeException | ||||
|      */ | ||||
|     private function readZipInfo($data) | ||||
|     { | ||||
|         $entries = []; | ||||
|  | ||||
|         // Find the last central directory header entry | ||||
|         $fhLast = strpos($data, self::CTRL_DIR_END); | ||||
|  | ||||
|         do { | ||||
|             $last = $fhLast; | ||||
|         } while (($fhLast = strpos($data, self::CTRL_DIR_END, $fhLast + 1)) !== false); | ||||
|  | ||||
|         // Find the central directory offset | ||||
|         $offset = 0; | ||||
|  | ||||
|         if ($last) { | ||||
|             $endOfCentralDirectory = unpack( | ||||
|                 'vNumberOfDisk/vNoOfDiskWithStartOfCentralDirectory/vNoOfCentralDirectoryEntriesOnDisk/' . | ||||
|                 'vTotalCentralDirectoryEntries/VSizeOfCentralDirectory/VCentralDirectoryOffset/vCommentLength', | ||||
|                 $data, | ||||
|                 $last + 4 | ||||
|             ); | ||||
|             $offset = $endOfCentralDirectory['CentralDirectoryOffset']; | ||||
|         } | ||||
|  | ||||
|         // Get details from central directory structure. | ||||
|         $fhStart    = strpos($data, self::CTRL_DIR_HEADER, $offset); | ||||
|         $dataLength = \strlen($data); | ||||
|  | ||||
|         do { | ||||
|             if ($dataLength < $fhStart + 31) { | ||||
|                 throw new \RuntimeException('Invalid ZIP Data'); | ||||
|             } | ||||
|  | ||||
|             $info = unpack('vMethod/VTime/VCRC32/VCompressed/VUncompressed/vLength', $data, $fhStart + 10); | ||||
|             $name = substr($data, $fhStart + 46, $info['Length']); | ||||
|  | ||||
|             $entries[$name] = [ | ||||
|                 'attr'       => null, | ||||
|                 'crc'        => sprintf('%08s', dechex($info['CRC32'])), | ||||
|                 'csize'      => $info['Compressed'], | ||||
|                 'date'       => null, | ||||
|                 '_dataStart' => null, | ||||
|                 'name'       => $name, | ||||
|                 'method'     => self::METHODS[$info['Method']], | ||||
|                 '_method'    => $info['Method'], | ||||
|                 'size'       => $info['Uncompressed'], | ||||
|                 'type'       => null, | ||||
|             ]; | ||||
|  | ||||
|             $entries[$name]['date'] = mktime( | ||||
|                 ($info['Time'] >> 11) & 0x1f, | ||||
|                 ($info['Time'] >> 5) & 0x3f, | ||||
|                 ($info['Time'] << 1) & 0x3e, | ||||
|                 ($info['Time'] >> 21) & 0x07, | ||||
|                 ($info['Time'] >> 16) & 0x1f, | ||||
|                 (($info['Time'] >> 25) & 0x7f) + 1980 | ||||
|             ); | ||||
|  | ||||
|             if ($dataLength < $fhStart + 43) { | ||||
|                 throw new \RuntimeException('Invalid ZIP data'); | ||||
|             } | ||||
|  | ||||
|             $info = unpack('vInternal/VExternal/VOffset', $data, $fhStart + 36); | ||||
|  | ||||
|             $entries[$name]['type'] = ($info['Internal'] & 0x01) ? 'text' : 'binary'; | ||||
|             $entries[$name]['attr'] = (($info['External'] & 0x10) ? 'D' : '-') . (($info['External'] & 0x20) ? 'A' : '-') | ||||
|                 . (($info['External'] & 0x03) ? 'S' : '-') . (($info['External'] & 0x02) ? 'H' : '-') . (($info['External'] & 0x01) ? 'R' : '-'); | ||||
|             $entries[$name]['offset'] = $info['Offset']; | ||||
|  | ||||
|             // Get details from local file header since we have the offset | ||||
|             $lfhStart = strpos($data, self::FILE_HEADER, $entries[$name]['offset']); | ||||
|  | ||||
|             if ($dataLength < $lfhStart + 34) { | ||||
|                 throw new \RuntimeException('Invalid ZIP Data'); | ||||
|             } | ||||
|  | ||||
|             $info                         = unpack('vMethod/VTime/VCRC32/VCompressed/VUncompressed/vLength/vExtraLength', $data, $lfhStart + 8); | ||||
|             $name                         = substr($data, $lfhStart + 30, $info['Length']); | ||||
|             $entries[$name]['_dataStart'] = $lfhStart + 30 + $info['Length'] + $info['ExtraLength']; | ||||
|  | ||||
|             // Bump the max execution time because not using the built in php zip libs makes this process slow. | ||||
|             @set_time_limit(ini_get('max_execution_time')); | ||||
|         } while (($fhStart = strpos($data, self::CTRL_DIR_HEADER, $fhStart + 46)) !== false); | ||||
|  | ||||
|         $this->metadata = array_values($entries); | ||||
|  | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Returns the file data for a file by offset in the ZIP archive | ||||
|      * | ||||
|      * @param   integer  $key  The position of the file in the archive. | ||||
|      * | ||||
|      * @return  string  Uncompressed file data buffer. | ||||
|      * | ||||
|      * @since   1.0 | ||||
|      */ | ||||
|     private function getFileData(int $key): string | ||||
|     { | ||||
|         if ($this->metadata[$key]['_method'] == 0x8) { | ||||
|             return gzinflate(substr($this->data, $this->metadata[$key]['_dataStart'], $this->metadata[$key]['csize'])); | ||||
|         } | ||||
|  | ||||
|         if ($this->metadata[$key]['_method'] == 0x0) { | ||||
|             // Files that aren't compressed. | ||||
|             return substr($this->data, $this->metadata[$key]['_dataStart'], $this->metadata[$key]['csize']); | ||||
|         } | ||||
|  | ||||
|         if ($this->metadata[$key]['_method'] == 0x12) { | ||||
|             // If bz2 extension is loaded use it | ||||
|             if (\extension_loaded('bz2')) { | ||||
|                 return bzdecompress(substr($this->data, $this->metadata[$key]['_dataStart'], $this->metadata[$key]['csize'])); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return ''; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Converts a UNIX timestamp to a 4-byte DOS date and time format (date in high 2-bytes, time in low 2-bytes allowing magnitude comparison). | ||||
|      * | ||||
|      * @param   integer  $unixtime  The current UNIX timestamp. | ||||
|      * | ||||
|      * @return  integer  The current date in a 4-byte DOS format. | ||||
|      * | ||||
|      * @since   1.0 | ||||
|      */ | ||||
|     protected function unix2DosTime($unixtime = null) | ||||
|     { | ||||
|         $timearray = $unixtime === null ? getdate() : getdate($unixtime); | ||||
|  | ||||
|         if ($timearray['year'] < 1980) { | ||||
|             $timearray['year']    = 1980; | ||||
|             $timearray['mon']     = 1; | ||||
|             $timearray['mday']    = 1; | ||||
|             $timearray['hours']   = 0; | ||||
|             $timearray['minutes'] = 0; | ||||
|             $timearray['seconds'] = 0; | ||||
|         } | ||||
|  | ||||
|         return (($timearray['year'] - 1980) << 25) | ($timearray['mon'] << 21) | ($timearray['mday'] << 16) | ($timearray['hours'] << 11) | | ||||
|             ($timearray['minutes'] << 5) | ($timearray['seconds'] >> 1); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Adds a "file" to the ZIP archive. | ||||
|      * | ||||
|      * @param   array  $file      File data array to add | ||||
|      * @param   array  $contents  An array of existing zipped files. | ||||
|      * @param   array  $ctrldir   An array of central directory information. | ||||
|      * | ||||
|      * @return  void | ||||
|      * | ||||
|      * @since   1.0 | ||||
|      * @todo    Review and finish implementation | ||||
|      */ | ||||
|     private function addToZipFile(array &$file, array &$contents, array &$ctrldir): void | ||||
|     { | ||||
|         $data = &$file['data']; | ||||
|         $name = str_replace('\\', '/', $file['name']); | ||||
|  | ||||
|         // See if time/date information has been provided. | ||||
|         $ftime = null; | ||||
|  | ||||
|         if (isset($file['time'])) { | ||||
|             $ftime = $file['time']; | ||||
|         } | ||||
|  | ||||
|         // Get the hex time. | ||||
|         $dtime    = dechex($this->unix2DosTime($ftime)); | ||||
|         $hexdtime = \chr(hexdec($dtime[6] . $dtime[7])) . \chr(hexdec($dtime[4] . $dtime[5])) . \chr(hexdec($dtime[2] . $dtime[3])) | ||||
|             . \chr(hexdec($dtime[0] . $dtime[1])); | ||||
|  | ||||
|         // Begin creating the ZIP data. | ||||
|         $fr = self::FILE_HEADER; | ||||
|  | ||||
|         // Version needed to extract. | ||||
|         $fr .= "\x14\x00"; | ||||
|  | ||||
|         // General purpose bit flag. | ||||
|         $fr .= "\x00\x00"; | ||||
|  | ||||
|         // Compression method. | ||||
|         $fr .= "\x08\x00"; | ||||
|  | ||||
|         // Last modification time/date. | ||||
|         $fr .= $hexdtime; | ||||
|  | ||||
|         // "Local file header" segment. | ||||
|         $uncLen = \strlen($data); | ||||
|         $crc    = crc32($data); | ||||
|         $zdata  = gzcompress($data); | ||||
|         $zdata  = substr(substr($zdata, 0, -4), 2); | ||||
|         $cLen   = \strlen($zdata); | ||||
|  | ||||
|         // CRC 32 information. | ||||
|         $fr .= pack('V', $crc); | ||||
|  | ||||
|         // Compressed filesize. | ||||
|         $fr .= pack('V', $cLen); | ||||
|  | ||||
|         // Uncompressed filesize. | ||||
|         $fr .= pack('V', $uncLen); | ||||
|  | ||||
|         // Length of filename. | ||||
|         $fr .= pack('v', \strlen($name)); | ||||
|  | ||||
|         // Extra field length. | ||||
|         $fr .= pack('v', 0); | ||||
|  | ||||
|         // File name. | ||||
|         $fr .= $name; | ||||
|  | ||||
|         // "File data" segment. | ||||
|         $fr .= $zdata; | ||||
|  | ||||
|         // Add this entry to array. | ||||
|         $oldOffset  = \strlen(implode('', $contents)); | ||||
|         $contents[] = &$fr; | ||||
|  | ||||
|         // Add to central directory record. | ||||
|         $cdrec = self::CTRL_DIR_HEADER; | ||||
|  | ||||
|         // Version made by. | ||||
|         $cdrec .= "\x00\x00"; | ||||
|  | ||||
|         // Version needed to extract | ||||
|         $cdrec .= "\x14\x00"; | ||||
|  | ||||
|         // General purpose bit flag | ||||
|         $cdrec .= "\x00\x00"; | ||||
|  | ||||
|         // Compression method | ||||
|         $cdrec .= "\x08\x00"; | ||||
|  | ||||
|         // Last mod time/date. | ||||
|         $cdrec .= $hexdtime; | ||||
|  | ||||
|         // CRC 32 information. | ||||
|         $cdrec .= pack('V', $crc); | ||||
|  | ||||
|         // Compressed filesize. | ||||
|         $cdrec .= pack('V', $cLen); | ||||
|  | ||||
|         // Uncompressed filesize. | ||||
|         $cdrec .= pack('V', $uncLen); | ||||
|  | ||||
|         // Length of filename. | ||||
|         $cdrec .= pack('v', \strlen($name)); | ||||
|  | ||||
|         // Extra field length. | ||||
|         $cdrec .= pack('v', 0); | ||||
|  | ||||
|         // File comment length. | ||||
|         $cdrec .= pack('v', 0); | ||||
|  | ||||
|         // Disk number start. | ||||
|         $cdrec .= pack('v', 0); | ||||
|  | ||||
|         // Internal file attributes. | ||||
|         $cdrec .= pack('v', 0); | ||||
|  | ||||
|         // External file attributes -'archive' bit set. | ||||
|         $cdrec .= pack('V', 32); | ||||
|  | ||||
|         // Relative offset of local header. | ||||
|         $cdrec .= pack('V', $oldOffset); | ||||
|  | ||||
|         // File name. | ||||
|         $cdrec .= $name; | ||||
|  | ||||
|         // Save to central directory array. | ||||
|         $ctrldir[] = &$cdrec; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Creates the ZIP file. | ||||
|      * | ||||
|      * Official ZIP file format: http://www.pkware.com/appnote.txt | ||||
|      * | ||||
|      * @param   array   $contents  An array of existing zipped files. | ||||
|      * @param   array   $ctrlDir   An array of central directory information. | ||||
|      * @param   string  $path      The path to store the archive. | ||||
|      * | ||||
|      * @return  boolean  True if successful | ||||
|      * | ||||
|      * @since   1.0 | ||||
|      * @todo    Review and finish implementation | ||||
|      */ | ||||
|     private function createZipFile(array $contents, array $ctrlDir, string $path): bool | ||||
|     { | ||||
|         $data = implode('', $contents); | ||||
|         $dir  = implode('', $ctrlDir); | ||||
|  | ||||
|         /* | ||||
|          * Buffer data: | ||||
|          * Total # of entries "on this disk". | ||||
|          * Total # of entries overall. | ||||
|          * Size of central directory. | ||||
|          * Offset to start of central dir. | ||||
|          * ZIP file comment length. | ||||
|          */ | ||||
|         $buffer = $data . $dir . self::CTRL_DIR_END . | ||||
|         pack('v', \count($ctrlDir)) . | ||||
|         pack('v', \count($ctrlDir)) . | ||||
|         pack('V', \strlen($dir)) . | ||||
|         pack('V', \strlen($data)) . | ||||
|         "\x00\x00"; | ||||
|  | ||||
|         return File::write($path, $buffer); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 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   1.1.10 | ||||
|      */ | ||||
|     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