3995d39e26cc7d734bf6729416660b30e7a02db1
[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 $canProceed = true;
45 if ($postValues['type'] === 'new') {
46 $newDatabaseName = $postValues['new'];
47 if ($this->isValidDatabaseName($newDatabaseName)) {
48 $createDatabaseResult = $this->databaseConnection->admin_query('CREATE DATABASE ' . $newDatabaseName . ' CHARACTER SET utf8');
49 if ($createDatabaseResult) {
50 $localConfigurationPathValuePairs['DB/Connections/Default/dbname'] = $newDatabaseName;
51 } else {
52 /** @var $errorStatus \TYPO3\CMS\Install\Status\ErrorStatus */
53 $errorStatus = GeneralUtility::makeInstance(\TYPO3\CMS\Install\Status\ErrorStatus::class);
54 $errorStatus->setTitle('Unable to create database');
55 $errorStatus->setMessage(
56 'Database with name ' . $newDatabaseName . ' could not be created.' .
57 ' Either your database name contains a reserved keyword or your database' .
58 ' user does not have sufficient permissions to create it.' .
59 ' Please choose an existing (empty) database or contact administration.'
60 );
61 $result[] = $errorStatus;
62 }
63 } else {
64 /** @var $errorStatus \TYPO3\CMS\Install\Status\ErrorStatus */
65 $errorStatus = GeneralUtility::makeInstance(\TYPO3\CMS\Install\Status\ErrorStatus::class);
66 $errorStatus->setTitle('Database name not valid');
67 $errorStatus->setMessage(
68 'Given database name must be shorter than fifty characters' .
69 ' and consist solely of basic latin letters (a-z), digits (0-9), dollar signs ($)' .
70 ' and underscores (_).'
71 );
72 $result[] = $errorStatus;
73 }
74 } elseif ($postValues['type'] === 'existing' && !empty($postValues['existing'])) {
75 // Only store database information when it's empty
76 $this->databaseConnection->setDatabaseName($postValues['existing']);
77 $this->databaseConnection->sql_select_db();
78 $existingTables = $this->databaseConnection->admin_get_tables();
79 $isInitialInstallation = $configurationManager->getConfigurationValueByPath('SYS/isInitialInstallationInProgress');
80 if (!$isInitialInstallation || empty($existingTables)) {
81 $localConfigurationPathValuePairs['DB/Connections/Default/dbname'] = $postValues['existing'];
82 }
83 // check if database charset is utf-8
84 $defaultDatabaseCharset = $this->getDefaultDatabaseCharset();
85 // also allow utf8mb4
86 if (substr($defaultDatabaseCharset, 0, 4) !== 'utf8') {
87 $errorStatus = GeneralUtility::makeInstance(\TYPO3\CMS\Install\Status\ErrorStatus::class);
88 $errorStatus->setTitle('Invalid Charset');
89 $errorStatus->setMessage(
90 'Your database uses character set "' . $defaultDatabaseCharset . '", ' .
91 'but only "utf8" is supported with TYPO3. You probably want to change this before proceeding.'
92 );
93 $result[] = $errorStatus;
94 $canProceed = false;
95 }
96 } else {
97 /** @var $errorStatus \TYPO3\CMS\Install\Status\ErrorStatus */
98 $errorStatus = GeneralUtility::makeInstance(\TYPO3\CMS\Install\Status\ErrorStatus::class);
99 $errorStatus->setTitle('No Database selected');
100 $errorStatus->setMessage('You must select a database.');
101 $result[] = $errorStatus;
102 }
103
104 if ($canProceed && !empty($localConfigurationPathValuePairs)) {
105 $configurationManager->setLocalConfigurationValuesByPathValuePairs($localConfigurationPathValuePairs);
106 }
107
108 return $result;
109 }
110
111 /**
112 * Step needs to be executed if database is not set or can
113 * not be selected.
114 *
115 * @return bool
116 */
117 public function needsExecution()
118 {
119 $this->initializeDatabaseConnection();
120 $result = true;
121 if ((string)$GLOBALS['TYPO3_CONF_VARS']['DB']['Connections']['Default']['dbname'] !== '') {
122 $this->databaseConnection->setDatabaseName($GLOBALS['TYPO3_CONF_VARS']['DB']['Connections']['Default']['dbname']);
123 try {
124 $selectResult = $this->databaseConnection->sql_select_db();
125 if ($selectResult === true) {
126 $result = false;
127 }
128 } catch (\RuntimeException $e) {
129 }
130 }
131 return $result;
132 }
133
134 /**
135 * Executes the step
136 *
137 * @return string Rendered content
138 */
139 protected function executeAction()
140 {
141 /** @var $configurationManager \TYPO3\CMS\Core\Configuration\ConfigurationManager */
142 $configurationManager = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Configuration\ConfigurationManager::class);
143 $isInitialInstallationInProgress = $configurationManager->getConfigurationValueByPath('SYS/isInitialInstallationInProgress');
144 $this->view->assign('databaseList', $this->getDatabaseList($isInitialInstallationInProgress));
145 $this->view->assign('isInitialInstallationInProgress', $isInitialInstallationInProgress);
146 $this->assignSteps();
147 return $this->view->render();
148 }
149
150 /**
151 * Returns list of available databases (with access-check based on username/password)
152 *
153 * @param bool $initialInstallation TRUE if first installation is in progress, FALSE if upgrading or usual access
154 * @return array List of available databases
155 */
156 protected function getDatabaseList($initialInstallation)
157 {
158 $this->initializeDatabaseConnection();
159 $databaseArray = $this->databaseConnection->admin_get_dbs();
160 // Remove mysql organizational tables from database list
161 $reservedDatabaseNames = array('mysql', 'information_schema', 'performance_schema');
162 $allPossibleDatabases = array_diff($databaseArray, $reservedDatabaseNames);
163
164 // If we are upgrading we show *all* databases the user has access to
165 if ($initialInstallation === false) {
166 return $allPossibleDatabases;
167 } else {
168 // In first installation we show all databases but disable not empty ones (with tables)
169 $databases = array();
170 foreach ($allPossibleDatabases as $database) {
171 $this->databaseConnection->setDatabaseName($database);
172 $this->databaseConnection->sql_select_db();
173 $existingTables = $this->databaseConnection->admin_get_tables();
174 $databases[] = array(
175 'name' => $database,
176 'tables' => count($existingTables),
177 );
178 }
179 return $databases;
180 }
181 }
182
183 /**
184 * Initialize database connection
185 *
186 * @return void
187 */
188 protected function initializeDatabaseConnection()
189 {
190 $this->databaseConnection = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Database\DatabaseConnection::class);
191 $this->databaseConnection->setDatabaseUsername($GLOBALS['TYPO3_CONF_VARS']['DB']['Connections']['Default']['user']);
192 $this->databaseConnection->setDatabasePassword($GLOBALS['TYPO3_CONF_VARS']['DB']['Connections']['Default']['password']);
193 $this->databaseConnection->setDatabaseHost($GLOBALS['TYPO3_CONF_VARS']['DB']['Connections']['Default']['host']);
194 $this->databaseConnection->setDatabasePort($GLOBALS['TYPO3_CONF_VARS']['DB']['Connections']['Default']['port']);
195 $this->databaseConnection->setDatabaseSocket($GLOBALS['TYPO3_CONF_VARS']['DB']['Connections']['Default']['unix_socket']);
196 $this->databaseConnection->sql_pconnect();
197 }
198
199 /**
200 * Validate the database name against the lowest common denominator of valid identifiers across different DBMS
201 *
202 * @param string $databaseName
203 * @return bool
204 */
205 protected function isValidDatabaseName($databaseName)
206 {
207 return strlen($databaseName) <= 50 && preg_match('/^[a-zA-Z0-9\$_]*$/', $databaseName);
208 }
209
210 /**
211 * Retrieves the default character set of the database.
212 *
213 * @return string
214 */
215 protected function getDefaultDatabaseCharset()
216 {
217 $result = $this->databaseConnection->admin_query('SHOW VARIABLES LIKE "character_set_database"');
218 $row = $this->databaseConnection->sql_fetch_assoc($result);
219
220 $key = $row['Variable_name'];
221 $value = $row['Value'];
222 $databaseCharset = '';
223
224 if ($key == 'character_set_database') {
225 $databaseCharset = $value;
226 }
227
228 return $databaseCharset;
229 }
230 }