aboutsummaryrefslogtreecommitdiff
path: root/lib/classes/JsonApi/Routes/Files/FileRefsContentShow.php
blob: 6cb6ef3a1318d25e22ab441b383144538b135449 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
<?php

namespace JsonApi\Routes\Files;

use JsonApi\Errors\AuthorizationFailedException;
use JsonApi\Errors\InternalServerError;
use JsonApi\Errors\RecordNotFoundException;
use JsonApi\NonJsonApiController;
use Psr\Container\ContainerInterface;
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
use Psr\Http\Message\StreamFactoryInterface;

class FileRefsContentShow extends NonJsonApiController
{
    use EtagHelperTrait;

    public function __construct(
        ContainerInterface $container,
        private StreamFactoryInterface $streamFactory
    ) {
        parent::__construct($container);
    }

    public function invoke(Request $request, Response $response, array $args): Response
    {
        if (!$fileRef = \FileRef::find($args['id'])) {
            throw new RecordNotFoundException();
        }

        if (!Authority::canDownloadFileRef($this->getUser($request), $fileRef)) {
            throw new AuthorizationFailedException();
        }

        return $this->sendFile($request, $response, $fileRef);
    }

    /**
     * Copied and slightly edited for easy merging of future changes
     * to sendfile.php.
     *
     * @SuppressWarnings(CyclomaticComplexity)
     * @SuppressWarnings(NPathComplexity)
     */
    private function sendFile(Request $request, Response $response, \FileRef $fileRef)
    {
        //replace bad charakters to avoid problems when saving the file
        $fileName = \FileManager::cleanFileName($fileRef->name);
        $filetype = $fileRef->getFileType();
        $pathFile = $filetype->getPath() ?: $filetype->getDownloadURL();
        $contentType = $fileRef->mime_type ?: get_mime_type($fileName);

        // check if linked file is obtainable
        if ('proxy' == $fileRef->file->metadata['access_type']) {
            $linkData = \FileManager::fetchURLMetadata($fileRef->file->metadata['url']);
            if (200 != $linkData['response_code']) {
                throw new InternalServerError(
                    _('Diese Datei wird von einem externen Server geladen und ist dort momentan nicht erreichbar!')
                );
            }
            $contentType = $linkData['Content-Type']
                         ? strstr($linkData['Content-Type'], ';', true)
                         : get_mime_type($fileName);

            $filesize = $linkData['Content-Length'] ?: false;
        }

        if ($filetype->getPath()) {
            $filesize = @filesize($pathFile);
            if (false === $filesize) {
                throw new InternalServerError(
                    _('Fehler beim Laden der Inhalte der Datei')
                );
            }

            [$done, $response] = $this->handleEtag($request, $response, $fileRef);
            if ($done) {
                return $response;
            }
        }

        if ('redirect' == $fileRef->file->metadata['access_type']) {
            return $response->withRedirect($fileRef->file->metadata['url']);
        }

        $contentBlacklisted = function ($mime) {
            foreach (['html', 'javascript', 'svg', 'xml'] as $check) {
                if (false !== stripos($mime, $check)) {
                    return true;
                }
            }

            return false;
        };
        if ($contentBlacklisted($contentType)) {
            $contentType = 'application/octet-stream';
        }

        $headers = [
            'Content-Type' => $contentType,
            'Content-Disposition' => 'attachment; '.encode_header_parameter('filename', $fileName),
        ];

        if ($filesize) {
            $headers['Content-Length'] = $filesize;
        }

        $isHttps = 'https' === $request->getUri()->getScheme();
        $headers['Cache-Control'] = $isHttps
                                  ? 'private'
                                  : 'no-cache, no-store, must-revalidate';

        \Metrics::increment('core.file_download');

        foreach ($headers as $key => $value) {
            if ($response->hasHeader($key)) {
                $response = $response->withAddedHeader($key, $value);
            } else {
                $response = $response->withHeader($key, $value);
            }
        }

        $fileRef->incrementDownloadCounter();

        $stream = $this->streamFactory->createStreamFromFile($pathFile, 'rb');

        return $response->withBody($stream);
    }
}