[BUGFIX] Base silent updates on LocalConfiguration values only
[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 // #52011
84 'GFX/im_combine_filename',
85 // #52088
86 'GFX/im_imvMaskState',
87 // #22687
88 'GFX/gdlib_2',
89 // #52012
90 'GFX/im_mask_temp_ext_noloss',
91 // #52088
92 'GFX/im_negate_mask',
93 // #52010
94 'GFX/im_no_effects',
95 // #18431
96 'GFX/noIconProc',
97 // #17606
98 'GFX/TTFLocaleConv',
99 // #39164
100 'SYS/additionalAllowedClassPrefixes',
101 // #27689
102 'SYS/caching/cacheBackends',
103 'SYS/caching/cacheFrontends',
104 // #38414
105 'SYS/extCache',
106 // #35923
107 'SYS/multiplyDBfieldSize',
108 // #46993
109 'SYS/T3instID',
110 );
111
112 /**
113 * Executed configuration upgrades. Single upgrade methods must throw a
114 * RedirectException if something was written to LocalConfiguration.
115 *
116 * @return void
117 */
118 public function execute() {
119 $this->generateEncryptionKeyIfNeeded();
120 $this->configureBackendLoginSecurity();
121 $this->configureSaltedPasswords();
122 $this->setProxyAuthScheme();
123 $this->disableImageMagickAndGdlibIfImageProcessingIsDisabled();
124 $this->disableImageMagickDetailSettingsIfImageMagickIsDisabled();
125 $this->setImageMagickDetailSettings();
126 $this->removeObsoleteLocalConfigurationSettings();
127 }
128
129 /**
130 * Some settings in LocalConfiguration vanished in DefaultConfiguration
131 * and have no impact on the core anymore.
132 * To keep the configuration clean, those old settings are just silently
133 * removed from LocalConfiguration if set.
134 *
135 * @return void
136 */
137 protected function removeObsoleteLocalConfigurationSettings() {
138 $removed = $this->configurationManager->removeLocalConfigurationKeysByPath($this->obsoleteLocalConfigurationSettings);
139 // If something was changed: Trigger a reload to have new values in next request
140 if ($removed) {
141 $this->throwRedirectException();
142 }
143 }
144
145 /**
146 * Backend login security is set to rsa if rsaauth
147 * is installed (but not used) otherwise the default value "normal" has to be used.
148 * This forces either 'normal' or 'rsa' to be set in LocalConfiguration.
149 *
150 * @return void
151 */
152 protected function configureBackendLoginSecurity() {
153 try {
154 $currentLoginSecurityLevelValue = $this->configurationManager->getLocalConfigurationValueByPath('BE/loginSecurityLevel');
155 if (ExtensionManagementUtility::isLoaded('rsaauth')
156 && $currentLoginSecurityLevelValue !== 'rsa'
157 ) {
158 $this->configurationManager->setLocalConfigurationValueByPath('BE/loginSecurityLevel', 'rsa');
159 $this->throwRedirectException();
160 } elseif (!ExtensionManagementUtility::isLoaded('rsaauth')
161 && $currentLoginSecurityLevelValue !== 'normal'
162 ) {
163 $this->configurationManager->setLocalConfigurationValueByPath('BE/loginSecurityLevel', 'normal');
164 $this->throwRedirectException();
165 }
166 } catch (\RuntimeException $e) {
167 // If an exception is thrown, the value is not set in LocalConfiguration
168 if (ExtensionManagementUtility::isLoaded('rsaauth')) {
169 $this->configurationManager->setLocalConfigurationValueByPath('BE/loginSecurityLevel', 'rsa');
170 $this->throwRedirectException();
171 } elseif (!ExtensionManagementUtility::isLoaded('rsaauth')) {
172 $this->configurationManager->setLocalConfigurationValueByPath('BE/loginSecurityLevel', 'normal');
173 $this->throwRedirectException();
174 }
175 }
176 }
177
178 /**
179 * Check the settings for salted passwords extension to load it as a required extension.
180 * Unset obsolete configuration options if given.
181 *
182 * @return void
183 */
184 protected function configureSaltedPasswords() {
185 $defaultConfiguration = $this->configurationManager->getDefaultConfiguration();
186 $defaultExtensionConfiguration = unserialize($defaultConfiguration['EXT']['extConf']['saltedpasswords']);
187 try {
188 $extensionConfiguration = @unserialize($this->configurationManager->getLocalConfigurationValueByPath('EXT/extConf/saltedpasswords'));
189 } catch (\RuntimeException $e) {
190 $extensionConfiguration = array();
191 }
192 if (is_array($extensionConfiguration) && !empty($extensionConfiguration)) {
193 if (isset($extensionConfiguration['BE.']['enabled'])) {
194 if ($extensionConfiguration['BE.']['enabled']) {
195 unset($extensionConfiguration['BE.']['enabled']);
196 } else {
197 $extensionConfiguration['BE.'] = $defaultExtensionConfiguration['BE.'];
198 }
199 $this->configurationManager->setLocalConfigurationValueByPath(
200 'EXT/extConf/saltedpasswords',
201 serialize($extensionConfiguration)
202 );
203 $this->throwRedirectException();
204 }
205 } else {
206 $this->configurationManager->setLocalConfigurationValueByPath(
207 'EXT/extConf/saltedpasswords',
208 serialize($defaultExtensionConfiguration)
209 );
210 $this->throwRedirectException();
211 }
212 }
213
214 /**
215 * The encryption key is crucial for securing form tokens
216 * and the whole TYPO3 link rendering later on. A random key is set here in
217 * LocalConfiguration if it does not exist yet. This might possible happen
218 * during upgrading and will happen during first install.
219 *
220 * @return void
221 */
222 protected function generateEncryptionKeyIfNeeded() {
223 try{
224 $currentValue = $this->configurationManager->getLocalConfigurationValueByPath('SYS/encryptionKey');
225 } catch (\RuntimeException $e) {
226 // If an exception is thrown, the value is not set in LocalConfiguration
227 $currentValue = '';
228 }
229
230 if (empty($currentValue)) {
231 $randomKey = GeneralUtility::getRandomHexString(96);
232 $this->configurationManager->setLocalConfigurationValueByPath('SYS/encryptionKey', $randomKey);
233 $this->throwRedirectException();
234 }
235 }
236
237 /**
238 * $GLOBALS['TYPO3_CONF_VARS']['HTTP']['proxy_auth_scheme'] must be either
239 * 'digest' or 'basic'. 'basic' is default in DefaultConfiguration, so the
240 * setting can be removed from LocalConfiguration if it is not set to 'digest'.
241 *
242 * @return void
243 */
244 protected function setProxyAuthScheme() {
245 // Get current value from LocalConfiguration
246 try {
247 $currentValueInLocalConfiguration = $this->configurationManager->getLocalConfigurationValueByPath('HTTP/proxy_auth_scheme');
248 } catch (\RuntimeException $e) {
249 // If an exception is thrown, the value is not set in LocalConfiguration, so we don't need to do anything
250 return;
251 }
252 if ($currentValueInLocalConfiguration !== 'digest') {
253 $this->configurationManager->removeLocalConfigurationKeysByPath(array('HTTP/proxy_auth_scheme'));
254 $this->throwRedirectException();
255 }
256 }
257
258 /**
259 * GFX/im and GFX/gdlib must be set to 0 if image_processing is disabled.
260 *
261 * "Configuration presets" in install tool is not type safe, so value
262 * comparisons here are not type safe too, to not trigger changes to
263 * LocalConfiguration again.
264 *
265 * @return void
266 */
267 protected function disableImageMagickAndGdlibIfImageProcessingIsDisabled() {
268 $changedValues = array();
269 try {
270 $currentImageProcessingValue = $this->configurationManager->getLocalConfigurationValueByPath('GFX/image_processing');
271 } catch (\RuntimeException $e) {
272 $currentImageProcessingValue = $this->configurationManager->getDefaultConfigurationValueByPath('GFX/image_processing');
273 }
274 try {
275 $currentImValue = $this->configurationManager->getLocalConfigurationValueByPath('GFX/im');
276 } catch (\RuntimeException $e) {
277 $currentImValue = $this->configurationManager->getDefaultConfigurationValueByPath('GFX/im');
278 }
279 try {
280 $currentGdlibValue = $this->configurationManager->getLocalConfigurationValueByPath('GFX/gdlib');
281 } catch (\RuntimeException $e) {
282 $currentGdlibValue = $this->configurationManager->getDefaultConfigurationValueByPath('GFX/gdlib');
283 }
284 // If image processing is fully disabled, im and gdlib sub settings must be 0
285 if (!$currentImageProcessingValue) {
286 if ($currentImValue != 0) {
287 $changedValues['GFX/im'] = 0;
288 }
289 if ($currentGdlibValue != 0) {
290 $changedValues['GFX/gdlib'] = 0;
291 }
292 }
293 if (count($changedValues) > 0) {
294 $this->configurationManager->setLocalConfigurationValuesByPathValuePairs($changedValues);
295 $this->throwRedirectException();
296 }
297 }
298
299 /**
300 * Detail configuration of Image Magick settings must be cleared
301 * if Image Magick handling is disabled.
302 *
303 * "Configuration presets" in install tool is not type safe, so value
304 * comparisons here are not type safe too, to not trigger changes to
305 * LocalConfiguration again.
306 *
307 * @return void
308 */
309 protected function disableImageMagickDetailSettingsIfImageMagickIsDisabled() {
310 $changedValues = array();
311 try {
312 $currentImValue = $this->configurationManager->getLocalConfigurationValueByPath('GFX/im');
313 }
314 catch (\RuntimeException $e) {
315 $currentImValue = $this->configurationManager->getDefaultConfigurationValueByPath('GFX/im');
316 }
317 try {
318 $currentImPathValue = $this->configurationManager->getLocalConfigurationValueByPath('GFX/im_path');
319 }
320 catch (\RuntimeException $e) {
321 $currentImPathValue = $this->configurationManager->getDefaultConfigurationValueByPath('GFX/im_path');
322 }
323 try {
324 $currentImPathLzwValue = $this->configurationManager->getLocalConfigurationValueByPath('GFX/im_path_lzw');
325 }
326 catch (\RuntimeException $e) {
327 $currentImPathLzwValue = $this->configurationManager->getDefaultConfigurationValueByPath('GFX/im_path_lzw');
328 }
329 try {
330 $currentImageFileExtValue = $this->configurationManager->getLocalConfigurationValueByPath('GFX/imagefile_ext');
331 }
332 catch (\RuntimeException $e) {
333 $currentImageFileExtValue = $this->configurationManager->getDefaultConfigurationValueByPath('GFX/imagefile_ext');
334 }
335 try {
336 $currentThumbnailsValue = $this->configurationManager->getLocalConfigurationValueByPath('GFX/thumbnails');
337 }
338 catch (\RuntimeException $e) {
339 $currentThumbnailsValue = $this->configurationManager->getDefaultConfigurationValueByPath('GFX/thumbnails');
340 }
341 if (!$currentImValue) {
342 if ($currentImPathValue != '') {
343 $changedValues['GFX/im_path'] = '';
344 }
345 if ($currentImPathLzwValue != '') {
346 $changedValues['GFX/im_path_lzw'] = '';
347 }
348 if ($currentImageFileExtValue !== 'gif,jpg,jpeg,png') {
349 $changedValues['GFX/imagefile_ext'] = 'gif,jpg,jpeg,png';
350 }
351 if ($currentThumbnailsValue != 0) {
352 $changedValues['GFX/thumbnails'] = 0;
353 }
354 }
355 if (count($changedValues) > 0) {
356 $this->configurationManager->setLocalConfigurationValuesByPathValuePairs($changedValues);
357 $this->throwRedirectException();
358 }
359 }
360
361 /**
362 * Detail configuration of Image Magick and Graphics Magick settings
363 * depending on main values.
364 *
365 * "Configuration presets" in install tool is not type safe, so value
366 * comparisons here are not type safe too, to not trigger changes to
367 * LocalConfiguration again.
368 *
369 * @return void
370 */
371 protected function setImageMagickDetailSettings() {
372 $changedValues = array();
373 try {
374 $currentIm5Value = $this->configurationManager->getLocalConfigurationValueByPath('GFX/im_version_5');
375 }
376 catch (\RuntimeException $e) {
377 $currentIm5Value = $this->configurationManager->getDefaultConfigurationValueByPath('GFX/im_version_5');
378 }
379 try {
380 $currentImMaskValue = $this->configurationManager->getLocalConfigurationValueByPath('GFX/im_mask_temp_ext_gif');
381 }
382 catch (\RuntimeException $e) {
383 $currentImMaskValue = $this->configurationManager->getDefaultConfigurationValueByPath('GFX/im_mask_temp_ext_gif');
384 }
385 try {
386 $currentIm5EffectsValue = $this->configurationManager->getLocalConfigurationValueByPath('GFX/im_v5effects');
387 }
388 catch (\RuntimeException $e) {
389 $currentIm5EffectsValue = $this->configurationManager->getDefaultConfigurationValueByPath('GFX/im_v5effects');
390 }
391 if (strlen($currentIm5Value) > 0) {
392 if ($currentImMaskValue != 1) {
393 $changedValues['GFX/im_mask_temp_ext_gif'] = 1;
394 }
395 if ($currentIm5Value === 'gm') {
396 if ($currentIm5EffectsValue != -1) {
397 $changedValues['GFX/im_v5effects'] = -1;
398 }
399 }
400 }
401 if (count($changedValues) > 0) {
402 $this->configurationManager->setLocalConfigurationValuesByPathValuePairs($changedValues);
403 $this->throwRedirectException();
404 }
405 }
406
407 /**
408 * Throw exception after configuration change to trigger a redirect.
409 *
410 * @throws \TYPO3\CMS\Install\Controller\Exception\RedirectException
411 */
412 protected function throwRedirectException() {
413 throw new \TYPO3\CMS\Install\Controller\Exception\RedirectException(
414 'Configuration updated, reload needed',
415 1379024938
416 );
417 }
418 }