[ Index ]

PHP Cross Reference of phpBB-3.3.12-deutsch

title

Body

[close]

/phpbb/filesystem/ -> filesystem.php (source)

   1  <?php
   2  /**
   3   *
   4   * This file is part of the phpBB Forum Software package.
   5   *
   6   * @copyright (c) phpBB Limited <https://www.phpbb.com>
   7   * @license GNU General Public License, version 2 (GPL-2.0)
   8   *
   9   * For full copyright and license information, please see
  10   * the docs/CREDITS.txt file.
  11   *
  12   */
  13  
  14  namespace phpbb\filesystem;
  15  
  16  use phpbb\filesystem\exception\filesystem_exception;
  17  
  18  /**
  19   * A class with various functions that are related to paths, files and the filesystem
  20   */
  21  class filesystem implements filesystem_interface
  22  {
  23      /**
  24       * Store some information about file ownership for phpBB's chmod function
  25       *
  26       * @var array
  27       */
  28      protected $chmod_info;
  29  
  30      /**
  31       * Stores current working directory
  32       *
  33       * @var string|bool        current working directory or false if it cannot be recovered
  34       */
  35      protected $working_directory;
  36  
  37      /**
  38       * Symfony's Filesystem component
  39       *
  40       * @var \Symfony\Component\Filesystem\Filesystem
  41       */
  42      protected $symfony_filesystem;
  43  
  44      /**
  45       * Constructor
  46       */
  47  	public function __construct()
  48      {
  49          $this->chmod_info            = array();
  50          $this->symfony_filesystem    = new \Symfony\Component\Filesystem\Filesystem();
  51          $this->working_directory    = null;
  52      }
  53  
  54      /**
  55       * {@inheritdoc}
  56       */
  57  	public function chgrp($files, $group, $recursive = false)
  58      {
  59          try
  60          {
  61              $this->symfony_filesystem->chgrp($files, $group, $recursive);
  62          }
  63          catch (\Symfony\Component\Filesystem\Exception\IOException $e)
  64          {
  65              // Try to recover filename
  66              // By the time this is written that is at the end of the message
  67              $error = trim($e->getMessage());
  68              $file = substr($error, strrpos($error, ' '));
  69  
  70              throw new filesystem_exception('FILESYSTEM_CANNOT_CHANGE_FILE_GROUP', $file, array(), $e);
  71          }
  72      }
  73  
  74      /**
  75       * {@inheritdoc}
  76       */
  77  	public function chmod($files, $perms = null, $recursive = false, $force_chmod_link = false)
  78      {
  79          if (is_null($perms))
  80          {
  81              // Default to read permission for compatibility reasons
  82              $perms = self::CHMOD_READ;
  83          }
  84  
  85          // Check if we got a permission flag
  86          if ($perms > self::CHMOD_ALL)
  87          {
  88              $file_perm = $perms;
  89  
  90              // Extract permissions
  91              //$owner = ($file_perm >> 6) & 7; // This will be ignored
  92              $group = ($file_perm >> 3) & 7;
  93              $other = ($file_perm >> 0) & 7;
  94  
  95              // Does any permissions provided? if so we add execute bit for directories
  96              $group = ($group !== 0) ? ($group | self::CHMOD_EXECUTE) : $group;
  97              $other = ($other !== 0) ? ($other | self::CHMOD_EXECUTE) : $other;
  98  
  99              // Compute directory permissions
 100              $dir_perm = (self::CHMOD_ALL << 6) + ($group << 3) + ($other << 3);
 101          }
 102          else
 103          {
 104              // Add execute bit to owner if execute bit is among perms
 105              $owner_perm    = (self::CHMOD_READ | self::CHMOD_WRITE) | ($perms & self::CHMOD_EXECUTE);
 106              $file_perm    = ($owner_perm << 6) + ($perms << 3) + ($perms << 0);
 107  
 108              // Compute directory permissions
 109              $perm = ($perms !== 0) ? ($perms | self::CHMOD_EXECUTE) : $perms;
 110              $dir_perm = (($owner_perm | self::CHMOD_EXECUTE) << 6) + ($perm << 3) + ($perm << 0);
 111          }
 112  
 113          // Symfony's filesystem component does not support extra execution flags on directories
 114          // so we need to implement it again
 115          foreach ($this->to_iterator($files) as $file)
 116          {
 117              if ($recursive && is_dir($file) && !is_link($file))
 118              {
 119                  $this->chmod(new \FilesystemIterator($file), $perms, true);
 120              }
 121  
 122              // Don't chmod links as mostly those require 0777 and that cannot be changed
 123              if (is_dir($file) || (is_link($file) && $force_chmod_link))
 124              {
 125                  if (true !== @chmod($file, $dir_perm))
 126                  {
 127                      throw new filesystem_exception('FILESYSTEM_CANNOT_CHANGE_FILE_PERMISSIONS', $file,  array());
 128                  }
 129              }
 130              else if (is_file($file))
 131              {
 132                  if (true !== @chmod($file, $file_perm))
 133                  {
 134                      throw new filesystem_exception('FILESYSTEM_CANNOT_CHANGE_FILE_PERMISSIONS', $file,  array());
 135                  }
 136              }
 137          }
 138      }
 139  
 140      /**
 141       * {@inheritdoc}
 142       */
 143  	public function chown($files, $user, $recursive = false)
 144      {
 145          try
 146          {
 147              $this->symfony_filesystem->chown($files, $user, $recursive);
 148          }
 149          catch (\Symfony\Component\Filesystem\Exception\IOException $e)
 150          {
 151              // Try to recover filename
 152              // By the time this is written that is at the end of the message
 153              $error = trim($e->getMessage());
 154              $file = substr($error, strrpos($error, ' '));
 155  
 156              throw new filesystem_exception('FILESYSTEM_CANNOT_CHANGE_FILE_GROUP', $file, array(), $e);
 157          }
 158      }
 159  
 160      /**
 161       * {@inheritdoc}
 162       */
 163  	public function clean_path($path)
 164      {
 165          $exploded = explode('/', $path);
 166          $filtered = array();
 167          foreach ($exploded as $part)
 168          {
 169              if ($part === '.' && !empty($filtered))
 170              {
 171                  continue;
 172              }
 173  
 174              if ($part === '..' && !empty($filtered) && $filtered[count($filtered) - 1] !== '.' && $filtered[count($filtered) - 1] !== '..')
 175              {
 176                  array_pop($filtered);
 177              }
 178              else
 179              {
 180                  $filtered[] = $part;
 181              }
 182          }
 183          $path = implode('/', $filtered);
 184          return $path;
 185      }
 186  
 187      /**
 188       * {@inheritdoc}
 189       */
 190  	public function copy($origin_file, $target_file, $override = false)
 191      {
 192          try
 193          {
 194              $this->symfony_filesystem->copy($origin_file, $target_file, $override);
 195          }
 196          catch (\Symfony\Component\Filesystem\Exception\IOException $e)
 197          {
 198              throw new filesystem_exception('FILESYSTEM_CANNOT_COPY_FILES', '', array(), $e);
 199          }
 200      }
 201  
 202      /**
 203       * {@inheritdoc}
 204       */
 205  	public function dump_file($filename, $content)
 206      {
 207          try
 208          {
 209              $this->symfony_filesystem->dumpFile($filename, $content);
 210          }
 211          catch (\Symfony\Component\Filesystem\Exception\IOException $e)
 212          {
 213              throw new filesystem_exception('FILESYSTEM_CANNOT_DUMP_FILE', $filename, array(), $e);
 214          }
 215      }
 216  
 217      /**
 218       * {@inheritdoc}
 219       */
 220  	public function exists($files)
 221      {
 222          return $this->symfony_filesystem->exists($files);
 223      }
 224  
 225      /**
 226       * {@inheritdoc}
 227       */
 228  	public function is_absolute_path($path)
 229      {
 230          return (isset($path[0]) && $path[0] === '/' || preg_match('#^[a-z]:[/\\\]#i', $path)) ? true : false;
 231      }
 232  
 233      /**
 234       * {@inheritdoc}
 235       */
 236  	public function is_readable($files, $recursive = false)
 237      {
 238          foreach ($this->to_iterator($files) as $file)
 239          {
 240              if ($recursive && is_dir($file) && !is_link($file))
 241              {
 242                  if (!$this->is_readable(new \FilesystemIterator($file), true))
 243                  {
 244                      return false;
 245                  }
 246              }
 247  
 248              if (!is_readable($file))
 249              {
 250                  return false;
 251              }
 252          }
 253  
 254          return true;
 255      }
 256  
 257      /**
 258       * {@inheritdoc}
 259       */
 260  	public function is_writable($files, $recursive = false)
 261      {
 262          if (defined('PHP_WINDOWS_VERSION_MAJOR') || !function_exists('is_writable'))
 263          {
 264              foreach ($this->to_iterator($files) as $file)
 265              {
 266                  if ($recursive && is_dir($file) && !is_link($file))
 267                  {
 268                      if (!$this->is_writable(new \FilesystemIterator($file), true))
 269                      {
 270                          return false;
 271                      }
 272                  }
 273  
 274                  if (!$this->phpbb_is_writable($file))
 275                  {
 276                      return false;
 277                  }
 278              }
 279          }
 280          else
 281          {
 282              // use built in is_writable
 283              foreach ($this->to_iterator($files) as $file)
 284              {
 285                  if ($recursive && is_dir($file) && !is_link($file))
 286                  {
 287                      if (!$this->is_writable(new \FilesystemIterator($file), true))
 288                      {
 289                          return false;
 290                      }
 291                  }
 292  
 293                  if (!is_writable($file))
 294                  {
 295                      return false;
 296                  }
 297              }
 298          }
 299  
 300          return true;
 301      }
 302  
 303      /**
 304       * {@inheritdoc}
 305       */
 306  	public function make_path_relative($end_path, $start_path)
 307      {
 308          return $this->symfony_filesystem->makePathRelative($end_path, $start_path);
 309      }
 310  
 311      /**
 312       * {@inheritdoc}
 313       */
 314  	public function mirror($origin_dir, $target_dir, \Traversable $iterator = null, $options = array())
 315      {
 316          try
 317          {
 318              $this->symfony_filesystem->mirror($origin_dir, $target_dir, $iterator, $options);
 319          }
 320          catch (\Symfony\Component\Filesystem\Exception\IOException $e)
 321          {
 322              $msg = $e->getMessage();
 323              $filename = substr($msg, strpos($msg, '"'), strrpos($msg, '"'));
 324  
 325              throw new filesystem_exception('FILESYSTEM_CANNOT_MIRROR_DIRECTORY', $filename, array(), $e);
 326          }
 327      }
 328  
 329      /**
 330       * {@inheritdoc}
 331       */
 332  	public function mkdir($dirs, $mode = 0777)
 333      {
 334          try
 335          {
 336              $this->symfony_filesystem->mkdir($dirs, $mode);
 337          }
 338          catch (\Symfony\Component\Filesystem\Exception\IOException $e)
 339          {
 340              $msg = $e->getMessage();
 341              $filename = substr($msg, strpos($msg, '"'), strrpos($msg, '"'));
 342  
 343              throw new filesystem_exception('FILESYSTEM_CANNOT_CREATE_DIRECTORY', $filename, array(), $e);
 344          }
 345      }
 346  
 347      /**
 348       * {@inheritdoc}
 349       */
 350  	public function phpbb_chmod($files, $perms = null, $recursive = false, $force_chmod_link = false)
 351      {
 352          if (is_null($perms))
 353          {
 354              // Default to read permission for compatibility reasons
 355              $perms = self::CHMOD_READ;
 356          }
 357  
 358          if (empty($this->chmod_info))
 359          {
 360              if (!function_exists('fileowner') || !function_exists('filegroup'))
 361              {
 362                  $this->chmod_info['process'] = false;
 363              }
 364              else
 365              {
 366                  $common_php_owner    = @fileowner(__FILE__);
 367                  $common_php_group    = @filegroup(__FILE__);
 368  
 369                  // And the owner and the groups PHP is running under.
 370                  $php_uid    = (function_exists('posix_getuid')) ? @posix_getuid() : false;
 371                  $php_gids    = (function_exists('posix_getgroups')) ? @posix_getgroups() : false;
 372  
 373                  // If we are unable to get owner/group, then do not try to set them by guessing
 374                  if (!$php_uid || empty($php_gids) || !$common_php_owner || !$common_php_group)
 375                  {
 376                      $this->chmod_info['process'] = false;
 377                  }
 378                  else
 379                  {
 380                      $this->chmod_info = array(
 381                          'process'        => true,
 382                          'common_owner'    => $common_php_owner,
 383                          'common_group'    => $common_php_group,
 384                          'php_uid'        => $php_uid,
 385                          'php_gids'        => $php_gids,
 386                      );
 387                  }
 388              }
 389          }
 390  
 391          if ($this->chmod_info['process'])
 392          {
 393              try
 394              {
 395                  foreach ($this->to_iterator($files) as $file)
 396                  {
 397                      $file_uid = @fileowner($file);
 398                      $file_gid = @filegroup($file);
 399  
 400                      // Change owner
 401                      if (is_writable($file) && $file_uid !== $this->chmod_info['common_owner'])
 402                      {
 403                          $this->chown($file, $this->chmod_info['common_owner'], $recursive);
 404                      }
 405  
 406                      // Change group
 407                      if (is_writable($file) && $file_gid !== $this->chmod_info['common_group'])
 408                      {
 409                          $this->chgrp($file, $this->chmod_info['common_group'], $recursive);
 410                      }
 411  
 412                      clearstatcache();
 413                      $file_uid = @fileowner($file);
 414                      $file_gid = @filegroup($file);
 415                  }
 416              }
 417              catch (filesystem_exception $e)
 418              {
 419                  $this->chmod_info['process'] = false;
 420              }
 421          }
 422  
 423          // Still able to process?
 424          if ($this->chmod_info['process'])
 425          {
 426              if ($file_uid === $this->chmod_info['php_uid'])
 427              {
 428                  $php = 'owner';
 429              }
 430              else if (in_array($file_gid, $this->chmod_info['php_gids']))
 431              {
 432                  $php = 'group';
 433              }
 434              else
 435              {
 436                  // Since we are setting the everyone bit anyway, no need to do expensive operations
 437                  $this->chmod_info['process'] = false;
 438              }
 439          }
 440  
 441          // We are not able to determine or change something
 442          if (!$this->chmod_info['process'])
 443          {
 444              $php = 'other';
 445          }
 446  
 447          switch ($php)
 448          {
 449              case 'owner':
 450                  try
 451                  {
 452                      $this->chmod($files, $perms, $recursive, $force_chmod_link);
 453                      clearstatcache();
 454                      if ($this->is_readable($files) && $this->is_writable($files))
 455                      {
 456                          break;
 457                      }
 458                  }
 459                  catch (filesystem_exception $e)
 460                  {
 461                      // Do nothing
 462                  }
 463              case 'group':
 464                  try
 465                  {
 466                      $this->chmod($files, $perms, $recursive, $force_chmod_link);
 467                      clearstatcache();
 468                      if ((!($perms & self::CHMOD_READ) || $this->is_readable($files, $recursive)) && (!($perms & self::CHMOD_WRITE) || $this->is_writable($files, $recursive)))
 469                      {
 470                          break;
 471                      }
 472                  }
 473                  catch (filesystem_exception $e)
 474                  {
 475                      // Do nothing
 476                  }
 477              case 'other':
 478              default:
 479                  $this->chmod($files, $perms, $recursive, $force_chmod_link);
 480              break;
 481          }
 482      }
 483  
 484      /**
 485       * {@inheritdoc}
 486       */
 487  	public function realpath($path)
 488      {
 489          if (!function_exists('realpath'))
 490          {
 491              return $this->phpbb_own_realpath($path);
 492          }
 493  
 494          $realpath = realpath($path);
 495  
 496          // Strangely there are provider not disabling realpath but returning strange values. :o
 497          // We at least try to cope with them.
 498          if ((!$this->is_absolute_path($path) && $realpath === $path) || $realpath === false)
 499          {
 500              return $this->phpbb_own_realpath($path);
 501          }
 502  
 503          // Check for DIRECTORY_SEPARATOR at the end (and remove it!)
 504          if (substr($realpath, -1) === DIRECTORY_SEPARATOR)
 505          {
 506              $realpath = substr($realpath, 0, -1);
 507          }
 508  
 509          return $realpath;
 510      }
 511  
 512      /**
 513       * {@inheritdoc}
 514       */
 515  	public function remove($files)
 516      {
 517          try
 518          {
 519              $this->symfony_filesystem->remove($files);
 520          }
 521          catch (\Symfony\Component\Filesystem\Exception\IOException $e)
 522          {
 523              // Try to recover filename
 524              // By the time this is written that is at the end of the message
 525              $error = trim($e->getMessage());
 526              $file = substr($error, strrpos($error, ' '));
 527  
 528              throw new filesystem_exception('FILESYSTEM_CANNOT_DELETE_FILES', $file, array(), $e);
 529          }
 530      }
 531  
 532      /**
 533       * {@inheritdoc}
 534       */
 535  	public function rename($origin, $target, $overwrite = false)
 536      {
 537          try
 538          {
 539              $this->symfony_filesystem->rename($origin, $target, $overwrite);
 540          }
 541          catch (\Symfony\Component\Filesystem\Exception\IOException $e)
 542          {
 543              $msg = $e->getMessage();
 544              $filename = substr($msg, strpos($msg, '"'), strrpos($msg, '"'));
 545  
 546              throw new filesystem_exception('FILESYSTEM_CANNOT_RENAME_FILE', $filename, array(), $e);
 547          }
 548      }
 549  
 550      /**
 551       * {@inheritdoc}
 552       */
 553  	public function symlink($origin_dir, $target_dir, $copy_on_windows = false)
 554      {
 555          try
 556          {
 557              $this->symfony_filesystem->symlink($origin_dir, $target_dir, $copy_on_windows);
 558          }
 559          catch (\Symfony\Component\Filesystem\Exception\IOException $e)
 560          {
 561              throw new filesystem_exception('FILESYSTEM_CANNOT_CREATE_SYMLINK', $origin_dir, array(), $e);
 562          }
 563      }
 564  
 565      /**
 566       * {@inheritdoc}
 567       */
 568  	public function touch($files, $time = null, $access_time = null)
 569      {
 570          try
 571          {
 572              $this->symfony_filesystem->touch($files, $time, $access_time);
 573          }
 574          catch (\Symfony\Component\Filesystem\Exception\IOException $e)
 575          {
 576              // Try to recover filename
 577              // By the time this is written that is at the end of the message
 578              $error = trim($e->getMessage());
 579              $file = substr($error, strrpos($error, ' '));
 580  
 581              throw new filesystem_exception('FILESYSTEM_CANNOT_TOUCH_FILES', $file, array(), $e);
 582          }
 583      }
 584  
 585      /**
 586       * phpBB's implementation of is_writable
 587       *
 588       * @todo Investigate if is_writable is still buggy
 589       *
 590       * @param string    $file    file/directory to check if writable
 591       *
 592       * @return bool    true if the given path is writable
 593       */
 594  	protected function phpbb_is_writable($file)
 595      {
 596          if (file_exists($file))
 597          {
 598              // Canonicalise path to absolute path
 599              $file = $this->realpath($file);
 600  
 601              if (is_dir($file))
 602              {
 603                  // Test directory by creating a file inside the directory
 604                  $result = @tempnam($file, 'i_w');
 605  
 606                  if (is_string($result) && file_exists($result))
 607                  {
 608                      unlink($result);
 609  
 610                      // Ensure the file is actually in the directory (returned realpathed)
 611                      return (strpos($result, $file) === 0) ? true : false;
 612                  }
 613              }
 614              else
 615              {
 616                  $handle = new \SplFileInfo($file);
 617  
 618                  // Returns TRUE if writable, FALSE otherwise
 619                  return $handle->isWritable();
 620              }
 621          }
 622          else
 623          {
 624              // file does not exist test if we can write to the directory
 625              $dir = dirname($file);
 626  
 627              if (file_exists($dir) && is_dir($dir) && $this->phpbb_is_writable($dir))
 628              {
 629                  return true;
 630              }
 631          }
 632  
 633          return false;
 634      }
 635  
 636      /**
 637       * Try to resolve real path when PHP's realpath fails to do so
 638       *
 639       * @param string    $path
 640       * @return bool|string
 641       */
 642  	protected function phpbb_own_realpath($path)
 643      {
 644          // Replace all directory separators with '/'
 645          $path = str_replace(DIRECTORY_SEPARATOR, '/', $path);
 646  
 647          $is_absolute_path = false;
 648          $path_prefix = '';
 649  
 650          if ($this->is_absolute_path($path))
 651          {
 652              $is_absolute_path = true;
 653          }
 654          else
 655          {
 656              // Resolve working directory and store it
 657              if (is_null($this->working_directory))
 658              {
 659                  if (function_exists('getcwd'))
 660                  {
 661                      $this->working_directory = str_replace(DIRECTORY_SEPARATOR, '/', getcwd());
 662                  }
 663  
 664                  //
 665                  // From this point on we really just guessing
 666                  // If chdir were called we screwed
 667                  //
 668                  else if (function_exists('debug_backtrace'))
 669                  {
 670                      $call_stack = debug_backtrace(0);
 671                      $this->working_directory = str_replace(DIRECTORY_SEPARATOR, '/', dirname($call_stack[count($call_stack) - 1]['file']));
 672                  }
 673                  else
 674                  {
 675                      //
 676                      // Assuming that the working directory is phpBB root
 677                      // we could use this as a fallback, when phpBB will use controllers
 678                      // everywhere this will be a safe assumption
 679                      //
 680                      //$dir_parts = explode(DIRECTORY_SEPARATOR, __DIR__);
 681                      //$namespace_parts = explode('\\', trim(__NAMESPACE__, '\\'));
 682  
 683                      //$namespace_part_count = count($namespace_parts);
 684  
 685                      // Check if we still loading from root
 686                      //if (array_slice($dir_parts, -$namespace_part_count) === $namespace_parts)
 687                      //{
 688                      //    $this->working_directory = implode('/', array_slice($dir_parts, 0, -$namespace_part_count));
 689                      //}
 690                      //else
 691                      //{
 692                      //    $this->working_directory = false;
 693                      //}
 694  
 695                      $this->working_directory = false;
 696                  }
 697              }
 698  
 699              if ($this->working_directory !== false)
 700              {
 701                  $is_absolute_path = true;
 702                  $path = $this->working_directory . '/' . $path;
 703              }
 704          }
 705  
 706          if ($is_absolute_path)
 707          {
 708              if (defined('PHP_WINDOWS_VERSION_MAJOR'))
 709              {
 710                  $path_prefix = $path[0] . ':';
 711                  $path = substr($path, 2);
 712              }
 713              else
 714              {
 715                  $path_prefix = '';
 716              }
 717          }
 718  
 719          $resolved_path = $this->resolve_path($path, $path_prefix, $is_absolute_path);
 720          if ($resolved_path === false)
 721          {
 722              return false;
 723          }
 724  
 725          if (!@file_exists($resolved_path) || (!@is_dir($resolved_path . '/') && !is_file($resolved_path)))
 726          {
 727              return false;
 728          }
 729  
 730          // Return OS specific directory separators
 731          $resolved = str_replace('/', DIRECTORY_SEPARATOR, $resolved_path);
 732  
 733          // Check for DIRECTORY_SEPARATOR at the end (and remove it!)
 734          if (substr($resolved, -1) === DIRECTORY_SEPARATOR)
 735          {
 736              return substr($resolved, 0, -1);
 737          }
 738  
 739          return $resolved;
 740      }
 741  
 742      /**
 743       * Convert file(s) to \Traversable object
 744       *
 745       * This is the same function as Symfony's toIterator, but that is private
 746       * so we cannot use it.
 747       *
 748       * @param string|array|\Traversable    $files    filename/list of filenames
 749       * @return \Traversable
 750       */
 751  	protected function to_iterator($files)
 752      {
 753          if (!$files instanceof \Traversable)
 754          {
 755              $files = new \ArrayObject(is_array($files) ? $files : array($files));
 756          }
 757  
 758          return $files;
 759      }
 760  
 761      /**
 762       * Try to resolve symlinks in path
 763       *
 764       * @param string    $path            The path to resolve
 765       * @param string    $prefix            The path prefix (on windows the drive letter)
 766       * @param bool         $absolute        Whether or not the path is absolute
 767       * @param bool        $return_array    Whether or not to return path parts
 768       *
 769       * @return string|array|bool    returns the resolved path or an array of parts of the path if $return_array is true
 770       *                                 or false if path cannot be resolved
 771       */
 772  	protected function resolve_path($path, $prefix = '', $absolute = false, $return_array = false)
 773      {
 774          if ($return_array)
 775          {
 776              $path = str_replace(DIRECTORY_SEPARATOR, '/', $path);
 777          }
 778  
 779          trim ($path, '/');
 780          $path_parts = explode('/', $path);
 781          $resolved = array();
 782          $resolved_path = $prefix;
 783          $file_found = false;
 784  
 785          foreach ($path_parts as $path_part)
 786          {
 787              if ($file_found)
 788              {
 789                  return false;
 790              }
 791  
 792              if (empty($path_part) || ($path_part === '.' && ($absolute || !empty($resolved))))
 793              {
 794                  continue;
 795              }
 796              else if ($absolute && $path_part === '..')
 797              {
 798                  if (empty($resolved))
 799                  {
 800                      // No directories above root
 801                      return false;
 802                  }
 803  
 804                  array_pop($resolved);
 805                  $resolved_path = false;
 806              }
 807              else if ($path_part === '..' && !empty($resolved) && !in_array($resolved[count($resolved) - 1], array('.', '..')))
 808              {
 809                  array_pop($resolved);
 810                  $resolved_path = false;
 811              }
 812              else
 813              {
 814                  if ($resolved_path === false)
 815                  {
 816                      if (empty($resolved))
 817                      {
 818                          $resolved_path = ($absolute) ? $prefix . '/' . $path_part : $path_part;
 819                      }
 820                      else
 821                      {
 822                          $tmp_array = $resolved;
 823                          if ($absolute)
 824                          {
 825                              array_unshift($tmp_array, $prefix);
 826                          }
 827  
 828                          $resolved_path = implode('/', $tmp_array);
 829                      }
 830                  }
 831  
 832                  $current_path = $resolved_path . '/' . $path_part;
 833  
 834                  // Resolve symlinks
 835                  if (@is_link($current_path))
 836                  {
 837                      if (!function_exists('readlink'))
 838                      {
 839                          return false;
 840                      }
 841  
 842                      $link = readlink($current_path);
 843  
 844                      // Is link has an absolute path in it?
 845                      if ($this->is_absolute_path($link))
 846                      {
 847                          if (defined('PHP_WINDOWS_VERSION_MAJOR'))
 848                          {
 849                              $prefix = $link[0] . ':';
 850                              $link = substr($link, 2);
 851                          }
 852                          else
 853                          {
 854                              $prefix = '';
 855                          }
 856  
 857                          $resolved = $this->resolve_path($link, $prefix, true, true);
 858                          $absolute = true;
 859                      }
 860                      else
 861                      {
 862                          $resolved = $this->resolve_path($resolved_path . '/' . $link, $prefix, $absolute, true);
 863                      }
 864  
 865                      if (!$resolved)
 866                      {
 867                          return false;
 868                      }
 869  
 870                      $resolved_path = false;
 871                  }
 872                  else if (@is_dir($current_path . '/'))
 873                  {
 874                      $resolved[] = $path_part;
 875                      $resolved_path = $current_path;
 876                  }
 877                  else if (@is_file($current_path))
 878                  {
 879                      $resolved[] = $path_part;
 880                      $resolved_path = $current_path;
 881                      $file_found = true;
 882                  }
 883                  else
 884                  {
 885                      return false;
 886                  }
 887              }
 888          }
 889  
 890          // If at the end of the path there were a .. or .
 891          // we need to build the path again.
 892          // Only doing this when a string is expected in return
 893          if ($resolved_path === false && $return_array === false)
 894          {
 895              if (empty($resolved))
 896              {
 897                  $resolved_path = ($absolute) ? $prefix . '/' : './';
 898              }
 899              else
 900              {
 901                  $tmp_array = $resolved;
 902                  if ($absolute)
 903                  {
 904                      array_unshift($tmp_array, $prefix);
 905                  }
 906  
 907                  $resolved_path = implode('/', $tmp_array);
 908              }
 909          }
 910  
 911          return ($return_array) ? $resolved : $resolved_path;
 912      }
 913  }


Generated: Sun Jun 23 12:25:44 2024 Cross-referenced by PHPXref 0.7.1