Completed
Push — master ( a486fe...b5b395 )
by
unknown
14:37
created

FileDumpController::dumpAction()   C

Complexity

Conditions 16
Paths 38

Size

Total Lines 56
Code Lines 33

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 16
eloc 33
nc 38
nop 1
dl 0
loc 56
rs 5.5666
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * This file is part of the TYPO3 CMS project.
7
 *
8
 * It is free software; you can redistribute it and/or modify it under
9
 * the terms of the GNU General Public License, either version 2
10
 * of the License, or any later version.
11
 *
12
 * For the full copyright and license information, please read the
13
 * LICENSE.txt file that was distributed with this source code.
14
 *
15
 * The TYPO3 project - inspiring people to share!
16
 */
17
18
namespace TYPO3\CMS\Core\Controller;
19
20
use Psr\Http\Message\ResponseInterface;
21
use Psr\Http\Message\ServerRequestInterface;
22
use TYPO3\CMS\Core\Http\Response;
23
use TYPO3\CMS\Core\Imaging\ImageManipulation\CropVariantCollection;
24
use TYPO3\CMS\Core\Resource\Exception\FileDoesNotExistException;
25
use TYPO3\CMS\Core\Resource\File;
26
use TYPO3\CMS\Core\Resource\FileInterface;
27
use TYPO3\CMS\Core\Resource\FileReference;
28
use TYPO3\CMS\Core\Resource\Hook\FileDumpEIDHookInterface;
29
use TYPO3\CMS\Core\Resource\ProcessedFile;
30
use TYPO3\CMS\Core\Resource\ProcessedFileRepository;
31
use TYPO3\CMS\Core\Resource\ResourceFactory;
32
use TYPO3\CMS\Core\Resource\Security\FileNameValidator;
33
use TYPO3\CMS\Core\Utility\GeneralUtility;
34
35
/**
36
 * Class FileDumpController
37
 */
38
class FileDumpController
39
{
40
    /**
41
     * @var ResourceFactory
42
     */
43
    protected $resourceFactory;
44
45
    public function __construct(ResourceFactory $resourceFactory)
46
    {
47
        $this->resourceFactory = $resourceFactory;
48
    }
49
50
    /**
51
     * Main method to dump a file
52
     *
53
     * @param ServerRequestInterface $request
54
     * @return ResponseInterface
55
     * @throws \InvalidArgumentException
56
     * @throws \RuntimeException
57
     * @throws FileDoesNotExistException
58
     * @throws \UnexpectedValueException
59
     */
60
    public function dumpAction(ServerRequestInterface $request): ResponseInterface
61
    {
62
        $parameters = $this->buildParametersFromRequest($request);
63
64
        if (!$this->isTokenValid($parameters, $request)) {
65
            return (new Response())->withStatus(403);
66
        }
67
        $file = $this->createFileObjectByParameters($parameters);
68
        if ($file === null) {
69
            return (new Response())->withStatus(404);
70
        }
71
72
        // Hook: allow some other process to do some security/access checks. Hook should return 403 response if access is rejected, void otherwise
73
        foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['FileDumpEID.php']['checkFileAccess'] ?? [] as $className) {
74
            $hookObject = GeneralUtility::makeInstance($className);
75
            if (!$hookObject instanceof FileDumpEIDHookInterface) {
76
                throw new \UnexpectedValueException($className . ' must implement interface ' . FileDumpEIDHookInterface::class, 1394442417);
77
            }
78
            $response = $hookObject->checkFileAccess($file);
79
            if ($response instanceof ResponseInterface) {
80
                return $response;
81
            }
82
        }
83
84
        // Apply cropping, if possible
85
        if (!$file instanceof ProcessedFile) {
86
            $cropVariant = $parameters['cv'] ?: 'default';
87
            $cropString = $file instanceof FileReference ? $file->getProperty('crop') : '';
88
            $cropArea = CropVariantCollection::create((string)$cropString)->getCropArea($cropVariant);
89
            $processingInstructions = [
90
                'crop' => $cropArea->isEmpty() ? null : $cropArea->makeAbsoluteBasedOnFile($file),
91
            ];
92
93
            // Apply width/height, if given
94
            if (!empty($parameters['s'])) {
95
                $size = GeneralUtility::trimExplode(':', $parameters['s']);
96
                $processingInstructions = array_merge(
97
                    $processingInstructions,
98
                    [
99
                        'width' => $size[0] ?? null,
100
                        'height' => $size[1] ?? null,
101
                        'minWidth' => $size[2] ? (int)$size[2] : null,
102
                        'minHeight' => $size[3] ? (int)$size[3] : null,
103
                        'maxWidth' => $size[4] ? (int)$size[4] : null,
104
                        'maxHeight' => $size[5] ? (int)$size[5] : null
105
                    ]
106
                );
107
            }
108
            if (is_callable([$file, 'getOriginalFile'])) {
109
                // Get the original file from the file reference
110
                $file = $file->getOriginalFile();
0 ignored issues
show
Bug introduced by
The method getOriginalFile() does not exist on TYPO3\CMS\Core\Resource\File. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

110
                /** @scrutinizer ignore-call */ 
111
                $file = $file->getOriginalFile();

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
111
            }
112
            $file = $file->process(ProcessedFile::CONTEXT_IMAGECROPSCALEMASK, $processingInstructions);
0 ignored issues
show
Bug introduced by
The method process() does not exist on TYPO3\CMS\Core\Resource\ProcessedFile. Did you maybe mean needsReprocessing()? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

112
            /** @scrutinizer ignore-call */ 
113
            $file = $file->process(ProcessedFile::CONTEXT_IMAGECROPSCALEMASK, $processingInstructions);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
Bug introduced by
The method process() does not exist on TYPO3\CMS\Core\Resource\FileReference. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

112
            /** @scrutinizer ignore-call */ 
113
            $file = $file->process(ProcessedFile::CONTEXT_IMAGECROPSCALEMASK, $processingInstructions);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
113
        }
114
115
        return $file->getStorage()->streamFile($file);
116
    }
