diff --git a/libraries/src/String/PunycodeHelper.php b/libraries/src/String/PunycodeHelper.php index 0a6344bfebdb3..6743233004a8d 100644 --- a/libraries/src/String/PunycodeHelper.php +++ b/libraries/src/String/PunycodeHelper.php @@ -94,27 +94,31 @@ public static function urlToPunycode($uri) $newuri = ''; if (!empty($parsed['scheme'])) { - // Assume :// is required although it is not always. - $newuri .= $parsed['scheme'] . '://'; + $newuri .= $parsed['scheme'] . ':'; } - if (!empty($newhost)) { - $newuri .= $newhost; + // The URL has a host, so it always has an authority, also when it is protocol-relative + $newuri .= '//'; + + if (isset($parsed['user'])) { + $newuri .= $parsed['user'] . (isset($parsed['pass']) ? ':' . $parsed['pass'] : '') . '@'; } + $newuri .= $newhost; + if (!empty($parsed['port'])) { $newuri .= ':' . $parsed['port']; } - if (!empty($parsed['path'])) { + if (isset($parsed['path'])) { $newuri .= $parsed['path']; } - if (!empty($parsed['query'])) { + if (isset($parsed['query'])) { $newuri .= '?' . $parsed['query']; } - if (!empty($parsed['fragment'])) { + if (isset($parsed['fragment'])) { $newuri .= '#' . $parsed['fragment']; } @@ -156,27 +160,31 @@ public static function urlToUTF8($uri) $newuri = ''; if (!empty($parsed['scheme'])) { - // Assume :// is required although it is not always. - $newuri .= $parsed['scheme'] . '://'; + $newuri .= $parsed['scheme'] . ':'; } - if (!empty($newhost)) { - $newuri .= $newhost; + // The URL has a host, so it always has an authority, also when it is protocol-relative + $newuri .= '//'; + + if (isset($parsed['user'])) { + $newuri .= $parsed['user'] . (isset($parsed['pass']) ? ':' . $parsed['pass'] : '') . '@'; } + $newuri .= $newhost; + if (!empty($parsed['port'])) { $newuri .= ':' . $parsed['port']; } - if (!empty($parsed['path'])) { + if (isset($parsed['path'])) { $newuri .= $parsed['path']; } - if (!empty($parsed['query'])) { + if (isset($parsed['query'])) { $newuri .= '?' . $parsed['query']; } - if (!empty($parsed['fragment'])) { + if (isset($parsed['fragment'])) { $newuri .= '#' . $parsed['fragment']; } diff --git a/tests/Unit/Libraries/Cms/String/PunycodeHelperTest.php b/tests/Unit/Libraries/Cms/String/PunycodeHelperTest.php new file mode 100644 index 0000000000000..496de08d00da4 --- /dev/null +++ b/tests/Unit/Libraries/Cms/String/PunycodeHelperTest.php @@ -0,0 +1,76 @@ + + * @license GNU General Public License version 2 or later; see LICENSE.txt + */ + +namespace Joomla\Tests\Unit\Libraries\Cms\String; + +use Joomla\CMS\String\PunycodeHelper; +use Joomla\Tests\Unit\UnitTestCase; + +/** + * Test class for \Joomla\CMS\String\PunycodeHelper. + * + * @since __DEPLOY_VERSION__ + */ +class PunycodeHelperTest extends UnitTestCase +{ + /** + * Provides UTF-8 URLs and their Punycode equivalents. + * + * @return array + * + * @since __DEPLOY_VERSION__ + */ + public static function urlProvider(): array + { + return [ + 'full url' => ['https://exämple.com:8080/path/file.php?a=1&b=2#top', 'https://xn--exmple-cua.com:8080/path/file.php?a=1&b=2#top'], + 'user and password' => ['https://user:secret@exämple.com/feed', 'https://user:secret@xn--exmple-cua.com/feed'], + 'user only' => ['ftp://user@exämple.com/file', 'ftp://user@xn--exmple-cua.com/file'], + 'protocol-relative url' => ['//exämple.com/page', '//xn--exmple-cua.com/page'], + 'query and fragment 0' => ['http://exämple.com/?0#0', 'http://xn--exmple-cua.com/?0#0'], + 'ascii url' => ['http://www.example.com/index.php', 'http://www.example.com/index.php'], + 'relative url' => ['/index.php?option=com_content', '/index.php?option=com_content'], + ]; + } + + /** + * Tests converting a UTF-8 URL to Punycode. + * + * @param string $utf8 The UTF-8 URL + * @param string $punycode The Punycode URL + * + * @return void + * + * @since __DEPLOY_VERSION__ + * + * @dataProvider urlProvider + */ + public function testUrlToPunycode(string $utf8, string $punycode): void + { + $this->assertSame($punycode, PunycodeHelper::urlToPunycode($utf8)); + } + + /** + * Tests converting a Punycode URL to UTF-8. + * + * @param string $utf8 The UTF-8 URL + * @param string $punycode The Punycode URL + * + * @return void + * + * @since __DEPLOY_VERSION__ + * + * @dataProvider urlProvider + */ + public function testUrlToUTF8(string $utf8, string $punycode): void + { + $this->assertSame($utf8, PunycodeHelper::urlToUTF8($punycode)); + } +}