From 30d13cd1c4827f792fb8385b0d7d7e26cf73222c Mon Sep 17 00:00:00 2001 From: Reno Reckling Date: Wed, 6 Mar 2024 14:24:00 +0100 Subject: [PATCH] feat: make search path for BinaryFinder customizable. This feature is important for nextcloud running on distributions like NixOS, where all the standard search paths do not exist. Also added tests. This fixes issue #43922 Signed-off-by: Reno Reckling --- config/config.sample.php | 18 ++++++- lib/private/BinaryFinder.php | 29 +++++++---- tests/lib/BinaryFinderTest.php | 92 ++++++++++++++++++++++++++++++++++ 3 files changed, 128 insertions(+), 11 deletions(-) create mode 100644 tests/lib/BinaryFinderTest.php diff --git a/config/config.sample.php b/config/config.sample.php index c21291cd3da03..47bc87d128a26 100644 --- a/config/config.sample.php +++ b/config/config.sample.php @@ -1962,7 +1962,7 @@ /** * Blacklist characters from being used in filenames. This is useful if you * have a filesystem or OS which does not support certain characters like windows. - * + * * The '/' and '\' characters are always forbidden. * * Example for windows systems: ``array('?', '<', '>', ':', '*', '|', '"', chr(0), "\n", "\r")`` @@ -2431,4 +2431,20 @@ * Defaults to ``true`` */ 'enable_non-accessible_features' => true, + +/** + * Directories where nextcloud looks for binaries. + * This is used to find external binaries like libreoffice, sendmail, ffmpeg and more. + * + * Defaults to ``['/usr/local/sbin','/usr/local/bin','/usr/sbin','/usr/bin','/sbin','/bin','/opt/bin']`` + */ +'binary_search_paths' => [ + '/usr/local/sbin', + '/usr/local/bin', + '/usr/sbin', + '/usr/bin', + '/sbin', + '/bin', + '/opt/bin', +], ]; diff --git a/lib/private/BinaryFinder.php b/lib/private/BinaryFinder.php index 17427e9261932..30c80d8987c70 100644 --- a/lib/private/BinaryFinder.php +++ b/lib/private/BinaryFinder.php @@ -3,6 +3,7 @@ declare(strict_types = 1); /** * @copyright 2022 Carl Schwan + * @copyright 2024 Reno Reckling * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify @@ -25,15 +26,28 @@ use OCP\IBinaryFinder; use OCP\ICache; use OCP\ICacheFactory; +use OCP\IConfig; use Symfony\Component\Process\ExecutableFinder; /** * Service that find the binary path for a program */ class BinaryFinder implements IBinaryFinder { + public const DEFAULT_BINARY_SEARCH_PATHS = [ + '/usr/local/sbin', + '/usr/local/bin', + '/usr/sbin', + '/usr/bin', + '/sbin', + '/bin', + '/opt/bin', + ]; private ICache $cache; - public function __construct(ICacheFactory $cacheFactory) { + public function __construct( + ICacheFactory $cacheFactory, + private IConfig $config + ) { $this->cache = $cacheFactory->createLocal('findBinaryPath'); } @@ -51,15 +65,10 @@ public function findBinaryPath(string $program) { if (\OCP\Util::isFunctionEnabled('exec')) { $exeSniffer = new ExecutableFinder(); // Returns null if nothing is found - $result = $exeSniffer->find($program, null, [ - '/usr/local/sbin', - '/usr/local/bin', - '/usr/sbin', - '/usr/bin', - '/sbin', - '/bin', - '/opt/bin', - ]); + $result = $exeSniffer->find( + $program, + null, + $this->config->getSystemValue('binary_search_paths', self::DEFAULT_BINARY_SEARCH_PATHS)); if ($result === null) { $result = false; } diff --git a/tests/lib/BinaryFinderTest.php b/tests/lib/BinaryFinderTest.php new file mode 100644 index 0000000000000..1734ff385a7d9 --- /dev/null +++ b/tests/lib/BinaryFinderTest.php @@ -0,0 +1,92 @@ + + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +namespace Test; + +use OC\BinaryFinder; +use OCP\ICache; +use OCP\ICacheFactory; +use OCP\IConfig; + +class BinaryFinderTest extends TestCase { + private ICache $cache; + private array $cacheMap; + private ICacheFactory $cacheFactory; + + protected function setUp(): void { + $this->cacheMap = []; + $this->cacheFactory = $this->createMock(ICacheFactory::class); + $this->cache = $this->createMock(ICache::class); + $this->cache->method('get')->will($this->returnCallback(function ($key) { + return array_key_exists($key, $this->cacheMap) ? $this->cacheMap[$key] : null; + })); + $this->cache->method('set')->will($this->returnCallback(function ($key, $val, $expires) { + $this->cacheMap[$key] = $val; + })); + $this->cacheFactory->method('createLocal')->with('findBinaryPath')->willReturn($this->cache); + + } + + public function testDefaultFindsCat() { + $config = $this->createMock(IConfig::class); + $config + ->method('getSystemValue') + ->with('binary_search_paths', $this->anything()) + ->will($this->returnCallback(function ($key, $default) { + return $default; + })); + $finder = new BinaryFinder($this->cacheFactory, $config); + $this->assertEquals($finder->findBinaryPath('cat'), '/usr/bin/cat'); + } + + public function testDefaultDoesNotFindCata() { + $config = $this->createMock(IConfig::class); + $config + ->method('getSystemValue') + ->with('binary_search_paths', $this->anything()) + ->will($this->returnCallback(function ($key, $default) { + return $default; + })); + $finder = new BinaryFinder($this->cacheFactory, $config); + $this->assertFalse($finder->findBinaryPath('cata')); + } + + public function testCustomPathFindsCat() { + $config = $this->createMock(IConfig::class); + $config + ->method('getSystemValue') + ->with('binary_search_paths', $this->anything()) + ->willReturn(['/usr/bin']); + $finder = new BinaryFinder($this->cacheFactory, $config); + $this->assertEquals($finder->findBinaryPath('cat'), '/usr/bin/cat'); + } + + public function testWrongCustomPathDoesNotFindCat() { + $config = $this->createMock(IConfig::class); + $config + ->method('getSystemValue') + ->with('binary_search_paths') + ->willReturn(['/wrong']); + $finder = new BinaryFinder($this->cacheFactory, $config); + $this->assertFalse($finder->findBinaryPath('cats')); + } +}