[TASK] Use BE Routing / PSR-7 instead of BackendUtility::getModuleUrl
[Packages/TYPO3.CMS.git] / typo3 / sysext / reports / Classes / Report / Status / ConfigurationStatus.php
1 <?php
2 namespace TYPO3\CMS\Reports\Report\Status;
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\Backend\Utility\BackendUtility;
18 use TYPO3\CMS\Core\Database\ConnectionPool;
19 use TYPO3\CMS\Core\Database\Query\QueryBuilder;
20 use TYPO3\CMS\Core\Localization\LanguageService;
21 use TYPO3\CMS\Core\Registry;
22 use TYPO3\CMS\Core\Utility\GeneralUtility;
23 use TYPO3\CMS\Reports\Status as ReportStatus;
24 use TYPO3\CMS\Reports\StatusProviderInterface;
25
26 /**
27 * Performs some checks about the install tool protection status
28 */
29 class ConfigurationStatus implements StatusProviderInterface
30 {
31 /**
32 * Determines the Install Tool's status, mainly concerning its protection.
33 *
34 * @return array List of statuses
35 */
36 public function getStatus()
37 {
38 $statuses = [
39 'emptyReferenceIndex' => $this->getReferenceIndexStatus(),
40 ];
41 if ($this->isMemcachedUsed()) {
42 $statuses['memcachedConnection'] = $this->getMemcachedConnectionStatus();
43 }
44 if (TYPO3_OS !== 'WIN') {
45 $statuses['createdFilesWorldWritable'] = $this->getCreatedFilesWorldWritableStatus();
46 $statuses['createdDirectoriesWorldWritable'] = $this->getCreatedDirectoriesWorldWritableStatus();
47 }
48 if ($this->isMysqlUsed()) {
49 $statuses['mysqlDatabaseUsesUtf8'] = $this->getMysqlDatabaseUtf8Status();
50 }
51 return $statuses;
52 }
53
54 /**
55 * Checks if sys_refindex is empty.
56 *
57 * @return \TYPO3\CMS\Reports\Status An object representing whether the reference index is empty or not
58 */
59 protected function getReferenceIndexStatus()
60 {
61 $value = $this->getLanguageService()->getLL('status_ok');
62 $message = '';
63 $severity = ReportStatus::OK;
64
65 $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('sys_refindex');
66 $count = $queryBuilder
67 ->count('*')
68 ->from('sys_refindex')
69 ->execute()
70 ->fetchColumn(0);
71
72 $registry = GeneralUtility::makeInstance(Registry::class);
73 $lastRefIndexUpdate = $registry->get('core', 'sys_refindex_lastUpdate');
74 /** @var \TYPO3\CMS\Backend\Routing\UriBuilder $uriBuilder */
75 $uriBuilder = GeneralUtility::makeInstance(\TYPO3\CMS\Backend\Routing\UriBuilder::class);
76 if (!$count && $lastRefIndexUpdate) {
77 $value = $this->getLanguageService()->getLL('status_empty');
78 $severity = ReportStatus::WARNING;
79 $url = (string)$uriBuilder->buildUriFromRoute('system_dbint') . '&id=0&SET[function]=refindex';
80 $message = sprintf($this->getLanguageService()->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:warning.backend_reference_index'), '<a href="' . htmlspecialchars($url) . '">', '</a>', BackendUtility::datetime($lastRefIndexUpdate));
81 }
82 return GeneralUtility::makeInstance(ReportStatus::class, $this->getLanguageService()->getLL('status_referenceIndex'), $value, $message, $severity);
83 }
84
85 /**
86 * Checks whether memcached is configured, if that's the case we assume it's also used.
87 *
88 * @return bool TRUE if memcached is used, FALSE otherwise.
89 */
90 protected function isMemcachedUsed()
91 {
92 $memcachedUsed = false;
93 $memcachedServers = $this->getConfiguredMemcachedServers();
94 if (!empty($memcachedServers)) {
95 $memcachedUsed = true;
96 }
97 return $memcachedUsed;
98 }
99
100 /**
101 * Gets the configured memcached server connections.
102 *
103 * @return array An array of configured memcached server connections.
104 */
105 protected function getConfiguredMemcachedServers()
106 {
107 $configurations = $GLOBALS['TYPO3_CONF_VARS']['SYS']['caching']['cacheConfigurations'] ?? [];
108 $memcachedServers = [];
109 foreach ($configurations as $table => $conf) {
110 if (is_array($conf)) {
111 foreach ($conf as $key => $value) {
112 if ($value === \TYPO3\CMS\Core\Cache\Backend\MemcachedBackend::class) {
113 $memcachedServers = $configurations[$table]['options']['servers'];
114 break;
115 }
116 }
117 }
118 }
119 return $memcachedServers;
120 }
121
122 /**
123 * Checks whether TYPO3 can connect to the configured memcached servers.
124 *
125 * @return \TYPO3\CMS\Reports\Status An object representing whether TYPO3 can connect to the configured memcached servers
126 */
127 protected function getMemcachedConnectionStatus()
128 {
129 $value = $this->getLanguageService()->getLL('status_ok');
130 $message = '';
131 $severity = ReportStatus::OK;
132 $failedConnections = [];
133 $defaultMemcachedPort = ini_get('memcache.default_port');
134 $memcachedServers = $this->getConfiguredMemcachedServers();
135 if (function_exists('memcache_connect') && is_array($memcachedServers)) {
136 foreach ($memcachedServers as $testServer) {
137 $configuredServer = $testServer;
138 if (substr($testServer, 0, 7) === 'unix://') {
139 $host = $testServer;
140 $port = 0;
141 } else {
142 if (substr($testServer, 0, 6) === 'tcp://') {
143 $testServer = substr($testServer, 6);
144 }
145 if (strstr($testServer, ':') !== false) {
146 list($host, $port) = explode(':', $testServer, 2);
147 } else {
148 $host = $testServer;
149 $port = $defaultMemcachedPort;
150 }
151 }
152 $memcachedConnection = @memcache_connect($host, $port);
153 if ($memcachedConnection != null) {
154 memcache_close($memcachedConnection);
155 } else {
156 $failedConnections[] = $configuredServer;
157 }
158 }
159 }
160 if (!empty($failedConnections)) {
161 $value = $this->getLanguageService()->getLL('status_connectionFailed');
162 $severity = ReportStatus::WARNING;
163 $message = $this->getLanguageService()->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:warning.memcache_not_usable') . '<br /><br />' . '<ul><li>' . implode('</li><li>', $failedConnections) . '</li></ul>';
164 }
165 return GeneralUtility::makeInstance(ReportStatus::class, $this->getLanguageService()->getLL('status_memcachedConfiguration'), $value, $message, $severity);
166 }
167
168 /**
169 * Warning, if fileCreateMask has write bit for 'others' set.
170 *
171 * @return \TYPO3\CMS\Reports\Status The writable status for 'others'
172 */
173 protected function getCreatedFilesWorldWritableStatus()
174 {
175 $value = $this->getLanguageService()->getLL('status_ok');
176 $message = '';
177 $severity = ReportStatus::OK;
178 if ((int)$GLOBALS['TYPO3_CONF_VARS']['SYS']['fileCreateMask'] % 10 & 2) {
179 $value = $GLOBALS['TYPO3_CONF_VARS']['SYS']['fileCreateMask'];
180 $severity = ReportStatus::WARNING;
181 $message = $this->getLanguageService()->getLL('status_CreatedFilePermissions.writable');
182 }
183 return GeneralUtility::makeInstance(ReportStatus::class, $this->getLanguageService()->getLL('status_CreatedFilePermissions'), $value, $message, $severity);
184 }
185
186 /**
187 * Warning, if folderCreateMask has write bit for 'others' set.
188 *
189 * @return \TYPO3\CMS\Reports\Status The writable status for 'others'
190 */
191 protected function getCreatedDirectoriesWorldWritableStatus()
192 {
193 $value = $this->getLanguageService()->getLL('status_ok');
194 $message = '';
195 $severity = ReportStatus::OK;
196 if ((int)$GLOBALS['TYPO3_CONF_VARS']['SYS']['folderCreateMask'] % 10 & 2) {
197 $value = $GLOBALS['TYPO3_CONF_VARS']['SYS']['folderCreateMask'];
198 $severity = ReportStatus::WARNING;
199 $message = $this->getLanguageService()->getLL('status_CreatedDirectoryPermissions.writable');
200 }
201 return GeneralUtility::makeInstance(ReportStatus::class, $this->getLanguageService()->getLL('status_CreatedDirectoryPermissions'), $value, $message, $severity);
202 }
203
204 /**
205 * Checks if the default connection is a MySQL compatible database instance.
206 *
207 * @return bool
208 */
209 protected function isMysqlUsed()
210 {
211 $connection = GeneralUtility::makeInstance(ConnectionPool::class)
212 ->getConnectionByName(ConnectionPool::DEFAULT_CONNECTION_NAME);
213
214 return strpos($connection->getServerVersion(), 'MySQL') === 0;
215 }
216
217 /**
218 * Checks the character set of the default database and reports an error if it is not utf-8.
219 *
220 * @return ReportStatus
221 */
222 protected function getMysqlDatabaseUtf8Status()
223 {
224 $connection = GeneralUtility::makeInstance(ConnectionPool::class)
225 ->getConnectionByName(ConnectionPool::DEFAULT_CONNECTION_NAME);
226 /** @var QueryBuilder $queryBuilder */
227 $queryBuilder = $connection->createQueryBuilder();
228 $defaultDatabaseCharset = (string)$queryBuilder->select('DEFAULT_CHARACTER_SET_NAME')
229 ->from('information_schema.SCHEMATA')
230 ->where(
231 $queryBuilder->expr()->eq(
232 'SCHEMA_NAME',
233 $queryBuilder->createNamedParameter($connection->getDatabase(), \PDO::PARAM_STR)
234 )
235 )
236 ->setMaxResults(1)
237 ->execute()
238 ->fetchColumn();
239
240 $severity = ReportStatus::OK;
241 $statusValue = $this->getLanguageService()->getLL('status_ok');
242 // also allow utf8mb4
243 if (strpos($defaultDatabaseCharset, 'utf8') !== 0) {
244 // If the default character set is e.g. latin1, BUT all tables in the system are UTF-8,
245 // we assume that TYPO3 has the correct charset for adding tables, and everything is fine
246 $nonUtf8TableCollationsFound = $queryBuilder->select('table_collation')
247 ->from('information_schema.tables')
248 ->where(
249 $queryBuilder->expr()->andX(
250 $queryBuilder->expr()->eq('table_schema', $queryBuilder->quote($connection->getDatabase())),
251 $queryBuilder->expr()->notLike('table_collation', $queryBuilder->quote('utf8%'))
252 )
253 )
254 ->setMaxResults(1)
255 ->execute();
256
257 if ($nonUtf8TableCollationsFound->rowCount() > 0) {
258 $message = sprintf($this->getLanguageService()
259 ->getLL('status_MysqlDatabaseCharacterSet_Unsupported'), $defaultDatabaseCharset);
260 $severity = ReportStatus::ERROR;
261 $statusValue = $this->getLanguageService()->getLL('status_wrongValue');
262 } else {
263 $message = $this->getLanguageService()->getLL('status_MysqlDatabaseCharacterSet_Info');
264 $severity = ReportStatus::INFO;
265 $statusValue = $this->getLanguageService()->getLL('status_info');
266 }
267 } else {
268 $message = $this->getLanguageService()->getLL('status_MysqlDatabaseCharacterSet_Ok');
269 }
270
271 return GeneralUtility::makeInstance(
272 ReportStatus::class,
273 $this->getLanguageService()->getLL('status_MysqlDatabaseCharacterSet'),
274 $statusValue,
275 $message,
276 $severity
277 );
278 }
279
280 /**
281 * @return LanguageService
282 */
283 protected function getLanguageService()
284 {
285 return $GLOBALS['LANG'];
286 }
287 }