[ Index ] |
PHP Cross Reference of Joomla 4.2.2 documentation |
[Summary view] [Print] [Text view]
1 <?php 2 3 /** 4 * Joomla! Content Management System 5 * 6 * @copyright (C) 2007 Open Source Matters, Inc. <https://www.joomla.org> 7 * @license GNU General Public License version 2 or later; see LICENSE.txt 8 */ 9 10 namespace Joomla\CMS\Installer; 11 12 use Joomla\Archive\Archive; 13 use Joomla\CMS\Factory; 14 use Joomla\CMS\Filesystem\File; 15 use Joomla\CMS\Filesystem\Folder; 16 use Joomla\CMS\Filesystem\Path; 17 use Joomla\CMS\Http\HttpFactory; 18 use Joomla\CMS\Language\Text; 19 use Joomla\CMS\Log\Log; 20 use Joomla\CMS\Plugin\PluginHelper; 21 use Joomla\CMS\Updater\Update; 22 use Joomla\CMS\Version; 23 24 // phpcs:disable PSR1.Files.SideEffects 25 \defined('JPATH_PLATFORM') or die; 26 // phpcs:enable PSR1.Files.SideEffects 27 28 /** 29 * Installer helper class 30 * 31 * @since 3.1 32 */ 33 abstract class InstallerHelper 34 { 35 /** 36 * Hash not validated identifier. 37 * 38 * @var integer 39 * @since 3.9.0 40 */ 41 public const HASH_NOT_VALIDATED = 0; 42 43 /** 44 * Hash validated identifier. 45 * 46 * @var integer 47 * @since 3.9.0 48 */ 49 public const HASH_VALIDATED = 1; 50 51 /** 52 * Hash not provided identifier. 53 * 54 * @var integer 55 * @since 3.9.0 56 */ 57 public const HASH_NOT_PROVIDED = 2; 58 59 /** 60 * Downloads a package 61 * 62 * @param string $url URL of file to download 63 * @param string|bool $target Download target filename or false to get the filename from the URL 64 * 65 * @return string|boolean Path to downloaded package or boolean false on failure 66 * 67 * @since 3.1 68 */ 69 public static function downloadPackage($url, $target = false) 70 { 71 // Capture PHP errors 72 $track_errors = ini_get('track_errors'); 73 ini_set('track_errors', true); 74 75 // Set user agent 76 $version = new Version(); 77 ini_set('user_agent', $version->getUserAgent('Installer')); 78 79 // Load installer plugins, and allow URL and headers modification 80 $headers = array(); 81 PluginHelper::importPlugin('installer'); 82 Factory::getApplication()->triggerEvent('onInstallerBeforePackageDownload', array(&$url, &$headers)); 83 84 try { 85 $response = HttpFactory::getHttp()->get($url, $headers); 86 } catch (\RuntimeException $exception) { 87 Log::add(Text::sprintf('JLIB_INSTALLER_ERROR_DOWNLOAD_SERVER_CONNECT', $exception->getMessage()), Log::WARNING, 'jerror'); 88 89 return false; 90 } 91 92 // Convert keys of headers to lowercase, to accommodate for case variations 93 $headers = array_change_key_case($response->headers, CASE_LOWER); 94 95 if (302 == $response->code && !empty($headers['location'])) { 96 return self::downloadPackage($headers['location']); 97 } elseif (200 != $response->code) { 98 Log::add(Text::sprintf('JLIB_INSTALLER_ERROR_DOWNLOAD_SERVER_CONNECT', $response->code), Log::WARNING, 'jerror'); 99 100 return false; 101 } 102 103 // Parse the Content-Disposition header to get the file name 104 if ( 105 !empty($headers['content-disposition']) 106 && preg_match("/\s*filename\s?=\s?(.*)/", $headers['content-disposition'][0], $parts) 107 ) { 108 $flds = explode(';', $parts[1]); 109 $target = trim($flds[0], '"'); 110 } 111 112 $tmpPath = Factory::getApplication()->get('tmp_path'); 113 114 // Set the target path if not given 115 if (!$target) { 116 $target = $tmpPath . '/' . self::getFilenameFromUrl($url); 117 } else { 118 $target = $tmpPath . '/' . basename($target); 119 } 120 121 // Write buffer to file 122 File::write($target, $response->body); 123 124 // Restore error tracking to what it was before 125 ini_set('track_errors', $track_errors); 126 127 // Bump the max execution time because not using built in php zip libs are slow 128 @set_time_limit(ini_get('max_execution_time')); 129 130 // Return the name of the downloaded package 131 return basename($target); 132 } 133 134 /** 135 * Unpacks a file and verifies it as a Joomla element package 136 * Supports .gz .tar .tar.gz and .zip 137 * 138 * @param string $packageFilename The uploaded package filename or install directory 139 * @param boolean $alwaysReturnArray If should return false (and leave garbage behind) or return $retval['type']=false 140 * 141 * @return array|boolean Array on success or boolean false on failure 142 * 143 * @since 3.1 144 */ 145 public static function unpack($packageFilename, $alwaysReturnArray = false) 146 { 147 // Path to the archive 148 $archivename = $packageFilename; 149 150 // Temporary folder to extract the archive into 151 $tmpdir = uniqid('install_'); 152 153 // Clean the paths to use for archive extraction 154 $extractdir = Path::clean(\dirname($packageFilename) . '/' . $tmpdir); 155 $archivename = Path::clean($archivename); 156 157 // Do the unpacking of the archive 158 try { 159 $archive = new Archive(array('tmp_path' => Factory::getApplication()->get('tmp_path'))); 160 $extract = $archive->extract($archivename, $extractdir); 161 } catch (\Exception $e) { 162 if ($alwaysReturnArray) { 163 return array( 164 'extractdir' => null, 165 'packagefile' => $archivename, 166 'type' => false, 167 ); 168 } 169 170 return false; 171 } 172 173 if (!$extract) { 174 if ($alwaysReturnArray) { 175 return array( 176 'extractdir' => null, 177 'packagefile' => $archivename, 178 'type' => false, 179 ); 180 } 181 182 return false; 183 } 184 185 /* 186 * Let's set the extraction directory and package file in the result array so we can 187 * cleanup everything properly later on. 188 */ 189 $retval['extractdir'] = $extractdir; 190 $retval['packagefile'] = $archivename; 191 192 /* 193 * Try to find the correct install directory. In case the package is inside a 194 * subdirectory detect this and set the install directory to the correct path. 195 * 196 * List all the items in the installation directory. If there is only one, and 197 * it is a folder, then we will set that folder to be the installation folder. 198 */ 199 $dirList = array_merge((array) Folder::files($extractdir, ''), (array) Folder::folders($extractdir, '')); 200 201 if (\count($dirList) === 1) { 202 if (Folder::exists($extractdir . '/' . $dirList[0])) { 203 $extractdir = Path::clean($extractdir . '/' . $dirList[0]); 204 } 205 } 206 207 /* 208 * We have found the install directory so lets set it and then move on 209 * to detecting the extension type. 210 */ 211 $retval['dir'] = $extractdir; 212 213 /* 214 * Get the extension type and return the directory/type array on success or 215 * false on fail. 216 */ 217 $retval['type'] = self::detectType($extractdir); 218 219 if ($alwaysReturnArray || $retval['type']) { 220 return $retval; 221 } else { 222 return false; 223 } 224 } 225 226 /** 227 * Method to detect the extension type from a package directory 228 * 229 * @param string $packageDirectory Path to package directory 230 * 231 * @return mixed Extension type string or boolean false on fail 232 * 233 * @since 3.1 234 */ 235 public static function detectType($packageDirectory) 236 { 237 // Search the install dir for an XML file 238 $files = Folder::files($packageDirectory, '\.xml$', 1, true); 239 240 if (!$files || !\count($files)) { 241 Log::add(Text::_('JLIB_INSTALLER_ERROR_NOTFINDXMLSETUPFILE'), Log::WARNING, 'jerror'); 242 243 return false; 244 } 245 246 foreach ($files as $file) { 247 $xml = simplexml_load_file($file); 248 249 if (!$xml) { 250 continue; 251 } 252 253 if ($xml->getName() !== 'extension') { 254 unset($xml); 255 continue; 256 } 257 258 $type = (string) $xml->attributes()->type; 259 260 // Free up memory 261 unset($xml); 262 263 return $type; 264 } 265 266 Log::add(Text::_('JLIB_INSTALLER_ERROR_NOTFINDJOOMLAXMLSETUPFILE'), Log::WARNING, 'jerror'); 267 268 return false; 269 } 270 271 /** 272 * Gets a file name out of a url 273 * 274 * @param string $url URL to get name from 275 * 276 * @return string Clean version of the filename or a unique id 277 * 278 * @since 3.1 279 */ 280 public static function getFilenameFromUrl($url) 281 { 282 $default = uniqid(); 283 284 if (!\is_string($url) || strpos($url, '/') === false) { 285 return $default; 286 } 287 288 // Get last part of the url (after the last slash). 289 $parts = explode('/', $url); 290 $filename = array_pop($parts); 291 292 // Replace special characters with underscores. 293 $filename = preg_replace('/[^a-z0-9\_\-\.]/i', '_', $filename); 294 295 // Replace multiple underscores with just one. 296 $filename = preg_replace('/__+/', '_', trim($filename, '_')); 297 298 // Return the cleaned filename or, if it is empty, a unique id. 299 return $filename ?: $default; 300 } 301 302 /** 303 * Clean up temporary uploaded package and unpacked extension 304 * 305 * @param string $package Path to the uploaded package file 306 * @param string $resultdir Path to the unpacked extension 307 * 308 * @return boolean True on success 309 * 310 * @since 3.1 311 */ 312 public static function cleanupInstall($package, $resultdir) 313 { 314 // Does the unpacked extension directory exist? 315 if ($resultdir && is_dir($resultdir)) { 316 Folder::delete($resultdir); 317 } 318 319 // Is the package file a valid file? 320 if (is_file($package)) { 321 File::delete($package); 322 } elseif (is_file(Path::clean(Factory::getApplication()->get('tmp_path') . '/' . $package))) { 323 // It might also be just a base filename 324 File::delete(Path::clean(Factory::getApplication()->get('tmp_path') . '/' . $package)); 325 } 326 } 327 328 /** 329 * Return the result of the checksum of a package with the SHA256/SHA384/SHA512 tags in the update server manifest 330 * 331 * @param string $packagefile Location of the package to be installed 332 * @param Update $updateObject The Update Object 333 * 334 * @return integer one if the hashes match, zero if hashes doesn't match, two if hashes not found 335 * 336 * @since 3.9.0 337 */ 338 public static function isChecksumValid($packagefile, $updateObject) 339 { 340 $hashes = array('sha256', 'sha384', 'sha512'); 341 $hashOnFile = false; 342 343 foreach ($hashes as $hash) { 344 if ($updateObject->get($hash, false)) { 345 $hashPackage = hash_file($hash, $packagefile); 346 $hashRemote = $updateObject->$hash->_data; 347 $hashOnFile = true; 348 349 if ($hashPackage !== strtolower($hashRemote)) { 350 return self::HASH_NOT_VALIDATED; 351 } 352 } 353 } 354 355 if ($hashOnFile) { 356 return self::HASH_VALIDATED; 357 } 358 359 return self::HASH_NOT_PROVIDED; 360 } 361 }
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 |