Your IP : 216.73.216.220


Current Path : /var/www/surf/TYPO3/vendor/typo3/cms-filelist/Classes/Controller/
Upload File :
Current File : /var/www/surf/TYPO3/vendor/typo3/cms-filelist/Classes/Controller/FileDownloadController.php

<?php

declare(strict_types=1);

/*
 * This file is part of the TYPO3 CMS project.
 *
 * It is free software; you can redistribute it and/or modify it under
 * the terms of the GNU General Public License, either version 2
 * of the License, or any later version.
 *
 * For the full copyright and license information, please read the
 * LICENSE.txt file that was distributed with this source code.
 *
 * The TYPO3 project - inspiring people to share!
 */

namespace TYPO3\CMS\Filelist\Controller;

use Psr\Http\Message\ResponseFactoryInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Message\StreamFactoryInterface;
use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
use TYPO3\CMS\Core\Context\Context;
use TYPO3\CMS\Core\Resource\FileInterface;
use TYPO3\CMS\Core\Resource\Filter\FileExtensionFilter;
use TYPO3\CMS\Core\Resource\Folder;
use TYPO3\CMS\Core\Resource\ResourceFactory;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Core\Utility\PathUtility;

/**
 * Controller class to create a zip file for given items from a file or folder.
 * @internal This class is a specific Backend controller implementation and is not considered part of the Public TYPO3 API.
 */
class FileDownloadController
{
    protected ResourceFactory $resourceFactory;
    protected ResponseFactoryInterface $responseFactory;
    protected StreamFactoryInterface $streamFactory;
    protected Context $context;

    public function __construct(
        ResourceFactory $resourceFactory,
        ResponseFactoryInterface $responseFactory,
        StreamFactoryInterface $streamFactory,
        Context $context
    ) {
        $this->resourceFactory = $resourceFactory;
        $this->responseFactory = $responseFactory;
        $this->streamFactory = $streamFactory;
        $this->context = $context;
    }

    public function handleRequest(ServerRequestInterface $request): ResponseInterface
    {
        $items = (array)($request->getParsedBody()['items'] ?? []);
        if ($items === []) {
            // Return in case no items are given
            return $this->responseFactory->createResponse(400);
        }

        $fileExtensionFilter = null;
        $fileDownloadConfiguration = (array)($this->getBackendUser()->getTSConfig()['options.']['file_list.']['fileDownload.'] ?? []);
        if ($fileDownloadConfiguration !== []) {
            if (!($fileDownloadConfiguration['enabled'] ?? true)) {
                // Return if file download is disabled
                return $this->responseFactory->createResponse(403);
            }
            // Initialize file extension filter, if configured
            $fileExtensionFilter = GeneralUtility::makeInstance(FileExtensionFilter::class);
            $fileExtensionFilter->setAllowedFileExtensions(
                GeneralUtility::trimExplode(',', (string)($fileDownloadConfiguration['allowedFileExtensions'] ?? ''), true)
            );
            $fileExtensionFilter->setDisallowedFileExtensions(
                GeneralUtility::trimExplode(',', (string)($fileDownloadConfiguration['disallowedFileExtensions'] ?? ''), true)
            );
        }

        $zipStream = tmpfile();
        if (!is_resource($zipStream)) {
            throw new \RuntimeException('Could not open temporary resource for creating archive', 1630346631);
        }
        $zipFileName = stream_get_meta_data($zipStream)['uri'];
        $zipFile = new \ZipArchive();
        $zipFile->open($zipFileName, \ZipArchive::OVERWRITE);
        $filesAdded = 0;
        foreach ($this->collectFiles($items) as $fileName => $fileObject) {
            // Add files with read permission and allowed file extension
            if (!$fileObject->getStorage()->checkFileActionPermission('read', $fileObject)
                || ($fileExtensionFilter !== null && !$fileExtensionFilter->isAllowed($fileObject->getExtension()))
            ) {
                continue;
            }
            $filesAdded++;
            $zipFile->addFile($fileObject->getForLocalProcessing(false), $fileName);
        }
        $zipFile->close();
        $response = $this->createResponse($zipFileName, $filesAdded);
        unlink($zipFileName);
        return $response;
    }

    protected function createResponse(string $temporaryFileName, int $filesAdded): ResponseInterface
    {
        if ($filesAdded === 0) {
            return $this->responseFactory->createResponse()
                ->withHeader('Content-Type', 'application/json; charset=utf-8')
                ->withBody($this->streamFactory->createStream(json_encode(['success' => false, 'status' => 'noFiles'])));
        }

        $downloadFileName = 'typo3_download_' . $this->context->getAspect('date')->getDateTime()->format('Y-m-d-His') . '.zip';
        return $this->responseFactory->createResponse()
            ->withHeader('Content-Type', 'application/zip')
            ->withHeader('Content-Disposition', 'attachment; filename=' . $downloadFileName)
            ->withHeader('Content-Transfer-Encoding', 'binary')
            ->withHeader('Pragma', 'no-cache')
            ->withHeader('Cache-Control', 'public, must-revalidate')
            ->withBody($this->streamFactory->createStreamFromFile($temporaryFileName));
    }

    /**
     * @return FileInterface[]
     */
    protected function collectFiles(array $items): array
    {
        $files = [];
        foreach ($items as $itemIdentifier) {
            $fileOrFolderObject = $this->resourceFactory->retrieveFileOrFolderObject($itemIdentifier);
            if ($fileOrFolderObject === null) {
                continue;
            }
            $baseIdentifier = dirname($fileOrFolderObject->getIdentifier());
            if ($fileOrFolderObject instanceof Folder) {
                // handle file / folder structure
                foreach ($this->getFilesAndFoldersRecursive($fileOrFolderObject) as $fileObject) {
                    $commonPrefix = (string)PathUtility::getCommonPrefix([$baseIdentifier, $fileObject->getIdentifier()]);
                    $files[substr($fileObject->getIdentifier(), strlen($commonPrefix))] = $fileObject;
                }
            } else {
                $files[$fileOrFolderObject->getName()] = $fileOrFolderObject;
            }
        }
        return $files;
    }

    protected function getFilesAndFoldersRecursive(Folder $folder): iterable
    {
        foreach ($folder->getSubfolders() as $subFolder) {
            yield from $this->getFilesAndFoldersRecursive($subFolder);
        }
        foreach ($folder->getFiles() as $file) {
            yield $file;
        }
    }

    protected function getBackendUser(): BackendUserAuthentication
    {
        return $GLOBALS['BE_USER'];
    }
}