[FEATURE] Introduce Request/Response based on PSR-7
[Packages/TYPO3.CMS.git] / typo3 / sysext / backend / Classes / Http / BackendModuleRequestHandler.php
1 <?php
2 namespace TYPO3\CMS\Backend\Http;
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 */
16
17 use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
18 use TYPO3\CMS\Core\Core\Bootstrap;
19 use TYPO3\CMS\Core\FormProtection\FormProtectionFactory;
20 use TYPO3\CMS\Core\Exception;
21 use TYPO3\CMS\Core\Utility\GeneralUtility;
22 use TYPO3\CMS\Extbase\Object\ObjectManager;
23 use Psr\Http\Message\ServerRequestInterface;
24
25 /**
26 * Handles the request for backend modules and wizards
27 */
28 class BackendModuleRequestHandler implements \TYPO3\CMS\Core\Core\RequestHandlerInterface {
29
30 /**
31 * @var Bootstrap
32 */
33 protected $bootstrap;
34
35 /**
36 * @var array
37 */
38 protected $moduleRegistry = array();
39
40 /**
41 * @var BackendUserAuthentication
42 */
43 protected $backendUserAuthentication;
44
45 /**
46 * Instance of the current Http Request
47 * @var ServerRequestInterface
48 */
49 protected $request;
50
51 /**
52 * Constructor handing over the bootstrap and the original request
53 *
54 * @param Bootstrap $bootstrap
55 */
56 public function __construct(Bootstrap $bootstrap) {
57 $this->bootstrap = $bootstrap;
58 }
59
60 /**
61 * Handles the request, evaluating the configuration and executes the module accordingly
62 *
63 * @param ServerRequestInterface $request
64 * @return NULL|\Psr\Http\Message\ResponseInterface
65 * @throws Exception
66 */
67 public function handleRequest(ServerRequestInterface $request) {
68 $this->request = $request;
69 $this->boot();
70
71 $this->moduleRegistry = $GLOBALS['TBE_MODULES'];
72
73 if (!$this->isValidModuleRequest()) {
74 throw new Exception('The CSRF protection token for the requested module is missing or invalid', 1417988921);
75 }
76
77 // Set to empty as it is not needed / always coming from typo3/mod.php
78 $GLOBALS['BACK_PATH'] = '';
79
80 $this->backendUserAuthentication = $GLOBALS['BE_USER'];
81
82 $moduleName = (string)$this->request->getQueryParams()['M'];
83 if ($this->isDispatchedModule($moduleName)) {
84 $isDispatched = $this->dispatchModule($moduleName);
85 } else {
86 $isDispatched = $this->callTraditionalModule($moduleName);
87 }
88 if ($isDispatched === FALSE) {
89 throw new Exception('No module "' . $moduleName . '" could be found.', 1294585070);
90 }
91 }
92
93 /**
94 * Execute TYPO3 bootstrap
95 */
96 protected function boot() {
97 // Evaluate the constant for skipping the BE user check for the bootstrap, will be done without the constant at a later point
98 if (defined('TYPO3_PROCEED_IF_NO_USER') && TYPO3_PROCEED_IF_NO_USER) {
99 $proceedIfNoUserIsLoggedIn = TRUE;
100 } else {
101 $proceedIfNoUserIsLoggedIn = FALSE;
102 }
103
104 $this->bootstrap->checkLockedBackendAndRedirectOrDie()
105 ->checkBackendIpOrDie()
106 ->checkSslBackendAndRedirectIfNeeded()
107 ->checkValidBrowserOrDie()
108 ->loadExtensionTables(TRUE)
109 ->initializeSpriteManager()
110 ->initializeBackendUser()
111 ->initializeBackendAuthentication($proceedIfNoUserIsLoggedIn)
112 ->initializeLanguageObject()
113 ->initializeBackendTemplate()
114 ->endOutputBufferingAndCleanPreviousOutput()
115 ->initializeOutputCompression()
116 ->sendHttpHeaders();
117 }
118
119 /**
120 * This request handler can handle any backend request coming from mod.php
121 *
122 * @param ServerRequestInterface $request
123 * @return bool
124 */
125 public function canHandleRequest(ServerRequestInterface $request) {
126 return (TYPO3_REQUESTTYPE & TYPO3_REQUESTTYPE_BE) && !empty((string)$request->getQueryParams()['M']);
127 }
128
129 /**
130 * Checks if all parameters are met.
131 *
132 * @return bool
133 */
134 protected function isValidModuleRequest() {
135 return $this->getFormProtection()->validateToken((string)$this->request->getQueryParams()['moduleToken'], 'moduleCall', (string)$this->request->getQueryParams()['M']);
136 }
137
138 /**
139 * A dispatched module, currently only Extbase modules are dispatched,
140 * traditional modules have a module path set.
141 *
142 * @param string $moduleName
143 * @return bool
144 */
145 protected function isDispatchedModule($moduleName) {
146 return empty($this->moduleRegistry['_PATHS'][$moduleName]);
147 }
148
149 /**
150 * Executes the module dispatcher which calls the module appropriately.
151 * Currently only used by Extbase
152 *
153 * @param string $moduleName
154 * @return bool
155 */
156 protected function dispatchModule($moduleName) {
157 if (is_array($this->moduleRegistry['_dispatcher'])) {
158 foreach ($this->moduleRegistry['_dispatcher'] as $dispatcherClassName) {
159 $dispatcher = GeneralUtility::makeInstance(ObjectManager::class)->get($dispatcherClassName);
160 if ($dispatcher->callModule($moduleName) === TRUE) {
161 return TRUE;
162 break;
163 }
164 }
165 }
166 return FALSE;
167 }
168
169 /**
170 * Calls traditional modules which are identified by having a index.php in their directory
171 * and were previously located within the global scope.
172 *
173 * @param string $moduleName
174 * @return bool
175 */
176 protected function callTraditionalModule($moduleName) {
177 $moduleBasePath = $this->moduleRegistry['_PATHS'][$moduleName];
178 $GLOBALS['MCONF'] = $moduleConfiguration = $this->getModuleConfiguration($moduleName);
179 if (!empty($moduleConfiguration['access'])) {
180 $this->backendUserAuthentication->modAccess($moduleConfiguration, TRUE);
181 }
182 if (file_exists($moduleBasePath . 'index.php')) {
183 global $SOBE;
184 require $moduleBasePath . 'index.php';
185 return TRUE;
186 }
187 return FALSE;
188 }
189
190 /**
191 * Returns the module configuration which is either provided in a conf.php file
192 * or during module registration
193 *
194 * @param string $moduleName
195 * @return array
196 */
197 protected function getModuleConfiguration($moduleName) {
198 $moduleBasePath = $this->moduleRegistry['_PATHS'][$moduleName];
199 if (file_exists($moduleBasePath . 'conf.php')) {
200 // Some modules still rely on this global configuration array in a conf.php file
201 require $moduleBasePath . 'conf.php';
202 $moduleConfiguration = $MCONF;
203 } else {
204 $moduleConfiguration = $this->moduleRegistry['_configuration'][$moduleName];
205 }
206 return $moduleConfiguration;
207 }
208
209
210 /**
211 * Returns the priority - how eager the handler is to actually handle the request.
212 *
213 * @return int The priority of the request handler.
214 */
215 public function getPriority() {
216 return 90;
217 }
218
219 /**
220 * Wrapper method for static form protection utility
221 *
222 * @return \TYPO3\CMS\Core\FormProtection\AbstractFormProtection
223 */
224 protected function getFormProtection() {
225 return FormProtectionFactory::get();
226 }
227
228 }