[ Index ]

PHP Cross Reference of Joomla 4.2.2 documentation

title

Body

[close]

/libraries/src/Filesystem/ -> Path.php (source)

   1  <?php
   2  
   3  /**
   4   * Joomla! Content Management System
   5   *
   6   * @copyright  (C) 2006 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\Filesystem;
  11  
  12  \defined('JPATH_PLATFORM') or die;
  13  
  14  use Joomla\CMS\Crypt\Crypt;
  15  
  16  if (!\defined('JPATH_ROOT')) {
  17      // Define a string constant for the root directory of the file system in native format
  18      \define('JPATH_ROOT', Path::clean(JPATH_SITE));
  19  }
  20  
  21  /**
  22   * A Path handling class
  23   *
  24   * @since  1.7.0
  25   */
  26  class Path
  27  {
  28      /**
  29       * Checks if a path's permissions can be changed.
  30       *
  31       * @param   string  $path  Path to check.
  32       *
  33       * @return  boolean  True if path can have mode changed.
  34       *
  35       * @since   1.7.0
  36       */
  37      public static function canChmod($path)
  38      {
  39          $perms = fileperms($path);
  40  
  41          if ($perms !== false) {
  42              if (@chmod($path, $perms ^ 0001)) {
  43                  @chmod($path, $perms);
  44  
  45                  return true;
  46              }
  47          }
  48  
  49          return false;
  50      }
  51  
  52      /**
  53       * Chmods files and directories recursively to given permissions.
  54       *
  55       * @param   string  $path        Root path to begin changing mode [without trailing slash].
  56       * @param   string  $filemode    Octal representation of the value to change file mode to [null = no change].
  57       * @param   string  $foldermode  Octal representation of the value to change folder mode to [null = no change].
  58       *
  59       * @return  boolean  True if successful [one fail means the whole operation failed].
  60       *
  61       * @since   1.7.0
  62       */
  63      public static function setPermissions($path, $filemode = '0644', $foldermode = '0755')
  64      {
  65          // Initialise return value
  66          $ret = true;
  67  
  68          if (is_dir($path)) {
  69              $dh = opendir($path);
  70  
  71              while ($file = readdir($dh)) {
  72                  if ($file != '.' && $file != '..') {
  73                      $fullpath = $path . '/' . $file;
  74  
  75                      if (is_dir($fullpath)) {
  76                          if (!self::setPermissions($fullpath, $filemode, $foldermode)) {
  77                              $ret = false;
  78                          }
  79                      } else {
  80                          if (isset($filemode)) {
  81                              if (!@ chmod($fullpath, octdec($filemode))) {
  82                                  $ret = false;
  83                              }
  84                          }
  85                      }
  86                  }
  87              }
  88  
  89              closedir($dh);
  90  
  91              if (isset($foldermode)) {
  92                  if (!@ chmod($path, octdec($foldermode))) {
  93                      $ret = false;
  94                  }
  95              }
  96          } else {
  97              if (isset($filemode)) {
  98                  $ret = @ chmod($path, octdec($filemode));
  99              }
 100          }
 101  
 102          return $ret;
 103      }
 104  
 105      /**
 106       * Get the permissions of the file/folder at a given path.
 107       *
 108       * @param   string  $path  The path of a file/folder.
 109       *
 110       * @return  string  Filesystem permissions.
 111       *
 112       * @since   1.7.0
 113       */
 114      public static function getPermissions($path)
 115      {
 116          $path = self::clean($path);
 117          $mode = @ decoct(@ fileperms($path) & 0777);
 118  
 119          if (\strlen($mode) < 3) {
 120              return '---------';
 121          }
 122  
 123          $parsed_mode = '';
 124  
 125          for ($i = 0; $i < 3; $i++) {
 126              // Read
 127              $parsed_mode .= ($mode[$i] & 04) ? 'r' : '-';
 128  
 129              // Write
 130              $parsed_mode .= ($mode[$i] & 02) ? 'w' : '-';
 131  
 132              // Execute
 133              $parsed_mode .= ($mode[$i] & 01) ? 'x' : '-';
 134          }
 135  
 136          return $parsed_mode;
 137      }
 138  
 139      /**
 140       * Checks for snooping outside of the file system root.
 141       *
 142       * @param   string  $path  A file system path to check.
 143       *
 144       * @return  string  A cleaned version of the path or exit on error.
 145       *
 146       * @throws  \Exception
 147       * @since   1.7.0
 148       */
 149      public static function check($path)
 150      {
 151          if (strpos($path, '..') !== false) {
 152              // Don't translate
 153              throw new \Exception(
 154                  sprintf(
 155                      '%s() - Use of relative paths not permitted',
 156                      __METHOD__
 157                  )
 158              );
 159          }
 160  
 161          $path = self::clean($path);
 162  
 163          if ((JPATH_ROOT != '') && strpos($path, self::clean(JPATH_ROOT)) !== 0) {
 164              throw new \Exception(
 165                  sprintf(
 166                      '%1$s() - Snooping out of bounds @ %2$s',
 167                      __METHOD__,
 168                      self::removeRoot($path)
 169                  )
 170              );
 171          }
 172  
 173          return $path;
 174      }
 175  
 176      /**
 177       * Function to strip additional / or \ in a path name.
 178       *
 179       * @param   string  $path  The path to clean.
 180       * @param   string  $ds    Directory separator (optional).
 181       *
 182       * @return  string  The cleaned path.
 183       *
 184       * @throws  \UnexpectedValueException
 185       * @since   1.7.0
 186       */
 187      public static function clean($path, $ds = DIRECTORY_SEPARATOR)
 188      {
 189          if (!\is_string($path) && !empty($path)) {
 190              throw new \UnexpectedValueException(
 191                  sprintf(
 192                      '%s() - $path is not a string',
 193                      __METHOD__
 194                  )
 195              );
 196          }
 197  
 198          $path = trim($path);
 199  
 200          if (empty($path)) {
 201              $path = JPATH_ROOT;
 202          } elseif (($ds === '\\') && substr($path, 0, 2) === '\\\\') {
 203              // Remove double slashes and backslashes and convert all slashes and backslashes to DIRECTORY_SEPARATOR
 204              // If dealing with a UNC path don't forget to prepend the path with a backslash.
 205              $path = "\\" . preg_replace('#[/\\\\]+#', $ds, $path);
 206          } else {
 207              $path = preg_replace('#[/\\\\]+#', $ds, $path);
 208          }
 209  
 210          return $path;
 211      }
 212  
 213      /**
 214       * Method to determine if script owns the path.
 215       *
 216       * @param   string  $path  Path to check ownership.
 217       *
 218       * @return  boolean  True if the php script owns the path passed.
 219       *
 220       * @since   1.7.0
 221       */
 222      public static function isOwner($path)
 223      {
 224          $tmp = md5(Crypt::genRandomBytes());
 225          $ssp = ini_get('session.save_path');
 226          $jtp = JPATH_SITE . '/tmp';
 227  
 228          // Try to find a writable directory
 229          $dir = false;
 230  
 231          foreach ([$jtp, $ssp, '/tmp'] as $currentDir) {
 232              if (is_writable($currentDir)) {
 233                  $dir = $currentDir;
 234  
 235                  break;
 236              }
 237          }
 238  
 239          if ($dir) {
 240              $test = $dir . '/' . $tmp;
 241  
 242              // Create the test file
 243              $blank = '';
 244              File::write($test, $blank, false);
 245  
 246              // Test ownership
 247              $return = (fileowner($test) == fileowner($path));
 248  
 249              // Delete the test file
 250              File::delete($test);
 251  
 252              return $return;
 253          }
 254  
 255          return false;
 256      }
 257  
 258      /**
 259       * Searches the directory paths for a given file.
 260       *
 261       * @param   mixed   $paths  An path string or array of path strings to search in
 262       * @param   string  $file   The file name to look for.
 263       *
 264       * @return  mixed   The full path and file name for the target file, or boolean false if the file is not found in any of the paths.
 265       *
 266       * @since   1.7.0
 267       */
 268      public static function find($paths, $file)
 269      {
 270          // Force to array
 271          if (!\is_array($paths) && !($paths instanceof \Iterator)) {
 272              settype($paths, 'array');
 273          }
 274  
 275          // Start looping through the path set
 276          foreach ($paths as $path) {
 277              // Get the path to the file
 278              $fullname = $path . '/' . $file;
 279  
 280              // Is the path based on a stream?
 281              if (strpos($path, '://') === false) {
 282                  // Not a stream, so do a realpath() to avoid directory
 283                  // traversal attempts on the local file system.
 284  
 285                  // Needed for substr() later
 286                  $path     = realpath($path);
 287                  $fullname = realpath($fullname);
 288              }
 289  
 290              /*
 291               * The substr() check added to make sure that the realpath()
 292               * results in a directory registered so that
 293               * non-registered directories are not accessible via directory
 294               * traversal attempts.
 295               */
 296              if (file_exists($fullname) && substr($fullname, 0, \strlen($path)) === $path) {
 297                  return $fullname;
 298              }
 299          }
 300  
 301          // Could not find the file in the set of paths
 302          return false;
 303      }
 304  
 305      /**
 306       * Resolves /./, /../ and multiple / in a string and returns the resulting absolute path, inspired by Flysystem
 307       * Removes trailing slashes
 308       *
 309       * @param   string  $path  A path to resolve
 310       *
 311       * @return  string  The resolved path
 312       *
 313       * @since   3.9.25
 314       */
 315      public static function resolve($path)
 316      {
 317          $path = static::clean($path);
 318  
 319          // Save start character for absolute path
 320          $startCharacter = ($path[0] === DIRECTORY_SEPARATOR) ? DIRECTORY_SEPARATOR : '';
 321  
 322          $parts = [];
 323  
 324          foreach (explode(DIRECTORY_SEPARATOR, $path) as $part) {
 325              switch ($part) {
 326                  case '':
 327                  case '.':
 328                      break;
 329  
 330                  case '..':
 331                      if (empty($parts)) {
 332                          throw new \Exception('Path is outside of the defined root');
 333                      }
 334  
 335                      array_pop($parts);
 336                      break;
 337  
 338                  default:
 339                      $parts[] = $part;
 340                      break;
 341              }
 342          }
 343  
 344          return $startCharacter . implode(DIRECTORY_SEPARATOR, $parts);
 345      }
 346  
 347      /**
 348       * Remove all references to root directory path and the system tmp path from a message
 349       *
 350       * @param   string  $message        The message to be cleaned
 351       * @param   string  $rootDirectory  Optional root directory, defaults to JPATH_ROOT
 352       *
 353       * @return  string
 354       *
 355       * @since   3.10.7
 356       */
 357      public static function removeRoot($message, $rootDirectory = null)
 358      {
 359          if (empty($rootDirectory)) {
 360              $rootDirectory = JPATH_ROOT;
 361          }
 362  
 363          $makePattern = static function ($dir) {
 364              return '~' . str_replace('~', '\\~', preg_replace('~[/\\\\]+~', '[/\\\\\\\\]+', $dir)) . '~';
 365          };
 366  
 367          $replacements = [
 368              $makePattern(static::clean($rootDirectory)) => '[ROOT]',
 369              $makePattern(sys_get_temp_dir())            => '[TMP]',
 370          ];
 371  
 372          return preg_replace(array_keys($replacements), array_values($replacements), $message);
 373      }
 374  }


Generated: Wed Sep 7 05:41:13 2022 Chilli.vc Blog - For Webmaster,Blog-Writer,System Admin and Domainer