[FEATURE] Install tool database port detection
[Packages/TYPO3.CMS.git] / typo3 / sysext / install / Classes / Controller / StepController.php
1 <?php
2 namespace TYPO3\CMS\Install\Controller;
3
4 /***************************************************************
5 * Copyright notice
6 *
7 * (c) 2013 Christian Kuhn <lolli@schwarzbu.ch>
8 * All rights reserved
9 *
10 * This script is part of the TYPO3 project. The TYPO3 project is
11 * free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * The GNU General Public License can be found at
17 * http://www.gnu.org/copyleft/gpl.html.
18 *
19 * This script is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
23 *
24 * This copyright notice MUST APPEAR in all copies of the script!
25 ***************************************************************/
26
27 use TYPO3\CMS\Core\Utility\GeneralUtility;
28
29 /**
30 * Install step controller, dispatcher class of step actions.
31 */
32 class StepController extends AbstractController {
33
34 /**
35 * @var array List of valid action names that need authentication. Order is important!
36 */
37 protected $authenticationActions = array(
38 'environmentAndFolders',
39 'databaseConnect',
40 'databaseSelect',
41 'databaseData',
42 'defaultConfiguration',
43 );
44
45 /**
46 * @var array List of obsolete configuration options in LocalConfiguration to be removed
47 */
48 protected $obsoleteLocalConfigurationSettings = array(
49 // #34092
50 'BE/forceCharset',
51 // #26519
52 'BE/loginLabels',
53 // #44506
54 'BE/loginNews',
55 // #30613
56 'BE/useOnContextMenuHandler',
57 // #48179
58 'EXT/em_mirrorListURL',
59 'EXT/em_wsdlURL',
60 // #43094
61 'EXT/extList',
62 // #35877
63 'EXT/extList_FE',
64 // #41813
65 'EXT/noEdit',
66 // #26090
67 'FE/defaultTypoScript_editorcfg',
68 'FE/defaultTypoScript_editorcfg.',
69 // #25099
70 'FE/simulateStaticDocuments',
71 // #22687
72 'GFX/gdlib_2',
73 // #18431
74 'GFX/noIconProc',
75 // #17606
76 'GFX/TTFLocaleConv',
77 // #39164
78 'SYS/additionalAllowedClassPrefixes',
79 // #27689
80 'SYS/caching/cacheBackends',
81 'SYS/caching/cacheFrontends',
82 // #38414
83 'SYS/extCache',
84 // #35923
85 'SYS/multiplyDBfieldSize',
86 // #46993
87 'SYS/T3instID',
88 );
89
90 /**
91 * Index action acts a a dispatcher to different steps
92 *
93 * @throws Exception
94 * @return void
95 */
96 public function execute() {
97 $this->loadBaseExtensions();
98 $this->initializeObjectManager();
99
100 // Warning: Order of these methods is security relevant and interferes with different access
101 // conditions (new/existing installation). See the single method comments for details.
102 $this->outputInstallToolNotEnabledMessageIfNeeded();
103 $this->migrateLocalconfToLocalConfigurationIfNeeded();
104 $this->outputInstallToolPasswordNotSetMessageIfNeeded();
105 $this->executeOrOutputFirstInstallStepIfNeeded();
106 $this->removeObsoleteLocalConfigurationSettings();
107 $this->generateEncryptionKeyIfNeeded();
108 $this->configureBackendLoginSecurity();
109 $this->configureSaltedpasswords();
110 $this->initializeSession();
111 $this->checkSessionToken();
112 $this->checkSessionLifetime();
113 $this->loginIfRequested();
114 $this->outputLoginFormIfNotAuthorized();
115 $this->executeSpecificStep();
116 $this->outputSpecificStep();
117 $this->redirectToTool();
118 }
119
120 /**
121 * Execute a step action if requested. If executed, a redirect is done, so
122 * the next request will render step one again if needed or initiate a
123 * request to test the next step.
124 *
125 * @throws Exception
126 * @return void
127 */
128 protected function executeSpecificStep() {
129 $action = $this->getAction();
130 $postValues = $this->getPostValues();
131 if ($action && isset($postValues['set']) && $postValues['set'] === 'execute') {
132 $stepAction = $this->getActionInstance($action);
133 $stepAction->setAction($action);
134 $stepAction->setToken($this->generateTokenForAction($action));
135 $stepAction->setPostValues($this->getPostValues());
136 $messages = $stepAction->execute();
137 $this->addSessionMessages($messages);
138 $this->redirect();
139 }
140 }
141
142 /**
143 * Render a specific step. Fallback to first step if none is given.
144 * The according step is instantiated and 'needsExecution' is called. If
145 * it needs execution, the step will be rendered, otherwise a redirect
146 * to test the next step is initiated.
147 *
148 * @return void
149 */
150 protected function outputSpecificStep() {
151 $action = $this->getAction();
152 if ($action === '') {
153 // First step action
154 list($action) = $this->authenticationActions;
155 }
156 $stepAction = $this->getActionInstance($action);
157 $stepAction->setAction($action);
158 $stepAction->setController('step');
159 $stepAction->setToken($this->generateTokenForAction($action));
160 $stepAction->setPostValues($this->getPostValues());
161
162 try {
163 // needsExecution() may throw a RedirectException to communicate that it changed
164 // configuration parameters and need an application reload.
165 $needsExecution = $stepAction->needsExecution();
166 } catch (Exception\RedirectException $e) {
167 $this->redirect();
168 }
169
170 if ($needsExecution) {
171 $stepAction->setMessages($this->session->getMessagesAndFlush());
172 $this->output($stepAction->handle());
173 } else {
174 // Redirect to next step if there are any
175 $currentPosition = array_keys($this->authenticationActions, $action, TRUE);
176 $nextAction = array_slice($this->authenticationActions, $currentPosition[0] + 1, 1);
177 if (!empty($nextAction)) {
178 $this->redirect('', $nextAction[0]);
179 }
180 }
181 }
182
183 /**
184 * Instantiate a specific action class
185 *
186 * @param string $action Action to instantiate
187 * @throws Exception
188 * @return \TYPO3\CMS\Install\Controller\Action\Step\StepInterface
189 */
190 protected function getActionInstance($action) {
191 $this->validateAuthenticationAction($action);
192 $actionClass = ucfirst($action);
193 /** @var \TYPO3\CMS\Install\Controller\Action\Step\StepInterface $stepAction */
194 $stepAction = $this->objectManager->get('TYPO3\\CMS\\Install\\Controller\\Action\\Step\\' . $actionClass);
195 if (!($stepAction instanceof Action\Step\StepInterface)) {
196 throw new Exception(
197 $action . ' does non implement StepInterface',
198 1371303903
199 );
200 }
201 return $stepAction;
202 }
203
204 /**
205 * If the last step was reached and none needs execution, a redirect
206 * to call the tool controller is initiated.
207 *
208 * @return void
209 */
210 protected function redirectToTool() {
211 $this->redirect('tool');
212 }
213
214 /**
215 * "Silent" upgrade very early in step installer, before rendering step 1:
216 * If typo3conf and typo3conf/localconf.php exist, but no typo3conf/LocalConfiguration,
217 * create LocalConfiguration.php / AdditionalConfiguration.php from localconf.php
218 * Might throw exception if typo3conf directory is not writable.
219 *
220 * @return void
221 */
222 protected function migrateLocalconfToLocalConfigurationIfNeeded() {
223 /** @var \TYPO3\CMS\Core\Configuration\ConfigurationManager $configurationManager */
224 $configurationManager = $this->objectManager->get('TYPO3\\CMS\\Core\\Configuration\\ConfigurationManager');
225
226 $localConfigurationFileLocation = $configurationManager->getLocalConfigurationFileLocation();
227 $localConfigurationFileExists = is_file($localConfigurationFileLocation);
228 $localConfFileLocation = PATH_typo3conf . 'localconf.php';
229 $localConfFileExists = is_file($localConfFileLocation);
230
231 if (is_dir(PATH_typo3conf) && $localConfFileExists && !$localConfigurationFileExists) {
232 $localConfContent = file($localConfFileLocation);
233
234 // Line array for the three categories: localConfiguration, db settings, additionalConfiguration
235 $typo3ConfigurationVariables = array();
236 $typo3DatabaseVariables = array();
237 $additionalConfiguration = array();
238 foreach ($localConfContent as $line) {
239 $line = trim($line);
240 $matches = array();
241 // Convert extList to array
242 if (
243 preg_match('/^\\$TYPO3_CONF_VARS\\[\'EXT\'\\]\\[\'extList\'\\] *={1} *\'(.+)\';{1}/', $line, $matches) === 1
244 || preg_match('/^\\$GLOBALS\\[\'TYPO3_CONF_VARS\'\\]\\[\'EXT\'\\]\\[\'extList\'\\] *={1} *\'(.+)\';{1}/', $line, $matches) === 1
245 ) {
246 $extListAsArray = GeneralUtility::trimExplode(',', $matches[1], TRUE);
247 $typo3ConfigurationVariables[] = '$TYPO3_CONF_VARS[\'EXT\'][\'extListArray\'] = ' . var_export($extListAsArray, TRUE) . ';';
248 } elseif (
249 preg_match('/^\\$TYPO3_CONF_VARS.+;{1}/', $line, $matches) === 1
250 ) {
251 $typo3ConfigurationVariables[] = $matches[0];
252 } elseif (
253 preg_match('/^\\$GLOBALS\\[\'TYPO3_CONF_VARS\'\\].+;{1}/', $line, $matches) === 1
254 ) {
255 $lineWithoutGlobals = str_replace('$GLOBALS[\'TYPO3_CONF_VARS\']', '$TYPO3_CONF_VARS', $matches[0]);
256 $typo3ConfigurationVariables[] = $lineWithoutGlobals;
257 } elseif (
258 preg_match('/^\\$typo_db.+;{1}/', $line, $matches) === 1
259 ) {
260 eval($matches[0]);
261 if (isset($typo_db_host)) {
262 $typo3DatabaseVariables['host'] = $typo_db_host;
263 } elseif (isset($typo_db)) {
264 $typo3DatabaseVariables['database'] = $typo_db;
265 } elseif (isset($typo_db_username)) {
266 $typo3DatabaseVariables['username'] = $typo_db_username;
267 } elseif (isset($typo_db_password)) {
268 $typo3DatabaseVariables['password'] = $typo_db_password;
269 } elseif (isset($typo_db_extTableDef_script)) {
270 $typo3DatabaseVariables['extTablesDefinitionScript'] = $typo_db_extTableDef_script;
271 }
272 unset($typo_db_host, $typo_db, $typo_db_username, $typo_db_password, $typo_db_extTableDef_script);
273 } elseif (
274 strlen($line) > 0 && preg_match('/^\\/\\/.+|^#.+|^<\\?php$|^<\\?$|^\\?>$/', $line, $matches) === 0
275 ) {
276 $additionalConfiguration[] = $line;
277 }
278 }
279
280 // Build new TYPO3_CONF_VARS array
281 $TYPO3_CONF_VARS = NULL;
282 // Issue #39434: Combining next two lines into one triggers a weird issue in some PHP versions
283 $evalData = implode(LF, $typo3ConfigurationVariables);
284 eval($evalData);
285
286 // Add db settings to array
287 $TYPO3_CONF_VARS['DB'] = $typo3DatabaseVariables;
288 $TYPO3_CONF_VARS = \TYPO3\CMS\Core\Utility\ArrayUtility::sortByKeyRecursive($TYPO3_CONF_VARS);
289
290 // Write out new LocalConfiguration file
291 $configurationManager->writeLocalConfiguration($TYPO3_CONF_VARS);
292
293 // Write out new AdditionalConfiguration file
294 if (sizeof($additionalConfiguration) > 0) {
295 $configurationManager->writeAdditionalConfiguration($additionalConfiguration);
296 } else {
297 @unlink($configurationManager->getAdditionalConfigurationFileLocation());
298 }
299
300 // Move localconf.php to localconf.obsolete.php
301 rename($localConfFileLocation, PATH_site . 'typo3conf/localconf.obsolete.php');
302
303 // Perform a reload to self, so bootstrap now uses new LocalConfiguration.php
304 $this->redirect();
305 }
306 }
307
308 /**
309 * Some settings in LocalConfiguration vanished in DefaultConfiguration
310 * and have no impact on the core anymore.
311 * To keep the configuration clean, those old settings are just silently
312 * removed from LocalConfiguration if set.
313 *
314 * @return void
315 */
316 protected function removeObsoleteLocalConfigurationSettings() {
317 /** @var \TYPO3\CMS\Core\Configuration\ConfigurationManager $configurationManager */
318 $configurationManager = $this->objectManager->get('TYPO3\\CMS\\Core\\Configuration\\ConfigurationManager');
319 $removed = $configurationManager->removeLocalConfigurationKeysByPath($this->obsoleteLocalConfigurationSettings);
320 // If something was changed: Trigger a reload to have new values in next request
321 if ($removed) {
322 $this->redirect();
323 }
324 }
325
326 /**
327 * The first install step has a special standing and needs separate handling:
328 * At this point no directory exists (no typo3conf, no typo3temp), so we can
329 * not start the session handling (that stores the install tool session within typo3temp).
330 * This also means, we can not start the token handling for CSRF protection. This
331 * is no real problem, since no local configuration or other security relevant
332 * information was created yet.
333 *
334 * So, if no typo3conf directory exists yet, the first step is just rendered, or
335 * executed if called so. After that, a redirect is initiated to proceed with
336 * other tasks.
337 *
338 * @return void
339 */
340 protected function executeOrOutputFirstInstallStepIfNeeded() {
341 $postValues = $this->getPostValues();
342
343 $wasExecuted= FALSE;
344 $errorMessagesFromExecute = array();
345 if (isset($postValues['action'])
346 && $postValues['action'] === 'environmentAndFolders'
347 ) {
348 /** @var \TYPO3\CMS\Install\Controller\Action\Step\StepInterface $action */
349 $action = $this->objectManager->get('TYPO3\\CMS\\Install\\Controller\\Action\\Step\\EnvironmentAndFolders');
350 $errorMessagesFromExecute = $action->execute();
351 $wasExecuted = TRUE;
352 }
353
354 /** @var \TYPO3\CMS\Install\Controller\Action\Step\StepInterface $action */
355 $action = $this->objectManager->get('TYPO3\\CMS\\Install\\Controller\\Action\\Step\\EnvironmentAndFolders');
356
357 try {
358 // needsExecution() may throw a RedirectException to communicate that it changed
359 // configuration parameters and need an application reload.
360 $needsExecution = $action->needsExecution();
361 } catch (Exception\RedirectException $e) {
362 $this->redirect();
363 }
364
365 if (!is_dir(PATH_typo3conf)
366 || count($errorMessagesFromExecute) > 0
367 || $needsExecution
368 ) {
369 /** @var \TYPO3\CMS\Install\Controller\Action\Step\StepInterface $action */
370 $action = $this->objectManager->get('TYPO3\\CMS\\Install\\Controller\\Action\\Step\\EnvironmentAndFolders');
371 $action->setController('step');
372 $action->setAction('environmentAndFolders');
373 if (count($errorMessagesFromExecute) > 0) {
374 $action->setMessages($errorMessagesFromExecute);
375 }
376 $this->output($action->handle());
377 }
378
379 if ($wasExecuted) {
380 $this->redirect();
381 }
382 }
383
384 /**
385 * "Silent" upgrade: The encryption key is crucial for securing form tokens
386 * and the whole TYPO3 link rendering later on. A random key is set here in
387 * LocalConfiguration if it does not exist yet. This might possible happen
388 * during upgrading and will happen during first install.
389 *
390 * @return void
391 */
392 protected function generateEncryptionKeyIfNeeded() {
393 if (empty($GLOBALS['TYPO3_CONF_VARS']['SYS']['encryptionKey'])) {
394 $randomKey = GeneralUtility::getRandomHexString(96);
395 /** @var \TYPO3\CMS\Core\Configuration\ConfigurationManager $configurationManager */
396 $configurationManager = $this->objectManager->get('TYPO3\\CMS\\Core\\Configuration\\ConfigurationManager');
397 $configurationManager->setLocalConfigurationValueByPath('SYS/encryptionKey', $randomKey);
398 $this->redirect();
399 }
400 }
401
402 /**
403 * "Silent" upgrade: Backend login security is set to rsa if rsaauth
404 * is installed (but not used) otherwise the default value "normal" has to be used.
405 *
406 * @return void
407 */
408 protected function configureBackendLoginSecurity() {
409 if (\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::isLoaded('rsaauth')
410 && $GLOBALS['TYPO3_CONF_VARS']['BE']['loginSecurityLevel'] !== 'rsa')
411 {
412 $configurationManager = $this->objectManager->get('TYPO3\\CMS\\Core\\Configuration\\ConfigurationManager');
413 $configurationManager->setLocalConfigurationValueByPath('BE/loginSecurityLevel', 'rsa');
414 $this->redirect();
415 } elseif (!\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::isLoaded('rsaauth')
416 && $GLOBALS['TYPO3_CONF_VARS']['BE']['loginSecurityLevel'] !== 'normal'
417 ) {
418 $configurationManager = $this->objectManager->get('TYPO3\\CMS\\Core\\Configuration\\ConfigurationManager');
419 $configurationManager->setLocalConfigurationValueByPath('BE/loginSecurityLevel', 'normal');
420 $this->redirect();
421 }
422 }
423
424 /**
425 * "Silent" upgrade: Check the settings for saltedpasswords extension to
426 * load it as a required extension.
427 *
428 * @return void
429 */
430 protected function configureSaltedpasswords() {
431 $configurationManager = $this->objectManager->get('TYPO3\\CMS\\Core\\Configuration\\ConfigurationManager');
432 $defaultConfiguration = $configurationManager->getDefaultConfiguration();
433 $defaultExtensionConfiguration = unserialize($defaultConfiguration['EXT']['extConf']['saltedpasswords']);
434 $extensionConfiguration = @unserialize($GLOBALS['TYPO3_CONF_VARS']['EXT']['extConf']['saltedpasswords']);
435 if (is_array($extensionConfiguration) && !empty($extensionConfiguration)) {
436 if (isset($extensionConfiguration['BE.']['enabled'])) {
437 if ($extensionConfiguration['BE.']['enabled']) {
438 unset($extensionConfiguration['BE.']['enabled']);
439 } else {
440 $extensionConfiguration['BE.'] = $defaultExtensionConfiguration['BE.'];
441 }
442 $configurationManager->setLocalConfigurationValueByPath(
443 'EXT/extConf/saltedpasswords',
444 serialize($extensionConfiguration)
445 );
446 $this->redirect();
447 }
448 } else {
449 $configurationManager->setLocalConfigurationValueByPath(
450 'EXT/extConf/saltedpasswords',
451 serialize($defaultExtensionConfiguration)
452 );
453 $this->redirect();
454 }
455 }
456 }
457 ?>