ee5f72be9c03ec37a9a63a96d53664780db18d2f
[Packages/TYPO3.CMS.git] / typo3 / sysext / install / Classes / Service / SilentConfigurationUpgradeService.php
1 <?php
2 namespace TYPO3\CMS\Install\Service;
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\ExtensionManagementUtility;
18 use TYPO3\CMS\Core\Utility\GeneralUtility;
19 use TYPO3\CMS\Install\Controller\Exception\RedirectException;
20
21 /**
22 * Execute "silent" LocalConfiguration upgrades if needed.
23 *
24 * Some LocalConfiguration settings are obsolete or changed over time.
25 * This class handles upgrades of these settings. It is called by
26 * the step controller at an early point.
27 *
28 * Every change is encapsulated in one method an must throw a RedirectException
29 * if new data is written to LocalConfiguration. This is caught by above
30 * step controller to initiate a redirect and start again with adapted configuration.
31 */
32 class SilentConfigurationUpgradeService {
33
34 /**
35 * @var \TYPO3\CMS\Extbase\Object\ObjectManager
36 * @inject
37 */
38 protected $objectManager = NULL;
39
40 /**
41 * @var \TYPO3\CMS\Core\Configuration\ConfigurationManager
42 * @inject
43 */
44 protected $configurationManager = NULL;
45
46 /**
47 * List of obsolete configuration options in LocalConfiguration to be removed
48 * Example:
49 * // #forge-ticket
50 * 'BE/somesetting',
51 *
52 * @var array
53 */
54 protected $obsoleteLocalConfigurationSettings = array(
55 // #62402
56 'INSTALL/wizardDone/TYPO3\\CMS\\Install\\Updates\\ExtensionManagerTables',
57 'INSTALL/wizardDone/TYPO3\\CMS\\Install\\Updates\\FileIdentifierHashUpdate',
58 'INSTALL/wizardDone/TYPO3\\CMS\\Install\\Updates\\FilemountUpdateWizard',
59 'INSTALL/wizardDone/TYPO3\\CMS\\Install\\Updates\\FilePermissionUpdate',
60 'INSTALL/wizardDone/TYPO3\\CMS\\Install\\Updates\\FileTableSplittingUpdate',
61 'INSTALL/wizardDone/TYPO3\\CMS\\Install\\Updates\\InitUpdateWizard',
62 'INSTALL/wizardDone/TYPO3\\CMS\\Install\\Updates\\MediaFlexformUpdate',
63 'INSTALL/wizardDone/TYPO3\\CMS\\Install\\Updates\\ReferenceIntegrityUpdateWizard',
64 'INSTALL/wizardDone/TYPO3\\CMS\\Install\\Updates\\RteFileLinksUpdateWizard',
65 'INSTALL/wizardDone/TYPO3\\CMS\\Install\\Updates\\RteMagicImagesUpdateWizard',
66 'INSTALL/wizardDone/TYPO3\\CMS\\Install\\Updates\\TceformsUpdateWizard',
67 'INSTALL/wizardDone/TYPO3\\CMS\\Install\\Updates\\TtContentUploadsUpdateWizard',
68 'INSTALL/wizardDone/TYPO3\\CMS\\Install\\Updates\\TruncateSysFileProcessedFileTable',
69 // #63818
70 'BE/staticFileEditPath',
71 // #64226
72 'BE/accessListRenderMode',
73 // #24900
74 'SYS/compat_version',
75 // #64643
76 'GFX/enable_typo3temp_db_tracking',
77 // #48542
78 'GFX/TTFdpi',
79 // #64872
80 'SYS/useCachingFramework',
81 // #65912
82 'FE/allowedTempPaths',
83 );
84
85 /**
86 * Executed configuration upgrades. Single upgrade methods must throw a
87 * RedirectException if something was written to LocalConfiguration.
88 *
89 * @return void
90 */
91 public function execute() {
92 $this->generateEncryptionKeyIfNeeded();
93 $this->configureBackendLoginSecurity();
94 $this->configureSaltedPasswords();
95 $this->setProxyAuthScheme();
96 $this->disableImageMagickAndGdlibIfImageProcessingIsDisabled();
97 $this->disableImageMagickDetailSettingsIfImageMagickIsDisabled();
98 $this->setImageMagickDetailSettings();
99 $this->removeObsoleteLocalConfigurationSettings();
100 }
101
102 /**
103 * Some settings in LocalConfiguration vanished in DefaultConfiguration
104 * and have no impact on the core anymore.
105 * To keep the configuration clean, those old settings are just silently
106 * removed from LocalConfiguration if set.
107 *
108 * @return void
109 */
110 protected function removeObsoleteLocalConfigurationSettings() {
111 $removed = $this->configurationManager->removeLocalConfigurationKeysByPath($this->obsoleteLocalConfigurationSettings);
112
113 // If something was changed: Trigger a reload to have new values in next request
114 if ($removed) {
115 $this->throwRedirectException();
116 }
117 }
118
119 /**
120 * Backend login security is set to rsa if rsaauth
121 * is installed (but not used) otherwise the default value "normal" has to be used.
122 * This forces either 'normal' or 'rsa' to be set in LocalConfiguration.
123 *
124 * @return void
125 */
126 protected function configureBackendLoginSecurity() {
127 try {
128 $currentLoginSecurityLevelValue = $this->configurationManager->getLocalConfigurationValueByPath('BE/loginSecurityLevel');
129 if (ExtensionManagementUtility::isLoaded('rsaauth')
130 && $currentLoginSecurityLevelValue !== 'rsa'
131 ) {
132 $this->configurationManager->setLocalConfigurationValueByPath('BE/loginSecurityLevel', 'rsa');
133 $this->throwRedirectException();
134 } elseif (!ExtensionManagementUtility::isLoaded('rsaauth')
135 && $currentLoginSecurityLevelValue !== 'normal'
136 ) {
137 $this->configurationManager->setLocalConfigurationValueByPath('BE/loginSecurityLevel', 'normal');
138 $this->throwRedirectException();
139 }
140 } catch (\RuntimeException $e) {
141 // If an exception is thrown, the value is not set in LocalConfiguration
142 if (ExtensionManagementUtility::isLoaded('rsaauth')) {
143 $this->configurationManager->setLocalConfigurationValueByPath('BE/loginSecurityLevel', 'rsa');
144 $this->throwRedirectException();
145 } elseif (!ExtensionManagementUtility::isLoaded('rsaauth')) {
146 $this->configurationManager->setLocalConfigurationValueByPath('BE/loginSecurityLevel', 'normal');
147 $this->throwRedirectException();
148 }
149 }
150 }
151
152 /**
153 * Check the settings for salted passwords extension to load it as a required extension.
154 * Unset obsolete configuration options if given.
155 *
156 * @return void
157 */
158 protected function configureSaltedPasswords() {
159 $defaultConfiguration = $this->configurationManager->getDefaultConfiguration();
160 $defaultExtensionConfiguration = unserialize($defaultConfiguration['EXT']['extConf']['saltedpasswords']);
161 try {
162 $extensionConfiguration = @unserialize($this->configurationManager->getLocalConfigurationValueByPath('EXT/extConf/saltedpasswords'));
163 } catch (\RuntimeException $e) {
164 $extensionConfiguration = array();
165 }
166 if (is_array($extensionConfiguration) && !empty($extensionConfiguration)) {
167 if (isset($extensionConfiguration['BE.']['enabled'])) {
168 if ($extensionConfiguration['BE.']['enabled']) {
169 unset($extensionConfiguration['BE.']['enabled']);
170 } else {
171 $extensionConfiguration['BE.'] = $defaultExtensionConfiguration['BE.'];
172 }
173 $this->configurationManager->setLocalConfigurationValueByPath(
174 'EXT/extConf/saltedpasswords',
175 serialize($extensionConfiguration)
176 );
177 $this->throwRedirectException();
178 }
179 } else {
180 $this->configurationManager->setLocalConfigurationValueByPath(
181 'EXT/extConf/saltedpasswords',
182 serialize($defaultExtensionConfiguration)
183 );
184 $this->throwRedirectException();
185 }
186 }
187
188 /**
189 * The encryption key is crucial for securing form tokens
190 * and the whole TYPO3 link rendering later on. A random key is set here in
191 * LocalConfiguration if it does not exist yet. This might possible happen
192 * during upgrading and will happen during first install.
193 *
194 * @return void
195 */
196 protected function generateEncryptionKeyIfNeeded() {
197 try{
198 $currentValue = $this->configurationManager->getLocalConfigurationValueByPath('SYS/encryptionKey');
199 } catch (\RuntimeException $e) {
200 // If an exception is thrown, the value is not set in LocalConfiguration
201 $currentValue = '';
202 }
203
204 if (empty($currentValue)) {
205 $randomKey = GeneralUtility::getRandomHexString(96);
206 $this->configurationManager->setLocalConfigurationValueByPath('SYS/encryptionKey', $randomKey);
207 $this->throwRedirectException();
208 }
209 }
210
211 /**
212 * $GLOBALS['TYPO3_CONF_VARS']['HTTP']['proxy_auth_scheme'] must be either
213 * 'digest' or 'basic'. 'basic' is default in DefaultConfiguration, so the
214 * setting can be removed from LocalConfiguration if it is not set to 'digest'.
215 *
216 * @return void
217 */
218 protected function setProxyAuthScheme() {
219 // Get current value from LocalConfiguration
220 try {
221 $currentValueInLocalConfiguration = $this->configurationManager->getLocalConfigurationValueByPath('HTTP/proxy_auth_scheme');
222 } catch (\RuntimeException $e) {
223 // If an exception is thrown, the value is not set in LocalConfiguration, so we don't need to do anything
224 return;
225 }
226 if ($currentValueInLocalConfiguration !== 'digest') {
227 $this->configurationManager->removeLocalConfigurationKeysByPath(array('HTTP/proxy_auth_scheme'));
228 $this->throwRedirectException();
229 }
230 }
231
232 /**
233 * GFX/im and GFX/gdlib must be set to 0 if image_processing is disabled.
234 *
235 * "Configuration presets" in install tool is not type safe, so value
236 * comparisons here are not type safe too, to not trigger changes to
237 * LocalConfiguration again.
238 *
239 * @return void
240 */
241 protected function disableImageMagickAndGdlibIfImageProcessingIsDisabled() {
242 $changedValues = array();
243 try {
244 $currentImageProcessingValue = $this->configurationManager->getLocalConfigurationValueByPath('GFX/image_processing');
245 } catch (\RuntimeException $e) {
246 $currentImageProcessingValue = $this->configurationManager->getDefaultConfigurationValueByPath('GFX/image_processing');
247 }
248 try {
249 $currentImValue = $this->configurationManager->getLocalConfigurationValueByPath('GFX/im');
250 } catch (\RuntimeException $e) {
251 $currentImValue = $this->configurationManager->getDefaultConfigurationValueByPath('GFX/im');
252 }
253 try {
254 $currentGdlibValue = $this->configurationManager->getLocalConfigurationValueByPath('GFX/gdlib');
255 } catch (\RuntimeException $e) {
256 $currentGdlibValue = $this->configurationManager->getDefaultConfigurationValueByPath('GFX/gdlib');
257 }
258 // If image processing is fully disabled, im and gdlib sub settings must be 0
259 if (!$currentImageProcessingValue) {
260 if ($currentImValue != 0) {
261 $changedValues['GFX/im'] = 0;
262 }
263 if ($currentGdlibValue != 0) {
264 $changedValues['GFX/gdlib'] = 0;
265 }
266 }
267 if (count($changedValues) > 0) {
268 $this->configurationManager->setLocalConfigurationValuesByPathValuePairs($changedValues);
269 $this->throwRedirectException();
270 }
271 }
272
273 /**
274 * Detail configuration of Image Magick settings must be cleared
275 * if Image Magick handling is disabled.
276 *
277 * "Configuration presets" in install tool is not type safe, so value
278 * comparisons here are not type safe too, to not trigger changes to
279 * LocalConfiguration again.
280 *
281 * @return void
282 */
283 protected function disableImageMagickDetailSettingsIfImageMagickIsDisabled() {
284 $changedValues = array();
285 try {
286 $currentImValue = $this->configurationManager->getLocalConfigurationValueByPath('GFX/im');
287 }
288 catch (\RuntimeException $e) {
289 $currentImValue = $this->configurationManager->getDefaultConfigurationValueByPath('GFX/im');
290 }
291 try {
292 $currentImPathValue = $this->configurationManager->getLocalConfigurationValueByPath('GFX/im_path');
293 }
294 catch (\RuntimeException $e) {
295 $currentImPathValue = $this->configurationManager->getDefaultConfigurationValueByPath('GFX/im_path');
296 }
297 try {
298 $currentImPathLzwValue = $this->configurationManager->getLocalConfigurationValueByPath('GFX/im_path_lzw');
299 }
300 catch (\RuntimeException $e) {
301 $currentImPathLzwValue = $this->configurationManager->getDefaultConfigurationValueByPath('GFX/im_path_lzw');
302 }
303 try {
304 $currentImageFileExtValue = $this->configurationManager->getLocalConfigurationValueByPath('GFX/imagefile_ext');
305 }
306 catch (\RuntimeException $e) {
307 $currentImageFileExtValue = $this->configurationManager->getDefaultConfigurationValueByPath('GFX/imagefile_ext');
308 }
309 try {
310 $currentThumbnailsValue = $this->configurationManager->getLocalConfigurationValueByPath('GFX/thumbnails');
311 }
312 catch (\RuntimeException $e) {
313 $currentThumbnailsValue = $this->configurationManager->getDefaultConfigurationValueByPath('GFX/thumbnails');
314 }
315 if (!$currentImValue) {
316 if ($currentImPathValue != '') {
317 $changedValues['GFX/im_path'] = '';
318 }
319 if ($currentImPathLzwValue != '') {
320 $changedValues['GFX/im_path_lzw'] = '';
321 }
322 if ($currentImageFileExtValue !== 'gif,jpg,jpeg,png') {
323 $changedValues['GFX/imagefile_ext'] = 'gif,jpg,jpeg,png';
324 }
325 if ($currentThumbnailsValue != 0) {
326 $changedValues['GFX/thumbnails'] = 0;
327 }
328 }
329 if (count($changedValues) > 0) {
330 $this->configurationManager->setLocalConfigurationValuesByPathValuePairs($changedValues);
331 $this->throwRedirectException();
332 }
333 }
334
335 /**
336 * Detail configuration of Image Magick and Graphics Magick settings
337 * depending on main values.
338 *
339 * "Configuration presets" in install tool is not type safe, so value
340 * comparisons here are not type safe too, to not trigger changes to
341 * LocalConfiguration again.
342 *
343 * @return void
344 */
345 protected function setImageMagickDetailSettings() {
346 $changedValues = array();
347 try {
348 $currentIm5Value = $this->configurationManager->getLocalConfigurationValueByPath('GFX/im_version_5');
349 }
350 catch (\RuntimeException $e) {
351 $currentIm5Value = $this->configurationManager->getDefaultConfigurationValueByPath('GFX/im_version_5');
352 }
353 try {
354 $currentImMaskValue = $this->configurationManager->getLocalConfigurationValueByPath('GFX/im_mask_temp_ext_gif');
355 }
356 catch (\RuntimeException $e) {
357 $currentImMaskValue = $this->configurationManager->getDefaultConfigurationValueByPath('GFX/im_mask_temp_ext_gif');
358 }
359 try {
360 $currentIm5EffectsValue = $this->configurationManager->getLocalConfigurationValueByPath('GFX/im_v5effects');
361 }
362 catch (\RuntimeException $e) {
363 $currentIm5EffectsValue = $this->configurationManager->getDefaultConfigurationValueByPath('GFX/im_v5effects');
364 }
365 if ((string)$currentIm5Value !== '') {
366 if ($currentImMaskValue != 1) {
367 $changedValues['GFX/im_mask_temp_ext_gif'] = 1;
368 }
369 if ($currentIm5Value === 'gm') {
370 if ($currentIm5EffectsValue != -1) {
371 $changedValues['GFX/im_v5effects'] = -1;
372 }
373 }
374 }
375 if (count($changedValues) > 0) {
376 $this->configurationManager->setLocalConfigurationValuesByPathValuePairs($changedValues);
377 $this->throwRedirectException();
378 }
379 }
380
381 /**
382 * Throw exception after configuration change to trigger a redirect.
383 *
384 * @throws RedirectException
385 */
386 protected function throwRedirectException() {
387 throw new RedirectException(
388 'Configuration updated, reload needed',
389 1379024938
390 );
391 }
392
393 }