Completed
Push — master ( 5aea1b...72a482 )
by
unknown
14:03
created

FileDumpController::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 1
dl 0
loc 3
rs 10
c 0
b 0
f 0
1
<?php
2
declare(strict_types = 1);
3
namespace TYPO3\CMS\Core\Controller;
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
use Psr\Http\Message\ResponseInterface;
19
use Psr\Http\Message\ServerRequestInterface;
20
use TYPO3\CMS\Core\Http\Response;
21
use TYPO3\CMS\Core\Imaging\ImageManipulation\CropVariantCollection;
22
use TYPO3\CMS\Core\Resource\Exception\FileDoesNotExistException;
23
use TYPO3\CMS\Core\Resource\File;
24
use TYPO3\CMS\Core\Resource\FileReference;
25
use TYPO3\CMS\Core\Resource\Hook\FileDumpEIDHookInterface;
26
use TYPO3\CMS\Core\Resource\ProcessedFile;
27
use TYPO3\CMS\Core\Resource\ProcessedFileRepository;
28
use TYPO3\CMS\Core\Resource\ResourceFactory;
29
use TYPO3\CMS\Core\Utility\GeneralUtility;
30
31
/**
32
 * Class FileDumpController
33
 */
34
class FileDumpController
35
{
36
    /**
37
     * @var ResourceFactory
38
     */
39
    protected $resourceFactory;
40
41
    public function __construct(ResourceFactory $resourceFactory)
42
    {
43
        $this->resourceFactory = $resourceFactory;
44
    }
45
46
    /**
47
     * Main method to dump a file
48
     *
49
     * @param ServerRequestInterface $request
50
     * @return ResponseInterface
51
     * @throws \InvalidArgumentException
52
     * @throws \RuntimeException
53
     * @throws FileDoesNotExistException
54
     * @throws \UnexpectedValueException
55
     */
56
    public function dumpAction(ServerRequestInterface $request): ResponseInterface
57
    {
58
        $parameters = $this->buildParametersFromRequest($request);
59
60
        if (!$this->isTokenValid($parameters, $request)) {
61
            return (new Response)->withStatus(403);
62
        }
63
        $file = $this->createFileObjectByParameters($parameters);
64
        if ($file === null) {
65
            return (new Response)->withStatus(404);
66
        }
67
68
        // Hook: allow some other process to do some security/access checks. Hook should return 403 response if access is rejected, void otherwise
69
        foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['FileDumpEID.php']['checkFileAccess'] ?? [] as $className) {
70
            $hookObject = GeneralUtility::makeInstance($className);
71
            if (!$hookObject instanceof FileDumpEIDHookInterface) {
72
                throw new \UnexpectedValueException($className . ' must implement interface ' . FileDumpEIDHookInterface::class, 1394442417);
73
            }
74
            $response = $hookObject->checkFileAccess($file);
75
            if ($response instanceof ResponseInterface) {
76
                return $response;
77
            }
78
        }
79
80
        // Apply cropping, if possible
81
        if (!$file instanceof ProcessedFile) {
82
            $cropVariant = $parameters['cv'] ?: 'default';
83
            $cropString = $file instanceof FileReference ? $file->getProperty('crop') : '';
84
            $cropArea = CropVariantCollection::create((string)$cropString)->getCropArea($cropVariant);
85
            $processingInstructions = [
86
                'crop' => $cropArea->isEmpty() ? null : $cropArea->makeAbsoluteBasedOnFile($file),
87
            ];
88
89
            // Apply width/height, if given
90
            if (!empty($parameters['s'])) {
91
                $size = GeneralUtility::trimExplode(':', $parameters['s']);
92
                $processingInstructions = array_merge(
93
                    $processingInstructions,
94
                    [
95
                        'width' => $size[0] ?? null,
96
                        'height' => $size[1] ?? null,
97
                        'minWidth' => $size[2] ? (int)$size[2] : null,
98
                        'minHeight' => $size[3] ? (int)$size[3] : null,
99
                        'maxWidth' => $size[4] ? (int)$size[4] : null,
100
                        'maxHeight' => $size[5] ? (int)$size[5] : null
101
                    ]
102
                );
103
            }
104
            if (is_callable([$file, 'getOriginalFile'])) {
105
                // Get the original file from the file reference
106
                $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

106
                /** @scrutinizer ignore-call */ 
107
                $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...
107
            }
108
            $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\FileReference. ( Ignorable by Annotation )

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

108
            /** @scrutinizer ignore-call */ 
109
            $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\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

108
            /** @scrutinizer ignore-call */ 
109
            $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...
109
        }
110
111
        return $file->getStorage()->streamFile($file);
112
    }
113
114
    protected function buildParametersFromRequest(ServerRequestInterface $request): array
115
    {
116
        $parameters = ['eID' => 'dumpFile'];
117
        $queryParams = $request->getQueryParams();
118
        // Identifier of what to process. f, r or p
119
        // Only needed while hash_equals
120
        $t = (string)($queryParams['t'] ?? '');
121
        if ($t) {
122
            $parameters['t'] = $t;
123
        }
124
        // sys_file
125
        $f = (string)($queryParams['f'] ?? '');
126
        if ($f) {
127
            $parameters['f'] = (int)$f;
128
        }
129
        // sys_file_reference
130
        $r = (string)($queryParams['r'] ?? '');
131
        if ($r) {
132
            $parameters['r'] = (int)$r;
133
        }
134
        // Processed file
135
        $p = (string)($queryParams['p'] ?? '');
136
        if ($p) {
137
            $parameters['p'] = (int)$p;
138
        }
139
        // File's width and height in this order: w:h:minW:minH:maxW:maxH
140
        $s = (string)($queryParams['s'] ?? '');
141
        if ($s) {
142
            $parameters['s'] = $s;
143
        }
144
        // File's crop variant
145
        $v = (string)($queryParams['cv'] ?? '');
146
        if ($v) {
147
            $parameters['cv'] = (string)$v;
148
        }
149
150
        return $parameters;
151
    }
152
153
    protected function isTokenValid(array $parameters, ServerRequestInterface $request): bool
154
    {
155
        return hash_equals(
156
            GeneralUtility::hmac(implode('|', $parameters), 'resourceStorageDumpFile'),
157
            $request->getQueryParams()['token'] ?? ''
158
        );
159
    }
160
161
    /**
162
     * @param array $parameters
163
     * @return File|FileReference|ProcessedFile|null
164
     */
165
    protected function createFileObjectByParameters(array $parameters)
166
    {
167
        $file = null;
168
        if (isset($parameters['f'])) {
169
            try {
170
                $file = $this->resourceFactory->getFileObject($parameters['f']);
171
                if ($file->isDeleted() || $file->isMissing()) {
172
                    $file = null;
173
                }
174
            } catch (\Exception $e) {
175
                $file = null;
176
            }
177
        } elseif (isset($parameters['r'])) {
178
            try {
179
                $file = $this->resourceFactory->getFileReferenceObject($parameters['r']);
180
                if ($file->isMissing()) {
181
                    $file = null;
182
                }
183
            } catch (\Exception $e) {
184
                $file = null;
185
            }
186
        } elseif (isset($parameters['p'])) {
187
            try {
188
                $processedFileRepository = GeneralUtility::makeInstance(ProcessedFileRepository::class);
189
                /** @var ProcessedFile|null $file */
190
                $file = $processedFileRepository->findByUid($parameters['p']);
191
                if (!$file || $file->isDeleted()) {
192
                    $file = null;
193
                }
194
            } catch (\Exception $e) {
195
                $file = null;
196
            }
197
        }
198
        return $file;
199
    }
200
}
201