generateEncryptionKeyIfNeeded(); $this->migrateImageProcessorSetting(); $this->transferHttpSettings(); $this->disableImageMagickDetailSettingsIfImageMagickIsDisabled(); $this->setImageMagickDetailSettings(); $this->removeDefaultColorspaceSettings(); $this->migrateLockSslSetting(); $this->migrateDatabaseConnectionSettings(); $this->migrateDatabaseConnectionCharset(); $this->migrateDatabaseDriverOptions(); $this->migrateCacheHashOptions(); $this->migrateExceptionErrors(); $this->migrateDisplayErrorsSetting(); $this->migrateSaltedPasswordsSettings(); $this->migrateCachingFrameworkCaches(); $this->migrateMailSettingsToSendmail(); $this->migrateMailSmtpEncryptSetting(); $this->migrateVersionNumberInFileNameSetting(); $this->migrateLanguageSettings(); // Should run at the end to prevent obsolete settings are removed before migration $this->removeObsoleteLocalConfigurationSettings(); } /** * Throw exception after configuration change to trigger a redirect. * * @throws ConfigurationChangedException */ protected function throwConfigurationChangedException(): void { throw new ConfigurationChangedException( 'Configuration updated, reload needed', 1379024938 ); } /** * Some settings in LocalConfiguration vanished in DefaultConfiguration * and have no impact on the core anymore. * To keep the configuration clean, those old settings are just silently * removed from LocalConfiguration if set. * * @throws ConfigurationChangedException */ protected function removeObsoleteLocalConfigurationSettings(): void { $removed = $this->configurationManager->removeLocalConfigurationKeysByPath($this->obsoleteLocalConfigurationSettings); // If something was changed: Trigger reload to have new values in next request if ($removed) { $this->throwConfigurationChangedException(); } } /** * The encryption key is crucial for securing form tokens * and the whole TYPO3 link rendering later on. A random key is set here in * LocalConfiguration if it does not exist yet. This might happen * during upgrading and will happen during first install. * * @throws ConfigurationChangedException */ protected function generateEncryptionKeyIfNeeded(): void { try { $currentValue = $this->configurationManager->getLocalConfigurationValueByPath('SYS/encryptionKey'); } catch (MissingArrayPathException) { // If an exception is thrown, the value is not set in LocalConfiguration $currentValue = ''; } if (empty($currentValue)) { $randomKey = GeneralUtility::makeInstance(Random::class)->generateRandomHexString(96); $this->configurationManager->setLocalConfigurationValueByPath('SYS/encryptionKey', $randomKey); $this->throwConfigurationChangedException(); } } /** * Parse old curl and HTTP options and set new HTTP options, related to Guzzle * * @throws ConfigurationChangedException */ protected function transferHttpSettings(): void { $changed = false; $newParameters = []; $obsoleteParameters = []; // Remove / migrate options to new options try { // Check if the adapter option is set, if so, set it to the parameters that are obsolete $this->configurationManager->getLocalConfigurationValueByPath('HTTP/adapter'); $obsoleteParameters[] = 'HTTP/adapter'; } catch (MissingArrayPathException) { // Migration done already } try { $newParameters['HTTP/version'] = $this->configurationManager->getLocalConfigurationValueByPath('HTTP/protocol_version'); $obsoleteParameters[] = 'HTTP/protocol_version'; } catch (MissingArrayPathException) { // Migration done already } try { $this->configurationManager->getLocalConfigurationValueByPath('HTTP/ssl_verify_host'); $obsoleteParameters[] = 'HTTP/ssl_verify_host'; } catch (MissingArrayPathException) { // Migration done already } try { $legacyUserAgent = $this->configurationManager->getLocalConfigurationValueByPath('HTTP/userAgent'); $newParameters['HTTP/headers/User-Agent'] = $legacyUserAgent; $obsoleteParameters[] = 'HTTP/userAgent'; } catch (MissingArrayPathException) { // Migration done already } // Redirects try { $legacyFollowRedirects = $this->configurationManager->getLocalConfigurationValueByPath('HTTP/follow_redirects'); $obsoleteParameters[] = 'HTTP/follow_redirects'; } catch (MissingArrayPathException) { $legacyFollowRedirects = ''; } try { $legacyMaximumRedirects = $this->configurationManager->getLocalConfigurationValueByPath('HTTP/max_redirects'); $obsoleteParameters[] = 'HTTP/max_redirects'; } catch (MissingArrayPathException) { $legacyMaximumRedirects = ''; } try { $legacyStrictRedirects = $this->configurationManager->getLocalConfigurationValueByPath('HTTP/strict_redirects'); $obsoleteParameters[] = 'HTTP/strict_redirects'; } catch (MissingArrayPathException) { $legacyStrictRedirects = ''; } // Check if redirects have been disabled if ($legacyFollowRedirects !== '' && (bool)$legacyFollowRedirects === false) { $newParameters['HTTP/allow_redirects'] = false; } elseif ($legacyMaximumRedirects !== '' || $legacyStrictRedirects !== '') { $newParameters['HTTP/allow_redirects'] = []; if ($legacyMaximumRedirects !== '' && (int)$legacyMaximumRedirects !== 5) { $newParameters['HTTP/allow_redirects']['max'] = (int)$legacyMaximumRedirects; } if ($legacyStrictRedirects !== '' && (bool)$legacyStrictRedirects === true) { $newParameters['HTTP/allow_redirects']['strict'] = true; } // defaults are used, no need to set the option in system/settings.php if (empty($newParameters['HTTP/allow_redirects'])) { unset($newParameters['HTTP/allow_redirects']); } } // Migrate Proxy settings try { // Currently without protocol or port $legacyProxyHost = $this->configurationManager->getLocalConfigurationValueByPath('HTTP/proxy_host'); $obsoleteParameters[] = 'HTTP/proxy_host'; } catch (MissingArrayPathException) { $legacyProxyHost = ''; } try { $legacyProxyPort = $this->configurationManager->getLocalConfigurationValueByPath('HTTP/proxy_port'); $obsoleteParameters[] = 'HTTP/proxy_port'; } catch (MissingArrayPathException) { $legacyProxyPort = ''; } try { $legacyProxyUser = $this->configurationManager->getLocalConfigurationValueByPath('HTTP/proxy_user'); $obsoleteParameters[] = 'HTTP/proxy_user'; } catch (MissingArrayPathException) { $legacyProxyUser = ''; } try { $legacyProxyPassword = $this->configurationManager->getLocalConfigurationValueByPath('HTTP/proxy_password'); $obsoleteParameters[] = 'HTTP/proxy_password'; } catch (MissingArrayPathException) { $legacyProxyPassword = ''; } // Auth Scheme: Basic, digest etc. try { $legacyProxyAuthScheme = $this->configurationManager->getLocalConfigurationValueByPath('HTTP/proxy_auth_scheme'); $obsoleteParameters[] = 'HTTP/proxy_auth_scheme'; } catch (MissingArrayPathException) { $legacyProxyAuthScheme = ''; } if ($legacyProxyHost !== '') { $proxy = 'http://'; if ($legacyProxyAuthScheme !== '' && $legacyProxyUser !== '' && $legacyProxyPassword !== '') { $proxy .= $legacyProxyUser . ':' . $legacyProxyPassword . '@'; } $proxy .= $legacyProxyHost; if ($legacyProxyPort !== '') { $proxy .= ':' . $legacyProxyPort; } $newParameters['HTTP/proxy'] = $proxy; } // Verify peers // see http://docs.guzzlephp.org/en/latest/request-options.html#verify try { $legacySslVerifyPeer = $this->configurationManager->getLocalConfigurationValueByPath('HTTP/ssl_verify_peer'); $obsoleteParameters[] = 'HTTP/ssl_verify_peer'; } catch (MissingArrayPathException) { $legacySslVerifyPeer = ''; } // Directory holding multiple Certificate Authority files try { $legacySslCaPath = $this->configurationManager->getLocalConfigurationValueByPath('HTTP/ssl_capath'); $obsoleteParameters[] = 'HTTP/ssl_capath'; } catch (MissingArrayPathException) { $legacySslCaPath = ''; } // Certificate Authority file to verify the peer with (use when ssl_verify_peer is TRUE) try { $legacySslCaFile = $this->configurationManager->getLocalConfigurationValueByPath('HTTP/ssl_cafile'); $obsoleteParameters[] = 'HTTP/ssl_cafile'; } catch (MissingArrayPathException) { $legacySslCaFile = ''; } if ($legacySslVerifyPeer !== '') { if ($legacySslCaFile !== '' && $legacySslCaPath !== '') { $newParameters['HTTP/verify'] = $legacySslCaPath . $legacySslCaFile; } elseif ((bool)$legacySslVerifyPeer === false) { $newParameters['HTTP/verify'] = false; } } // SSL Key + Passphrase // Name of a file containing local certificate try { $legacySslLocalCert = $this->configurationManager->getLocalConfigurationValueByPath('HTTP/ssl_local_cert'); $obsoleteParameters[] = 'HTTP/ssl_local_cert'; } catch (MissingArrayPathException) { $legacySslLocalCert = ''; } // Passphrase with which local certificate was encoded try { $legacySslPassphrase = $this->configurationManager->getLocalConfigurationValueByPath('HTTP/ssl_passphrase'); $obsoleteParameters[] = 'HTTP/ssl_passphrase'; } catch (MissingArrayPathException) { $legacySslPassphrase = ''; } if ($legacySslLocalCert !== '') { if ($legacySslPassphrase !== '') { $newParameters['HTTP/ssl_key'] = [ $legacySslLocalCert, $legacySslPassphrase, ]; } else { $newParameters['HTTP/ssl_key'] = $legacySslLocalCert; } } // Update the LocalConfiguration file if obsolete parameters or new parameters are set if (!empty($obsoleteParameters)) { $this->configurationManager->removeLocalConfigurationKeysByPath($obsoleteParameters); $changed = true; } if (!empty($newParameters)) { $this->configurationManager->setLocalConfigurationValuesByPathValuePairs($newParameters); $changed = true; } if ($changed) { $this->throwConfigurationChangedException(); } } /** * Detail configuration of Image Magick settings must be cleared * if Image Magick handling is disabled. * * "Configuration presets" in install tool is not type safe, so value * comparisons here are not type safe too, to not trigger changes to * LocalConfiguration again. * * @throws ConfigurationChangedException */ protected function disableImageMagickDetailSettingsIfImageMagickIsDisabled(): void { $changedValues = []; try { $currentEnabledValue = $this->configurationManager->getLocalConfigurationValueByPath('GFX/processor_enabled'); } catch (MissingArrayPathException) { $currentEnabledValue = $this->configurationManager->getDefaultConfigurationValueByPath('GFX/processor_enabled'); } try { $currentPathValue = $this->configurationManager->getLocalConfigurationValueByPath('GFX/processor_path'); } catch (MissingArrayPathException) { $currentPathValue = $this->configurationManager->getDefaultConfigurationValueByPath('GFX/processor_path'); } try { $currentImageFileExtValue = $this->configurationManager->getLocalConfigurationValueByPath('GFX/imagefile_ext'); } catch (MissingArrayPathException) { $currentImageFileExtValue = $this->configurationManager->getDefaultConfigurationValueByPath('GFX/imagefile_ext'); } try { $currentThumbnailsValue = $this->configurationManager->getLocalConfigurationValueByPath('GFX/thumbnails'); } catch (MissingArrayPathException) { $currentThumbnailsValue = $this->configurationManager->getDefaultConfigurationValueByPath('GFX/thumbnails'); } if (!$currentEnabledValue) { if ($currentPathValue != '') { $changedValues['GFX/processor_path'] = ''; } if ($currentImageFileExtValue !== 'gif,jpg,jpeg,png') { $changedValues['GFX/imagefile_ext'] = 'gif,jpg,jpeg,png'; } if ($currentThumbnailsValue != 0) { $changedValues['GFX/thumbnails'] = 0; } } if (!empty($changedValues)) { $this->configurationManager->setLocalConfigurationValuesByPathValuePairs($changedValues); $this->throwConfigurationChangedException(); } } /** * Detail configuration of Image Magick and Graphics Magick settings * depending on main values. * * "Configuration presets" in install tool is not type safe, so value * comparisons here are not type safe too, to not trigger changes to * LocalConfiguration again. * * @throws ConfigurationChangedException */ protected function setImageMagickDetailSettings(): void { $changedValues = []; try { $currentProcessorValue = $this->configurationManager->getLocalConfigurationValueByPath('GFX/processor'); } catch (MissingArrayPathException) { $currentProcessorValue = $this->configurationManager->getDefaultConfigurationValueByPath('GFX/processor'); } try { $currentProcessorEffectsValue = $this->configurationManager->getLocalConfigurationValueByPath('GFX/processor_effects'); } catch (MissingArrayPathException) { $currentProcessorEffectsValue = $this->configurationManager->getDefaultConfigurationValueByPath('GFX/processor_effects'); } if ((string)$currentProcessorValue !== '') { if (!is_bool($currentProcessorEffectsValue)) { $changedValues['GFX/processor_effects'] = (int)$currentProcessorEffectsValue > 0; } } if (!empty($changedValues)) { $this->configurationManager->setLocalConfigurationValuesByPathValuePairs($changedValues); $this->throwConfigurationChangedException(); } } /** * Migrate the definition of the image processor from the configuration value * im_version_5 to the setting processor. * * @throws ConfigurationChangedException */ protected function migrateImageProcessorSetting(): void { $changedSettings = []; $settingsToRename = [ 'GFX/im' => 'GFX/processor_enabled', 'GFX/im_version_5' => 'GFX/processor', 'GFX/im_v5effects' => 'GFX/processor_effects', 'GFX/im_path' => 'GFX/processor_path', 'GFX/im_path_lzw' => 'GFX/processor_path_lzw', 'GFX/im_mask_temp_ext_gif' => 'GFX/processor_allowTemporaryMasksAsPng', 'GFX/im_noScaleUp' => 'GFX/processor_allowUpscaling', 'GFX/im_noFramePrepended' => 'GFX/processor_allowFrameSelection', 'GFX/im_stripProfileCommand' => 'GFX/processor_stripColorProfileCommand', 'GFX/im_useStripProfileByDefault' => 'GFX/processor_stripColorProfileByDefault', 'GFX/colorspace' => 'GFX/processor_colorspace', ]; foreach ($settingsToRename as $oldPath => $newPath) { try { $value = $this->configurationManager->getLocalConfigurationValueByPath($oldPath); $this->configurationManager->setLocalConfigurationValueByPath($newPath, $value); $changedSettings[$oldPath] = true; } catch (MissingArrayPathException) { // If an exception is thrown, the value is not set in LocalConfiguration $changedSettings[$oldPath] = false; } } if (!empty($changedSettings['GFX/im_version_5'])) { $currentProcessorValue = $this->configurationManager->getLocalConfigurationValueByPath('GFX/im_version_5'); $newProcessorValue = $currentProcessorValue === 'gm' ? 'GraphicsMagick' : 'ImageMagick'; $this->configurationManager->setLocalConfigurationValueByPath('GFX/processor', $newProcessorValue); } if (!empty($changedSettings['GFX/im_noScaleUp'])) { $currentProcessorValue = $this->configurationManager->getLocalConfigurationValueByPath('GFX/im_noScaleUp'); $newProcessorValue = !$currentProcessorValue; $this->configurationManager->setLocalConfigurationValueByPath( 'GFX/processor_allowUpscaling', $newProcessorValue ); } if (!empty($changedSettings['GFX/im_noFramePrepended'])) { $currentProcessorValue = $this->configurationManager->getLocalConfigurationValueByPath('GFX/im_noFramePrepended'); $newProcessorValue = !$currentProcessorValue; $this->configurationManager->setLocalConfigurationValueByPath( 'GFX/processor_allowFrameSelection', $newProcessorValue ); } if (!empty($changedSettings['GFX/im_mask_temp_ext_gif'])) { $currentProcessorValue = $this->configurationManager->getLocalConfigurationValueByPath('GFX/im_mask_temp_ext_gif'); $newProcessorValue = !$currentProcessorValue; $this->configurationManager->setLocalConfigurationValueByPath( 'GFX/processor_allowTemporaryMasksAsPng', $newProcessorValue ); } if (!empty(array_filter($changedSettings))) { $this->configurationManager->removeLocalConfigurationKeysByPath(array_keys($changedSettings)); $this->throwConfigurationChangedException(); } } /** * Migrate the configuration setting BE/lockSSL to boolean if set in the system/settings.php file * * @throws ConfigurationChangedException */ protected function migrateLockSslSetting(): void { try { $currentOption = $this->configurationManager->getLocalConfigurationValueByPath('BE/lockSSL'); // check if the current option is an integer/string and if it is active if (!is_bool($currentOption) && (int)$currentOption > 0) { $this->configurationManager->setLocalConfigurationValueByPath('BE/lockSSL', true); $this->throwConfigurationChangedException(); } } catch (MissingArrayPathException) { // no change inside the system/settings.php found, so nothing needs to be modified } } /** * Move the database connection settings to a "Default" connection * * @throws ConfigurationChangedException */ protected function migrateDatabaseConnectionSettings(): void { $confManager = $this->configurationManager; $newSettings = []; $removeSettings = []; try { $value = $confManager->getLocalConfigurationValueByPath('DB/username'); $removeSettings[] = 'DB/username'; $newSettings['DB/Connections/Default/user'] = $value; } catch (MissingArrayPathException) { // Old setting does not exist, do nothing } try { $value = $confManager->getLocalConfigurationValueByPath('DB/password'); $removeSettings[] = 'DB/password'; $newSettings['DB/Connections/Default/password'] = $value; } catch (MissingArrayPathException) { // Old setting does not exist, do nothing } try { $value = $confManager->getLocalConfigurationValueByPath('DB/host'); $removeSettings[] = 'DB/host'; $newSettings['DB/Connections/Default/host'] = $value; } catch (MissingArrayPathException) { // Old setting does not exist, do nothing } try { $value = $confManager->getLocalConfigurationValueByPath('DB/port'); $removeSettings[] = 'DB/port'; $newSettings['DB/Connections/Default/port'] = $value; } catch (MissingArrayPathException) { // Old setting does not exist, do nothing } try { $value = $confManager->getLocalConfigurationValueByPath('DB/socket'); $removeSettings[] = 'DB/socket'; // Remove empty socket connects if (!empty($value)) { $newSettings['DB/Connections/Default/unix_socket'] = $value; } } catch (MissingArrayPathException) { // Old setting does not exist, do nothing } try { $value = $confManager->getLocalConfigurationValueByPath('DB/database'); $removeSettings[] = 'DB/database'; $newSettings['DB/Connections/Default/dbname'] = $value; } catch (MissingArrayPathException) { // Old setting does not exist, do nothing } try { $value = (bool)$confManager->getLocalConfigurationValueByPath('SYS/dbClientCompress'); $removeSettings[] = 'SYS/dbClientCompress'; if ($value) { $newSettings['DB/Connections/Default/driverOptions'] = [ 'flags' => MYSQLI_CLIENT_COMPRESS, ]; } } catch (MissingArrayPathException) { // Old setting does not exist, do nothing } try { $value = (bool)$confManager->getLocalConfigurationValueByPath('SYS/no_pconnect'); $removeSettings[] = 'SYS/no_pconnect'; if (!$value) { $newSettings['DB/Connections/Default/persistentConnection'] = true; } } catch (MissingArrayPathException) { // Old setting does not exist, do nothing } try { $value = $confManager->getLocalConfigurationValueByPath('SYS/setDBinit'); $removeSettings[] = 'SYS/setDBinit'; $newSettings['DB/Connections/Default/initCommands'] = $value; } catch (MissingArrayPathException) { // Old setting does not exist, do nothing } try { $confManager->getLocalConfigurationValueByPath('DB/Connections/Default/charset'); } catch (MissingArrayPathException) { // If there is no charset option yet, add it. $newSettings['DB/Connections/Default/charset'] = 'utf8'; } try { $confManager->getLocalConfigurationValueByPath('DB/Connections/Default/driver'); } catch (MissingArrayPathException) { // Use the mysqli driver by default if no value has been provided yet $newSettings['DB/Connections/Default/driver'] = 'mysqli'; } // Add new settings and remove old ones if (!empty($newSettings)) { $confManager->setLocalConfigurationValuesByPathValuePairs($newSettings); } if (!empty($removeSettings)) { $confManager->removeLocalConfigurationKeysByPath($removeSettings); } // Throw redirect if something was changed if (!empty($newSettings) || !empty($removeSettings)) { $this->throwConfigurationChangedException(); } } /** * Migrate the configuration setting DB/Connections/Default/charset to 'utf8' as * 'utf-8' is not supported by all MySQL versions. * * @throws ConfigurationChangedException */ protected function migrateDatabaseConnectionCharset(): void { $confManager = $this->configurationManager; try { $driver = $confManager->getLocalConfigurationValueByPath('DB/Connections/Default/driver'); $charset = $confManager->getLocalConfigurationValueByPath('DB/Connections/Default/charset'); if (in_array($driver, ['mysqli', 'pdo_mysql', 'drizzle_pdo_mysql'], true) && $charset === 'utf-8') { $confManager->setLocalConfigurationValueByPath('DB/Connections/Default/charset', 'utf8'); $this->throwConfigurationChangedException(); } } catch (MissingArrayPathException) { // no incompatible charset configuration found, so nothing needs to be modified } } /** * Migrate the configuration setting DB/Connections/Default/driverOptions to array type. * * @throws ConfigurationChangedException */ protected function migrateDatabaseDriverOptions(): void { $confManager = $this->configurationManager; try { $options = $confManager->getLocalConfigurationValueByPath('DB/Connections/Default/driverOptions'); if (!is_array($options)) { $confManager->setLocalConfigurationValueByPath( 'DB/Connections/Default/driverOptions', ['flags' => (int)$options] ); $this->throwConfigurationChangedException(); } } catch (MissingArrayPathException) { // no driver options found, nothing needs to be modified } } /** * Migrate single cache hash related options under "FE" into "FE/cacheHash" * * @throws ConfigurationChangedException */ protected function migrateCacheHashOptions(): void { $confManager = $this->configurationManager; $removeSettings = []; $newSettings = []; try { $value = $confManager->getLocalConfigurationValueByPath('FE/cHashOnlyForParameters'); $removeSettings[] = 'FE/cHashOnlyForParameters'; $newSettings['FE/cacheHash/cachedParametersWhiteList'] = GeneralUtility::trimExplode(',', $value, true); } catch (MissingArrayPathException) { // Migration done already } try { $value = $confManager->getLocalConfigurationValueByPath('FE/cHashExcludedParameters'); $removeSettings[] = 'FE/cHashExcludedParameters'; $newSettings['FE/cacheHash/excludedParameters'] = GeneralUtility::trimExplode(',', $value, true); } catch (MissingArrayPathException) { // Migration done already } try { $value = $confManager->getLocalConfigurationValueByPath('FE/cHashRequiredParameters'); $removeSettings[] = 'FE/cHashRequiredParameters'; $newSettings['FE/cacheHash/requireCacheHashPresenceParameters'] = GeneralUtility::trimExplode(',', $value, true); } catch (MissingArrayPathException) { // Migration done already } try { $value = $confManager->getLocalConfigurationValueByPath('FE/cHashExcludedParametersIfEmpty'); $removeSettings[] = 'FE/cHashExcludedParametersIfEmpty'; if (trim($value) === '*') { $newSettings['FE/cacheHash/excludeAllEmptyParameters'] = true; } else { $newSettings['FE/cacheHash/excludedParametersIfEmpty'] = GeneralUtility::trimExplode(',', $value, true); } } catch (MissingArrayPathException) { // Migration done already } // Add new settings and remove old ones if (!empty($newSettings)) { $confManager->setLocalConfigurationValuesByPathValuePairs($newSettings); } if (!empty($removeSettings)) { $confManager->removeLocalConfigurationKeysByPath($removeSettings); } // Throw redirect if something was changed if (!empty($newSettings) || !empty($removeSettings)) { $this->throwConfigurationChangedException(); } } /** * Migrate SYS/exceptionalErrors to not contain E_USER_DEPRECATED * * @throws ConfigurationChangedException */ protected function migrateExceptionErrors(): void { $confManager = $this->configurationManager; try { $currentOption = (int)$confManager->getLocalConfigurationValueByPath('SYS/exceptionalErrors'); // make sure E_USER_DEPRECATED is not part of the exceptionalErrors if ($currentOption & E_USER_DEPRECATED) { $confManager->setLocalConfigurationValueByPath('SYS/exceptionalErrors', $currentOption & ~E_USER_DEPRECATED); $this->throwConfigurationChangedException(); } } catch (MissingArrayPathException) { // no change inside the system/settings.php found, so nothing needs to be modified } } /** * Migrate SYS/displayErrors to not contain 2 * * @throws ConfigurationChangedException */ protected function migrateDisplayErrorsSetting(): void { $confManager = $this->configurationManager; try { $currentOption = (int)$confManager->getLocalConfigurationValueByPath('SYS/displayErrors'); // make sure displayErrors is set to 2 if ($currentOption === 2) { $confManager->setLocalConfigurationValueByPath('SYS/displayErrors', -1); $this->throwConfigurationChangedException(); } } catch (MissingArrayPathException) { // no change inside the system/settings.php found, so nothing needs to be modified } } /** * Migrate salted passwords extension configuration settings to BE/passwordHashing and FE/passwordHashing * * @throws ConfigurationChangedException */ protected function migrateSaltedPasswordsSettings(): void { $confManager = $this->configurationManager; $configsToRemove = []; try { $extensionConfiguration = (array)$confManager->getLocalConfigurationValueByPath('EXTENSIONS/saltedpasswords'); $configsToRemove[] = 'EXTENSIONS/saltedpasswords'; } catch (MissingArrayPathException) { $extensionConfiguration = []; } // Migration already done if (empty($extensionConfiguration)) { return; } // Upgrade to the best available hash method. This is only done once since that code will no longer be reached // after first migration because extConf and EXTENSIONS array entries are gone then. Thus, a manual selection // to some different hash mechanism will not be touched again after first upgrade. // Phpass is always available, so we have some last fallback if the others don't kick in $okHashMethods = [ Argon2iPasswordHash::class, Argon2idPasswordHash::class, BcryptPasswordHash::class, Pbkdf2PasswordHash::class, PhpassPasswordHash::class, ]; $newMethods = []; foreach (['BE', 'FE'] as $mode) { foreach ($okHashMethods as $className) { /** @var PasswordHashInterface $instance */ $instance = GeneralUtility::makeInstance($className); if ($instance->isAvailable()) { $newMethods[$mode] = $className; break; } } } // We only need to write to LocalConfiguration if method is different from Argon2i in DefaultConfiguration $newConfig = []; if ($newMethods['BE'] !== Argon2iPasswordHash::class) { $newConfig['BE/passwordHashing/className'] = $newMethods['BE']; } if ($newMethods['FE'] !== Argon2iPasswordHash::class) { $newConfig['FE/passwordHashing/className'] = $newMethods['FE']; } if (!empty($newConfig)) { $confManager->setLocalConfigurationValuesByPathValuePairs($newConfig); } $confManager->removeLocalConfigurationKeysByPath($configsToRemove); $this->throwConfigurationChangedException(); } /** * Renames all SYS[caching][cache] configuration names to names without the prefix "cache_". * see #88366 * * @throws ConfigurationChangedException */ protected function migrateCachingFrameworkCaches(): void { $confManager = $this->configurationManager; try { $cacheConfigurations = (array)$confManager->getLocalConfigurationValueByPath('SYS/caching/cacheConfigurations'); $newConfig = []; $hasBeenModified = false; foreach ($cacheConfigurations as $identifier => $cacheConfiguration) { if (str_starts_with($identifier, 'cache_')) { $identifier = substr($identifier, 6); $hasBeenModified = true; } $newConfig[$identifier] = $cacheConfiguration; } if ($hasBeenModified) { $confManager->setLocalConfigurationValueByPath('SYS/caching/cacheConfigurations', $newConfig); $this->throwConfigurationChangedException(); } } catch (MissingArrayPathException) { // no change inside the system/settings.php found, so nothing needs to be modified } } /** * Migrates "mail" to "sendmail" as "mail" (PHP's built-in mail() method) is not supported anymore * with Symfony components. * See #88643 * * @throws ConfigurationChangedException */ protected function migrateMailSettingsToSendmail(): void { $confManager = $this->configurationManager; try { $transport = $confManager->getLocalConfigurationValueByPath('MAIL/transport'); if ($transport === 'mail') { $confManager->setLocalConfigurationValueByPath('MAIL/transport', 'sendmail'); $confManager->setLocalConfigurationValueByPath('MAIL/transport_sendmail_command', (string)@ini_get('sendmail_path')); $this->throwConfigurationChangedException(); } } catch (MissingArrayPathException) { // no change inside the system/settings.php found, so nothing needs to be modified } } /** * Migrates MAIL/transport_smtp_encrypt to a boolean value * See #91070, #90295, #88643 and https://github.com/symfony/symfony/commit/5b8c4676d059 * * @throws ConfigurationChangedException */ protected function migrateMailSmtpEncryptSetting(): void { $confManager = $this->configurationManager; try { $transport = $confManager->getLocalConfigurationValueByPath('MAIL/transport'); if ($transport === 'smtp') { $encrypt = $confManager->getLocalConfigurationValueByPath('MAIL/transport_smtp_encrypt'); if (is_string($encrypt)) { // SwiftMailer used 'tls' as identifier to connect with STARTTLS via SMTP (as usually used with port 587). // See https://github.com/swiftmailer/swiftmailer/blob/v5.4.10/lib/classes/Swift/Transport/EsmtpTransport.php#L144 if ($encrypt === 'tls') { // With TYPO3 v10 the MAIL/transport_smtp_encrypt option is passed as constructor parameter $tls to // Symfony\Component\Mailer\Transport\Smtp\EsmtpTransport // $tls = true instructs to start a SMTPS connection – that means SSL/TLS via SMTPS, not STARTTLS via SMTP. // That means symfony/mailer will use STARTTLS when $tls = false or ($tls = null with port != 465) is passed. // Actually symfony/mailer will use STARTTLS by default now. // Due to the misleading name (transport_smtp_encrypt) we avoid to set the option to false, but rather remove it. // Note: symfony/mailer provides no way to enforce STARTTLS usage, see https://github.com/symfony/symfony/commit/5b8c4676d059 $confManager->removeLocalConfigurationKeysByPath(['MAIL/transport_smtp_encrypt']); } elseif ($encrypt === '') { $confManager->setLocalConfigurationValueByPath('MAIL/transport_smtp_encrypt', false); } else { $confManager->setLocalConfigurationValueByPath('MAIL/transport_smtp_encrypt', true); } $this->throwConfigurationChangedException(); } } } catch (MissingArrayPathException) { // no change inside the system/settings.php found, so nothing needs to be modified } } /** * Migrate [FE][versionNumberInFilename] to become a boolean flag * * @throws ConfigurationChangedException */ protected function migrateVersionNumberInFileNameSetting(): void { try { $confManager = $this->configurationManager; $currentOption = $confManager->getLocalConfigurationValueByPath('FE/versionNumberInFilename'); if ($currentOption === true) { return; } if ($currentOption === 'embed') { $confManager->setLocalConfigurationValueByPath('FE/versionNumberInFilename', true); } else { $confManager->removeLocalConfigurationKeysByPath(['FE/versionNumberInFilename']); } $this->throwConfigurationChangedException(); } catch (MissingArrayPathException) { // no flag set, so nothing to be configured } } /** * Remove the colorspace setting if it's already the recommended default for a given processor * * @throws ConfigurationChangedException */ protected function removeDefaultColorspaceSettings(): void { try { $confManager = $this->configurationManager; $currentProcessor = $confManager->getLocalConfigurationValueByPath('GFX/processor'); $currentColorspace = $confManager->getLocalConfigurationValueByPath('GFX/processor_colorspace'); if ($currentProcessor === 'ImageMagick' && $currentColorspace === 'sRGB' || $currentProcessor === 'GraphicsMagick' && $currentColorspace === 'RGB') { $confManager->removeLocalConfigurationKeysByPath(['GFX/processor_colorspace']); $this->throwConfigurationChangedException(); } } catch (MissingArrayPathException) { // no change inside the system/settings.php found, so nothing needs to be modified } } /** * Migrate 'SYS'/'lang' related configuration settings to 'LANG' * * @throws ConfigurationChangedException */ protected function migrateLanguageSettings(): void { $confManager = $this->configurationManager; $newSettings = []; $removeSettings = []; // Migrate SYS/lang/requireApprovedLocalizations => LANG/requireApprovedLocalizations try { $value = $confManager->getLocalConfigurationValueByPath('SYS/lang/requireApprovedLocalizations'); $removeSettings[] = 'SYS/lang/requireApprovedLocalizations'; $newSettings['LANG/requireApprovedLocalizations'] = $value; } catch (MissingArrayPathException) { // Old setting does not exist, do nothing } // Migrate SYS/lang/format => LANG/format try { $value = $confManager->getLocalConfigurationValueByPath('SYS/lang/format'); $removeSettings[] = 'SYS/lang/format'; $newSettings['LANG/format'] = $value; } catch (MissingArrayPathException) { // Old setting does not exist, do nothing } // Migrate EXTCONF/lang/availableLanguages => LANG/availableLocales try { $value = $confManager->getLocalConfigurationValueByPath('EXTCONF/lang/availableLanguages'); $removeSettings[] = 'EXTCONF/lang/availableLanguages'; $newSettings['LANG/availableLocales'] = $value; } catch (MissingArrayPathException) { // Old setting does not exist, do nothing } // Migrate SYS/locallangXMLOverride => LANG/resourceOverrides try { $value = $confManager->getLocalConfigurationValueByPath('SYS/locallangXMLOverride'); $removeSettings[] = 'SYS/locallangXMLOverride'; $newSettings['LANG/resourceOverrides'] = $value; } catch (MissingArrayPathException) { // Old setting does not exist, do nothing } // Add new settings and remove old ones if (!empty($newSettings)) { $confManager->setLocalConfigurationValuesByPathValuePairs($newSettings); } if (!empty($removeSettings)) { $confManager->removeLocalConfigurationKeysByPath($removeSettings); } // Throw redirect if something was changed if (!empty($newSettings) || !empty($removeSettings)) { $this->throwConfigurationChangedException(); } } }