Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
94.37% covered (success)
94.37%
67 / 71
83.33% covered (warning)
83.33%
5 / 6
CRAP
0.00% covered (danger)
0.00%
0 / 1
SetupInstallPathResolver
94.37% covered (success)
94.37%
67 / 71
83.33% covered (warning)
83.33%
5 / 6
12.03
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 resolve
100.00% covered (success)
100.00%
26 / 26
100.00% covered (success)
100.00%
1 / 1
1
 resolveJava
100.00% covered (success)
100.00%
22 / 22
100.00% covered (success)
100.00%
1 / 1
3
 resolveDirectoryResource
42.86% covered (danger)
42.86%
3 / 7
0.00% covered (danger)
0.00%
0 / 1
2.75
 resolveAppDataFolder
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
3
 normalizeArchitecture
100.00% covered (success)
100.00%
9 / 9
100.00% covered (success)
100.00%
1 / 1
2
1<?php
2
3declare(strict_types=1);
4/**
5 * SPDX-FileCopyrightText: 2026 LibreCode coop and contributors
6 * SPDX-License-Identifier: AGPL-3.0-or-later
7 */
8
9namespace OCA\Libresign\Service\Install;
10
11use OCA\Libresign\AppInfo\Application;
12use OCA\Libresign\Exception\InvalidSignatureException;
13use OCP\Files\NotFoundException;
14use OCP\IAppConfig;
15use OCP\IConfig;
16
17class SetupInstallPathResolver {
18    private string $instanceId;
19
20    public function __construct(
21        private IConfig $config,
22        private IAppConfig $appConfig,
23        private DependencyStorage $dependencyStorage,
24    ) {
25        $this->instanceId = (string)$this->config->getSystemValue('instanceid');
26    }
27
28    public function resolve(InstallTarget $target, string $resource): string {
29        $installPath = match ($resource) {
30            'java' => $this->resolveJava($target),
31            'jsignpdf' => $this->resolveDirectoryResource(
32                $target,
33                'jsignpdf_path',
34                'jsignpdf',
35                static fn (string $path): string => dirname($path),
36                'JSignPdf',
37            ),
38            'pdftk' => $this->resolveDirectoryResource(
39                $target,
40                'pdftk_path',
41                'pdftk',
42                static fn (string $path): string => substr($path, 0, -strlen('/pdftk.jar')),
43                'PDFtk',
44            ),
45            'cfssl' => $this->resolveDirectoryResource(
46                $target,
47                'cfssl_bin',
48                'cfssl',
49                static fn (string $path): string => substr($path, 0, -strlen('/cfssl')),
50                'CFSSL',
51            ),
52            default => throw new InvalidSignatureException(sprintf('Unsupported setup resource "%s".', $resource)),
53        };
54
55        return $this->normalizeArchitecture($installPath, $target);
56    }
57
58    private function resolveJava(InstallTarget $target): string {
59        $path = $this->appConfig->getValueString(Application::APP_ID, 'java_path');
60        if ($path === '') {
61            return $this->resolveAppDataFolder(
62                $target->architecture() . '/' . $target->distro() . '/java/jdk-' . InstallService::JAVA_URL_PATH_NAME . '-jre',
63                'Java',
64            );
65        }
66
67        $installPath = substr($path, 0, -strlen('/bin/java'));
68        $expected = $this->instanceId
69            . '/libresign/'
70            . $target->architecture()
71            . '/'
72            . $target->distro()
73            . '/java';
74
75        if (str_contains($installPath, $expected)) {
76            return $installPath;
77        }
78
79        return (string)preg_replace(
80            '/'
81            . preg_quote($this->instanceId, '/')
82            . '\/libresign\/([^\/]+)\/([^\/]+)\/java/i',
83            $expected,
84            $installPath,
85        );
86    }
87
88    /**
89     * @param callable(string): string $configuredPathToDirectory
90     */
91    private function resolveDirectoryResource(
92        InstallTarget $target,
93        string $configKey,
94        string $resource,
95        callable $configuredPathToDirectory,
96        string $displayName,
97    ): string {
98        $path = $this->appConfig->getValueString(Application::APP_ID, $configKey);
99        if ($path === '') {
100            return $this->resolveAppDataFolder(
101                $target->architecture() . '/' . $resource,
102                $displayName,
103            );
104        }
105        return $configuredPathToDirectory($path);
106    }
107
108    private function resolveAppDataFolder(string $relativePath, string $displayName): string {
109        try {
110            $folder = $this->dependencyStorage->rootFolder()->getFolder($relativePath);
111            $path = $this->dependencyStorage->pathOfFolder($folder);
112            if (is_dir($path)) {
113                return $path;
114            }
115        } catch (NotFoundException) {
116        }
117        throw new InvalidSignatureException($displayName . ' path not found at app config.');
118    }
119
120    private function normalizeArchitecture(string $installPath, InstallTarget $target): string {
121        if (str_contains($installPath, $target->architecture())) {
122            return $installPath;
123        }
124
125        return (string)preg_replace(
126            '/'
127            . preg_quote($this->instanceId, '/')
128            . '\/libresign\/([^\/]+)/i',
129            $this->instanceId . '/libresign/' . $target->architecture(),
130            $installPath,
131        );
132    }
133}