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