Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
69.44% covered (warning)
69.44%
25 / 36
62.50% covered (warning)
62.50%
5 / 8
CRAP
0.00% covered (danger)
0.00%
0 / 1
DependencyStorage
69.44% covered (warning)
69.44%
25 / 36
62.50% covered (warning)
62.50%
5 / 8
23.30
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
 rootFolder
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 resourceFolder
100.00% covered (success)
100.00%
14 / 14
100.00% covered (success)
100.00%
1 / 1
5
 childFolder
63.64% covered (warning)
63.64%
7 / 11
0.00% covered (danger)
0.00%
0 / 1
6.20
 pathOfFolder
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 pathOfFile
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 dataDirectory
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 permissionException
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 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\Exception\LibresignException;
12use OCA\Libresign\Files\TSimpleFile;
13use OCP\Files\AppData\IAppDataFactory;
14use OCP\Files\IAppData;
15use OCP\Files\NotFoundException;
16use OCP\Files\NotPermittedException;
17use OCP\Files\SimpleFS\ISimpleFile;
18use OCP\Files\SimpleFS\ISimpleFolder;
19use OCP\IConfig;
20
21class DependencyStorage {
22    use TSimpleFile {
23        getInternalPathOfFile as getInternalPathOfFileTrait;
24        getInternalPathOfFolder as getInternalPathOfFolderTrait;
25    }
26
27    private IAppData $appData;
28
29    public function __construct(
30        IAppDataFactory $appDataFactory,
31        private IConfig $config,
32    ) {
33        $this->appData = $appDataFactory->get('libresign');
34    }
35
36    public function rootFolder(): ISimpleFolder {
37        return $this->appData->getFolder('/');
38    }
39
40    public function resourceFolder(
41        InstallTarget $target,
42        string $path = '',
43        bool $empty = false,
44    ): ISimpleFolder {
45        if ($path === '') {
46            $path = $target->architecture();
47        } elseif ($path === 'java') {
48            $path = $target->architecture() . '/' . $target->distro() . '/java';
49        } else {
50            $path = $target->architecture() . '/' . ltrim($path, '/');
51        }
52
53        $folder = $this->rootFolder();
54        $parts = explode('/', $path);
55        foreach ($parts as $index => $part) {
56            $folder = $this->childFolder(
57                $folder,
58                $part,
59                $empty && $index === array_key_last($parts),
60            );
61        }
62
63        return $folder;
64    }
65
66    public function childFolder(
67        ISimpleFolder $parent,
68        string $name,
69        bool $empty = false,
70    ): ISimpleFolder {
71        try {
72            $folder = $parent->getFolder($name);
73            if (!$empty) {
74                return $folder;
75            }
76            $folder->delete();
77            return $parent->newFolder($name);
78        } catch (NotFoundException) {
79            try {
80                return $parent->newFolder($name);
81            } catch (NotPermittedException $e) {
82                throw $this->permissionException('create', $parent, $e);
83            }
84        } catch (NotPermittedException $e) {
85            throw $this->permissionException('access', $parent, $e);
86        }
87    }
88
89    public function pathOfFolder(ISimpleFolder $folder): string {
90        return $this->dataDirectory() . '/' . $this->getInternalPathOfFolderTrait($folder);
91    }
92
93    public function pathOfFile(ISimpleFile $file): string {
94        return $this->dataDirectory() . '/' . $this->getInternalPathOfFileTrait($file);
95    }
96
97    private function dataDirectory(): string {
98        return $this->config->getSystemValue('datadirectory', \OC::$SERVERROOT . '/data/');
99    }
100
101    private function permissionException(
102        string $operation,
103        ISimpleFolder $parent,
104        NotPermittedException $previous,
105    ): LibresignException {
106        return new LibresignException(
107            'LibreSign cannot ' . $operation . ' its dependency directory in app data. '
108            . 'Check that the Nextcloud process can write to ' . $this->pathOfFolder($parent)
109            . ' and review the Nextcloud server log for the original permission error.',
110            previous: $previous,
111        );
112    }
113}