[TASK] Re-work/simplify copyright header in PHP files - Part 2
[Packages/TYPO3.CMS.git] / typo3 / sysext / install / Classes / Controller / StepController.php
1 <?php
2 namespace TYPO3\CMS\Install\Controller;
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\Utility\GeneralUtility;
18
19 /**
20 * Install step controller, dispatcher class of step actions.
21 */
22 class StepController extends AbstractController {
23
24 /**
25 * @var array List of valid action names that need authentication. Order is important!
26 */
27 protected $authenticationActions = array(
28 'environmentAndFolders',
29 'databaseConnect',
30 'databaseSelect',
31 'databaseData',
32 'defaultConfiguration',
33 );
34
35 /**
36 * Index action acts a a dispatcher to different steps
37 *
38 * Warning: Order of these methods is security relevant and interferes with different access
39 * conditions (new/existing installation). See the single method comments for details.
40 *
41 * @throws Exception
42 * @return void
43 */
44 public function execute() {
45 $this->loadBaseExtensions();
46 $this->initializeObjectManager();
47
48 $this->outputInstallToolNotEnabledMessageIfNeeded();
49 $this->migrateLocalconfToLocalConfigurationIfNeeded();
50 $this->outputInstallToolPasswordNotSetMessageIfNeeded();
51 $this->migrateExtensionListToPackageStatesFile();
52 $this->executeOrOutputFirstInstallStepIfNeeded();
53 $this->executeSilentConfigurationUpgradesIfNeeded();
54 $this->initializeSession();
55 $this->checkSessionToken();
56 $this->checkSessionLifetime();
57 $this->loginIfRequested();
58 $this->outputLoginFormIfNotAuthorized();
59 $this->executeSpecificStep();
60 $this->outputSpecificStep();
61 $this->redirectToTool();
62 }
63
64 /**
65 * Execute a step action if requested. If executed, a redirect is done, so
66 * the next request will render step one again if needed or initiate a
67 * request to test the next step.
68 *
69 * @throws Exception
70 * @return void
71 */
72 protected function executeSpecificStep() {
73 $action = $this->getAction();
74 $postValues = $this->getPostValues();
75 if ($action && isset($postValues['set']) && $postValues['set'] === 'execute') {
76 $stepAction = $this->getActionInstance($action);
77 $stepAction->setAction($action);
78 $stepAction->setToken($this->generateTokenForAction($action));
79 $stepAction->setPostValues($this->getPostValues());
80 $messages = $stepAction->execute();
81 $this->addSessionMessages($messages);
82 $this->redirect();
83 }
84 }
85
86 /**
87 * Render a specific step. Fallback to first step if none is given.
88 * The according step is instantiated and 'needsExecution' is called. If
89 * it needs execution, the step will be rendered, otherwise a redirect
90 * to test the next step is initiated.
91 *
92 * @return void
93 */
94 protected function outputSpecificStep() {
95 $action = $this->getAction();
96 if ($action === '') {
97 // First step action
98 list($action) = $this->authenticationActions;
99 }
100 $stepAction = $this->getActionInstance($action);
101 $stepAction->setAction($action);
102 $stepAction->setController('step');
103 $stepAction->setToken($this->generateTokenForAction($action));
104 $stepAction->setPostValues($this->getPostValues());
105
106 $needsExecution = TRUE;
107 try {
108 // needsExecution() may throw a RedirectException to communicate that it changed
109 // configuration parameters and need an application reload.
110 $needsExecution = $stepAction->needsExecution();
111 } catch (Exception\RedirectException $e) {
112 $this->redirect();
113 }
114
115 if ($needsExecution) {
116 if ($this->isInitialInstallationInProgress()) {
117 $currentStep = (array_search($action, $this->authenticationActions) + 1);
118 $totalSteps = count($this->authenticationActions);
119 $stepAction->setStepsCounter($currentStep, $totalSteps);
120 }
121 $stepAction->setMessages($this->session->getMessagesAndFlush());
122 $this->output($stepAction->handle());
123 } else {
124 // Redirect to next step if there are any
125 $currentPosition = array_keys($this->authenticationActions, $action, TRUE);
126 $nextAction = array_slice($this->authenticationActions, $currentPosition[0] + 1, 1);
127 if (!empty($nextAction)) {
128 $this->redirect('', $nextAction[0]);
129 }
130 }
131 }
132
133 /**
134 * Instantiate a specific action class
135 *
136 * @param string $action Action to instantiate
137 * @throws Exception
138 * @return \TYPO3\CMS\Install\Controller\Action\Step\StepInterface
139 */
140 protected function getActionInstance($action) {
141 $this->validateAuthenticationAction($action);
142 $actionClass = ucfirst($action);
143 /** @var \TYPO3\CMS\Install\Controller\Action\Step\StepInterface $stepAction */
144 $stepAction = $this->objectManager->get('TYPO3\\CMS\\Install\\Controller\\Action\\Step\\' . $actionClass);
145 if (!($stepAction instanceof Action\Step\StepInterface)) {
146 throw new Exception(
147 $action . ' does non implement StepInterface',
148 1371303903
149 );
150 }
151 return $stepAction;
152 }
153
154 /**
155 * If the last step was reached and none needs execution, a redirect
156 * to call the tool controller is initiated.
157 *
158 * @return void
159 */
160 protected function redirectToTool() {
161 $this->redirect('tool');
162 }
163
164 /**
165 * Migrate localconf.php to LocalConfiguration if needed. This is done early in
166 * install tool to ease further handling.
167 *
168 * If typo3conf and typo3conf/localconf.php exist, but no typo3conf/LocalConfiguration,
169 * create LocalConfiguration.php / AdditionalConfiguration.php from localconf.php
170 * Might throw exception if typo3conf directory is not writable.
171 *
172 * @return void
173 */
174 protected function migrateLocalconfToLocalConfigurationIfNeeded() {
175 /** @var \TYPO3\CMS\Core\Configuration\ConfigurationManager $configurationManager */
176 $configurationManager = $this->objectManager->get('TYPO3\\CMS\\Core\\Configuration\\ConfigurationManager');
177
178 $localConfigurationFileLocation = $configurationManager->getLocalConfigurationFileLocation();
179 $localConfigurationFileExists = is_file($localConfigurationFileLocation);
180 $localConfFileLocation = PATH_typo3conf . 'localconf.php';
181 $localConfFileExists = is_file($localConfFileLocation);
182
183 if (is_dir(PATH_typo3conf) && $localConfFileExists && !$localConfigurationFileExists) {
184 $localConfContent = file($localConfFileLocation);
185
186 // Line array for the three categories: localConfiguration, db settings, additionalConfiguration
187 $typo3ConfigurationVariables = array();
188 $typo3DatabaseVariables = array();
189 $additionalConfiguration = array();
190 foreach ($localConfContent as $line) {
191 $line = trim($line);
192 $matches = array();
193 // Convert extList to array
194 if (
195 preg_match('/^\\$TYPO3_CONF_VARS\\[\'EXT\'\\]\\[\'extList\'\\] *={1} *\'(.+)\';{1}/', $line, $matches) === 1
196 || preg_match('/^\\$GLOBALS\\[\'TYPO3_CONF_VARS\'\\]\\[\'EXT\'\\]\\[\'extList\'\\] *={1} *\'(.+)\';{1}/', $line, $matches) === 1
197 ) {
198 $extListAsArray = GeneralUtility::trimExplode(',', $matches[1], TRUE);
199 $typo3ConfigurationVariables[] = '$TYPO3_CONF_VARS[\'EXT\'][\'extListArray\'] = ' . var_export($extListAsArray, TRUE) . ';';
200 } elseif (
201 preg_match('/^\\$TYPO3_CONF_VARS.+;{1}/', $line, $matches) === 1
202 ) {
203 $typo3ConfigurationVariables[] = $matches[0];
204 } elseif (
205 preg_match('/^\\$GLOBALS\\[\'TYPO3_CONF_VARS\'\\].+;{1}/', $line, $matches) === 1
206 ) {
207 $lineWithoutGlobals = str_replace('$GLOBALS[\'TYPO3_CONF_VARS\']', '$TYPO3_CONF_VARS', $matches[0]);
208 $typo3ConfigurationVariables[] = $lineWithoutGlobals;
209 } elseif (
210 preg_match('/^\\$typo_db.+;{1}/', $line, $matches) === 1
211 ) {
212 eval($matches[0]);
213 if (isset($typo_db_host)) {
214 $typo3DatabaseVariables['host'] = $typo_db_host;
215 } elseif (isset($typo_db)) {
216 $typo3DatabaseVariables['database'] = $typo_db;
217 } elseif (isset($typo_db_username)) {
218 $typo3DatabaseVariables['username'] = $typo_db_username;
219 } elseif (isset($typo_db_password)) {
220 $typo3DatabaseVariables['password'] = $typo_db_password;
221 } elseif (isset($typo_db_extTableDef_script)) {
222 $typo3DatabaseVariables['extTablesDefinitionScript'] = $typo_db_extTableDef_script;
223 }
224 unset($typo_db_host, $typo_db, $typo_db_username, $typo_db_password, $typo_db_extTableDef_script);
225 } elseif (
226 strlen($line) > 0 && preg_match('/^\\/\\/.+|^#.+|^<\\?php$|^<\\?$|^\\?>$/', $line, $matches) === 0
227 ) {
228 $additionalConfiguration[] = $line;
229 }
230 }
231
232 // Build new TYPO3_CONF_VARS array
233 $TYPO3_CONF_VARS = NULL;
234 // Issue #39434: Combining next two lines into one triggers a weird issue in some PHP versions
235 $evalData = implode(LF, $typo3ConfigurationVariables);
236 eval($evalData);
237
238 // Add db settings to array
239 $TYPO3_CONF_VARS['DB'] = $typo3DatabaseVariables;
240 $TYPO3_CONF_VARS = \TYPO3\CMS\Core\Utility\ArrayUtility::sortByKeyRecursive($TYPO3_CONF_VARS);
241
242 // Write out new LocalConfiguration file
243 $configurationManager->writeLocalConfiguration($TYPO3_CONF_VARS);
244
245 // Write out new AdditionalConfiguration file
246 if (sizeof($additionalConfiguration) > 0) {
247 $configurationManager->writeAdditionalConfiguration($additionalConfiguration);
248 } else {
249 @unlink($configurationManager->getAdditionalConfigurationFileLocation());
250 }
251
252 // Move localconf.php to localconf.obsolete.php
253 rename($localConfFileLocation, PATH_site . 'typo3conf/localconf.obsolete.php');
254
255 // Perform a reload to self, so bootstrap now uses new LocalConfiguration.php
256 $this->redirect();
257 }
258 }
259
260 /**
261 * Create PackageStates.php if missing and LocalConfiguration exists.
262 *
263 * This typically happens during upgrading from 6.1 or lower, all valid packages
264 * from old EXT/extListArray will be marked active.
265 *
266 * It is also fired if PackageStates.php is deleted on a running 6.2 instance,
267 * all packages marked as "part of minimal system" are activated in this case.
268 *
269 * The step installer creates typo3conf/, LocalConfiguration and PackageStates in
270 * one call, so an "installation in progress" does not trigger creation of
271 * PackageStates here.
272 *
273 * @throws \Exception
274 * @return void
275 */
276 protected function migrateExtensionListToPackageStatesFile() {
277 /** @var \TYPO3\CMS\Core\Configuration\ConfigurationManager $configurationManager */
278 $configurationManager = $this->objectManager->get('TYPO3\\CMS\\Core\\Configuration\\ConfigurationManager');
279 $localConfigurationFileLocation = $configurationManager->getLocalConfigurationFileLocation();
280 $localConfigurationFileExists = is_file($localConfigurationFileLocation);
281 $packageStatesFilePath = PATH_typo3conf . 'PackageStates.php';
282 $localConfigurationBackupFilePath = preg_replace(
283 '/\\.php$/',
284 '.beforePackageStatesMigration.php',
285 $configurationManager->getLocalConfigurationFileLocation()
286 );
287
288 if (file_exists($packageStatesFilePath)
289 || (is_dir(PATH_typo3conf) && !$localConfigurationFileExists)
290 || !is_dir(PATH_typo3conf)
291 ) {
292 return;
293 }
294
295 try {
296 /** @var \TYPO3\CMS\Core\Package\FailsafePackageManager $packageManager */
297 $packageManager = \TYPO3\CMS\Core\Core\Bootstrap::getInstance()->getEarlyInstance('TYPO3\\Flow\\Package\\PackageManager');
298
299 // Activate all packages required for a minimal usable system
300 $packages = $packageManager->getAvailablePackages();
301 foreach ($packages as $package) {
302 /** @var $package \TYPO3\CMS\Core\Package\PackageInterface */
303 if ($package instanceof \TYPO3\CMS\Core\Package\PackageInterface
304 && $package->isPartOfMinimalUsableSystem()
305 ) {
306 $packageManager->activatePackage($package->getPackageKey());
307 }
308 }
309
310 // Activate all packages from LocalConfiguration EXT/extListArray if there is such an entry during upgrading.
311 $extensionsFromExtListArray = array();
312 try {
313 $extensionsFromExtListArray = $configurationManager->getLocalConfigurationValueByPath('EXT/extListArray');
314 } catch (\RuntimeException $exception) {
315 }
316 foreach ($extensionsFromExtListArray as $loadedExtension) {
317 try {
318 $packageManager->activatePackage($loadedExtension);
319 } catch (\TYPO3\Flow\Package\Exception\UnknownPackageException $exception) {
320 // Skip unavailable packages silently
321 }
322 }
323
324 // Backup LocalConfiguration.php
325 copy(
326 $configurationManager->getLocalConfigurationFileLocation(),
327 $localConfigurationBackupFilePath
328 );
329
330 $packageManager->forceSortAndSavePackageStates();
331
332 // Perform a reload to self, so bootstrap now uses new PackageStates.php
333 $this->redirect();
334 } catch (\Exception $exception) {
335 if (file_exists($packageStatesFilePath)) {
336 unlink($packageStatesFilePath);
337 }
338 if (file_exists($localConfigurationBackupFilePath)) {
339 unlink($localConfigurationBackupFilePath);
340 }
341 throw $exception;
342 }
343 }
344
345 /**
346 * The first install step has a special standing and needs separate handling:
347 * At this point no directory exists (no typo3conf, no typo3temp), so we can
348 * not start the session handling (that stores the install tool session within typo3temp).
349 * This also means, we can not start the token handling for CSRF protection. This
350 * is no real problem, since no local configuration or other security relevant
351 * information was created yet.
352 *
353 * So, if no typo3conf directory exists yet, the first step is just rendered, or
354 * executed if called so. After that, a redirect is initiated to proceed with
355 * other tasks.
356 *
357 * @return void
358 */
359 protected function executeOrOutputFirstInstallStepIfNeeded() {
360 $postValues = $this->getPostValues();
361
362 $wasExecuted = FALSE;
363 $errorMessagesFromExecute = array();
364 if (isset($postValues['action'])
365 && $postValues['action'] === 'environmentAndFolders'
366 ) {
367 /** @var \TYPO3\CMS\Install\Controller\Action\Step\StepInterface $action */
368 $action = $this->objectManager->get('TYPO3\\CMS\\Install\\Controller\\Action\\Step\\EnvironmentAndFolders');
369 $errorMessagesFromExecute = $action->execute();
370 $wasExecuted = TRUE;
371 }
372
373 /** @var \TYPO3\CMS\Install\Controller\Action\Step\StepInterface $action */
374 $action = $this->objectManager->get('TYPO3\\CMS\\Install\\Controller\\Action\\Step\\EnvironmentAndFolders');
375
376 $needsExecution = TRUE;
377 try {
378 // needsExecution() may throw a RedirectException to communicate that it changed
379 // configuration parameters and need an application reload.
380 $needsExecution = $action->needsExecution();
381 } catch (Exception\RedirectException $e) {
382 $this->redirect();
383 }
384
385 $testReflection = new \ReflectionMethod(get_class($this), __FUNCTION__);
386 if (!@is_dir(PATH_typo3conf)
387 || $needsExecution
388 || $testReflection->getDocComment() === FALSE
389 ) {
390 /** @var \TYPO3\CMS\Install\Controller\Action\Step\StepInterface $action */
391 $action = $this->objectManager->get('TYPO3\\CMS\\Install\\Controller\\Action\\Step\\EnvironmentAndFolders');
392 if ($this->isInitialInstallationInProgress()) {
393 $currentStep = (array_search('environmentAndFolders', $this->authenticationActions) + 1);
394 $totalSteps = count($this->authenticationActions);
395 $action->setStepsCounter($currentStep, $totalSteps);
396 }
397 $action->setController('step');
398 $action->setAction('environmentAndFolders');
399 if (count($errorMessagesFromExecute) > 0) {
400 $action->setMessages($errorMessagesFromExecute);
401 }
402 $this->output($action->handle());
403 }
404
405 if ($wasExecuted) {
406 $this->redirect();
407 }
408 }
409
410 /**
411 * Call silent upgrade class, redirect to self if configuration was changed.
412 *
413 * @return void
414 */
415 protected function executeSilentConfigurationUpgradesIfNeeded() {
416 /** @var \TYPO3\CMS\Install\Service\SilentConfigurationUpgradeService $upgradeService */
417 $upgradeService = $this->objectManager->get(
418 'TYPO3\\CMS\\Install\\Service\\SilentConfigurationUpgradeService'
419 );
420 try {
421 $upgradeService->execute();
422 } catch (Exception\RedirectException $e) {
423 $this->redirect();
424 }
425 }
426 }