Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
17 / 17
100.00% covered (success)
100.00%
3 / 3
CRAP
100.00% covered (success)
100.00%
1 / 1
FileUploadHelper
100.00% covered (success)
100.00%
17 / 17
100.00% covered (success)
100.00%
3 / 3
8
100.00% covered (success)
100.00%
1 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 validateUploadedFile
100.00% covered (success)
100.00%
12 / 12
100.00% covered (success)
100.00%
1 / 1
5
 readUploadedFile
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
2
1<?php
2
3declare(strict_types=1);
4/**
5 * SPDX-FileCopyrightText: 2025 LibreCode coop and contributors
6 * SPDX-License-Identifier: AGPL-3.0-or-later
7 */
8
9namespace OCA\Libresign\Helper;
10
11use InvalidArgumentException;
12use OCP\Files\IFilenameValidator;
13use OCP\IL10N;
14
15/**
16 * Helper for validating and processing uploaded files
17 */
18class FileUploadHelper {
19    public function __construct(
20        private IL10N $l10n,
21        private IFilenameValidator $filenameValidator,
22    ) {
23    }
24
25    /**
26     * Validate uploaded file from $_FILES
27     *
28     * @param array $uploadedFile Single file from $_FILES (e.g., $_FILES['file'])
29     * @throws InvalidArgumentException
30     */
31    public function validateUploadedFile(array $uploadedFile): void {
32        if ($uploadedFile['error'] !== 0) {
33            @unlink($uploadedFile['tmp_name']);
34            // TRANSLATORS Validation error shown when the browser upload did not produce a usable temporary file.
35            throw new InvalidArgumentException($this->l10n->t('The uploaded file is invalid'));
36        }
37
38        if (!is_uploaded_file($uploadedFile['tmp_name'])) {
39            @unlink($uploadedFile['tmp_name']);
40            // TRANSLATORS Validation error shown when the received file was not uploaded through the expected HTTP upload flow.
41            throw new InvalidArgumentException($this->l10n->t('The uploaded file is invalid'));
42        }
43
44        if ($uploadedFile['size'] > \OCP\Util::uploadLimit()) {
45            @unlink($uploadedFile['tmp_name']);
46            // TRANSLATORS Validation error shown when an uploaded file is larger than the server's configured upload limit.
47            throw new InvalidArgumentException($this->l10n->t('The uploaded file is too large'));
48        }
49
50        if (!$this->filenameValidator->isFilenameValid(basename((string)$uploadedFile['tmp_name']))) {
51            @unlink($uploadedFile['tmp_name']);
52            // TRANSLATORS Validation error shown when the temporary upload filename fails security validation.
53            throw new InvalidArgumentException($this->l10n->t('The uploaded file is invalid'));
54        }
55    }
56
57    /**
58     * Read content from uploaded file
59     *
60     * @param array $uploadedFile Single file from $_FILES
61     * @return string File content
62     * @throws InvalidArgumentException
63     */
64    public function readUploadedFile(array $uploadedFile): string {
65        $content = @file_get_contents($uploadedFile['tmp_name']);
66        if ($content === false) {
67            // TRANSLATORS Error shown when LibreSign cannot read the temporary file contents after upload.
68            throw new InvalidArgumentException($this->l10n->t('Cannot read the uploaded file'));
69        }
70        return $content;
71    }
72}