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