27702e76d4190133f48b5bf40a6dce42e172b37e
[Packages/TYPO3.CMS.git] / typo3 / sysext / install / Classes / Service / SilentConfigurationUpgradeService.php
1 <?php
2 namespace TYPO3\CMS\Install\Service;
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\ExtensionManagementUtility;
28 use TYPO3\CMS\Core\Utility\GeneralUtility;
29
30 /**
31 * Execute "silent" LocalConfiguration upgrades if needed.
32 *
33 * Some LocalConfiguration settings are obsolete or changed over time.
34 * This class handles upgrades of these settings. It is called by
35 * the step controller at an early point.
36 *
37 * Every change is encapsulated in one method an must throw a RedirectException
38 * if new data is written to LocalConfiguration. This is catched by above
39 * step controller to initiate a redirect and start again with adapted configuration.
40 */
41 class SilentConfigurationUpgradeService {
42
43 /**
44 * @var \TYPO3\CMS\Extbase\Object\ObjectManager
45 * @inject
46 */
47 protected $objectManager = NULL;
48
49 /**
50 * @var \TYPO3\CMS\Core\Configuration\ConfigurationManager
51 * @inject
52 */
53 protected $configurationManager = NULL;
54
55 /**
56 * @var array List of obsolete configuration options in LocalConfiguration to be removed
57 */
58 protected $obsoleteLocalConfigurationSettings = array(
59 // #34092
60 'BE/forceCharset',
61 // #26519
62 'BE/loginLabels',
63 // #44506
64 'BE/loginNews',
65 // #52013
66 'BE/TSconfigConditions',
67 // #30613
68 'BE/useOnContextMenuHandler',
69 // #48179
70 'EXT/em_mirrorListURL',
71 'EXT/em_wsdlURL',
72 // #43094
73 'EXT/extList',
74 // #35877
75 'EXT/extList_FE',
76 // #41813
77 'EXT/noEdit',
78 // #26090
79 'FE/defaultTypoScript_editorcfg',
80 'FE/defaultTypoScript_editorcfg.',
81 // #25099
82 'FE/simulateStaticDocuments',
83 // #52786
84 'FE/logfile_dir',
85 // #52011
86 'GFX/im_combine_filename',
87 // #52088
88 'GFX/im_imvMaskState',
89 // #22687
90 'GFX/gdlib_2',
91 // #52012
92 'GFX/im_mask_temp_ext_noloss',
93 // #52088
94 'GFX/im_negate_mask',
95 // #52010
96 'GFX/im_no_effects',
97 // #18431
98 'GFX/noIconProc',
99 // #17606
100 'GFX/TTFLocaleConv',
101 // #39164
102 'SYS/additionalAllowedClassPrefixes',
103 // #27689
104 'SYS/caching/cacheBackends',
105 'SYS/caching/cacheFrontends',
106 // #38414
107 'SYS/extCache',
108 // #35923
109 'SYS/multiplyDBfieldSize',
110 // #46993
111 'SYS/T3instID',
112 // #52857
113 'SYS/forceReturnPath',
114 );
115
116 /**
117 * Executed configuration upgrades. Single upgrade methods must throw a
118 * RedirectException if something was written to LocalConfiguration.
119 *
120 * @return void
121 */
122 public function execute() {
123 $this->generateEncryptionKeyIfNeeded();
124 $this->configureBackendLoginSecurity();
125 $this->configureSaltedPasswords();
126 $this->setProxyAuthScheme();
127 $this->disableImageMagickAndGdlibIfImageProcessingIsDisabled();
128 $this->disableImageMagickDetailSettingsIfImageMagickIsDisabled();
129 $this->setImageMagickDetailSettings();
130 $this->addFileTableToDefaultCategorizedTablesIfAlreadyCustomized();
131 $this->removeObsoleteLocalConfigurationSettings();
132 }
133
134 /**
135 * Some settings in LocalConfiguration vanished in DefaultConfiguration
136 * and have no impact on the core anymore.
137 * To keep the configuration clean, those old settings are just silently
138 * removed from LocalConfiguration if set.
139 *
140 * @return void
141 */
142 protected function removeObsoleteLocalConfigurationSettings() {
143 $removed = $this->configurationManager->removeLocalConfigurationKeysByPath($this->obsoleteLocalConfigurationSettings);
144 // If something was changed: Trigger a reload to have new values in next request
145 if ($removed) {
146 $this->throwRedirectException();
147 }
148 }
149
150 /**
151 * Backend login security is set to rsa if rsaauth
152 * is installed (but not used) otherwise the default value "normal" has to be used.
153 * This forces either 'normal' or 'rsa' to be set in LocalConfiguration.
154 *
155 * @return void
156 */
157 protected function configureBackendLoginSecurity() {
158 try {
159 $currentLoginSecurityLevelValue = $this->configurationManager->getLocalConfigurationValueByPath('BE/loginSecurityLevel');
160 if (ExtensionManagementUtility::isLoaded('rsaauth')
161 && $currentLoginSecurityLevelValue !== 'rsa'
162 ) {
163 $this->configurationManager->setLocalConfigurationValueByPath('BE/loginSecurityLevel', 'rsa');
164 $this->throwRedirectException();
165 } elseif (!ExtensionManagementUtility::isLoaded('rsaauth')
166 && $currentLoginSecurityLevelValue !== 'normal'
167 ) {
168 $this->configurationManager->setLocalConfigurationValueByPath('BE/loginSecurityLevel', 'normal');
169 $this->throwRedirectException();
170 }
171 } catch (\RuntimeException $e) {
172 // If an exception is thrown, the value is not set in LocalConfiguration
173 if (ExtensionManagementUtility::isLoaded('rsaauth')) {
174 $this->configurationManager->setLocalConfigurationValueByPath('BE/loginSecurityLevel', 'rsa');
175 $this->throwRedirectException();
176 } elseif (!ExtensionManagementUtility::isLoaded('rsaauth')) {
177 $this->configurationManager->setLocalConfigurationValueByPath('BE/loginSecurityLevel', 'normal');
178 $this->throwRedirectException();
179 }
180 }
181 }
182
183 /**
184 * Check the settings for salted passwords extension to load it as a required extension.
185 * Unset obsolete configuration options if given.
186 *
187 * @return void
188 */
189 protected function configureSaltedPasswords() {
190 $defaultConfiguration = $this->configurationManager->getDefaultConfiguration();
191 $defaultExtensionConfiguration = unserialize($defaultConfiguration['EXT']['extConf']['saltedpasswords']);
192 try {
193 $extensionConfiguration = @unserialize($this->configurationManager->getLocalConfigurationValueByPath('EXT/extConf/saltedpasswords'));
194 } catch (\RuntimeException $e) {
195 $extensionConfiguration = array();
196 }
197 if (is_array($extensionConfiguration) && !empty($extensionConfiguration)) {
198 if (isset($extensionConfiguration['BE.']['enabled'])) {
199 if ($extensionConfiguration['BE.']['enabled']) {
200 unset($extensionConfiguration['BE.']['enabled']);
201 } else {
202 $extensionConfiguration['BE.'] = $defaultExtensionConfiguration['BE.'];
203 }
204 $this->configurationManager->setLocalConfigurationValueByPath(
205 'EXT/extConf/saltedpasswords',
206 serialize($extensionConfiguration)
207 );
208 $this->throwRedirectException();
209 }
210 } else {
211 $this->configurationManager->setLocalConfigurationValueByPath(
212 'EXT/extConf/saltedpasswords',
213 serialize($defaultExtensionConfiguration)
214 );
215 $this->throwRedirectException();
216 }
217 }
218
219 /**
220 * The encryption key is crucial for securing form tokens
221 * and the whole TYPO3 link rendering later on. A random key is set here in
222 * LocalConfiguration if it does not exist yet. This might possible happen
223 * during upgrading and will happen during first install.
224 *
225 * @return void
226 */
227 protected function generateEncryptionKeyIfNeeded() {
228 try{
229 $currentValue = $this->configurationManager->getLocalConfigurationValueByPath('SYS/encryptionKey');
230 } catch (\RuntimeException $e) {
231 // If an exception is thrown, the value is not set in LocalConfiguration
232 $currentValue = '';
233 }
234
235 if (empty($currentValue)) {
236 $randomKey = GeneralUtility::getRandomHexString(96);
237 $this->configurationManager->setLocalConfigurationValueByPath('SYS/encryptionKey', $randomKey);
238 $this->throwRedirectException();
239 }
240 }
241
242 /**
243 * $GLOBALS['TYPO3_CONF_VARS']['HTTP']['proxy_auth_scheme'] must be either
244 * 'digest' or 'basic'. 'basic' is default in DefaultConfiguration, so the
245 * setting can be removed from LocalConfiguration if it is not set to 'digest'.
246 *
247 * @return void
248 */
249 protected function setProxyAuthScheme() {
250 // Get current value from LocalConfiguration
251 try {
252 $currentValueInLocalConfiguration = $this->configurationManager->getLocalConfigurationValueByPath('HTTP/proxy_auth_scheme');
253 } catch (\RuntimeException $e) {
254 // If an exception is thrown, the value is not set in LocalConfiguration, so we don't need to do anything
255 return;
256 }
257 if ($currentValueInLocalConfiguration !== 'digest') {
258 $this->configurationManager->removeLocalConfigurationKeysByPath(array('HTTP/proxy_auth_scheme'));
259 $this->throwRedirectException();
260 }
261 }
262
263 /**
264 * GFX/im and GFX/gdlib must be set to 0 if image_processing is disabled.
265 *
266 * "Configuration presets" in install tool is not type safe, so value
267 * comparisons here are not type safe too, to not trigger changes to
268 * LocalConfiguration again.
269 *
270 * @return void
271 */
272 protected function disableImageMagickAndGdlibIfImageProcessingIsDisabled() {
273 $changedValues = array();
274 try {
275 $currentImageProcessingValue = $this->configurationManager->getLocalConfigurationValueByPath('GFX/image_processing');
276 } catch (\RuntimeException $e) {
277 $currentImageProcessingValue = $this->configurationManager->getDefaultConfigurationValueByPath('GFX/image_processing');
278 }
279 try {
280 $currentImValue = $this->configurationManager->getLocalConfigurationValueByPath('GFX/im');
281 } catch (\RuntimeException $e) {
282 $currentImValue = $this->configurationManager->getDefaultConfigurationValueByPath('GFX/im');
283 }
284 try {
285 $currentGdlibValue = $this->configurationManager->getLocalConfigurationValueByPath('GFX/gdlib');
286 } catch (\RuntimeException $e) {
287 $currentGdlibValue = $this->configurationManager->getDefaultConfigurationValueByPath('GFX/gdlib');
288 }
289 // If image processing is fully disabled, im and gdlib sub settings must be 0
290 if (!$currentImageProcessingValue) {
291 if ($currentImValue != 0) {
292 $changedValues['GFX/im'] = 0;
293 }
294 if ($currentGdlibValue != 0) {
295 $changedValues['GFX/gdlib'] = 0;
296 }
297 }
298 if (count($changedValues) > 0) {
299 $this->configurationManager->setLocalConfigurationValuesByPathValuePairs($changedValues);
300 $this->throwRedirectException();
301 }
302 }
303
304 /**
305 * Detail configuration of Image Magick settings must be cleared
306 * if Image Magick handling is disabled.
307 *
308 * "Configuration presets" in install tool is not type safe, so value
309 * comparisons here are not type safe too, to not trigger changes to
310 * LocalConfiguration again.
311 *
312 * @return void
313 */
314 protected function disableImageMagickDetailSettingsIfImageMagickIsDisabled() {
315 $changedValues = array();
316 try {
317 $currentImValue = $this->configurationManager->getLocalConfigurationValueByPath('GFX/im');
318 }
319 catch (\RuntimeException $e) {
320 $currentImValue = $this->configurationManager->getDefaultConfigurationValueByPath('GFX/im');
321 }
322 try {
323 $currentImPathValue = $this->configurationManager->getLocalConfigurationValueByPath('GFX/im_path');
324 }
325 catch (\RuntimeException $e) {
326 $currentImPathValue = $this->configurationManager->getDefaultConfigurationValueByPath('GFX/im_path');
327 }
328 try {
329 $currentImPathLzwValue = $this->configurationManager->getLocalConfigurationValueByPath('GFX/im_path_lzw');
330 }
331 catch (\RuntimeException $e) {
332 $currentImPathLzwValue = $this->configurationManager->getDefaultConfigurationValueByPath('GFX/im_path_lzw');
333 }
334 try {
335 $currentImageFileExtValue = $this->configurationManager->getLocalConfigurationValueByPath('GFX/imagefile_ext');
336 }
337 catch (\RuntimeException $e) {
338 $currentImageFileExtValue = $this->configurationManager->getDefaultConfigurationValueByPath('GFX/imagefile_ext');
339 }
340 try {
341 $currentThumbnailsValue = $this->configurationManager->getLocalConfigurationValueByPath('GFX/thumbnails');
342 }
343 catch (\RuntimeException $e) {
344 $currentThumbnailsValue = $this->configurationManager->getDefaultConfigurationValueByPath('GFX/thumbnails');
345 }
346 if (!$currentImValue) {
347 if ($currentImPathValue != '') {
348 $changedValues['GFX/im_path'] = '';
349 }
350 if ($currentImPathLzwValue != '') {
351 $changedValues['GFX/im_path_lzw'] = '';
352 }
353 if ($currentImageFileExtValue !== 'gif,jpg,jpeg,png') {
354 $changedValues['GFX/imagefile_ext'] = 'gif,jpg,jpeg,png';
355 }
356 if ($currentThumbnailsValue != 0) {
357 $changedValues['GFX/thumbnails'] = 0;
358 }
359 }
360 if (count($changedValues) > 0) {
361 $this->configurationManager->setLocalConfigurationValuesByPathValuePairs($changedValues);
362 $this->throwRedirectException();
363 }
364 }
365
366 /**
367 * Detail configuration of Image Magick and Graphics Magick settings
368 * depending on main values.
369 *
370 * "Configuration presets" in install tool is not type safe, so value
371 * comparisons here are not type safe too, to not trigger changes to
372 * LocalConfiguration again.
373 *
374 * @return void
375 */
376 protected function setImageMagickDetailSettings() {
377 $changedValues = array();
378 try {
379 $currentIm5Value = $this->configurationManager->getLocalConfigurationValueByPath('GFX/im_version_5');
380 }
381 catch (\RuntimeException $e) {
382 $currentIm5Value = $this->configurationManager->getDefaultConfigurationValueByPath('GFX/im_version_5');
383 }
384 try {
385 $currentImMaskValue = $this->configurationManager->getLocalConfigurationValueByPath('GFX/im_mask_temp_ext_gif');
386 }
387 catch (\RuntimeException $e) {
388 $currentImMaskValue = $this->configurationManager->getDefaultConfigurationValueByPath('GFX/im_mask_temp_ext_gif');
389 }
390 try {
391 $currentIm5EffectsValue = $this->configurationManager->getLocalConfigurationValueByPath('GFX/im_v5effects');
392 }
393 catch (\RuntimeException $e) {
394 $currentIm5EffectsValue = $this->configurationManager->getDefaultConfigurationValueByPath('GFX/im_v5effects');
395 }
396 if (strlen($currentIm5Value) > 0) {
397 if ($currentImMaskValue != 1) {
398 $changedValues['GFX/im_mask_temp_ext_gif'] = 1;
399 }
400 if ($currentIm5Value === 'gm') {
401 if ($currentIm5EffectsValue != -1) {
402 $changedValues['GFX/im_v5effects'] = -1;
403 }
404 }
405 }
406 if (count($changedValues) > 0) {
407 $this->configurationManager->setLocalConfigurationValuesByPathValuePairs($changedValues);
408 $this->throwRedirectException();
409 }
410 }
411
412 /**
413 * Make sure file table is categorized as of TYPO3 6.2. To enable DAM Migration
414 * sys_file_metadata table is included in DefaultConfiguration.
415 * If the setting already has been modified but does not contain sys_file_metadata: add it
416 *
417 * @return void
418 */
419 protected function addFileTableToDefaultCategorizedTablesIfAlreadyCustomized() {
420 /** @var \TYPO3\CMS\Core\Configuration\ConfigurationManager $configurationManager */
421 $configurationManager = $this->objectManager->get('TYPO3\\CMS\\Core\\Configuration\\ConfigurationManager');
422
423 $default = $configurationManager->getDefaultConfigurationValueByPath('SYS/defaultCategorizedTables');
424 try {
425 $actual = $configurationManager->getLocalConfigurationValueByPath('SYS/defaultCategorizedTables');
426 } catch(\RuntimeException $e) {
427 $actual = '';
428 }
429
430 $tables = GeneralUtility::trimExplode(',', $actual);
431 if ($actual !== '' && $actual !== $default && !in_array('sys_file_metadata', $tables)) {
432 $tables[] = 'sys_file_metadata';
433 $configurationManager->setLocalConfigurationValueByPath('SYS/defaultCategorizedTables', implode(',', $tables));
434 $this->throwRedirectException();
435 }
436 }
437
438 /**
439 * Throw exception after configuration change to trigger a redirect.
440 *
441 * @throws \TYPO3\CMS\Install\Controller\Exception\RedirectException
442 */
443 protected function throwRedirectException() {
444 throw new \TYPO3\CMS\Install\Controller\Exception\RedirectException(
445 'Configuration updated, reload needed',
446 1379024938
447 );
448 }
449 }