[!!!][TASK] Rewrite install tool
[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 * Index action acts a a dispatcher to different steps
47 *
48 * @throws Exception
49 * @return void
50 */
51 public function execute() {
52 $this->loadBaseExtensions();
53 $this->initializeObjectManager();
54
55 // Warning: Order of this methods is security relevant and interferes with different access
56 // conditions (new/existing installation). See the single method comments for details.
57 $this->outputInstallToolNotEnabledMessageIfNeeded();
58 $this->migrateLocalconfToLocalConfigurationIfNeeded();
59 $this->outputInstallToolPasswordNotSetMessageIfNeeded();
60 $this->executeOrOutputFirstInstallStepIfNeeded();
61 $this->generateEncryptionKeyIfNeeded();
62 $this->initializeSession();
63 $this->checkSessionToken();
64 $this->checkSessionLifetime();
65 $this->loginIfRequested();
66 $this->outputLoginFormIfNotAuthorized();
67 $this->executeSpecificStep();
68 $this->outputSpecificStep();
69 $this->redirectToTool();
70 }
71
72 /**
73 * Execute a step action if requested. If executed, a redirect is done, so
74 * the next request will render step one again if needed or initiate a
75 * request to test the next step.
76 *
77 * @throws Exception
78 * @return void
79 */
80 protected function executeSpecificStep() {
81 $action = $this->getAction();
82 $postValues = $this->getPostValues();
83 if ($action && isset($postValues['set']) && $postValues['set'] === 'execute') {
84 $stepAction = $this->getActionInstance($action);
85 $stepAction->setAction($action);
86 $stepAction->setToken($this->generateTokenForAction($action));
87 $stepAction->setPostValues($this->getPostValues());
88 $messages = $stepAction->execute();
89 $this->addSessionMessages($messages);
90 $this->redirect();
91 }
92 }
93
94 /**
95 * Render a specific step. Fallback to first step if none is given.
96 * The according step is instantiated and 'needsExecution' is called. If
97 * it needs execution, the step will be rendered, otherwise a redirect
98 * to test the next step is initiated.
99 *
100 * @return void
101 */
102 protected function outputSpecificStep() {
103 $action = $this->getAction();
104 if ($action === '') {
105 // First step action
106 list($action) = $this->authenticationActions;
107 }
108 $stepAction = $this->getActionInstance($action);
109 $stepAction->setAction($action);
110 $stepAction->setController('step');
111 $stepAction->setToken($this->generateTokenForAction($action));
112 $stepAction->setPostValues($this->getPostValues());
113
114 if ($stepAction->needsExecution()) {
115 $stepAction->setMessages($this->session->getMessagesAndFlush());
116 $this->output($stepAction->handle());
117 } else {
118 // Redirect to next step if there are any
119 $currentPosition = array_keys($this->authenticationActions, $action, TRUE);
120 $nextAction = array_slice($this->authenticationActions, $currentPosition[0] + 1, 1);
121 if (!empty($nextAction)) {
122 $this->redirect('', $nextAction[0]);
123 }
124 }
125 }
126
127 /**
128 * Instantiate a specific action class
129 *
130 * @param string $action Action to instantiate
131 * @throws Exception
132 * @return \TYPO3\CMS\Install\Controller\Action\Step\StepInterface
133 */
134 protected function getActionInstance($action) {
135 $this->validateAuthenticationAction($action);
136 $actionClass = ucfirst($action);
137 /** @var \TYPO3\CMS\Install\Controller\Action\Step\StepInterface $stepAction */
138 $stepAction = $this->objectManager->get('TYPO3\\CMS\\Install\\Controller\\Action\\Step\\' . $actionClass);
139 if (!($stepAction instanceof Action\Step\StepInterface)) {
140 throw new Exception(
141 $action . ' does non implement StepInterface',
142 1371303903
143 );
144 }
145 return $stepAction;
146 }
147
148 /**
149 * If the last step was reached and none needs execution, a redirect
150 * to call the tool controller is initiated.
151 *
152 * @return void
153 */
154 protected function redirectToTool() {
155 $this->redirect('tool');
156 }
157
158 /**
159 * "Silent" upgrade very early in step installer, before rendering step 1:
160 * If typo3conf and typo3conf/localconf.php exist, but no typo3conf/LocalConfiguration,
161 * create LocalConfiguration.php / AdditionalConfiguration.php from localconf.php
162 * Might throw exception if typo3conf directory is not writable.
163 *
164 * @return void
165 */
166 protected function migrateLocalconfToLocalConfigurationIfNeeded() {
167 /** @var \TYPO3\CMS\Core\Configuration\ConfigurationManager $configurationManager */
168 $configurationManager = $this->objectManager->get('TYPO3\\CMS\\Core\\Configuration\\ConfigurationManager');
169
170 $localConfigurationFileLocation = $configurationManager->getLocalConfigurationFileLocation();
171 $localConfigurationFileExists = is_file($localConfigurationFileLocation);
172 $localConfFileLocation = PATH_typo3conf . 'localconf.php';
173 $localConfFileExists = is_file($localConfFileLocation);
174
175 if (is_dir(PATH_typo3conf) && $localConfFileExists && !$localConfigurationFileExists) {
176 $localConfContent = file($localConfFileLocation);
177
178 // Line array for the three categories: localConfiguration, db settings, additionalConfiguration
179 $typo3ConfigurationVariables = array();
180 $typo3DatabaseVariables = array();
181 $additionalConfiguration = array();
182 foreach ($localConfContent as $line) {
183 $line = trim($line);
184 $matches = array();
185 // Convert extList to array
186 if (
187 preg_match('/^\\$TYPO3_CONF_VARS\\[\'EXT\'\\]\\[\'extList\'\\] *={1} *\'(.+)\';{1}/', $line, $matches) === 1
188 || preg_match('/^\\$GLOBALS\\[\'TYPO3_CONF_VARS\'\\]\\[\'EXT\'\\]\\[\'extList\'\\] *={1} *\'(.+)\';{1}/', $line, $matches) === 1
189 ) {
190 $extListAsArray = GeneralUtility::trimExplode(',', $matches[1], TRUE);
191 $typo3ConfigurationVariables[] = '$TYPO3_CONF_VARS[\'EXT\'][\'extListArray\'] = ' . var_export($extListAsArray, TRUE) . ';';
192 } elseif (
193 preg_match('/^\\$TYPO3_CONF_VARS.+;{1}/', $line, $matches) === 1
194 ) {
195 $typo3ConfigurationVariables[] = $matches[0];
196 } elseif (
197 preg_match('/^\\$GLOBALS\\[\'TYPO3_CONF_VARS\'\\].+;{1}/', $line, $matches) === 1
198 ) {
199 $lineWithoutGlobals = str_replace('$GLOBALS[\'TYPO3_CONF_VARS\']', '$TYPO3_CONF_VARS', $matches[0]);
200 $typo3ConfigurationVariables[] = $lineWithoutGlobals;
201 } elseif (
202 preg_match('/^\\$typo_db.+;{1}/', $line, $matches) === 1
203 ) {
204 eval($matches[0]);
205 if (isset($typo_db_host)) {
206 $typo3DatabaseVariables['host'] = $typo_db_host;
207 } elseif (isset($typo_db)) {
208 $typo3DatabaseVariables['database'] = $typo_db;
209 } elseif (isset($typo_db_username)) {
210 $typo3DatabaseVariables['username'] = $typo_db_username;
211 } elseif (isset($typo_db_password)) {
212 $typo3DatabaseVariables['password'] = $typo_db_password;
213 } elseif (isset($typo_db_extTableDef_script)) {
214 $typo3DatabaseVariables['extTablesDefinitionScript'] = $typo_db_extTableDef_script;
215 }
216 unset($typo_db_host, $typo_db, $typo_db_username, $typo_db_password, $typo_db_extTableDef_script);
217 } elseif (
218 strlen($line) > 0 && preg_match('/^\\/\\/.+|^#.+|^<\\?php$|^<\\?$|^\\?>$/', $line, $matches) === 0
219 ) {
220 $additionalConfiguration[] = $line;
221 }
222 }
223
224 // Build new TYPO3_CONF_VARS array
225 $TYPO3_CONF_VARS = NULL;
226 eval(implode(LF, $typo3ConfigurationVariables));
227
228 // Add db settings to array
229 $TYPO3_CONF_VARS['DB'] = $typo3DatabaseVariables;
230 $TYPO3_CONF_VARS = \TYPO3\CMS\Core\Utility\ArrayUtility::sortByKeyRecursive($TYPO3_CONF_VARS);
231
232 // Write out new LocalConfiguration file
233 $configurationManager->writeLocalConfiguration($TYPO3_CONF_VARS);
234
235 // Write out new AdditionalConfiguration file
236 if (sizeof($additionalConfiguration) > 0) {
237 $configurationManager->writeAdditionalConfiguration($additionalConfiguration);
238 } else {
239 @unlink($configurationManager->getAdditionalConfigurationFileLocation());
240 }
241
242 // Move localconf.php to localconf.obsolete.php
243 rename($localConfFileLocation, PATH_site . 'typo3conf/localconf.obsolete.php');
244
245 // Perform a reload to self, so bootstrap now uses new LocalConfiguration.php
246 $this->redirect();
247 }
248 }
249
250 /**
251 * The first install step has a special standing and needs separate handling:
252 * At this point no directory exists (no typo3conf, no typo3temp), so we can
253 * not start the session handling (that stores the install tool session within typo3temp).
254 * This also means, we can not start the token handling for CSRF protection. This
255 * is no real problem, since no local configuration or other security relevant
256 * information was created yet.
257 *
258 * So, if no typo3conf directory exists yet, the first step is just rendered, or
259 * executed if called so. After that, a redirect is initiated to proceed with
260 * other tasks.
261 *
262 * @return void
263 */
264 protected function executeOrOutputFirstInstallStepIfNeeded() {
265 $postValues = $this->getPostValues();
266
267 $wasExecuted= FALSE;
268 $errorMessagesFromExecute = array();
269 if (isset($postValues['action'])
270 && $postValues['action'] === 'environmentAndFolders'
271 ) {
272 /** @var \TYPO3\CMS\Install\Controller\Action\Step\StepInterface $action */
273 $action = $this->objectManager->get('TYPO3\\CMS\\Install\\Controller\\Action\\Step\\EnvironmentAndFolders');
274 $errorMessagesFromExecute = $action->execute();
275 $wasExecuted = TRUE;
276 }
277
278 /** @var \TYPO3\CMS\Install\Controller\Action\Step\StepInterface $action */
279 $action = $this->objectManager->get('TYPO3\\CMS\\Install\\Controller\\Action\\Step\\EnvironmentAndFolders');
280 $needsExecution = $action->needsExecution();
281
282 if (!is_dir(PATH_typo3conf)
283 || count($errorMessagesFromExecute) > 0
284 || $needsExecution
285 ) {
286 /** @var \TYPO3\CMS\Install\Controller\Action\Step\StepInterface $action */
287 $action = $this->objectManager->get('TYPO3\\CMS\\Install\\Controller\\Action\\Step\\EnvironmentAndFolders');
288 $action->setController('step');
289 $action->setAction('environmentAndFolders');
290 if (count($errorMessagesFromExecute) > 0) {
291 $action->setMessages($errorMessagesFromExecute);
292 }
293 $this->output($action->handle());
294 }
295
296 if ($wasExecuted) {
297 $this->redirect();
298 }
299 }
300
301 /**
302 * "Silent" upgrade: The encryption key is crucial for securing form tokens
303 * and the whole TYPO3 link rendering later on. A random key is set here in
304 * LocalConfiguration if it does not exist yet. This might possible happen
305 * during upgrading and will happen during first install.
306 *
307 * @return void
308 */
309 protected function generateEncryptionKeyIfNeeded() {
310 if (empty($GLOBALS['TYPO3_CONF_VARS']['SYS']['encryptionKey'])) {
311 $randomKey = GeneralUtility::getRandomHexString(96);
312 /** @var \TYPO3\CMS\Core\Configuration\ConfigurationManager $configurationManager */
313 $configurationManager = $this->objectManager->get('TYPO3\\CMS\\Core\\Configuration\\ConfigurationManager');
314 $configurationManager->setLocalConfigurationValueByPath('SYS/encryptionKey', $randomKey);
315 $this->redirect();
316 }
317 }
318 }
319 ?>