1<?php
2namespace TYPO3\CMS\Core\Error;
3
4/*
5 * This file is part of the TYPO3 CMS project.
6 *
7 * It is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU General Public License, either version 2
9 * of the License, or any later version.
10 *
11 * For the full copyright and license information, please read the
12 * LICENSE.txt file that was distributed with this source code.
13 *
14 * The TYPO3 project - inspiring people to share!
15 */
16use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
17use TYPO3\CMS\Core\Controller\ErrorPageController;
18use TYPO3\CMS\Core\Messaging\AbstractMessage;
19use TYPO3\CMS\Core\Utility\GeneralUtility;
20
21/**
22 * An exception handler which catches any exception and
23 * renders an error page without backtrace (Web) or a slim
24 * message on CLI.
25 */
26class ProductionExceptionHandler extends AbstractExceptionHandler
27{
28    /**
29     * Default title for error messages
30     *
31     * @var string
32     */
33    protected $defaultTitle = 'Oops, an error occurred!';
34
35    /**
36     * Default message for error messages
37     *
38     * @var string
39     */
40    protected $defaultMessage = '';
41
42    /**
43     * Constructs this exception handler - registers itself as the default exception handler.
44     */
45    public function __construct()
46    {
47        set_exception_handler([$this, 'handleException']);
48    }
49
50    /**
51     * Echoes an exception for the web.
52     *
53     * @param \Throwable $exception The throwable object.
54     */
55    public function echoExceptionWeb(\Throwable $exception)
56    {
57        $this->sendStatusHeaders($exception);
58        $this->writeLogEntries($exception, self::CONTEXT_WEB);
59        echo GeneralUtility::makeInstance(ErrorPageController::class)->errorAction(
60            $this->getTitle($exception),
61            $this->getMessage($exception),
62            AbstractMessage::ERROR,
63            $this->discloseExceptionInformation($exception) ? $exception->getCode() : 0
64        );
65    }
66
67    /**
68     * Echoes an exception for the command line.
69     *
70     * @param \Throwable $exception The throwable object.
71     */
72    public function echoExceptionCLI(\Throwable $exception)
73    {
74        $filePathAndName = $exception->getFile();
75        $exceptionCodeNumber = $exception->getCode() > 0 ? '#' . $exception->getCode() . ': ' : '';
76        $this->writeLogEntries($exception, self::CONTEXT_CLI);
77        echo LF . 'Uncaught TYPO3 Exception ' . $exceptionCodeNumber . $exception->getMessage() . LF;
78        echo 'thrown in file ' . $filePathAndName . LF;
79        echo 'in line ' . $exception->getLine() . LF . LF;
80        die(1);
81    }
82
83    /**
84     * Determines, whether Exception details should be outputted
85     *
86     * @param \Throwable $exception The throwable object.
87     * @return bool
88     */
89    protected function discloseExceptionInformation(\Throwable $exception)
90    {
91        // Allow message to be shown in production mode if the exception is about
92        // trusted host configuration.  By doing so we do not disclose
93        // any valuable information to an attacker but avoid confusions among TYPO3 admins
94        // in production context.
95        if ($exception->getCode() === 1396795884) {
96            return true;
97        }
98        // Show client error messages 40x in every case
99        if ($exception instanceof Http\AbstractClientErrorException) {
100            return true;
101        }
102        // Only show errors if a BE user is authenticated
103        if ($GLOBALS['BE_USER'] instanceof BackendUserAuthentication) {
104            return $GLOBALS['BE_USER']->user['uid'] > 0;
105        }
106        return false;
107    }
108
109    /**
110     * Returns the title for the error message
111     *
112     * @param \Throwable $exception The throwable object.
113     * @return string
114     */
115    protected function getTitle(\Throwable $exception)
116    {
117        if ($this->discloseExceptionInformation($exception) && method_exists($exception, 'getTitle') && $exception->getTitle() !== '') {
118            return $exception->getTitle();
119        }
120        return $this->defaultTitle;
121    }
122
123    /**
124     * Returns the message for the error message
125     *
126     * @param \Throwable $exception The throwable object.
127     * @return string
128     */
129    protected function getMessage(\Throwable $exception)
130    {
131        if ($this->discloseExceptionInformation($exception)) {
132            return $exception->getMessage();
133        }
134        return $this->defaultMessage;
135    }
136}
137