117
118
    protected function buildParametersFromRequest(ServerRequestInterface $request): array
119
    {
120
        $parameters = ['eID' => 'dumpFile'];
121
        $queryParams = $request->getQueryParams();
122
        // Identifier of what to process. f, r or p
123
        // Only needed while hash_equals
124
        $t = (string)($queryParams['t'] ?? '');
125
        if ($t) {
126
            $parameters['t'] = $t;
127
        }
128
        // sys_file
129
        $f = (string)($queryParams['f'] ?? '');
130
        if ($f) {
131
            $parameters['f'] = (int)$f;
132
        }
133
        // sys_file_reference
134
        $r = (string)($queryParams['r'] ?? '');
135
        if ($r) {
136
            $parameters['r'] = (int)$r;
137
        }
138
        // Processed file
139
        $p = (string)($queryParams['p'] ?? '');
140
        if ($p) {
141
            $parameters['p'] = (int)$p;
142
        }
143
        // File's width and height in this order: w:h:minW:minH:maxW:maxH
144
        $s = (string)($queryParams['s'] ?? '');
145
        if ($s) {
146
            $parameters['s'] = $s;
147
        }
148
        // File's crop variant
149
        $v = (string)($queryParams['cv'] ?? '');
150
        if ($v) {
151
            $parameters['cv'] = (string)$v;
152
        }
153
154
        return $parameters;
155
    }
156
157
    protected function isTokenValid(array $parameters, ServerRequestInterface $request): bool
158
    {
159
        return hash_equals(
160
            GeneralUtility::hmac(implode('|', $parameters), 'resourceStorageDumpFile'),
161
            $request->getQueryParams()['token'] ?? ''
162
        );
163
    }
164
165
    /**
166
     * @param array $parameters
167
     * @return File|FileReference|ProcessedFile|null
168
     */
169
    protected function createFileObjectByParameters(array $parameters)
170
    {
171
        $file = null;
172
        if (isset($parameters['f'])) {
173
            try {
174
                $file = $this->resourceFactory->getFileObject($parameters['f']);
175
                if ($file->isDeleted() || $file->isMissing()) {
176
                    $file = null;
177
                }
178
                if (!$this->isFileValid($file)) {
0 ignored issues
show
Bug introduced by
It seems like $file can also be of type null; however, parameter $file of TYPO3\CMS\Core\Controlle...ntroller::isFileValid() does only seem to accept TYPO3\CMS\Core\Resource\FileInterface, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

178
                if (!$this->isFileValid(/** @scrutinizer ignore-type */ $file)) {
Loading history...
179
                    $file = null;
180
                }
181
            } catch (\Exception $e) {
182
                $file = null;
183
            }
184
        } elseif (isset($parameters['r'])) {
185
            try {
186
                $file = $this->resourceFactory->getFileReferenceObject($parameters['r']);
187
                if ($file->isMissing()) {
188
                    $file = null;
189
                }
190
                if (!$this->isFileValid($file->getOriginalFile())) {
191
                    $file = null;
192
                }
193
            } catch (\Exception $e) {
194
                $file = null;
195
            }
196
        } elseif (isset($parameters['p'])) {
197
            try {
198
                $processedFileRepository = GeneralUtility::makeInstance(ProcessedFileRepository::class);
199
                /** @var ProcessedFile|null $file */
200
                $file = $processedFileRepository->findByUid($parameters['p']);
201
                if (!$file || $file->isDeleted()) {
202
                    $file = null;
203
                }
204
                if (!$this->isFileValid($file->getOriginalFile())) {
205
                    $file = null;
206
                }
207
            } catch (\Exception $e) {
208
                $file = null;
209
            }
210
        }
211
        return $file;
212
    }
213
214
    protected function isFileValid(FileInterface $file): bool
215
    {
216
        return $file->getStorage()->getDriverType() !== 'Local'
217
            || GeneralUtility::makeInstance(FileNameValidator::class)
218
                ->isValid(basename($file->getIdentifier()));
219
    }
220
}
221