Verzeichnisstruktur phpBB-3.2.0


Veröffentlicht
06.01.2017

So funktioniert es


Auf das letzte Element klicken. Dies geht jeweils ein Schritt zurück

Auf das Icon klicken, dies öffnet das Verzeichnis. Nochmal klicken schließt das Verzeichnis.
Auf den Verzeichnisnamen klicken, dies zeigt nur das Verzeichnis mit Inhalt an

      (Beispiel Datei-Icons)

Auf das Icon klicken um den Quellcode anzuzeigen

filesystem.php

Zuletzt modifiziert: 09.10.2024, 12:52 - Dateigröße: 20.69 KiB


 001  <?php
 002  /**
 003   *
 004   * This file is part of the phpBB Forum Software package.
 005   *
 006   * @copyright (c) phpBB Limited <https://www.phpbb.com>
 007   * @license GNU General Public License, version 2 (GPL-2.0)
 008   *
 009   * For full copyright and license information, please see
 010   * the docs/CREDITS.txt file.
 011   *
 012   */
 013   
 014  namespace phpbb\filesystem;
 015   
 016  use phpbb\filesystem\exception\filesystem_exception;
 017   
 018  /**
 019   * A class with various functions that are related to paths, files and the filesystem
 020   */
 021  class filesystem implements filesystem_interface
 022  {
 023      /**
 024       * Store some information about file ownership for phpBB's chmod function
 025       *
 026       * @var array
 027       */
 028      protected $chmod_info;
 029   
 030      /**
 031       * Stores current working directory
 032       *
 033       * @var string|bool        current working directory or false if it cannot be recovered
 034       */
 035      protected $working_directory;
 036   
 037      /**
 038       * Symfony's Filesystem component
 039       *
 040       * @var \Symfony\Component\Filesystem\Filesystem
 041       */
 042      protected $symfony_filesystem;
 043   
 044      /**
 045       * Constructor
 046       */
 047      public function __construct()
 048      {
 049          $this->chmod_info            = array();
 050          $this->symfony_filesystem    = new \Symfony\Component\Filesystem\Filesystem();
 051          $this->working_directory    = null;
 052      }
 053   
 054      /**
 055       * {@inheritdoc}
 056       */
 057      public function chgrp($files, $group, $recursive = false)
 058      {
 059          try
 060          {
 061              $this->symfony_filesystem->chgrp($files, $group, $recursive);
 062          }
 063          catch (\Symfony\Component\Filesystem\Exception\IOException $e)
 064          {
 065              // Try to recover filename
 066              // By the time this is written that is at the end of the message
 067              $error = trim($e->getMessage());
 068              $file = substr($error, strrpos($error, ' '));
 069   
 070              throw new filesystem_exception('CANNOT_CHANGE_FILE_GROUP', $file, array(), $e);
 071          }
 072      }
 073   
 074      /**
 075       * {@inheritdoc}
 076       */
 077      public function chmod($files, $perms = null, $recursive = false, $force_chmod_link = false)
 078      {
 079          if (is_null($perms))
 080          {
 081              // Default to read permission for compatibility reasons
 082              $perms = self::CHMOD_READ;
 083          }
 084   
 085          // Check if we got a permission flag
 086          if ($perms > self::CHMOD_ALL)
 087          {
 088              $file_perm = $perms;
 089   
 090              // Extract permissions
 091              //$owner = ($file_perm >> 6) & 7; // This will be ignored
 092              $group = ($file_perm >> 3) & 7;
 093              $other = ($file_perm >> 0) & 7;
 094   
 095              // Does any permissions provided? if so we add execute bit for directories
 096              $group = ($group !== 0) ? ($group | self::CHMOD_EXECUTE) : $group;
 097              $other = ($other !== 0) ? ($other | self::CHMOD_EXECUTE) : $other;
 098   
 099              // 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('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('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('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[sizeof($filtered) - 1] !== '.' && $filtered[sizeof($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('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('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('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('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('posic_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 ($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 ($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('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('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('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('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 = @fopen($file, 'c');
 617   
 618                  if (is_resource($handle))
 619                  {
 620                      fclose($handle);
 621                      return true;
 622                  }
 623              }
 624          }
 625          else
 626          {
 627              // file does not exist test if we can write to the directory
 628              $dir = dirname($file);
 629   
 630              if (file_exists($dir) && is_dir($dir) && $this->phpbb_is_writable($dir))
 631              {
 632                  return true;
 633              }
 634          }
 635   
 636          return false;
 637      }
 638   
 639      /**
 640       * Try to resolve real path when PHP's realpath failes to do so
 641       *
 642       * @param string    $path
 643       * @return bool|string
 644       */
 645      protected function phpbb_own_realpath($path)
 646      {
 647          // Replace all directory separators with '/'
 648          $path = str_replace(DIRECTORY_SEPARATOR, '/', $path);
 649   
 650          $is_absolute_path = false;
 651          $path_prefix = '';
 652   
 653          if ($this->is_absolute_path($path))
 654          {
 655              $is_absolute_path = true;
 656          }
 657          else
 658          {
 659              // Resolve working directory and store it
 660              if (is_null($this->working_directory))
 661              {
 662                  if (function_exists('getcwd'))
 663                  {
 664                      $this->working_directory = str_replace(DIRECTORY_SEPARATOR, '/', getcwd());
 665                  }
 666   
 667                  //
 668                  // From this point on we really just guessing
 669                  // If chdir were called we screwed
 670                  //
 671                  else if (function_exists('debug_backtrace'))
 672                  {
 673                      $call_stack = debug_backtrace(0);
 674                      $this->working_directory = str_replace(DIRECTORY_SEPARATOR, '/', dirname($call_stack[sizeof($call_stack) - 1]['file']));
 675                  }
 676                  else
 677                  {
 678                      //
 679                      // Assuming that the working directory is phpBB root
 680                      // we could use this as a fallback, when phpBB will use controllers
 681                      // everywhere this will be a safe assumption
 682                      //
 683                      //$dir_parts = explode(DIRECTORY_SEPARATOR, __DIR__);
 684                      //$namespace_parts = explode('\\', trim(__NAMESPACE__, '\\'));
 685   
 686                      //$namespace_part_count = sizeof($namespace_parts);
 687   
 688                      // Check if we still loading from root
 689                      //if (array_slice($dir_parts, -$namespace_part_count) === $namespace_parts)
 690                      //{
 691                      //    $this->working_directory = implode('/', array_slice($dir_parts, 0, -$namespace_part_count));
 692                      //}
 693                      //else
 694                      //{
 695                      //    $this->working_directory = false;
 696                      //}
 697   
 698                      $this->working_directory = false;
 699                  }
 700              }
 701   
 702              if ($this->working_directory !== false)
 703              {
 704                  $is_absolute_path = true;
 705                  $path = $this->working_directory . '/' . $path;
 706              }
 707          }
 708   
 709          if ($is_absolute_path)
 710          {
 711              if (defined('PHP_WINDOWS_VERSION_MAJOR'))
 712              {
 713                  $path_prefix = $path[0] . ':';
 714                  $path = substr($path, 2);
 715              }
 716              else
 717              {
 718                  $path_prefix = '';
 719              }
 720          }
 721   
 722          $resolved_path = $this->resolve_path($path, $path_prefix, $is_absolute_path);
 723          if ($resolved_path === false)
 724          {
 725              return false;
 726          }
 727   
 728          if (!@file_exists($resolved_path) || (!@is_dir($resolved_path . '/') && !is_file($resolved_path)))
 729          {
 730              return false;
 731          }
 732   
 733          // Return OS specific directory separators
 734          $resolved = str_replace('/', DIRECTORY_SEPARATOR, $resolved_path);
 735   
 736          // Check for DIRECTORY_SEPARATOR at the end (and remove it!)
 737          if (substr($resolved, -1) === DIRECTORY_SEPARATOR)
 738          {
 739              return substr($resolved, 0, -1);
 740          }
 741   
 742          return $resolved;
 743      }
 744   
 745      /**
 746       * Convert file(s) to \Traversable object
 747       *
 748       * This is the same function as Symfony's toIterator, but that is private
 749       * so we cannot use it.
 750       *
 751       * @param string|array|\Traversable    $files    filename/list of filenames
 752       * @return \Traversable
 753       */
 754      protected function to_iterator($files)
 755      {
 756          if (!$files instanceof \Traversable)
 757          {
 758              $files = new \ArrayObject(is_array($files) ? $files : array($files));
 759          }
 760   
 761          return $files;
 762      }
 763   
 764      /**
 765       * Try to resolve symlinks in path
 766       *
 767       * @param string    $path            The path to resolve
 768       * @param string    $prefix            The path prefix (on windows the drive letter)
 769       * @param bool         $absolute        Whether or not the path is absolute
 770       * @param bool        $return_array    Whether or not to return path parts
 771       *
 772       * @return string|array|bool    returns the resolved path or an array of parts of the path if $return_array is true
 773       *                                 or false if path cannot be resolved
 774       */
 775      protected function resolve_path($path, $prefix = '', $absolute = false, $return_array = false)
 776      {
 777          if ($return_array)
 778          {
 779              $path = str_replace(DIRECTORY_SEPARATOR, '/', $path);
 780          }
 781   
 782          trim ($path, '/');
 783          $path_parts = explode('/', $path);
 784          $resolved = array();
 785          $resolved_path = $prefix;
 786          $file_found = false;
 787   
 788          foreach ($path_parts as $path_part)
 789          {
 790              if ($file_found)
 791              {
 792                  return false;
 793              }
 794   
 795              if (empty($path_part) || ($path_part === '.' && ($absolute || !empty($resolved))))
 796              {
 797                  continue;
 798              }
 799              else if ($absolute && $path_part === '..')
 800              {
 801                  if (empty($resolved))
 802                  {
 803                      // No directories above root
 804                      return false;
 805                  }
 806   
 807                  array_pop($resolved);
 808                  $resolved_path = false;
 809              }
 810              else if ($path_part === '..' && !empty($resolved) && !in_array($resolved[sizeof($resolved) - 1], array('.', '..')))
 811              {
 812                  array_pop($resolved);
 813                  $resolved_path = false;
 814              }
 815              else
 816              {
 817                  if ($resolved_path === false)
 818                  {
 819                      if (empty($resolved))
 820                      {
 821                          $resolved_path = ($absolute) ? $prefix . '/' . $path_part : $path_part;
 822                      }
 823                      else
 824                      {
 825                          $tmp_array = $resolved;
 826                          if ($absolute)
 827                          {
 828                              array_unshift($tmp_array, $prefix);
 829                          }
 830   
 831                          $resolved_path = implode('/', $tmp_array);
 832                      }
 833                  }
 834   
 835                  $current_path = $resolved_path . '/' . $path_part;
 836   
 837                  // Resolve symlinks
 838                  if (is_link($current_path))
 839                  {
 840                      if (!function_exists('readlink'))
 841                      {
 842                          return false;
 843                      }
 844   
 845                      $link = readlink($current_path);
 846   
 847                      // Is link has an absolute path in it?
 848                      if ($this->is_absolute_path($link))
 849                      {
 850                          if (defined('PHP_WINDOWS_VERSION_MAJOR'))
 851                          {
 852                              $prefix = $link[0] . ':';
 853                              $link = substr($link, 2);
 854                          }
 855                          else
 856                          {
 857                              $prefix = '';
 858                          }
 859   
 860                          $resolved = $this->resolve_path($link, $prefix, true, true);
 861                          $absolute = true;
 862                      }
 863                      else
 864                      {
 865                          $resolved = $this->resolve_path($resolved_path . '/' . $link, $prefix, $absolute, true);
 866                      }
 867   
 868                      if (!$resolved)
 869                      {
 870                          return false;
 871                      }
 872   
 873                      $resolved_path = false;
 874                  }
 875                  else if (is_dir($current_path . '/'))
 876                  {
 877                      $resolved[] = $path_part;
 878                      $resolved_path = $current_path;
 879                  }
 880                  else if (is_file($current_path))
 881                  {
 882                      $resolved[] = $path_part;
 883                      $resolved_path = $current_path;
 884                      $file_found = true;
 885                  }
 886                  else
 887                  {
 888                      return false;
 889                  }
 890              }
 891          }
 892   
 893          // If at the end of the path there were a .. or .
 894          // we need to build the path again.
 895          // Only doing this when a string is expected in return
 896          if ($resolved_path === false && $return_array === false)
 897          {
 898              if (empty($resolved))
 899              {
 900                  $resolved_path = ($absolute) ? $prefix . '/' : './';
 901              }
 902              else
 903              {
 904                  $tmp_array = $resolved;
 905                  if ($absolute)
 906                  {
 907                      array_unshift($tmp_array, $prefix);
 908                  }
 909   
 910                  $resolved_path = implode('/', $tmp_array);
 911              }
 912          }
 913   
 914          return ($return_array) ? $resolved : $resolved_path;
 915      }
 916  }
 917