diff --git a/phpBB/config/services.yml b/phpBB/config/services.yml index 735a49c99b..a7e708d492 100644 --- a/phpBB/config/services.yml +++ b/phpBB/config/services.yml @@ -96,6 +96,7 @@ services: - @config - @controller.provider - @ext.manager + - @symfony_request - %core.root_path% - %core.php_ext% @@ -304,6 +305,7 @@ services: arguments: - @symfony_request - @filesystem + - @request - %core.root_path% - %core.php_ext% - %core.adm_relative_path% diff --git a/phpBB/phpbb/controller/helper.php b/phpBB/phpbb/controller/helper.php index 930bc42a98..e330fb5b6d 100644 --- a/phpBB/phpbb/controller/helper.php +++ b/phpBB/phpbb/controller/helper.php @@ -40,6 +40,9 @@ class helper */ protected $config; + /* @var \phpbb\symfony_request */ + protected $symfony_request; + /** * phpBB root path * @var string @@ -60,14 +63,16 @@ class helper * @param \phpbb\config\config $config Config object * @param \phpbb\controller\provider $provider Path provider * @param \phpbb\extension\manager $manager Extension manager object + * @param \phpbb\symfony_request $symfony_request Symfony Request object * @param string $phpbb_root_path phpBB root path * @param string $php_ext PHP extension */ - public function __construct(\phpbb\template\template $template, \phpbb\user $user, \phpbb\config\config $config, \phpbb\controller\provider $provider, \phpbb\extension\manager $manager, $phpbb_root_path, $php_ext) + public function __construct(\phpbb\template\template $template, \phpbb\user $user, \phpbb\config\config $config, \phpbb\controller\provider $provider, \phpbb\extension\manager $manager, \phpbb\symfony_request $symfony_request, $phpbb_root_path, $php_ext) { $this->template = $template; $this->user = $user; $this->config = $config; + $this->symfony_request = $symfony_request; $this->phpbb_root_path = $phpbb_root_path; $this->php_ext = $php_ext; $provider->find_routing_files($manager->get_finder()); @@ -151,4 +156,14 @@ class helper return $this->render('message_body.html', $this->user->lang('INFORMATION'), $code); } + + /** + * Return the current url + * + * @return string + */ + public function get_current_url() + { + return generate_board_url(true) . $this->symfony_request->getRequestUri(); + } } diff --git a/phpBB/phpbb/path_helper.php b/phpBB/phpbb/path_helper.php index b592cc4460..a8592eac6c 100644 --- a/phpBB/phpbb/path_helper.php +++ b/phpBB/phpbb/path_helper.php @@ -24,6 +24,9 @@ class path_helper /** @var \phpbb\filesystem */ protected $filesystem; + /** @var \phpbb\request\request_interface */ + protected $request; + /** @var string */ protected $phpbb_root_path; @@ -41,13 +44,16 @@ class path_helper * * @param \phpbb\symfony_request $symfony_request * @param \phpbb\filesystem $filesystem + * @param \phpbb\request\request_interface $request * @param string $phpbb_root_path Relative path to phpBB root * @param string $php_ext PHP extension (php) + * @param mixed $adm_relative_path Relative path admin path to adm/ root */ - public function __construct(\phpbb\symfony_request $symfony_request, \phpbb\filesystem $filesystem, $phpbb_root_path, $php_ext, $adm_relative_path = null) + public function __construct(\phpbb\symfony_request $symfony_request, \phpbb\filesystem $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; @@ -98,7 +104,13 @@ class path_helper { $path = substr($path, strlen($this->phpbb_root_path)); - return $this->filesystem->clean_path($this->get_web_root_path() . $path); + $web_root_path = $this->get_web_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; @@ -170,6 +182,34 @@ class path_helper 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')) + { + $referer_web_root_path = $this->get_web_root_path_from_ajax_referer( + $this->symfony_request->get('_referer'), + $this->symfony_request->getSchemeAndHttpHost() . $this->symfony_request->getBasePath() + ); + return $this->web_root_path = $this->phpbb_root_path . $referer_web_root_path; + } + // How many corrections might we need? $corrections = substr_count($path_info, '/'); @@ -190,6 +230,65 @@ class path_helper 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 (strpos($absolute_board_url, $referer_dir) !== 0) + { + $fixed_root_path .= '../'; + $referer_dir = dirname($referer_dir); + } + + $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 * diff --git a/tests/avatar/manager_test.php b/tests/avatar/manager_test.php index 246397ad6c..de505e2c9f 100644 --- a/tests/avatar/manager_test.php +++ b/tests/avatar/manager_test.php @@ -38,6 +38,7 @@ class phpbb_avatar_manager_test extends \phpbb_test_case new phpbb_mock_request() ), new \phpbb\filesystem(), + $this->getMock('\phpbb\request\request'), $phpbb_root_path, $phpEx ); diff --git a/tests/controller/helper_route_test.php b/tests/controller/helper_route_test.php index 621efaa830..206c3a4f0b 100644 --- a/tests/controller/helper_route_test.php +++ b/tests/controller/helper_route_test.php @@ -26,6 +26,7 @@ class phpbb_controller_helper_route_test extends phpbb_test_case new phpbb_mock_request() ), new \phpbb\filesystem(), + $this->getMock('\phpbb\request\request'), $phpbb_root_path, $phpEx ); diff --git a/tests/extension/metadata_manager_test.php b/tests/extension/metadata_manager_test.php index 6b6ea80d82..535e4fe0d5 100644 --- a/tests/extension/metadata_manager_test.php +++ b/tests/extension/metadata_manager_test.php @@ -53,6 +53,7 @@ class phpbb_extension_metadata_manager_test extends phpbb_database_test_case new phpbb_mock_request() ), new \phpbb\filesystem(), + $this->getMock('\phpbb\request\request'), $this->phpbb_root_path, $this->phpEx ), diff --git a/tests/functions/build_url_test.php b/tests/functions/build_url_test.php index 7a70bddc71..06415a424e 100644 --- a/tests/functions/build_url_test.php +++ b/tests/functions/build_url_test.php @@ -30,10 +30,11 @@ class phpbb_build_url_test extends phpbb_test_case new phpbb_mock_request() ), new \phpbb\filesystem(), + $this->getMock('\phpbb\request\request'), $phpbb_root_path, 'php' ); - $phpbb_container->set('path_helper', $path_helper); + $phpbb_container->set('path_helper', $phpbb_path_helper); } public function build_url_test_data() { diff --git a/tests/mock/controller_helper.php b/tests/mock/controller_helper.php index 9f70f8e96c..f9d231258e 100644 --- a/tests/mock/controller_helper.php +++ b/tests/mock/controller_helper.php @@ -23,4 +23,9 @@ class phpbb_mock_controller_helper extends \phpbb\controller\helper $provider->find_routing_files($manager->get_finder()); $this->route_collection = $provider->find($phpbb_root_path_ext)->get_routes(); } + + public function get_current_url() + { + return ''; + } } diff --git a/tests/path_helper/path_helper_test.php b/tests/path_helper/path_helper_test.php index 9866cb6efe..27e94d6a07 100644 --- a/tests/path_helper/path_helper_test.php +++ b/tests/path_helper/path_helper_test.php @@ -29,6 +29,7 @@ class phpbb_path_helper_test extends phpbb_test_case new phpbb_mock_request() ), new \phpbb\filesystem(), + $this->getMock('\phpbb\request\request'), $this->phpbb_root_path, 'php' ); @@ -158,6 +159,7 @@ class phpbb_path_helper_test extends phpbb_test_case $path_helper = new \phpbb\path_helper( $symfony_request, new \phpbb\filesystem(), + $this->getMock('\phpbb\request\request'), $this->phpbb_root_path, 'php' ); @@ -338,4 +340,58 @@ class phpbb_path_helper_test extends phpbb_test_case { $this->assertEquals($expected, $this->path_helper->append_url_params($url, $params, $is_amp)); } + + public function get_web_root_path_from_ajax_referer_data() + { + return array( + array( + 'http://www.phpbb.com/community/route1/route2/', + 'http://www.phpbb.com/community', + '../../', + ), + array( + 'http://www.phpbb.com/community/route1/route2', + 'http://www.phpbb.com/community', + '../', + ), + array( + 'http://www.phpbb.com/community/route1', + 'http://www.phpbb.com/community', + '', + ), + array( + 'http://www.phpbb.com/community/', + 'http://www.phpbb.com/community', + '', + ), + array( + 'http://www.phpbb.com/notcommunity/route1/route2/', + 'http://www.phpbb.com/community', + '../../../community/', + ), + array( + 'http://www.phpbb.com/notcommunity/route1/route2', + 'http://www.phpbb.com/community', + '../../community/', + ), + array( + 'http://www.phpbb.com/notcommunity/route1', + 'http://www.phpbb.com/community', + '../community/', + ), + array( + 'http://www.phpbb.com/notcommunity/', + 'http://www.phpbb.com/community', + '../community/', + ), + ); + } + + /** + * @dataProvider get_web_root_path_from_ajax_referer_data + */ + public function test_get_web_root_path_from_ajax_referer($referer_url, $board_url, $expected) + { + $this->assertEquals($this->phpbb_root_path . $expected, $this->path_helper->get_web_root_path_from_ajax_referer($referer_url, $board_url)); + } } diff --git a/tests/security/redirect_test.php b/tests/security/redirect_test.php index fb1011cde0..3961c2781e 100644 --- a/tests/security/redirect_test.php +++ b/tests/security/redirect_test.php @@ -63,6 +63,7 @@ class phpbb_security_redirect_test extends phpbb_security_test_base new phpbb_mock_request() ), new \phpbb\filesystem(), + $this->getMock('\phpbb\request\request'), $this->phpbb_root_path, 'php' ); diff --git a/tests/template/template_events_test.php b/tests/template/template_events_test.php index ce3c90b78a..c415c969fe 100644 --- a/tests/template/template_events_test.php +++ b/tests/template/template_events_test.php @@ -143,6 +143,7 @@ Zeta test event in all', new phpbb_mock_request() ), new \phpbb\filesystem(), + $this->getMock('\phpbb\request\request'), $phpbb_root_path, $phpEx ); diff --git a/tests/template/template_test_case.php b/tests/template/template_test_case.php index 00b823b2c4..83446b5352 100644 --- a/tests/template/template_test_case.php +++ b/tests/template/template_test_case.php @@ -72,6 +72,7 @@ class phpbb_template_template_test_case extends phpbb_test_case new phpbb_mock_request() ), new \phpbb\filesystem(), + $this->getMock('\phpbb\request\request'), $phpbb_root_path, $phpEx ); diff --git a/tests/template/template_test_case_with_tree.php b/tests/template/template_test_case_with_tree.php index 4f778a9c1c..68ecc4b706 100644 --- a/tests/template/template_test_case_with_tree.php +++ b/tests/template/template_test_case_with_tree.php @@ -27,6 +27,7 @@ class phpbb_template_template_test_case_with_tree extends phpbb_template_templat new phpbb_mock_request() ), new \phpbb\filesystem(), + $this->getMock('\phpbb\request\request'), $phpbb_root_path, $phpEx ); diff --git a/tests/test_framework/phpbb_session_test_case.php b/tests/test_framework/phpbb_session_test_case.php index 8a5d582573..d4fc174a12 100644 --- a/tests/test_framework/phpbb_session_test_case.php +++ b/tests/test_framework/phpbb_session_test_case.php @@ -32,6 +32,7 @@ abstract class phpbb_session_test_case extends phpbb_database_test_case $phpbb_path_helper = new \phpbb\path_helper( $symfony_request, $phpbb_filesystem, + $this->getMock('\phpbb\request\request'), $phpbb_root_path, $phpEx );