[ Index ] |
PHP Cross Reference of Joomla 4.2.2 documentation |
[Summary view] [Print] [Text view]
1 <?php 2 /** 3 * Part of the Joomla Framework Archive Package 4 * 5 * @copyright Copyright (C) 2005 - 2021 Open Source Matters, Inc. All rights reserved. 6 * @license GNU General Public License version 2 or later; see LICENSE 7 */ 8 9 namespace Joomla\Archive; 10 11 use Joomla\Filesystem\File; 12 use Joomla\Filesystem\Folder; 13 use Joomla\Filesystem\Path; 14 15 /** 16 * Tar format adapter for the Archive package 17 * 18 * This class is inspired from and draws heavily in code and concept from the Compress package of 19 * The Horde Project <http://www.horde.org> 20 * 21 * @contributor Michael Slusarz <[email protected]> 22 * @contributor Michael Cochrane <[email protected]> 23 * 24 * @since 1.0 25 */ 26 class Tar implements ExtractableInterface 27 { 28 /** 29 * Tar file types. 30 * 31 * @var array 32 * @since 1.0 33 */ 34 private const TYPES = [ 35 0x0 => 'Unix file', 36 0x30 => 'File', 37 0x31 => 'Link', 38 0x32 => 'Symbolic link', 39 0x33 => 'Character special file', 40 0x34 => 'Block special file', 41 0x35 => 'Directory', 42 0x36 => 'FIFO special file', 43 0x37 => 'Contiguous file', 44 ]; 45 46 /** 47 * Tar file data buffer 48 * 49 * @var string 50 * @since 1.0 51 */ 52 private $data; 53 54 /** 55 * Tar file metadata array 56 * 57 * @var array 58 * @since 1.0 59 */ 60 private $metadata; 61 62 /** 63 * Holds the options array. 64 * 65 * @var array|\ArrayAccess 66 * @since 1.0 67 */ 68 protected $options = []; 69 70 /** 71 * Create a new Archive object. 72 * 73 * @param array|\ArrayAccess $options An array of options or an object that implements \ArrayAccess 74 * 75 * @since 1.0 76 * @throws \InvalidArgumentException 77 */ 78 public function __construct($options = []) 79 { 80 if (!\is_array($options) && !($options instanceof \ArrayAccess)) 81 { 82 throw new \InvalidArgumentException( 83 'The options param must be an array or implement the ArrayAccess interface.' 84 ); 85 } 86 87 $this->options = $options; 88 } 89 90 /** 91 * Extract a ZIP compressed file to a given path 92 * 93 * @param string $archive Path to ZIP archive to extract 94 * @param string $destination Path to extract archive into 95 * 96 * @return boolean True if successful 97 * 98 * @since 1.0 99 * @throws \RuntimeException 100 */ 101 public function extract($archive, $destination) 102 { 103 $this->metadata = null; 104 $this->data = file_get_contents($archive); 105 106 if (!$this->data) 107 { 108 throw new \RuntimeException('Unable to read archive'); 109 } 110 111 $this->getTarInfo($this->data); 112 113 for ($i = 0, $n = \count($this->metadata); $i < $n; $i++) 114 { 115 $type = strtolower($this->metadata[$i]['type']); 116 117 if ($type == 'file' || $type == 'unix file') 118 { 119 $buffer = $this->metadata[$i]['data']; 120 $path = Path::clean($destination . '/' . $this->metadata[$i]['name']); 121 122 if (!$this->isBelow($destination, $destination . '/' . $this->metadata[$i]['name'])) 123 { 124 throw new \OutOfBoundsException('Unable to write outside of destination path', 100); 125 } 126 127 // Make sure the destination folder exists 128 if (!Folder::create(\dirname($path))) 129 { 130 throw new \RuntimeException('Unable to create destination folder ' . \dirname($path)); 131 } 132 133 if (!File::write($path, $buffer)) 134 { 135 throw new \RuntimeException('Unable to write entry to file ' . $path); 136 } 137 } 138 } 139 140 return true; 141 } 142 143 /** 144 * Tests whether this adapter can unpack files on this computer. 145 * 146 * @return boolean True if supported 147 * 148 * @since 1.0 149 */ 150 public static function isSupported() 151 { 152 return true; 153 } 154 155 /** 156 * Get the list of files/data from a Tar archive buffer and builds a metadata array. 157 * 158 * Array structure: 159 * <pre> 160 * KEY: Position in the array 161 * VALUES: 'attr' -- File attributes 162 * 'data' -- Raw file contents 163 * 'date' -- File modification time 164 * 'name' -- Filename 165 * 'size' -- Original file size 166 * 'type' -- File type 167 * </pre> 168 * 169 * @param string $data The Tar archive buffer. 170 * 171 * @return void 172 * 173 * @since 1.0 174 * @throws \RuntimeException 175 */ 176 protected function getTarInfo(&$data) 177 { 178 $position = 0; 179 $returnArray = []; 180 181 while ($position < \strlen($data)) 182 { 183 $info = @unpack( 184 'Z100filename/Z8mode/Z8uid/Z8gid/Z12size/Z12mtime/Z8checksum/Ctypeflag/Z100link/Z6magic/Z2version/Z32uname/Z32gname/Z8devmajor/Z8devminor', 185 substr($data, $position) 186 ); 187 188 /* 189 * This variable has been set in the previous loop, meaning that the filename was present in the previous block 190 * to allow more than 100 characters - see below 191 */ 192 if (isset($longlinkfilename)) 193 { 194 $info['filename'] = $longlinkfilename; 195 unset($longlinkfilename); 196 } 197 198 if (!$info) 199 { 200 throw new \RuntimeException('Unable to decompress data'); 201 } 202 203 $position += 512; 204 $contents = substr($data, $position, octdec($info['size'])); 205 $position += ceil(octdec($info['size']) / 512) * 512; 206 207 if ($info['filename']) 208 { 209 $file = [ 210 'attr' => null, 211 'data' => null, 212 'date' => octdec($info['mtime']), 213 'name' => trim($info['filename']), 214 'size' => octdec($info['size']), 215 'type' => self::TYPES[$info['typeflag']] ?? null, 216 ]; 217 218 if (($info['typeflag'] == 0) || ($info['typeflag'] == 0x30) || ($info['typeflag'] == 0x35)) 219 { 220 // File or folder. 221 $file['data'] = $contents; 222 223 $mode = hexdec(substr($info['mode'], 4, 3)); 224 $file['attr'] = (($info['typeflag'] == 0x35) ? 'd' : '-') 225 . (($mode & 0x400) ? 'r' : '-') 226 . (($mode & 0x200) ? 'w' : '-') 227 . (($mode & 0x100) ? 'x' : '-') 228 . (($mode & 0x040) ? 'r' : '-') 229 . (($mode & 0x020) ? 'w' : '-') 230 . (($mode & 0x010) ? 'x' : '-') 231 . (($mode & 0x004) ? 'r' : '-') 232 . (($mode & 0x002) ? 'w' : '-') 233 . (($mode & 0x001) ? 'x' : '-'); 234 } 235 elseif (\chr($info['typeflag']) == 'L' && $info['filename'] == '././@LongLink') 236 { 237 // GNU tar ././@LongLink support - the filename is actually in the contents, set a variable here so we can test in the next loop 238 $longlinkfilename = $contents; 239 240 // And the file contents are in the next block so we'll need to skip this 241 continue; 242 } 243 244 $returnArray[] = $file; 245 } 246 } 247 248 $this->metadata = $returnArray; 249 } 250 251 /** 252 * Check if a path is below a given destination path 253 * 254 * @param string $destination The destination path 255 * @param string $path The path to be checked 256 * 257 * @return boolean 258 * 259 * @since 2.0.1 260 */ 261 private function isBelow($destination, $path): bool 262 { 263 $absoluteRoot = Path::clean(Path::resolve($destination)); 264 $absolutePath = Path::clean(Path::resolve($path)); 265 266 return strpos($absolutePath, $absoluteRoot) === 0; 267 } 268 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Wed Sep 7 05:41:13 2022 | Chilli.vc Blog - For Webmaster,Blog-Writer,System Admin and Domainer |