• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

LibreSign / libresign / 20041537490

08 Dec 2025 08:17PM UTC coverage: 44.146%. First build
20041537490

Pull #6021

github

web-flow
Merge 9088d4fab into eb7183cb5
Pull Request #6021: feat: docmdp implementation

66 of 113 new or added lines in 10 files covered. (58.41%)

5678 of 12862 relevant lines covered (44.15%)

5.1 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

63.16
/lib/Service/TFile.php
1
<?php
2

3
declare(strict_types=1);
4
/**
5
 * SPDX-FileCopyrightText: 2020-2024 LibreCode coop and contributors
6
 * SPDX-License-Identifier: AGPL-3.0-or-later
7
 */
8

9
namespace OCA\Libresign\Service;
10

11
use OCA\Libresign\Exception\LibresignException;
12
use OCA\Libresign\Handler\DocMdpHandler;
13
use OCA\Libresign\Vendor\setasign\Fpdi\PdfParserService\Type\PdfTypeException;
14
use OCP\Files\Node;
15
use OCP\Http\Client\IClientService;
16

17
trait TFile {
18
        /** @var ?string */
19
        private $mimetype = null;
20
        protected IClientService $client;
21
        protected DocMdpHandler $docMdpHandler;
22

23
        public function getNodeFromData(array $data): Node {
24
                if (!$this->folderService->getUserId()) {
15✔
25
                        $this->folderService->setUserId($data['userManager']->getUID());
12✔
26
                }
27
                if (isset($data['file']['fileNode']) && $data['file']['fileNode'] instanceof Node) {
15✔
28
                        return $data['file']['fileNode'];
1✔
29
                }
30
                if (isset($data['file']['fileId'])) {
15✔
31
                        return $this->folderService->getFileById($data['file']['fileId']);
×
32
                }
33
                if (isset($data['file']['path'])) {
15✔
34
                        return $this->folderService->getFileByPath($data['file']['path']);
×
35
                }
36

37
                $content = $this->getFileRaw($data);
15✔
38
                $extension = $this->getExtension($content);
15✔
39

40
                $this->validateFileContent($content, $extension);
15✔
41

42
                $userFolder = $this->folderService->getFolder();
15✔
43
                $folderName = $this->folderService->getFolderName($data, $data['userManager']);
15✔
44
                $folderToFile = $userFolder->newFolder($folderName);
15✔
45
                return $folderToFile->newFile($data['name'] . '.' . $extension, $content);
15✔
46
        }
47

48
        /**
49
         * @throws \Exception
50
         * @throws LibresignException
51
         */
52
        public function validateFileContent(string $content, string $extension): void {
53
                if ($extension === 'pdf') {
20✔
54
                        $this->validatePdfStringWithFpdi($content);
19✔
55
                        $this->validateDocMdpAllowsSignatures($content);
19✔
56
                }
57
        }
58

59
        private function setMimeType(string $mimetype): void {
60
                $this->validateHelper->validateMimeTypeAcceptedByMime($mimetype);
15✔
61
                $this->mimetype = $mimetype;
15✔
62
        }
63

64
        private function getMimeType(string $content): ?string {
65
                if (!$this->mimetype) {
15✔
66
                        $this->setMimeType($this->mimeTypeDetector->detectString($content));
15✔
67
                }
68
                return $this->mimetype;
15✔
69
        }
70

71
        private function getExtension(string $content): string {
72
                $mimetype = $this->getMimeType($content);
15✔
73
                $mappings = $this->mimeTypeDetector->getAllMappings();
15✔
74
                foreach ($mappings as $ext => $mimetypes) {
15✔
75
                        // Single digit extensions will be treated as integers
76
                        // Let's make sure they are strings
77
                        // https://github.com/nextcloud/server/issues/42902
78
                        $ext = (string)$ext;
15✔
79
                        if ($ext[0] === '_') {
15✔
80
                                // comment
81
                                continue;
15✔
82
                        }
83
                        if (in_array($mimetype, $mimetypes)) {
15✔
84
                                return $ext;
15✔
85
                        }
86
                }
87
                return '';
×
88
        }
89

90
        /**
91
         * @return resource|string
92
         */
93
        private function getFileRaw(array $data) {
94
                if (!empty($data['file']['url'])) {
15✔
95
                        if (!filter_var($data['file']['url'], FILTER_VALIDATE_URL)) {
×
96
                                throw new \Exception($this->l10n->t('Invalid URL file'));
×
97
                        }
98
                        try {
99
                                $response = $this->client->newClient()->get($data['file']['url']);
×
100
                        } catch (\Throwable) {
×
101
                                throw new \Exception($this->l10n->t('Invalid URL file'));
×
102
                        }
103
                        $mimetypeFromHeader = $response->getHeader('Content-Type');
×
104
                        $content = (string)$response->getBody();
×
105
                        if (!$content) {
×
106
                                throw new \Exception($this->l10n->t('Empty file'));
×
107
                        }
108
                        $mimeTypeFromContent = $this->getMimeType($content);
×
109
                        if ($mimetypeFromHeader !== $mimeTypeFromContent) {
×
110
                                throw new \Exception($this->l10n->t('Invalid URL file'));
×
111
                        }
112
                } else {
113
                        $content = $this->getFileFromBase64($data['file']['base64']);
15✔
114
                }
115
                return $content;
15✔
116
        }
117

118
        private function getFileFromBase64(string $base64): string {
119
                $withMime = explode(',', $base64);
15✔
120
                if (count($withMime) === 2) {
15✔
121
                        $withMime[0] = explode(';', $withMime[0]);
×
122
                        $withMime[0][0] = explode(':', $withMime[0][0]);
×
123
                        $mimeTypeFromType = $withMime[0][0][1];
×
124

125
                        $base64 = $withMime[1];
×
126

127
                        $content = base64_decode($base64);
×
128
                        $mimeTypeFromContent = $this->getMimeType($content);
×
129
                        if ($mimeTypeFromType !== $mimeTypeFromContent) {
×
130
                                throw new \Exception($this->l10n->t('Invalid URL file'));
×
131
                        }
132
                        $this->setMimeType($mimeTypeFromContent);
×
133
                } else {
134
                        $content = base64_decode($base64);
15✔
135
                        $this->getMimeType($content);
15✔
136
                }
137
                return $content;
15✔
138
        }
139

140
        /**
141
         * Validates a PDF. Triggers error if invalid.
142
         *
143
         * @param string $string
144
         *
145
         * @throws PdfTypeException
146
         */
147
        private function validatePdfStringWithFpdi($string): void {
148
                try {
149
                        $parser = new \OCA\Libresign\Vendor\Smalot\PdfParser\Parser();
19✔
150
                        $parser->parseContent($string);
19✔
151
                } catch (\Throwable $th) {
×
152
                        $this->logger->error($th->getMessage());
×
153
                        throw new \Exception($this->l10n->t('Invalid PDF'));
×
154
                }
155
        }
156

157
        /**
158
         * @throws LibresignException
159
         */
160
        private function validateDocMdpAllowsSignatures(string $pdfContent): void {
161
                $resource = fopen('php://memory', 'r+');
19✔
162
                if (!is_resource($resource)) {
19✔
NEW
163
                        return;
×
164
                }
165

166
                try {
167
                        fwrite($resource, $pdfContent);
19✔
168
                        rewind($resource);
19✔
169

170
                        if (!$this->docMdpHandler->allowsAdditionalSignatures($resource)) {
19✔
171
                                throw new LibresignException(
1✔
172
                                        $this->l10n->t('This document has been certified with no changes allowed, so no additional signatures can be added.')
1✔
173
                                );
1✔
174
                        }
175
                } finally {
176
                        fclose($resource);
19✔
177
                }
178
        }
179
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc