Skip to content
Snippets Groups Projects
FileRefsContentShow.php 4.05 KiB
Newer Older
<?php

namespace JsonApi\Routes\Files;

use JsonApi\Errors\AuthorizationFailedException;
use JsonApi\Errors\InternalServerError;
use JsonApi\Errors\RecordNotFoundException;
use JsonApi\NonJsonApiController;
use GuzzleHttp\Psr7;
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;

class FileRefsContentShow extends NonJsonApiController
{
    use EtagHelperTrait;

    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')
                );
            }

            list($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 = Psr7\stream_for(fopen($pathFile, 'rb'));

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