Code Coverage |
||||||||||
Classes and Traits |
Functions and Methods |
Lines |
||||||||
Total | |
0.00% |
0 / 1 |
|
66.67% |
10 / 15 |
CRAP | |
90.42% |
151 / 167 |
path_helper | |
0.00% |
0 / 1 |
|
66.67% |
10 / 15 |
57.66 | |
90.42% |
151 / 167 |
__construct | |
100.00% |
1 / 1 |
1 | |
100.00% |
7 / 7 |
|||
get_phpbb_root_path | |
100.00% |
1 / 1 |
1 | |
100.00% |
1 / 1 |
|||
get_adm_relative_path | |
100.00% |
1 / 1 |
1 | |
100.00% |
1 / 1 |
|||
get_php_ext | |
100.00% |
1 / 1 |
1 | |
100.00% |
1 / 1 |
|||
update_web_root_path | |
0.00% |
0 / 1 |
5.07 | |
85.71% |
12 / 14 |
|||
remove_web_root_path | |
0.00% |
0 / 1 |
2.26 | |
60.00% |
3 / 5 |
|||
get_web_root_path | |
0.00% |
0 / 1 |
9.59 | |
80.65% |
25 / 31 |
|||
get_web_root_path_from_ajax_referer | |
0.00% |
0 / 1 |
7.12 | |
86.67% |
26 / 30 |
|||
clean_url | |
100.00% |
1 / 1 |
2 | |
100.00% |
7 / 7 |
|||
glue_url_params | |
100.00% |
1 / 1 |
3 | |
100.00% |
9 / 9 |
|||
get_url_parts | |
100.00% |
1 / 1 |
8 | |
100.00% |
27 / 27 |
|||
strip_url_params | |
100.00% |
1 / 1 |
5 | |
100.00% |
13 / 13 |
|||
append_url_params | |
100.00% |
1 / 1 |
3 | |
100.00% |
9 / 9 |
|||
get_valid_page | |
100.00% |
1 / 1 |
6 | |
100.00% |
10 / 10 |
|||
is_router_used | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 2 |
<?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. | |
* | |
*/ | |
namespace phpbb; | |
/** | |
* A class with various functions that are related to paths, files and the filesystem | |
*/ | |
class path_helper | |
{ | |
/** @var \phpbb\symfony_request */ | |
protected $symfony_request; | |
/** @var \phpbb\filesystem\filesystem_interface */ | |
protected $filesystem; | |
/** @var \phpbb\request\request_interface */ | |
protected $request; | |
/** @var string */ | |
protected $phpbb_root_path; | |
/** @var string */ | |
protected $adm_relative_path; | |
/** @var string */ | |
protected $php_ext; | |
/** @var string */ | |
protected $web_root_path; | |
/** | |
* Constructor | |
* | |
* @param \phpbb\symfony_request $symfony_request | |
* @param \phpbb\filesystem\filesystem_interface $filesystem | |
* @param \phpbb\request\request_interface $request | |
* @param string $phpbb_root_path Relative path to phpBB root | |
* @param string $php_ext PHP file extension | |
* @param mixed $adm_relative_path Relative path admin path to adm/ root | |
*/ | |
public function __construct(\phpbb\symfony_request $symfony_request, \phpbb\filesystem\filesystem_interface $filesystem, \phpbb\request\request_interface $request, $phpbb_root_path, $php_ext, $adm_relative_path = null) | |
{ | |
$this->symfony_request = $symfony_request; | |
$this->filesystem = $filesystem; | |
$this->request = $request; | |
$this->phpbb_root_path = $phpbb_root_path; | |
$this->php_ext = $php_ext; | |
$this->adm_relative_path = $adm_relative_path; | |
} | |
/** | |
* Get the phpBB root path | |
* | |
* @return string | |
*/ | |
public function get_phpbb_root_path() | |
{ | |
return $this->phpbb_root_path; | |
} | |
/** | |
* Get the adm root path | |
* | |
* @return string | |
*/ | |
public function get_adm_relative_path() | |
{ | |
return $this->adm_relative_path; | |
} | |
/** | |
* Get the php extension | |
* | |
* @return string | |
*/ | |
public function get_php_ext() | |
{ | |
return $this->php_ext; | |
} | |
/** | |
* Update a web path to the correct relative root path | |
* | |
* This replaces $phpbb_root_path . some_url with | |
* get_web_root_path() . some_url | |
* | |
* @param string $path The path to be updated | |
* @return string | |
*/ | |
public function update_web_root_path($path) | |
{ | |
$web_root_path = $this->get_web_root_path(); | |
// Removes the web root path if it is already present | |
if (strpos($path, $web_root_path) === 0) | |
{ | |
$path = $this->phpbb_root_path . substr($path, strlen($web_root_path)); | |
} | |
if (strpos($path, $this->phpbb_root_path) === 0) | |
{ | |
$path = substr($path, strlen($this->phpbb_root_path)); | |
if (substr($web_root_path, -8) === 'app.php/' && substr($path, 0, 7) === 'app.php') | |
{ | |
$path = substr($path, 8); | |
} | |
return $this->filesystem->clean_path($web_root_path . $path); | |
} | |
return $path; | |
} | |
/** | |
* Strips away the web root path and prepends the normal root path | |
* | |
* This replaces get_web_root_path() . some_url with | |
* $phpbb_root_path . some_url | |
* | |
* @param string $path The path to be updated | |
* @return string | |
*/ | |
public function remove_web_root_path($path) | |
{ | |
if (strpos($path, $this->get_web_root_path()) === 0) | |
{ | |
$path = substr($path, strlen($this->get_web_root_path())); | |
return $this->phpbb_root_path . $path; | |
} | |
return $path; | |
} | |
/** | |
* Get a relative root path from the current URL | |
* | |
* @return string | |
*/ | |
public function get_web_root_path() | |
{ | |
if ($this->symfony_request === null) | |
{ | |
return $this->phpbb_root_path; | |
} | |
if (null !== $this->web_root_path) | |
{ | |
return $this->web_root_path; | |
} | |
// We do not need to escape $path_info, $request_uri and $script_name because we can not find their content in the result. | |
// Path info (e.g. /foo/bar) | |
$path_info = $this->filesystem->clean_path($this->symfony_request->getPathInfo()); | |
// Full request URI (e.g. phpBB/app.php/foo/bar) | |
$request_uri = $this->symfony_request->getRequestUri(); | |
// Script name URI (e.g. phpBB/app.php) | |
$script_name = $this->symfony_request->getScriptName(); | |
/* | |
* If the path info is empty but we're using app.php, then we | |
* might be using an empty route like app.php/ which is | |
* supported by symfony's routing | |
*/ | |
if ($path_info === '/' && preg_match('/app\.' . $this->php_ext . '\/$/', $request_uri)) | |
{ | |
return $this->web_root_path = $this->filesystem->clean_path('./../' . $this->phpbb_root_path); | |
} | |
/* | |
* If the path info is empty (single /), then we're not using | |
* a route like app.php/foo/bar | |
*/ | |
if ($path_info === '/') | |
{ | |
return $this->web_root_path = $this->phpbb_root_path; | |
} | |
/* | |
* Check AJAX request: | |
* If the current request is a AJAX we need to fix the paths. | |
* We need to get the root path based on the Referer, so we can use | |
* the generated URLs in the template of the Referer. If we do not | |
* generate the relative path based on the Referer, but based on the | |
* currently requested URL, the generated URLs will not point to the | |
* intended locations: | |
* Referer desired URL desired relative root path | |
* memberlist.php faq.php ./ | |
* memberlist.php app.php/foo/bar ./ | |
* app.php/foo memberlist.php ../ | |
* app.php/foo app.php/fox ../ | |
* app.php/foo/bar memberlist.php ../../ | |
* ../page.php memberlist.php ./phpBB/ | |
* ../sub/page.php memberlist.php ./../phpBB/ | |
* | |
* The referer must be specified as a parameter in the query. | |
*/ | |
if ($this->request->is_ajax() && $this->symfony_request->get('_referer')) | |
{ | |
// We need to escape $absolute_board_url because it can be partially concatenated to the result. | |
$absolute_board_url = $this->request->escape($this->symfony_request->getSchemeAndHttpHost() . $this->symfony_request->getBasePath(), true); | |
$referer_web_root_path = $this->get_web_root_path_from_ajax_referer( | |
$this->symfony_request->get('_referer'), | |
$absolute_board_url | |
); | |
return $this->web_root_path = $this->phpbb_root_path . $referer_web_root_path; | |
} | |
// How many corrections might we need? | |
$corrections = substr_count($path_info, '/'); | |
/* | |
* If the script name (e.g. phpBB/app.php) does not exists in the | |
* requestUri (e.g. phpBB/app.php/foo/template), then we are rewriting | |
* the URL. So we must reduce the slash count by 1. | |
*/ | |
if (strpos($request_uri, $script_name) !== 0) | |
{ | |
$corrections--; | |
} | |
// Prepend ../ to the phpbb_root_path as many times as / exists in path_info | |
$this->web_root_path = $this->filesystem->clean_path( | |
'./' . str_repeat('../', $corrections) . $this->phpbb_root_path | |
); | |
return $this->web_root_path; | |
} | |
/** | |
* Get the web root path of the referer form an ajax request | |
* | |
* @param string $absolute_referer_url | |
* @param string $absolute_board_url | |
* @return string | |
*/ | |
public function get_web_root_path_from_ajax_referer($absolute_referer_url, $absolute_board_url) | |
{ | |
// If the board URL is in the beginning of the referer, this means | |
// we the referer is in the board URL or a subdirectory of it. | |
// So we just need to count the / (slashes) in the left over part of | |
// the referer and prepend ../ the the current root_path, to get the | |
// web root path of the referer. | |
if (strpos($absolute_referer_url, $absolute_board_url) === 0) | |
{ | |
$relative_referer_path = substr($absolute_referer_url, strlen($absolute_board_url)); | |
$has_params = strpos($relative_referer_path, '?'); | |
if ($has_params !== false) | |
{ | |
$relative_referer_path = substr($relative_referer_path, 0, $has_params); | |
} | |
$corrections = substr_count($relative_referer_path, '/'); | |
return $this->phpbb_root_path . str_repeat('../', $corrections - 1); | |
} | |
// If not, it's a bit more complicated. We go to the parent directory | |
// of the referer until we find the remaining referer in the board URL. | |
// Foreach directory we need to add a ../ to the fixed root_path. | |
// When we finally found it, we need to remove the remaining referer | |
// from the board URL, to get the boards root path. | |
// If the then append these two strings, we get our fixed web root path. | |
$fixed_root_path = ''; | |
$referer_dir = $absolute_referer_url; | |
$has_params = strpos($referer_dir, '?'); | |
if ($has_params !== false) | |
{ | |
$referer_dir = substr($referer_dir, 0, $has_params); | |
} | |
// If we do not find a slash at the end of the referer, we come | |
// from a file. So the first dirname() does not need a traversal | |
// path correction. | |
if (substr($referer_dir, -1) !== '/') | |
{ | |
$referer_dir = dirname($referer_dir); | |
} | |
while (($dir_position = strpos($absolute_board_url, $referer_dir)) !== 0) | |
{ | |
$fixed_root_path .= '../'; | |
$referer_dir = dirname($referer_dir); | |
// Just return phpbb_root_path if we reach the top directory | |
if ($referer_dir === '.') | |
{ | |
return $this->phpbb_root_path; | |
} | |
} | |
$fixed_root_path .= substr($absolute_board_url, strlen($referer_dir) + 1); | |
// Add trailing slash | |
return $this->phpbb_root_path . $fixed_root_path . '/'; | |
} | |
/** | |
* Eliminates useless . and .. components from specified URL | |
* | |
* @param string $url URL to clean | |
* | |
* @return string Cleaned URL | |
*/ | |
public function clean_url($url) | |
{ | |
$delimiter_position = strpos($url, '://'); | |
// URL should contain :// but it shouldn't start with it. | |
// Do not clean URLs that do not fit these constraints. | |
if (empty($delimiter_position)) | |
{ | |
return $url; | |
} | |
$scheme = substr($url, 0, $delimiter_position) . '://'; | |
// Add length of URL delimiter to position | |
$path = substr($url, $delimiter_position + 3); | |
return $scheme . $this->filesystem->clean_path($path); | |
} | |
/** | |
* Glue URL parameters together | |
* | |
* @param array $params URL parameters in the form of array(name => value) | |
* @return string Returns the glued string, e.g. name1=value1&name2&name3=value3 | |
*/ | |
public function glue_url_params($params) | |
{ | |
$_params = array(); | |
foreach ($params as $key => $value) | |
{ | |
// some parameters do not have value | |
if ($value !== null) | |
{ | |
$_params[] = $key . '=' . $value; | |
} | |
else | |
{ | |
$_params[] = $key; | |
} | |
} | |
return implode('&', $_params); | |
} | |
/** | |
* Get the base and parameters of a URL | |
* | |
* @param string $url URL to break apart | |
* @param bool $is_amp Is the parameter separator &. Defaults to true. | |
* @return array Returns the base and parameters in the form of array('base' => string, 'params' => array(name => value)) | |
*/ | |
public function get_url_parts($url, $is_amp = true) | |
{ | |
$separator = ($is_amp) ? '&' : '&'; | |
$params = array(); | |
if (strpos($url, '?') !== false) | |
{ | |
$base = substr($url, 0, strpos($url, '?')); | |
$args = substr($url, strlen($base) + 1); | |
$args = ($args) ? explode($separator, $args) : array(); | |
foreach ($args as $argument) | |
{ | |
if (empty($argument)) | |
{ | |
continue; | |
} | |
// some parameters don't have value | |
if (strpos($argument, '=') !== false) | |
{ | |
list($key, $value) = explode('=', $argument, 2); | |
} | |
else | |
{ | |
$key = $argument; | |
$value = null; | |
} | |
if ($key === '') | |
{ | |
continue; | |
} | |
$params[$key] = $value; | |
} | |
} | |
else | |
{ | |
$base = $url; | |
} | |
return array( | |
'base' => $base, | |
'params' => $params, | |
); | |
} | |
/** | |
* Strip parameters from an already built URL. | |
* | |
* @param string $url URL to strip parameters from | |
* @param array|string $strip Parameters to strip. | |
* @param bool $is_amp Is the parameter separator &. Defaults to true. | |
* @return string Returns the new URL. | |
*/ | |
public function strip_url_params($url, $strip, $is_amp = true) | |
{ | |
$url_parts = $this->get_url_parts($url, $is_amp); | |
$params = $url_parts['params']; | |
if (!is_array($strip)) | |
{ | |
$strip = array($strip); | |
} | |
if (!empty($params)) | |
{ | |
// Strip the parameters off | |
foreach ($strip as $param) | |
{ | |
unset($params[$param]); | |
} | |
} | |
return $url_parts['base'] . (($params) ? '?' . $this->glue_url_params($params) : ''); | |
} | |
/** | |
* Append parameters to an already built URL. | |
* | |
* @param string $url URL to append parameters to | |
* @param array $new_params Parameters to add in the form of array(name => value) | |
* @param bool $is_amp Is the parameter separator &. Defaults to true. | |
* @return string Returns the new URL. | |
*/ | |
public function append_url_params($url, $new_params, $is_amp = true) | |
{ | |
$url_parts = $this->get_url_parts($url, $is_amp); | |
$params = array_merge($url_parts['params'], $new_params); | |
// Move the sid to the end if it's set | |
if (isset($params['sid'])) | |
{ | |
$sid = $params['sid']; | |
unset($params['sid']); | |
$params['sid'] = $sid; | |
} | |
return $url_parts['base'] . (($params) ? '?' . $this->glue_url_params($params) : ''); | |
} | |
/** | |
* Get a valid page | |
* | |
* @param string $page The page to verify | |
* @param bool $mod_rewrite Whether mod_rewrite is enabled, default: false | |
* | |
* @return string A valid page based on given page and mod_rewrite | |
*/ | |
public function get_valid_page($page, $mod_rewrite = false) | |
{ | |
// We need to be cautious here. | |
// On some situations, the redirect path is an absolute URL, sometimes a relative path | |
// For a relative path, let's prefix it with $phpbb_root_path to point to the correct location, | |
// else we use the URL directly. | |
$url_parts = parse_url($page); | |
// URL | |
if ($url_parts === false || empty($url_parts['scheme']) || empty($url_parts['host'])) | |
{ | |
// Remove 'app.php/' from the page, when rewrite is enabled. | |
// Treat app.php as a reserved file name and remove on mod rewrite | |
// even if it might not be in the phpBB root. | |
if ($mod_rewrite && ($app_position = strpos($page, 'app.' . $this->php_ext . '/')) !== false) | |
{ | |
$page = substr($page, 0, $app_position) . substr($page, $app_position + strlen('app.' . $this->php_ext . '/')); | |
} | |
// Remove preceding slashes from page name and prepend root path | |
$page = $this->get_phpbb_root_path() . ltrim($page, '/\\'); | |
} | |
return $page; | |
} | |
/** | |
* Tells if the router is currently in use (if the current page is a route or not) | |
* | |
* @return bool | |
*/ | |
public function is_router_used() | |
{ | |
// Script name URI (e.g. phpBB/app.php) | |
$script_name = $this->symfony_request->getScriptName(); | |
return basename($script_name) === 'app.' . $this->php_ext; | |
} | |
} |