From 70e4e86148daebbe7d2a831c2dad839cf1374c35 Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Fri, 25 Jul 2025 18:55:21 +0200 Subject: [PATCH] Support IPv6 in database DSN (#9937) --- CHANGELOG.md | 1 + program/lib/Roundcube/rcube_db.php | 11 +++++++++- tests/Framework/DBTest.php | 35 +++++++++++++++++++++++++----- 3 files changed, 40 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 90fb5e2ef..aa023c0c9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## Unreleased +- Support IPv6 in database DSN (#9937) ## Release 1.7-beta diff --git a/program/lib/Roundcube/rcube_db.php b/program/lib/Roundcube/rcube_db.php index 24115bc13..83e199b1c 100644 --- a/program/lib/Roundcube/rcube_db.php +++ b/program/lib/Roundcube/rcube_db.php @@ -1280,9 +1280,18 @@ class rcube_db // process the different protocol options $parsed['protocol'] = !empty($proto) ? $proto : 'tcp'; $proto_opts = rawurldecode($proto_opts); - if (strpos($proto_opts, ':') !== false) { + + // Support IPv6 in the host spec. + if (preg_match('/(\[[a-f0-9:]+\])/i', $proto_opts, $matches)) { + $proto_opts = str_replace($matches[1], '', $proto_opts); + if (($pos = strpos($proto_opts, ':')) !== false) { + $parsed['port'] = substr($proto_opts, $pos + 1); + } + $proto_opts = $matches[1]; + } elseif (strpos($proto_opts, ':') !== false) { [$proto_opts, $parsed['port']] = explode(':', $proto_opts); } + if ($parsed['protocol'] == 'tcp' && strlen($proto_opts)) { $parsed['hostspec'] = $proto_opts; } elseif ($parsed['protocol'] == 'unix') { diff --git a/tests/Framework/DBTest.php b/tests/Framework/DBTest.php index 2ccd78725..73b8a61bc 100644 --- a/tests/Framework/DBTest.php +++ b/tests/Framework/DBTest.php @@ -181,9 +181,7 @@ class DBTest extends TestCase public function test_parse_dsn() { - $dsn = 'mysql://USERNAME:PASSWORD@HOST:3306/DATABASE'; - - $result = \rcube_db::parse_dsn($dsn); + $result = \rcube_db::parse_dsn('mysql://USERNAME:PASSWORD@HOST:3306/DATABASE'); $this->assertSame('mysql', $result['phptype']); $this->assertSame('USERNAME', $result['username']); @@ -192,9 +190,7 @@ class DBTest extends TestCase $this->assertSame('HOST', $result['hostspec']); $this->assertSame('DATABASE', $result['database']); - $dsn = 'pgsql:///DATABASE'; - - $result = \rcube_db::parse_dsn($dsn); + $result = \rcube_db::parse_dsn('pgsql:///DATABASE'); $this->assertSame('pgsql', $result['phptype']); $this->assertTrue(!array_key_exists('username', $result)); @@ -202,6 +198,33 @@ class DBTest extends TestCase $this->assertTrue(!array_key_exists('port', $result)); $this->assertTrue(!array_key_exists('hostspec', $result)); $this->assertSame('DATABASE', $result['database']); + + $result = \rcube_db::parse_dsn('mysql://user:pass@[fd00:3::11]:3306/roundcubemail'); + + $this->assertSame('mysql', $result['phptype']); + $this->assertSame('user', $result['username']); + $this->assertSame('pass', $result['password']); + $this->assertSame('[fd00:3::11]', $result['hostspec']); + $this->assertSame('3306', $result['port']); + $this->assertSame('roundcubemail', $result['database']); + + $result = \rcube_db::parse_dsn('mysql://user:pass@[::1]/roundcubemail'); + + $this->assertSame('mysql', $result['phptype']); + $this->assertSame('user', $result['username']); + $this->assertSame('pass', $result['password']); + $this->assertSame('[::1]', $result['hostspec']); + $this->assertTrue(!array_key_exists('port', $result)); + $this->assertSame('roundcubemail', $result['database']); + + $result = \rcube_db::parse_dsn('mysql://192.168.0.1:1234/roundcubemail'); + + $this->assertSame('mysql', $result['phptype']); + $this->assertSame('192.168.0.1', $result['hostspec']); + $this->assertSame('1234', $result['port']); + $this->assertTrue(!array_key_exists('username', $result)); + $this->assertTrue(!array_key_exists('password', $result)); + $this->assertSame('roundcubemail', $result['database']); } /**