1
0
mirror of https://github.com/phpbb/phpbb.git synced 2025-08-06 00:37:42 +02:00

[ticket/13697] Moving filesystem related functions to filesystem service

* Moving filesystem service to \phpbb\filesystem namespace
 * Wraping Symfony's Filesystem component
 * Moving filesystem related functions from includes/functions.php
   into \phpbb\filesystem\filesystem
   Functions moved (and deprecated):
     - phpbb_chmod
     - phpbb_is_writable
     - phpbb_is_absolute
     - phpbb_own_realpath
     - phpbb_realpath
 * Adding interface for filesystem service

PHPBB3-13697
This commit is contained in:
MateBartus
2015-03-12 00:25:00 +01:00
parent f86c5d905b
commit 4bdef6fd21
82 changed files with 1859 additions and 657 deletions

View File

@@ -311,448 +311,6 @@ function phpbb_version_compare($version1, $version2, $operator = null)
}
}
/**
* Global function for chmodding directories and files for internal use
*
* This function determines owner and group whom the file belongs to and user and group of PHP and then set safest possible file permissions.
* The function determines owner and group from common.php file and sets the same to the provided file.
* The function uses bit fields to build the permissions.
* The function sets the appropiate execute bit on directories.
*
* Supported constants representing bit fields are:
*
* CHMOD_ALL - all permissions (7)
* CHMOD_READ - read permission (4)
* CHMOD_WRITE - write permission (2)
* CHMOD_EXECUTE - execute permission (1)
*
* NOTE: The function uses POSIX extension and fileowner()/filegroup() functions. If any of them is disabled, this function tries to build proper permissions, by calling is_readable() and is_writable() functions.
*
* @param string $filename The file/directory to be chmodded
* @param int $perms Permissions to set
*
* @return bool true on success, otherwise false
*/
function phpbb_chmod($filename, $perms = CHMOD_READ)
{
static $_chmod_info;
// Return if the file no longer exists.
if (!file_exists($filename))
{
return false;
}
// Determine some common vars
if (empty($_chmod_info))
{
if (!function_exists('fileowner') || !function_exists('filegroup'))
{
// No need to further determine owner/group - it is unknown
$_chmod_info['process'] = false;
}
else
{
global $phpbb_root_path, $phpEx;
// Determine owner/group of common.php file and the filename we want to change here
$common_php_owner = @fileowner($phpbb_root_path . 'common.' . $phpEx);
$common_php_group = @filegroup($phpbb_root_path . 'common.' . $phpEx);
// And the owner and the groups PHP is running under.
$php_uid = (function_exists('posix_getuid')) ? @posix_getuid() : false;
$php_gids = (function_exists('posix_getgroups')) ? @posix_getgroups() : false;
// If we are unable to get owner/group, then do not try to set them by guessing
if (!$php_uid || empty($php_gids) || !$common_php_owner || !$common_php_group)
{
$_chmod_info['process'] = false;
}
else
{
$_chmod_info = array(
'process' => true,
'common_owner' => $common_php_owner,
'common_group' => $common_php_group,
'php_uid' => $php_uid,
'php_gids' => $php_gids,
);
}
}
}
if ($_chmod_info['process'])
{
$file_uid = @fileowner($filename);
$file_gid = @filegroup($filename);
// Change owner
if (@chown($filename, $_chmod_info['common_owner']))
{
clearstatcache();
$file_uid = @fileowner($filename);
}
// Change group
if (@chgrp($filename, $_chmod_info['common_group']))
{
clearstatcache();
$file_gid = @filegroup($filename);
}
// If the file_uid/gid now match the one from common.php we can process further, else we are not able to change something
if ($file_uid != $_chmod_info['common_owner'] || $file_gid != $_chmod_info['common_group'])
{
$_chmod_info['process'] = false;
}
}
// Still able to process?
if ($_chmod_info['process'])
{
if ($file_uid == $_chmod_info['php_uid'])
{
$php = 'owner';
}
else if (in_array($file_gid, $_chmod_info['php_gids']))
{
$php = 'group';
}
else
{
// Since we are setting the everyone bit anyway, no need to do expensive operations
$_chmod_info['process'] = false;
}
}
// We are not able to determine or change something
if (!$_chmod_info['process'])
{
$php = 'other';
}
// Owner always has read/write permission
$owner = CHMOD_READ | CHMOD_WRITE;
if (is_dir($filename))
{
$owner |= CHMOD_EXECUTE;
// Only add execute bit to the permission if the dir needs to be readable
if ($perms & CHMOD_READ)
{
$perms |= CHMOD_EXECUTE;
}
}
switch ($php)
{
case 'owner':
$result = @chmod($filename, ($owner << 6) + (0 << 3) + (0 << 0));
clearstatcache();
if (is_readable($filename) && phpbb_is_writable($filename))
{
break;
}
case 'group':
$result = @chmod($filename, ($owner << 6) + ($perms << 3) + (0 << 0));
clearstatcache();
if ((!($perms & CHMOD_READ) || is_readable($filename)) && (!($perms & CHMOD_WRITE) || phpbb_is_writable($filename)))
{
break;
}
case 'other':
$result = @chmod($filename, ($owner << 6) + ($perms << 3) + ($perms << 0));
clearstatcache();
if ((!($perms & CHMOD_READ) || is_readable($filename)) && (!($perms & CHMOD_WRITE) || phpbb_is_writable($filename)))
{
break;
}
default:
return false;
break;
}
return $result;
}
/**
* Test if a file/directory is writable
*
* This function calls the native is_writable() when not running under
* Windows and it is not disabled.
*
* @param string $file Path to perform write test on
* @return bool True when the path is writable, otherwise false.
*/
function phpbb_is_writable($file)
{
if (strtolower(substr(PHP_OS, 0, 3)) === 'win' || !function_exists('is_writable'))
{
if (file_exists($file))
{
// Canonicalise path to absolute path
$file = phpbb_realpath($file);
if (is_dir($file))
{
// Test directory by creating a file inside the directory
$result = @tempnam($file, 'i_w');
if (is_string($result) && file_exists($result))
{
unlink($result);
// Ensure the file is actually in the directory (returned realpathed)
return (strpos($result, $file) === 0) ? true : false;
}
}
else
{
$handle = @fopen($file, 'r+');
if (is_resource($handle))
{
fclose($handle);
return true;
}
}
}
else
{
// file does not exist test if we can write to the directory
$dir = dirname($file);
if (file_exists($dir) && is_dir($dir) && phpbb_is_writable($dir))
{
return true;
}
}
return false;
}
else
{
return is_writable($file);
}
}
/**
* Checks if a path ($path) is absolute or relative
*
* @param string $path Path to check absoluteness of
* @return boolean
*/
function phpbb_is_absolute($path)
{
return (isset($path[0]) && $path[0] == '/' || preg_match('#^[a-z]:[/\\\]#i', $path)) ? true : false;
}
/**
* @author Chris Smith <chris@project-minerva.org>
* @copyright 2006 Project Minerva Team
* @param string $path The path which we should attempt to resolve.
* @return mixed
*/
function phpbb_own_realpath($path)
{
global $request;
// Now to perform funky shizzle
// Switch to use UNIX slashes
$path = str_replace(DIRECTORY_SEPARATOR, '/', $path);
$path_prefix = '';
// Determine what sort of path we have
if (phpbb_is_absolute($path))
{
$absolute = true;
if ($path[0] == '/')
{
// Absolute path, *NIX style
$path_prefix = '';
}
else
{
// Absolute path, Windows style
// Remove the drive letter and colon
$path_prefix = $path[0] . ':';
$path = substr($path, 2);
}
}
else
{
// Relative Path
// Prepend the current working directory
if (function_exists('getcwd'))
{
// This is the best method, hopefully it is enabled!
$path = str_replace(DIRECTORY_SEPARATOR, '/', getcwd()) . '/' . $path;
$absolute = true;
if (preg_match('#^[a-z]:#i', $path))
{
$path_prefix = $path[0] . ':';
$path = substr($path, 2);
}
else
{
$path_prefix = '';
}
}
else if ($request->server('SCRIPT_FILENAME'))
{
// Warning: If chdir() has been used this will lie!
// Warning: This has some problems sometime (CLI can create them easily)
$filename = htmlspecialchars_decode($request->server('SCRIPT_FILENAME'));
$path = str_replace(DIRECTORY_SEPARATOR, '/', dirname($filename)) . '/' . $path;
$absolute = true;
$path_prefix = '';
}
else
{
// We have no way of getting the absolute path, just run on using relative ones.
$absolute = false;
$path_prefix = '.';
}
}
// Remove any repeated slashes
$path = preg_replace('#/{2,}#', '/', $path);
// Remove the slashes from the start and end of the path
$path = trim($path, '/');
// Break the string into little bits for us to nibble on
$bits = explode('/', $path);
// Remove any . in the path, renumber array for the loop below
$bits = array_values(array_diff($bits, array('.')));
// Lets get looping, run over and resolve any .. (up directory)
for ($i = 0, $max = sizeof($bits); $i < $max; $i++)
{
// @todo Optimise
if ($bits[$i] == '..' )
{
if (isset($bits[$i - 1]))
{
if ($bits[$i - 1] != '..')
{
// We found a .. and we are able to traverse upwards, lets do it!
unset($bits[$i]);
unset($bits[$i - 1]);
$i -= 2;
$max -= 2;
$bits = array_values($bits);
}
}
else if ($absolute) // ie. !isset($bits[$i - 1]) && $absolute
{
// We have an absolute path trying to descend above the root of the filesystem
// ... Error!
return false;
}
}
}
// Prepend the path prefix
array_unshift($bits, $path_prefix);
$resolved = '';
$max = sizeof($bits) - 1;
// Check if we are able to resolve symlinks, Windows (prior to Vista and Server 2008) cannot.
$symlink_resolve = (function_exists('readlink')) ? true : false;
foreach ($bits as $i => $bit)
{
if (@is_dir("$resolved/$bit") || ($i == $max && @is_file("$resolved/$bit")))
{
// Path Exists
if ($symlink_resolve && is_link("$resolved/$bit") && ($link = readlink("$resolved/$bit")))
{
// Resolved a symlink.
$resolved = $link . (($i == $max) ? '' : '/');
continue;
}
}
else
{
// Something doesn't exist here!
// This is correct realpath() behaviour but sadly open_basedir and safe_mode make this problematic
// return false;
}
$resolved .= $bit . (($i == $max) ? '' : '/');
}
// @todo If the file exists fine and open_basedir only has one path we should be able to prepend it
// because we must be inside that basedir, the question is where...
// @internal The slash in is_dir() gets around an open_basedir restriction
if (!@file_exists($resolved) || (!@is_dir($resolved . '/') && !is_file($resolved)))
{
return false;
}
// Put the slashes back to the native operating systems slashes
$resolved = str_replace('/', DIRECTORY_SEPARATOR, $resolved);
// Check for DIRECTORY_SEPARATOR at the end (and remove it!)
if (substr($resolved, -1) == DIRECTORY_SEPARATOR)
{
return substr($resolved, 0, -1);
}
return $resolved; // We got here, in the end!
}
if (!function_exists('realpath'))
{
/**
* A wrapper for realpath
* @ignore
*/
function phpbb_realpath($path)
{
return phpbb_own_realpath($path);
}
}
else
{
/**
* A wrapper for realpath
*/
function phpbb_realpath($path)
{
$realpath = realpath($path);
// Strangely there are provider not disabling realpath but returning strange values. :o
// We at least try to cope with them.
if ($realpath === $path || $realpath === false)
{
return phpbb_own_realpath($path);
}
// Check for DIRECTORY_SEPARATOR at the end (and remove it!)
if (substr($realpath, -1) == DIRECTORY_SEPARATOR)
{
$realpath = substr($realpath, 0, -1);
}
return $realpath;
}
}
// functions used for building option fields
/**
@@ -1029,7 +587,7 @@ function markread($mode, $forum_id = false, $topic_id = false, $post_time = 0, $
/**
* This event is used for performing actions directly before marking forums,
* topics or posts as read.
*
*
* It is also possible to prevent the marking. For that, the $should_markread parameter
* should be set to FALSE.
*
@@ -3913,11 +3471,13 @@ function msg_handler($errno, $msg_text, $errfile, $errline)
*/
function phpbb_filter_root_path($errfile)
{
global $phpbb_filesystem;
static $root_path;
if (empty($root_path))
{
$root_path = phpbb_realpath(dirname(__FILE__) . '/../');
$root_path = $phpbb_filesystem->realpath(dirname(__FILE__) . '/../');
}
return str_replace(array($root_path, '\\'), array('[ROOT]', '/'), $errfile);