<?php
/**
*
* This file is part of the phpBB Forum Software package.
*
* @copyright (c) phpBB Limited <https://www.phpbb.com>
* @license GNU General Public License, version 2 (GPL-2.0)
*
* For full copyright and license information, please see
* the docs/CREDITS.txt file.
*
*/
/**
* @ignore
*/
if (!defined('IN_PHPBB'))
{
exit;
}
// Common global functions
/**
* Load the autoloaders added by the extensions.
*
* @param string $phpbb_root_path Path to the phpbb root directory.
*/
function phpbb_load_extensions_autoloaders($phpbb_root_path)
{
$iterator = new \RecursiveIteratorIterator(
new \phpbb\recursive_dot_prefix_filter_iterator(
new \RecursiveDirectoryIterator(
$phpbb_root_path . 'ext/',
\FilesystemIterator::SKIP_DOTS | \FilesystemIterator::FOLLOW_SYMLINKS
)
),
\RecursiveIteratorIterator::SELF_FIRST
);
$iterator->setMaxDepth(2);
foreach ($iterator as $file_info)
{
if ($file_info->getFilename() === 'vendor' && $iterator->getDepth() === 2)
{
$filename = $file_info->getRealPath() . '/autoload.php';
if (file_exists($filename))
{
require $filename;
}
}
}
}
/**
* Casts a variable to the given type.
*
* @deprecated
*/
function set_var(&$result, $var, $type, $multibyte = false)
{
// no need for dependency injection here, if you have the object, call the method yourself!
$type_cast_helper = new \phpbb\request\type_cast_helper();
$type_cast_helper->set_var($result, $var, $type, $multibyte);
}
/**
* Generates an alphanumeric random string of given length
*
* @return string
*/
function gen_rand_string($num_chars = 8)
{
// [a, z] + [0, 9] = 36
return substr(strtoupper(base_convert(unique_id(), 16, 36)), 0, $num_chars);
}
/**
* Generates a user-friendly alphanumeric random string of given length
* We remove 0 and O so users cannot confuse those in passwords etc.
*
* @return string
*/
function gen_rand_string_friendly($num_chars = 8)
{
$rand_str = unique_id();
// Remove Z and Y from the base_convert(), replace 0 with Z and O with Y
// [a, z] + [0, 9] - {z, y} = [a, z] + [0, 9] - {0, o} = 34
$rand_str = str_replace(array('0', 'O'), array('Z', 'Y'), strtoupper(base_convert($rand_str, 16, 34)));
return substr($rand_str, 0, $num_chars);
}
/**
* Return unique id
* @param string $extra additional entropy
*/
function unique_id($extra = 'c')
{
static $dss_seeded = false;
global $config;
$val = $config['rand_seed'] . microtime();
$val = md5($val);
$config['rand_seed'] = md5($config['rand_seed'] . $val . $extra);
if ($dss_seeded !== true && ($config['rand_seed_last_update'] < time() - rand(1,10)))
{
$config->set('rand_seed_last_update', time(), false);
$config->set('rand_seed', $config['rand_seed'], false);
$dss_seeded = true;
}
return substr($val, 4, 16);
}
/**
* Wrapper for mt_rand() which allows swapping $min and $max parameters.
*
* PHP does not allow us to swap the order of the arguments for mt_rand() anymore.
* (since PHP 5.3.4, see http://bugs.php.net/46587)
*
* @param int $min Lowest value to be returned
* @param int $max Highest value to be returned
*
* @return int Random integer between $min and $max (or $max and $min)
*/
function phpbb_mt_rand($min, $max)
{
return ($min > $max) ? mt_rand($max, $min) : mt_rand($min, $max);
}
/**
* Wrapper for getdate() which returns the equivalent array for UTC timestamps.
*
* @param int $time Unix timestamp (optional)
*
* @return array Returns an associative array of information related to the timestamp.
* See http://www.php.net/manual/en/function.getdate.php
*/
function phpbb_gmgetdate($time = false)
{
if ($time === false)
{
$time = time();
}
// getdate() interprets timestamps in local time.
// What follows uses the fact that getdate() and
// date('Z') balance each other out.
return getdate($time - date('Z'));
}
/**
* Return formatted string for filesizes
*
* @param mixed $value filesize in bytes
* (non-negative number; int, float or string)
* @param bool $string_only true if language string should be returned
* @param array $allowed_units only allow these units (data array indexes)
*
* @return mixed data array if $string_only is false
*/
function get_formatted_filesize($value, $string_only = true, $allowed_units = false)
{
global $user;
$available_units = array(
'tb' => array(
'min' => 1099511627776, // pow(2, 40)
'index' => 4,
'si_unit' => 'TB',
'iec_unit' => 'TIB',
),
'gb' => array(
'min' => 1073741824, // pow(2, 30)
'index' => 3,
'si_unit' => 'GB',
'iec_unit' => 'GIB',
),
'mb' => array(
'min' => 1048576, // pow(2, 20)
'index' => 2,
'si_unit' => 'MB',
'iec_unit' => 'MIB',
),
'kb' => array(
'min' => 1024, // pow(2, 10)
'index' => 1,
'si_unit' => 'KB',
'iec_unit' => 'KIB',
),
'b' => array(
'min' => 0,
'index' => 0,
'si_unit' => 'BYTES', // Language index
'iec_unit' => 'BYTES', // Language index
),
);
foreach ($available_units as $si_identifier => $unit_info)
{
if (!empty($allowed_units) && $si_identifier != 'b' && !in_array($si_identifier, $allowed_units))
{
continue;
}
if ($value >= $unit_info['min'])
{
$unit_info['si_identifier'] = $si_identifier;
break;
}
}
unset($available_units);
for ($i = 0; $i < $unit_info['index']; $i++)
{
$value /= 1024;
}
$value = round($value, 2);
// Lookup units in language dictionary
$unit_info['si_unit'] = (isset($user->lang[$unit_info['si_unit']])) ? $user->lang[$unit_info['si_unit']] : $unit_info['si_unit'];
$unit_info['iec_unit'] = (isset($user->lang[$unit_info['iec_unit']])) ? $user->lang[$unit_info['iec_unit']] : $unit_info['iec_unit'];
// Default to IEC
$unit_info['unit'] = $unit_info['iec_unit'];
if (!$string_only)
{
$unit_info['value'] = $value;
return $unit_info;
}
return $value . ' ' . $unit_info['unit'];
}
/**
* Determine whether we are approaching the maximum execution time. Should be called once
* at the beginning of the script in which it's used.
* @return bool Either true if the maximum execution time is nearly reached, or false
* if some time is still left.
*/
function still_on_time($extra_time = 15)
{
static $max_execution_time, $start_time;
$current_time = microtime(true);
if (empty($max_execution_time))
{
$max_execution_time = (function_exists('ini_get')) ? (int) @ini_get('max_execution_time') : (int) @get_cfg_var('max_execution_time');
// If zero, then set to something higher to not let the user catch the ten seconds barrier.
if ($max_execution_time === 0)
{
$max_execution_time = 50 + $extra_time;
}
$max_execution_time = min(max(10, ($max_execution_time - $extra_time)), 50);
// For debugging purposes
// $max_execution_time = 10;
global $starttime;
$start_time = (empty($starttime)) ? $current_time : $starttime;
}
return (ceil($current_time - $start_time) < $max_execution_time) ? true : false;
}
/**
* Hashes an email address to a big integer
*
* @param string $email Email address
*
* @return string Unsigned Big Integer
*/
function phpbb_email_hash($email)
{
return sprintf('%u', crc32(strtolower($email))) . strlen($email);
}
/**
* Wrapper for version_compare() that allows using uppercase A and B
* for alpha and beta releases.
*
* See http://www.php.net/manual/en/function.version-compare.php
*
* @param string $version1 First version number
* @param string $version2 Second version number
* @param string $operator Comparison operator (optional)
*
* @return mixed Boolean (true, false) if comparison operator is specified.
* Integer (-1, 0, 1) otherwise.
*/
function phpbb_version_compare($version1, $version2, $operator = null)
{
$version1 = strtolower($version1);
$version2 = strtolower($version2);
if (is_null($operator))
{
return version_compare($version1, $version2);
}
else
{
return version_compare($version1, $version2, $operator);
}
}
/**
* 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]);
|