TYPO3 v15 dev-main snapshot ()

This commit is contained in:
2026-08-10 22:31:31 +02:00
commit 3e43c11539
407 changed files with 51272 additions and 0 deletions
+162
View File
@@ -0,0 +1,162 @@
<?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\Install\Middleware;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;
use TYPO3\CMS\Core\Configuration\ConfigurationManager;
use TYPO3\CMS\Core\FormProtection\FormProtectionFactory;
use TYPO3\CMS\Core\Http\JsonResponse;
use TYPO3\CMS\Install\Controller\InstallerController;
use TYPO3\CMS\Install\Service\EnableFileService;
use TYPO3\CMS\Install\Service\LateBootService;
use TYPO3\CMS\Install\Service\SessionService;
/**
* Middleware to walk through the web installation process of TYPO3
* @internal This class is only meant to be used within EXT:install and is not part of the TYPO3 Core API.
*/
readonly class Installer implements MiddlewareInterface
{
public function __construct(
private LateBootService $lateBootService,
private FormProtectionFactory $formProtectionFactory,
private SessionService $sessionService
) {}
/**
* Handles an Install Tool request when nothing is there
*
* @throws \RuntimeException
*/
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
{
if (!$this->canHandleRequest()) {
return $handler->handle($request);
}
// This is required for icon API, that still has no way to pass
// a request/ normalizedParams to the icon URL generation
$GLOBALS['TYPO3_REQUEST'] = $request;
$container = $this->lateBootService->getContainer();
$backup = $this->lateBootService->makeCurrent($container);
// Lazy load InstallerController, to instantiate the class and the dependencies only if we handle an install request.
$controller = $container->get(InstallerController::class);
$actionName = $request->getParsedBody()['install']['action'] ?? $request->getQueryParams()['install']['action'] ?? 'init';
$action = $actionName . 'Action';
if ($actionName === 'init' || $actionName === 'mainLayout') {
$response = $controller->$action($request);
} elseif ($actionName === 'checkInstallerAvailable') {
$response = new JsonResponse([
'success' => $this->isInstallerAvailable(),
]);
} elseif ($actionName === 'showInstallerNotAvailable') {
$response = $controller->showInstallerNotAvailableAction($request);
} elseif ($actionName === 'checkEnvironmentAndFolders'
|| $actionName === 'showEnvironmentAndFolders'
|| $actionName === 'executeEnvironmentAndFolders'
) {
$this->throwIfInstallerIsNotAvailable();
$response = $controller->$action($request);
} else {
$this->throwIfInstallerIsNotAvailable();
// With main folder layout available, sessions can be handled
$this->sessionService->installSessionHandler($request);
$this->sessionService->startSession();
if ($this->sessionService->isExpired($request)) {
$this->sessionService->refreshSession();
}
$postValues = $request->getParsedBody()['install'] ?? [];
$sessionTokenOk = false;
if (empty($postValues)) {
// No post data is there, no token check necessary
$sessionTokenOk = true;
}
if (isset($postValues['token'])) {
// A token must be given as soon as there is POST data
$formProtection = $this->formProtectionFactory->createFromRequest($request);
if ($actionName === '') {
throw new \RuntimeException('No POST action given for token check', 1505647681);
}
$sessionTokenOk = $formProtection->validateToken($postValues['token'], 'installTool', $actionName);
}
if (!$sessionTokenOk) {
$this->sessionService->resetSession();
$this->sessionService->startSession();
throw new \RuntimeException('Invalid session token', 1505647737);
}
if (!method_exists($controller, $action)) {
// Sanitize action method, preventing injecting whatever method name
throw new \RuntimeException(
'Unknown action method ' . $action . ' in controller InstallerController',
1505687700
);
}
$response = $controller->$action($request);
if ($actionName === 'executeDefaultConfiguration') {
// Executing last step cleans session
$this->sessionService->destroySession($request);
}
}
$this->lateBootService->makeCurrent(null, $backup);
return $response;
}
/**
* First installation is in progress, if system/settings.php does not exist,
* or if FIRST_INSTALL file exists.
*/
protected function canHandleRequest(): bool
{
$localConfigurationFileLocation = (new ConfigurationManager())->getSystemConfigurationFileLocation();
return !@is_file($localConfigurationFileLocation) || EnableFileService::isFirstInstallAllowed();
}
/**
* @throws \RuntimeException If installer is not available due to missing FIRST_INSTALL
*/
protected function throwIfInstallerIsNotAvailable()
{
if (!$this->isInstallerAvailable()) {
throw new \RuntimeException(
'Installer not available',
1505637427
);
}
}
/**
* @return bool TRUE if FIRST_INSTALL file exists
*/
protected function isInstallerAvailable(): bool
{
if (EnableFileService::isFirstInstallAllowed()) {
return true;
}
return false;
}
}