e892f0be22d6d91df690a24eb2b246365821bf9f
[Packages/TYPO3.CMS.git] / typo3 / sysext / install / Classes / Controller / Action / Step / DatabaseSelect.php
1 <?php
2 namespace TYPO3\CMS\Install\Controller\Action\Step;
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 * Database select step.
21 * This step is only rendered if database is mysql. With dbal,
22 * database name is submitted by previous step already.
23 */
24 class DatabaseSelect extends AbstractStepAction
25 {
26 /**
27 * @var \TYPO3\CMS\Core\Database\DatabaseConnection
28 */
29 protected $databaseConnection = null;
30
31 /**
32 * Create database if needed, save selected db name in configuration
33 *
34 * @return array<\TYPO3\CMS\Install\Status\StatusInterface>
35 */
36 public function execute()
37 {
38 $result = array();
39 $this->initializeDatabaseConnection();
40 $postValues = $this->postValues['values'];
41 $localConfigurationPathValuePairs = array();
42 /** @var $configurationManager \TYPO3\CMS\Core\Configuration\ConfigurationManager */
43 $configurationManager = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Configuration\ConfigurationManager::class);
44 if ($postValues['type'] === 'new') {
45 $newDatabaseName = $postValues['new'];
46 if ($this->isValidDatabaseName($newDatabaseName)) {
47 $createDatabaseResult = $this->databaseConnection->admin_query('CREATE DATABASE ' . $newDatabaseName . ' CHARACTER SET utf8');
48 if ($createDatabaseResult) {
49 $localConfigurationPathValuePairs['DB/database'] = $newDatabaseName;
50 } else {
51 /** @var $errorStatus \TYPO3\CMS\Install\Status\ErrorStatus */
52 $errorStatus = GeneralUtility::makeInstance(\TYPO3\CMS\Install\Status\ErrorStatus::class);
53 $errorStatus->setTitle('Unable to create database');
54 $errorStatus->setMessage(
55 'Database with name ' . $newDatabaseName . ' could not be created.' .
56 ' Either your database name contains a reserved keyword or your database' .
57 ' user does not have sufficient permissions to create it.' .
58 ' Please choose an existing (empty) database or contact administration.'
59 );
60 $result[] = $errorStatus;
61 }
62 } else {
63 /** @var $errorStatus \TYPO3\CMS\Install\Status\ErrorStatus */
64 $errorStatus = GeneralUtility::makeInstance(\TYPO3\CMS\Install\Status\ErrorStatus::class);
65 $errorStatus->setTitle('Database name not valid');
66 $errorStatus->setMessage(
67 'Given database name must be shorter than fifty characters' .
68 ' and consist solely of basic latin letters (a-z), digits (0-9), dollar signs ($)' .
69 ' and underscores (_).'
70 );
71 $result[] = $errorStatus;
72 }
73 } elseif ($postValues['type'] === 'existing' && !empty($postValues['existing'])) {
74 // Only store database information when it's empty
75 $this->databaseConnection->setDatabaseName($postValues['existing']);
76 $this->databaseConnection->sql_select_db();
77 $existingTables = $this->databaseConnection->admin_get_tables();
78 $isInitialInstallation = $configurationManager->getConfigurationValueByPath('SYS/isInitialInstallationInProgress');
79 if (!$isInitialInstallation || empty($existingTables)) {
80 $localConfigurationPathValuePairs['DB/database'] = $postValues['existing'];
81 }
82 } else {
83 /** @var $errorStatus \TYPO3\CMS\Install\Status\ErrorStatus */
84 $errorStatus = GeneralUtility::makeInstance(\TYPO3\CMS\Install\Status\ErrorStatus::class);
85 $errorStatus->setTitle('No Database selected');
86 $errorStatus->setMessage('You must select a database.');
87 $result[] = $errorStatus;
88 }
89
90 if (!empty($localConfigurationPathValuePairs)) {
91 $configurationManager->setLocalConfigurationValuesByPathValuePairs($localConfigurationPathValuePairs);
92 }
93
94 return $result;
95 }
96
97 /**
98 * Step needs to be executed if database is not set or can
99 * not be selected.
100 *
101 * @return bool
102 */
103 public function needsExecution()
104 {
105 $this->initializeDatabaseConnection();
106 $result = true;
107 if ((string)$GLOBALS['TYPO3_CONF_VARS']['DB']['database'] !== '') {
108 $this->databaseConnection->setDatabaseName($GLOBALS['TYPO3_CONF_VARS']['DB']['database']);
109 try {
110 $selectResult = $this->databaseConnection->sql_select_db();
111 if ($selectResult === true) {
112 $result = false;
113 }
114 } catch (\RuntimeException $e) {
115 }
116 }
117 return $result;
118 }
119
120 /**
121 * Executes the step
122 *
123 * @return string Rendered content
124 */
125 protected function executeAction()
126 {
127 /** @var $configurationManager \TYPO3\CMS\Core\Configuration\ConfigurationManager */
128 $configurationManager = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Configuration\ConfigurationManager::class);
129 $isInitialInstallationInProgress = $configurationManager->getConfigurationValueByPath('SYS/isInitialInstallationInProgress');
130 $this->view->assign('databaseList', $this->getDatabaseList($isInitialInstallationInProgress));
131 $this->view->assign('isInitialInstallationInProgress', $isInitialInstallationInProgress);
132 $this->assignSteps();
133 return $this->view->render();
134 }
135
136 /**
137 * Returns list of available databases (with access-check based on username/password)
138 *
139 * @param bool $initialInstallation TRUE if first installation is in progress, FALSE if upgrading or usual access
140 * @return array List of available databases
141 */
142 protected function getDatabaseList($initialInstallation)
143 {
144 $this->initializeDatabaseConnection();
145 $databaseArray = $this->databaseConnection->admin_get_dbs();
146 // Remove mysql organizational tables from database list
147 $reservedDatabaseNames = array('mysql', 'information_schema', 'performance_schema');
148 $allPossibleDatabases = array_diff($databaseArray, $reservedDatabaseNames);
149
150 // If we are upgrading we show *all* databases the user has access to
151 if ($initialInstallation === false) {
152 return $allPossibleDatabases;
153 } else {
154 // In first installation we show all databases but disable not empty ones (with tables)
155 $databases = array();
156 foreach ($allPossibleDatabases as $database) {
157 $this->databaseConnection->setDatabaseName($database);
158 $this->databaseConnection->sql_select_db();
159 $existingTables = $this->databaseConnection->admin_get_tables();
160 $databases[] = array(
161 'name' => $database,
162 'tables' => count($existingTables),
163 );
164 }
165 return $databases;
166 }
167 }
168
169 /**
170 * Initialize database connection
171 *
172 * @return void
173 */
174 protected function initializeDatabaseConnection()
175 {
176 $this->databaseConnection = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Database\DatabaseConnection::class);
177 $this->databaseConnection->setDatabaseUsername($GLOBALS['TYPO3_CONF_VARS']['DB']['username']);
178 $this->databaseConnection->setDatabasePassword($GLOBALS['TYPO3_CONF_VARS']['DB']['password']);
179 $this->databaseConnection->setDatabaseHost($GLOBALS['TYPO3_CONF_VARS']['DB']['host']);
180 $this->databaseConnection->setDatabasePort($GLOBALS['TYPO3_CONF_VARS']['DB']['port']);
181 $this->databaseConnection->setDatabaseSocket($GLOBALS['TYPO3_CONF_VARS']['DB']['socket']);
182 $this->databaseConnection->sql_pconnect();
183 }
184
185 /**
186 * Validate the database name against the lowest common denominator of valid identifiers across different DBMS
187 *
188 * @param string $databaseName
189 * @return bool
190 */
191 protected function isValidDatabaseName($databaseName)
192 {
193 return strlen($databaseName) <= 50 && preg_match('/^[a-zA-Z0-9\$_]*$/', $databaseName);
194 }
195 }