‪TYPO3CMS  9.5
ErrorController.php
Go to the documentation of this file.
1 <?php
2 declare(strict_types = 1);
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;
29 
35 {
46  public function ‪unavailableAction(ServerRequestInterface $request, string $message, array $reasons = []): ResponseInterface
47  {
49  throw new ‪ServiceUnavailableException($message, 1518472181);
50  }
51  $errorHandler = $this->‪getErrorHandlerFromSite($request, 500);
52  if ($errorHandler instanceof ‪PageErrorHandlerInterface) {
53  return $errorHandler->handlePageError($request, $message, $reasons);
54  }
55  return $this->‪handlePageError(
56  ‪$GLOBALS['TYPO3_CONF_VARS']['FE']['pageUnavailable_handling'],
57  ‪$GLOBALS['TYPO3_CONF_VARS']['FE']['pageUnavailable_handling_statheader'],
58  $message,
59  $reasons
60  );
61  }
62 
73  public function ‪pageNotFoundAction(ServerRequestInterface $request, string $message, array $reasons = []): ResponseInterface
74  {
75  $errorHandler = $this->‪getErrorHandlerFromSite($request, 404);
76  if ($errorHandler instanceof ‪PageErrorHandlerInterface) {
77  return $errorHandler->handlePageError($request, $message, $reasons);
78  }
79  if (!‪$GLOBALS['TYPO3_CONF_VARS']['FE']['pageNotFound_handling']) {
80  throw new ‪PageNotFoundException($message, 1518472189);
81  }
82  return $this->‪handlePageError(
83  ‪$GLOBALS['TYPO3_CONF_VARS']['FE']['pageNotFound_handling'],
84  ‪$GLOBALS['TYPO3_CONF_VARS']['FE']['pageNotFound_handling_statheader'],
85  $message,
86  $reasons
87  );
88  }
89 
99  public function ‪accessDeniedAction(ServerRequestInterface $request, string $message, array $reasons = []): ResponseInterface
100  {
101  $errorHandler = $this->‪getErrorHandlerFromSite($request, 403);
102  if ($errorHandler instanceof ‪PageErrorHandlerInterface) {
103  return $errorHandler->handlePageError($request, $message, $reasons);
104  }
105  return $this->‪handlePageError(
106  ‪$GLOBALS['TYPO3_CONF_VARS']['FE']['pageNotFound_handling'],
107  ‪$GLOBALS['TYPO3_CONF_VARS']['FE']['pageNotFound_handling_accessdeniedheader'],
108  $message,
109  $reasons
110  );
111  }
112 
119  protected function ‪isPageUnavailableHandlerConfigured(): bool
120  {
121  return
122  ‪$GLOBALS['TYPO3_CONF_VARS']['FE']['pageUnavailable_handling']
123  && !GeneralUtility::cmpIP(
124  GeneralUtility::getIndpEnv('REMOTE_ADDR'),
125  ‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['devIPmask']
126  )
127  ;
128  }
129 
140  protected function ‪handlePageError($errorHandler, string $header = '', string $reason = '', array $pageAccessFailureReasons = []): ResponseInterface
141  {
142  $response = null;
143  $content = '';
144  // Simply boolean; Just shows TYPO3 error page with reason:
145  if (is_bool($errorHandler) || strtolower($errorHandler) === 'true' || (string)$errorHandler === '1') {
146  $content = GeneralUtility::makeInstance(ErrorPageController::class)->errorAction(
147  'Page Not Found',
148  'The page did not exist or was inaccessible.' . ($reason ? ' Reason: ' . $reason : '')
149  );
150  } elseif (GeneralUtility::isFirstPartOfStr($errorHandler, 'USER_FUNCTION:')) {
151  $funcRef = trim(substr($errorHandler, 14));
152  $params = [
153  'currentUrl' => GeneralUtility::getIndpEnv('REQUEST_URI'),
154  'reasonText' => $reason,
155  'pageAccessFailureReasons' => $pageAccessFailureReasons
156  ];
157  try {
158  $content = GeneralUtility::callUserFunction($funcRef, $params, $this);
159  } catch (\‪Exception $e) {
160  throw new \RuntimeException('Error: 404 page by USER_FUNCTION "' . $funcRef . '" failed.', 1518472235, $e);
161  }
162  } elseif (GeneralUtility::isFirstPartOfStr($errorHandler, 'READFILE:')) {
163  $readFile = GeneralUtility::getFileAbsFileName(trim(substr($errorHandler, 9)));
164  if (@is_file($readFile)) {
165  $content = str_replace(
166  [
167  '###CURRENT_URL###',
168  '###REASON###'
169  ],
170  [
171  GeneralUtility::getIndpEnv('REQUEST_URI'),
172  htmlspecialchars($reason)
173  ],
174  file_get_contents($readFile)
175  );
176  } else {
177  throw new \RuntimeException('Configuration Error: 404 page "' . $readFile . '" could not be found.', 1518472245);
178  }
179  } elseif (GeneralUtility::isFirstPartOfStr($errorHandler, 'REDIRECT:')) {
180  $response = new ‪RedirectResponse(substr($errorHandler, 9));
181  } elseif ($errorHandler !== '') {
182  // Check if URL is relative
183  $urlParts = parse_url($errorHandler);
184  // parse_url could return an array without the key "host", the empty check works better than strict check
185  if (empty($urlParts['host'])) {
186  $urlParts['host'] = GeneralUtility::getIndpEnv('HTTP_HOST');
187  if ($errorHandler[0] === '/') {
188  $errorHandler = GeneralUtility::getIndpEnv('TYPO3_REQUEST_HOST') . $errorHandler;
189  } else {
190  $errorHandler = GeneralUtility::getIndpEnv('TYPO3_REQUEST_DIR') . $errorHandler;
191  }
192  $checkBaseTag = false;
193  } else {
194  $checkBaseTag = true;
195  }
196  // Check recursion
197  if ($errorHandler === GeneralUtility::getIndpEnv('TYPO3_REQUEST_URL')) {
198  $reason = $reason ?: 'Page cannot be found.';
199  $reason .= LF . LF . 'Additionally, ' . $errorHandler . ' was not found while trying to retrieve the error document.';
200  throw new \RuntimeException(nl2br(htmlspecialchars($reason)), 1518472252);
201  }
202  // Prepare headers
203  $requestHeaders = [
204  'User-agent' => GeneralUtility::getIndpEnv('HTTP_USER_AGENT'),
205  'Referer' => GeneralUtility::getIndpEnv('TYPO3_REQUEST_URL')
206  ];
207  $report = [];
208  $res = GeneralUtility::getUrl($errorHandler, 1, $requestHeaders, $report);
209  if ((int)$report['error'] !== 0 && (int)$report['error'] !== 200) {
210  throw new \RuntimeException('Failed to fetch error page "' . $errorHandler . '", reason: ' . $report['message'], 1518472257);
211  }
212  if ($res === false) {
213  // Last chance -- redirect
214  $response = new ‪RedirectResponse($errorHandler);
215  } else {
216  // Header and content are separated by an empty line
217  list($returnedHeaders, $content) = explode(CRLF . CRLF, $res, 2);
218  $content .= CRLF;
219  // Forward these response headers to the client
220  $forwardHeaders = [
221  'Content-Type:'
222  ];
223  $headerArr = preg_split('/\\r|\\n/', $returnedHeaders, -1, PREG_SPLIT_NO_EMPTY);
224  foreach ($headerArr as $headerLine) {
225  foreach ($forwardHeaders as $h) {
226  if (preg_match('/^' . $h . '/', $headerLine)) {
227  $header .= CRLF . $headerLine;
228  }
229  }
230  }
231  // Put <base> if necessary
232  if ($checkBaseTag) {
233  // If content already has <base> tag, we do not need to do anything
234  if (false === stristr($content, '<base ')) {
235  // Generate href for base tag
236  $base = $urlParts['scheme'] . '://';
237  if ($urlParts['user'] != '') {
238  $base .= $urlParts['user'];
239  if ($urlParts['pass'] != '') {
240  $base .= ':' . $urlParts['pass'];
241  }
242  $base .= '@';
243  }
244  $base .= $urlParts['host'];
245  // Add path portion skipping possible file name
246  $base .= preg_replace('/(.*\\/)[^\\/]*/', '${1}', $urlParts['path']);
247  // Put it into content (generate also <head> if necessary)
248  $replacement = LF . '<base href="' . htmlentities($base) . '" />' . LF;
249  if (stristr($content, '<head>')) {
250  $content = preg_replace('/(<head>)/i', '\\1' . $replacement, $content);
251  } else {
252  $content = preg_replace('/(<html[^>]*>)/i', '\\1<head>' . $replacement . '</head>', $content);
253  }
254  }
255  }
256  }
257  } else {
258  $content = GeneralUtility::makeInstance(ErrorPageController::class)->errorAction(
259  'Page Not Found',
260  $reason ? 'Reason: ' . $reason : 'Page cannot be found.'
261  );
262  }
263 
264  if (!$response) {
265  $response = new ‪HtmlResponse($content);
266  }
267  return $this->‪applySanitizedHeadersToResponse($response, $header);
268  }
269 
278  protected function ‪applySanitizedHeadersToResponse(ResponseInterface $response, string $headers): ResponseInterface
279  {
280  if (!empty($headers)) {
281  $headerArr = preg_split('/\\r|\\n/', $headers, -1, PREG_SPLIT_NO_EMPTY);
282  foreach ($headerArr as $headerLine) {
283  if (strpos($headerLine, 'HTTP/') === 0 && strpos($headerLine, ':') === false) {
284  list($protocolVersion, $statusCode, $reasonPhrase) = explode(' ', $headerLine, 3);
285  list(, $protocolVersion) = explode('/', $protocolVersion, 2);
286  $response = $response
287  ->withProtocolVersion((int)$protocolVersion)
288  ->withStatus($statusCode, $reasonPhrase);
289  } else {
290  list($headerName, $value) = GeneralUtility::trimExplode(':', $headerLine, 2);
291  $response = $response->withHeader($headerName, $value);
292  }
293  }
294  }
295  return $response;
296  }
297 
305  protected function ‪getErrorHandlerFromSite(ServerRequestInterface $request, int $statusCode): ?‪PageErrorHandlerInterface
306  {
307  $site = $request->getAttribute('site');
308  if ($site instanceof ‪Site) {
309  try {
310  return $site->getErrorHandler($statusCode);
312  // No error handler found, so fallback back to the generic TYPO3 error handler.
313  }
314  }
315  return null;
316  }
317 }
‪TYPO3\CMS\Frontend\Controller\ErrorController\applySanitizedHeadersToResponse
‪ResponseInterface applySanitizedHeadersToResponse(ResponseInterface $response, string $headers)
Definition: ErrorController.php:278
‪TYPO3\CMS\Frontend\Controller\ErrorController\handlePageError
‪ResponseInterface handlePageError($errorHandler, string $header='', string $reason='', array $pageAccessFailureReasons=[])
Definition: ErrorController.php:140
‪TYPO3\CMS\Core\Controller\ErrorPageController
Definition: ErrorPageController.php:31
‪TYPO3\CMS\Frontend\Controller\ErrorController\unavailableAction
‪ResponseInterface unavailableAction(ServerRequestInterface $request, string $message, array $reasons=[])
Definition: ErrorController.php:46
‪TYPO3\CMS\Core\Exception
Definition: Exception.php:21
‪TYPO3\CMS\Core\Error\PageErrorHandler\PageErrorHandlerNotConfiguredException
Definition: PageErrorHandlerNotConfiguredException.php:26
‪TYPO3\CMS\Frontend\Controller\ErrorController\accessDeniedAction
‪ResponseInterface accessDeniedAction(ServerRequestInterface $request, string $message, array $reasons=[])
Definition: ErrorController.php:99
‪TYPO3\CMS\Frontend\Controller\ErrorController
Definition: ErrorController.php:35
‪TYPO3\CMS\Core\Site\Entity\Site
Definition: Site.php:39
‪TYPO3\CMS\Core\Error\Http\PageNotFoundException
Definition: PageNotFoundException.php:21
‪TYPO3\CMS\Core\Error\Http\ServiceUnavailableException
Definition: ServiceUnavailableException.php:21
‪TYPO3\CMS\Core\Http\RedirectResponse
Definition: RedirectResponse.php:27
‪TYPO3\CMS\Core\Error\PageErrorHandler\PageErrorHandlerInterface
Definition: PageErrorHandlerInterface.php:28
‪TYPO3\CMS\Frontend\Controller\ErrorController\isPageUnavailableHandlerConfigured
‪bool isPageUnavailableHandlerConfigured()
Definition: ErrorController.php:119
‪$GLOBALS
‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['adminpanel']['modules']
Definition: ext_localconf.php:5
‪TYPO3\CMS\Frontend\Controller
Definition: ErrorController.php:3
‪TYPO3\CMS\Frontend\Controller\ErrorController\getErrorHandlerFromSite
‪PageErrorHandlerInterface null getErrorHandlerFromSite(ServerRequestInterface $request, int $statusCode)
Definition: ErrorController.php:305
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:45
‪TYPO3\CMS\Frontend\Controller\ErrorController\pageNotFoundAction
‪ResponseInterface pageNotFoundAction(ServerRequestInterface $request, string $message, array $reasons=[])
Definition: ErrorController.php:73
‪TYPO3\CMS\Core\Http\HtmlResponse
Definition: HtmlResponse.php:25