Completed
Push — master ( ac0864...f471cc )
by
unknown
13:32
created

migrateMailSmtpEncryptSetting()   A

Complexity

Conditions 5
Paths 11

Size

Total Lines 26
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 5
eloc 12
c 1
b 0
f 0
nc 11
nop 0
dl 0
loc 26
rs 9.5555
1
<?php
2
3
/*
4
 * This file is part of the TYPO3 CMS project.
5
 *
6
 * It is free software; you can redistribute it and/or modify it under
7
 * the terms of the GNU General Public License, either version 2
8
 * of the License, or any later version.
9
 *
10
 * For the full copyright and license information, please read the
11
 * LICENSE.txt file that was distributed with this source code.
12
 *
13
 * The TYPO3 project - inspiring people to share!
14
 */
15
16
namespace TYPO3\CMS\Install\Service;
17
18
use TYPO3\CMS\Core\Configuration\ConfigurationManager;
19
use TYPO3\CMS\Core\Crypto\PasswordHashing\Argon2idPasswordHash;
20
use TYPO3\CMS\Core\Crypto\PasswordHashing\Argon2iPasswordHash;
21
use TYPO3\CMS\Core\Crypto\PasswordHashing\BcryptPasswordHash;
22
use TYPO3\CMS\Core\Crypto\PasswordHashing\PasswordHashInterface;
23
use TYPO3\CMS\Core\Crypto\PasswordHashing\Pbkdf2PasswordHash;
24
use TYPO3\CMS\Core\Crypto\PasswordHashing\PhpassPasswordHash;
25
use TYPO3\CMS\Core\Crypto\Random;
26
use TYPO3\CMS\Core\Utility\Exception\MissingArrayPathException;
27
use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
28
use TYPO3\CMS\Core\Utility\GeneralUtility;
29
use TYPO3\CMS\Install\Service\Exception\ConfigurationChangedException;
30
31
/**
32
 * Execute "silent" LocalConfiguration upgrades if needed.
33
 *
34
 * Some LocalConfiguration settings are obsolete or changed over time.
35
 * This class handles upgrades of these settings. It is called by
36
 * the step controller at an early point.
37
 *
38
 * Every change is encapsulated in one method and must throw a ConfigurationChangedException
39
 * if new data is written to LocalConfiguration. This is caught by above
40
 * step controller to initiate a redirect and start again with adapted configuration.
41
 * @internal This class is only meant to be used within EXT:install and is not part of the TYPO3 Core API.
42
 */
43
class SilentConfigurationUpgradeService
44
{
45
    /**
46
     * @var ConfigurationManager
47
     */
48
    protected $configurationManager;
49
50
    /**
51
     * List of obsolete configuration options in LocalConfiguration to be removed
52
     * Example:
53
     *    // #forge-ticket
54
     *    'BE/somesetting',
55
     *
56
     * @var array
57
     */
58
    protected $obsoleteLocalConfigurationSettings = [
59
        // #72400
60
        'BE/spriteIconGenerator_handler',
61
        // #72417
62
        'SYS/lockingMode',
63
        // #72473
64
        'FE/secureFormmail',
65
        'FE/strictFormmail',
66
        'FE/formmailMaxAttachmentSize',
67
        // #72337
68
        'SYS/t3lib_cs_utils',
69
        'SYS/t3lib_cs_convMethod',
70
        // #72604
71
        'SYS/maxFileNameLength',
72
        // #72602
73
        'BE/unzip_path',
74
        // #72615
75
        'BE/notificationPrefix',
76
        // #72616
77
        'BE/XCLASS',
78
        'FE/XCLASS',
79
        // #43085
80
        'GFX/image_processing',
81
        // #70056
82
        'SYS/curlUse',
83
        'SYS/curlProxyNTLM',
84
        'SYS/curlProxyServer',
85
        'SYS/curlProxyTunnel',
86
        'SYS/curlProxyUserPass',
87
        'SYS/curlTimeout',
88
        // #75355
89
        'BE/niceFlexFormXMLtags',
90
        'BE/compactFlexFormXML',
91
        // #75625
92
        'SYS/clearCacheSystem',
93
        // #77411
94
        'SYS/caching/cacheConfigurations/extbase_typo3dbbackend_tablecolumns',
95
        // #77460
96
        'SYS/caching/cacheConfigurations/extbase_typo3dbbackend_queries',
97
        // #79513
98
        'FE/lockHashKeyWords',
99
        'BE/lockHashKeyWords',
100
        // #78835
101
        'SYS/cookieHttpOnly',
102
        // #71095
103
        'BE/lang',
104
        // #80050
105
        'FE/cHashIncludePageId',
106
        // #80711
107
        'FE/noPHPscriptInclude',
108
        'FE/maxSessionDataSize',
109
        // #82162
110
        'SYS/enable_errorDLOG',
111
        'SYS/enable_exceptionDLOG',
112
        // #82377
113
        'EXT/allowSystemInstall',
114
        // #82421
115
        'SYS/sqlDebug',
116
        'SYS/no_pconnect',
117
        'SYS/setDBinit',
118
        'SYS/dbClientCompress',
119
        // #82430
120
        'SYS/syslogErrorReporting',
121
        // #82639
122
        'SYS/enable_DLOG',
123
        'SC_OPTIONS/t3lib/class.t3lib_userauth.php/writeDevLog',
124
        'SC_OPTIONS/t3lib/class.t3lib_userauth.php/writeDevLogBE',
125
        'SC_OPTIONS/t3lib/class.t3lib_userauth.php/writeDevLogFE',
126
        // #82438
127
        'SYS/enableDeprecationLog',
128
        // #82680
129
        'GFX/png_truecolor',
130
        // #82803
131
        'FE/content_doktypes',
132
        // #83081
133
        'BE/fileExtensions',
134
        // #83768
135
        'SYS/doNotCheckReferer',
136
        // #83878
137
        'SYS/isInitialInstallationInProgress',
138
        'SYS/isInitialDatabaseImportDone',
139
        // #84810
140
        'BE/explicitConfirmationOfTranslation',
141
        // #87482
142
        'EXT/extConf',
143
        // #87767
144
        'SYS/recursiveDomainSearch',
145
        // #88376
146
        'FE/pageNotFound_handling',
147
        'FE/pageNotFound_handling_statheader',
148
        'FE/pageNotFound_handling_accessdeniedheader',
149
        'FE/pageUnavailable_handling',
150
        'FE/pageUnavailable_handling_statheader',
151
        // #88458
152
        'FE/get_url_id_token',
153
        // #88500
154
        'BE/RTE_imageStorageDir',
155
        // #89645
156
        'SYS/systemLog',
157
        'SYS/systemLogLevel',
158
    ];
159
160
    public function __construct(ConfigurationManager $configurationManager)
161
    {
162
        $this->configurationManager = $configurationManager;
163
    }
164
165
    /**
166
     * Executed configuration upgrades. Single upgrade methods must throw a
167
     * ConfigurationChangedException if something was written to LocalConfiguration.
168
     *
169
     * @throws ConfigurationChangedException
170
     */
171
    public function execute()
172
    {
173
        $this->generateEncryptionKeyIfNeeded();
174
        $this->configureBackendLoginSecurity();
175
        $this->configureFrontendLoginSecurity();
176
        $this->migrateImageProcessorSetting();
177
        $this->transferHttpSettings();
178
        $this->disableImageMagickDetailSettingsIfImageMagickIsDisabled();
179
        $this->setImageMagickDetailSettings();
180
        $this->migrateThumbnailsPngSetting();
181
        $this->migrateLockSslSetting();
182
        $this->migrateDatabaseConnectionSettings();
183
        $this->migrateDatabaseConnectionCharset();
184
        $this->migrateDatabaseDriverOptions();
185
        $this->migrateLangDebug();
186
        $this->migrateCacheHashOptions();
187
        $this->migrateExceptionErrors();
188
        $this->migrateDisplayErrorsSetting();
189
        $this->migrateSaltedPasswordsSettings();
190
        $this->migrateCachingFrameworkCaches();
191
        $this->migrateMailSettingsToSendmail();
192
        $this->migrateMailSmtpEncryptSetting();
193
194
        // Should run at the end to prevent obsolete settings are removed before migration
195
        $this->removeObsoleteLocalConfigurationSettings();
196
    }
197
198
    /**
199
     * Some settings in LocalConfiguration vanished in DefaultConfiguration
200
     * and have no impact on the core anymore.
201
     * To keep the configuration clean, those old settings are just silently
202
     * removed from LocalConfiguration if set.
203
     *
204
     * @throws ConfigurationChangedException
205
     */
206
    protected function removeObsoleteLocalConfigurationSettings()
207
    {
208
        $removed = $this->configurationManager->removeLocalConfigurationKeysByPath($this->obsoleteLocalConfigurationSettings);
209
210
        // If something was changed: Trigger a reload to have new values in next request
211
        if ($removed) {
212
            $this->throwConfigurationChangedException();
213
        }
214
    }
215
216
    /**
217
     * Backend login security is set to rsa if rsaauth
218
     * is installed (but not used) otherwise the default value "normal" has to be used.
219
     * This forces either 'normal' or 'rsa' to be set in LocalConfiguration.
220
     *
221
     * @throws ConfigurationChangedException
222
     */
223
    protected function configureBackendLoginSecurity()
224
    {
225
        $rsaauthLoaded = ExtensionManagementUtility::isLoaded('rsaauth');
226
        try {
227
            $currentLoginSecurityLevelValue = $this->configurationManager->getLocalConfigurationValueByPath('BE/loginSecurityLevel');
228
            if ($rsaauthLoaded && $currentLoginSecurityLevelValue !== 'rsa') {
229
                $this->configurationManager->setLocalConfigurationValueByPath('BE/loginSecurityLevel', 'rsa');
230
                $this->throwConfigurationChangedException();
231
            } elseif (!$rsaauthLoaded && $currentLoginSecurityLevelValue !== 'normal') {
232
                $this->configurationManager->setLocalConfigurationValueByPath('BE/loginSecurityLevel', 'normal');
233
                $this->throwConfigurationChangedException();
234
            }
235
        } catch (MissingArrayPathException $e) {
236
            // If an exception is thrown, the value is not set in LocalConfiguration
237
            $this->configurationManager->setLocalConfigurationValueByPath(
238
                'BE/loginSecurityLevel',
239
                $rsaauthLoaded ? 'rsa' : 'normal'
240
            );
241
            $this->throwConfigurationChangedException();
242
        }
243
    }
244
245
    /**
246
     * Frontend login security is set to normal in case
247
     * any other value is set while ext:rsaauth is not loaded.
248
     *
249
     * @throws ConfigurationChangedException
250
     */
251
    protected function configureFrontendLoginSecurity()
252
    {
253
        $rsaauthLoaded = ExtensionManagementUtility::isLoaded('rsaauth');
254
        try {
255
            $currentLoginSecurityLevelValue = $this->configurationManager->getLocalConfigurationValueByPath('FE/loginSecurityLevel');
256
            if (!$rsaauthLoaded && $currentLoginSecurityLevelValue !== 'normal') {
257
                $this->configurationManager->setLocalConfigurationValueByPath('FE/loginSecurityLevel', 'normal');
258
                $this->throwConfigurationChangedException();
259
            }
260
        } catch (MissingArrayPathException $e) {
261
            // no value set, just ignore
262
        }
263
    }
264
265
    /**
266
     * The encryption key is crucial for securing form tokens
267
     * and the whole TYPO3 link rendering later on. A random key is set here in
268
     * LocalConfiguration if it does not exist yet. This might possible happen
269
     * during upgrading and will happen during first install.
270
     *
271
     * @throws ConfigurationChangedException
272
     */
273
    protected function generateEncryptionKeyIfNeeded()
274
    {
275
        try {
276
            $currentValue = $this->configurationManager->getLocalConfigurationValueByPath('SYS/encryptionKey');
277
        } catch (MissingArrayPathException $e) {
278
            // If an exception is thrown, the value is not set in LocalConfiguration
279
            $currentValue = '';
280
        }
281
282
        if (empty($currentValue)) {
283
            $randomKey = GeneralUtility::makeInstance(Random::class)->generateRandomHexString(96);
284
            $this->configurationManager->setLocalConfigurationValueByPath('SYS/encryptionKey', $randomKey);
285
            $this->throwConfigurationChangedException();
286
        }
287
    }
288
289
    /**
290
     * Parse old curl and HTTP options and set new HTTP options, related to Guzzle
291
     *
292
     * @throws ConfigurationChangedException
293
     */
294
    protected function transferHttpSettings()
295
    {
296
        $changed = false;
297
        $newParameters = [];
298
        $obsoleteParameters = [];
299
300
        // Remove / migrate options to new options
301
        try {
302
            // Check if the adapter option is set, if so, set it to the parameters that are obsolete
303
            $this->configurationManager->getLocalConfigurationValueByPath('HTTP/adapter');
304
            $obsoleteParameters[] = 'HTTP/adapter';
305
        } catch (MissingArrayPathException $e) {
306
            // Migration done already
307
        }
308
        try {
309
            $newParameters['HTTP/version'] = $this->configurationManager->getLocalConfigurationValueByPath('HTTP/protocol_version');
310
            $obsoleteParameters[] = 'HTTP/protocol_version';
311
        } catch (MissingArrayPathException $e) {
312
            // Migration done already
313
        }
314
        try {
315
            $this->configurationManager->getLocalConfigurationValueByPath('HTTP/ssl_verify_host');
316
            $obsoleteParameters[] = 'HTTP/ssl_verify_host';
317
        } catch (MissingArrayPathException $e) {
318
            // Migration done already
319
        }
320
        try {
321
            $legacyUserAgent = $this->configurationManager->getLocalConfigurationValueByPath('HTTP/userAgent');
322
            $newParameters['HTTP/headers/User-Agent'] = $legacyUserAgent;
323
            $obsoleteParameters[] = 'HTTP/userAgent';
324
        } catch (MissingArrayPathException $e) {
325
            // Migration done already
326
        }
327
328
        // Redirects
329
        try {
330
            $legacyFollowRedirects = $this->configurationManager->getLocalConfigurationValueByPath('HTTP/follow_redirects');
331
            $obsoleteParameters[] = 'HTTP/follow_redirects';
332
        } catch (MissingArrayPathException $e) {
333
            $legacyFollowRedirects = '';
334
        }
335
        try {
336
            $legacyMaximumRedirects = $this->configurationManager->getLocalConfigurationValueByPath('HTTP/max_redirects');
337
            $obsoleteParameters[] = 'HTTP/max_redirects';
338
        } catch (MissingArrayPathException $e) {
339
            $legacyMaximumRedirects = '';
340
        }
341
        try {
342
            $legacyStrictRedirects = $this->configurationManager->getLocalConfigurationValueByPath('HTTP/strict_redirects');
343
            $obsoleteParameters[] = 'HTTP/strict_redirects';
344
        } catch (MissingArrayPathException $e) {
345
            $legacyStrictRedirects = '';
346
        }
347
348
        // Check if redirects have been disabled
349
        if ($legacyFollowRedirects !== '' && (bool)$legacyFollowRedirects === false) {
350
            $newParameters['HTTP/allow_redirects'] = false;
351
        } elseif ($legacyMaximumRedirects !== '' || $legacyStrictRedirects !== '') {
352
            $newParameters['HTTP/allow_redirects'] = [];
353
            if ($legacyMaximumRedirects !== '' && (int)$legacyMaximumRedirects !== 5) {
354
                $newParameters['HTTP/allow_redirects']['max'] = (int)$legacyMaximumRedirects;
355
            }
356
            if ($legacyStrictRedirects !== '' && (bool)$legacyStrictRedirects === true) {
357
                $newParameters['HTTP/allow_redirects']['strict'] = true;
358
            }
359
            // defaults are used, no need to set the option in LocalConfiguration.php
360
            if (empty($newParameters['HTTP/allow_redirects'])) {
361
                unset($newParameters['HTTP/allow_redirects']);
362
            }
363
        }
364
365
        // Migrate Proxy settings
366
        try {
367
            // Currently without protocol or port
368
            $legacyProxyHost = $this->configurationManager->getLocalConfigurationValueByPath('HTTP/proxy_host');
369
            $obsoleteParameters[] = 'HTTP/proxy_host';
370
        } catch (MissingArrayPathException $e) {
371
            $legacyProxyHost = '';
372
        }
373
        try {
374
            $legacyProxyPort = $this->configurationManager->getLocalConfigurationValueByPath('HTTP/proxy_port');
375
            $obsoleteParameters[] = 'HTTP/proxy_port';
376
        } catch (MissingArrayPathException $e) {
377
            $legacyProxyPort = '';
378
        }
379
        try {
380
            $legacyProxyUser = $this->configurationManager->getLocalConfigurationValueByPath('HTTP/proxy_user');
381
            $obsoleteParameters[] = 'HTTP/proxy_user';
382
        } catch (MissingArrayPathException $e) {
383
            $legacyProxyUser = '';
384
        }
385
        try {
386
            $legacyProxyPassword = $this->configurationManager->getLocalConfigurationValueByPath('HTTP/proxy_password');
387
            $obsoleteParameters[] = 'HTTP/proxy_password';
388
        } catch (MissingArrayPathException $e) {
389
            $legacyProxyPassword = '';
390
        }
391
        // Auth Scheme: Basic, digest etc.
392
        try {
393
            $legacyProxyAuthScheme = $this->configurationManager->getLocalConfigurationValueByPath('HTTP/proxy_auth_scheme');
394
            $obsoleteParameters[] = 'HTTP/proxy_auth_scheme';
395
        } catch (MissingArrayPathException $e) {
396
            $legacyProxyAuthScheme = '';
397
        }
398
399
        if ($legacyProxyHost !== '') {
400
            $proxy = 'http://';
401
            if ($legacyProxyAuthScheme !== '' && $legacyProxyUser !== '' && $legacyProxyPassword !== '') {
402
                $proxy .= $legacyProxyUser . ':' . $legacyProxyPassword . '@';
403
            }
404
            $proxy .= $legacyProxyHost;
405
            if ($legacyProxyPort !== '') {
406
                $proxy .= ':' . $legacyProxyPort;
407
            }
408
            $newParameters['HTTP/proxy'] = $proxy;
409
        }
410
411
        // Verify peers
412
        // see http://docs.guzzlephp.org/en/latest/request-options.html#verify
413
        try {
414
            $legacySslVerifyPeer = $this->configurationManager->getLocalConfigurationValueByPath('HTTP/ssl_verify_peer');
415
            $obsoleteParameters[] = 'HTTP/ssl_verify_peer';
416
        } catch (MissingArrayPathException $e) {
417
            $legacySslVerifyPeer = '';
418
        }
419
420
        // Directory holding multiple Certificate Authority files
421
        try {
422
            $legacySslCaPath = $this->configurationManager->getLocalConfigurationValueByPath('HTTP/ssl_capath');
423
            $obsoleteParameters[] = 'HTTP/ssl_capath';
424
        } catch (MissingArrayPathException $e) {
425
            $legacySslCaPath = '';
426
        }
427
        // Certificate Authority file to verify the peer with (use when ssl_verify_peer is TRUE)
428
        try {
429
            $legacySslCaFile = $this->configurationManager->getLocalConfigurationValueByPath('HTTP/ssl_cafile');
430
            $obsoleteParameters[] = 'HTTP/ssl_cafile';
431
        } catch (MissingArrayPathException $e) {
432
            $legacySslCaFile = '';
433
        }
434
        if ($legacySslVerifyPeer !== '') {
435
            if ($legacySslCaFile !== '' && $legacySslCaPath !== '') {
436
                $newParameters['HTTP/verify'] = $legacySslCaPath . $legacySslCaFile;
437
            } elseif ((bool)$legacySslVerifyPeer === false) {
438
                $newParameters['HTTP/verify'] = false;
439
            }
440
        }
441
442
        // SSL Key + Passphrase
443
        // Name of a file containing local certificate
444
        try {
445
            $legacySslLocalCert = $this->configurationManager->getLocalConfigurationValueByPath('HTTP/ssl_local_cert');
446
            $obsoleteParameters[] = 'HTTP/ssl_local_cert';
447
        } catch (MissingArrayPathException $e) {
448
            $legacySslLocalCert = '';
449
        }
450
451
        // Passphrase with which local certificate was encoded
452
        try {
453
            $legacySslPassphrase = $this->configurationManager->getLocalConfigurationValueByPath('HTTP/ssl_passphrase');
454
            $obsoleteParameters[] = 'HTTP/ssl_passphrase';
455
        } catch (MissingArrayPathException $e) {
456
            $legacySslPassphrase = '';
457
        }
458
459
        if ($legacySslLocalCert !== '') {
460
            if ($legacySslPassphrase !== '') {
461
                $newParameters['HTTP/ssl_key'] = [
462
                    $legacySslLocalCert,
463
                    $legacySslPassphrase
464
                ];
465
            } else {
466
                $newParameters['HTTP/ssl_key'] = $legacySslLocalCert;
467
            }
468
        }
469
470
        // Update the LocalConfiguration file if obsolete parameters or new parameters are set
471
        if (!empty($obsoleteParameters)) {
472
            $this->configurationManager->removeLocalConfigurationKeysByPath($obsoleteParameters);
473
            $changed = true;
474
        }
475
        if (!empty($newParameters)) {
476
            $this->configurationManager->setLocalConfigurationValuesByPathValuePairs($newParameters);
477
            $changed = true;
478
        }
479
        if ($changed) {
480
            $this->throwConfigurationChangedException();
481
        }
482
    }
483
484
    /**
485
     * Detail configuration of Image Magick settings must be cleared
486
     * if Image Magick handling is disabled.
487
     *
488
     * "Configuration presets" in install tool is not type safe, so value
489
     * comparisons here are not type safe too, to not trigger changes to
490
     * LocalConfiguration again.
491
     *
492
     * @throws ConfigurationChangedException
493
     */
494
    protected function disableImageMagickDetailSettingsIfImageMagickIsDisabled()
495
    {
496
        $changedValues = [];
497
        try {
498
            $currentEnabledValue = $this->configurationManager->getLocalConfigurationValueByPath('GFX/processor_enabled');
499
        } catch (MissingArrayPathException $e) {
500
            $currentEnabledValue = $this->configurationManager->getDefaultConfigurationValueByPath('GFX/processor_enabled');
501
        }
502
503
        try {
504
            $currentPathValue = $this->configurationManager->getLocalConfigurationValueByPath('GFX/processor_path');
505
        } catch (MissingArrayPathException $e) {
506
            $currentPathValue = $this->configurationManager->getDefaultConfigurationValueByPath('GFX/processor_path');
507
        }
508
509
        try {
510
            $currentPathLzwValue = $this->configurationManager->getLocalConfigurationValueByPath('GFX/processor_path_lzw');
511
        } catch (MissingArrayPathException $e) {
512
            $currentPathLzwValue = $this->configurationManager->getDefaultConfigurationValueByPath('GFX/processor_path_lzw');
513
        }
514
515
        try {
516
            $currentImageFileExtValue = $this->configurationManager->getLocalConfigurationValueByPath('GFX/imagefile_ext');
517
        } catch (MissingArrayPathException $e) {
518
            $currentImageFileExtValue = $this->configurationManager->getDefaultConfigurationValueByPath('GFX/imagefile_ext');
519
        }
520
521
        try {
522
            $currentThumbnailsValue = $this->configurationManager->getLocalConfigurationValueByPath('GFX/thumbnails');
523
        } catch (MissingArrayPathException $e) {
524
            $currentThumbnailsValue = $this->configurationManager->getDefaultConfigurationValueByPath('GFX/thumbnails');
525
        }
526
527
        if (!$currentEnabledValue) {
528
            if ($currentPathValue != '') {
529
                $changedValues['GFX/processor_path'] = '';
530
            }
531
            if ($currentPathLzwValue != '') {
532
                $changedValues['GFX/processor_path_lzw'] = '';
533
            }
534
            if ($currentImageFileExtValue !== 'gif,jpg,jpeg,png') {
535
                $changedValues['GFX/imagefile_ext'] = 'gif,jpg,jpeg,png';
536
            }
537
            if ($currentThumbnailsValue != 0) {
538
                $changedValues['GFX/thumbnails'] = 0;
539
            }
540
        }
541
        if (!empty($changedValues)) {
542
            $this->configurationManager->setLocalConfigurationValuesByPathValuePairs($changedValues);
543
            $this->throwConfigurationChangedException();
544
        }
545
    }
546
547
    /**
548
     * Detail configuration of Image Magick and Graphics Magick settings
549
     * depending on main values.
550
     *
551
     * "Configuration presets" in install tool is not type safe, so value
552
     * comparisons here are not type safe too, to not trigger changes to
553
     * LocalConfiguration again.
554
     *
555
     * @throws ConfigurationChangedException
556
     */
557
    protected function setImageMagickDetailSettings()
558
    {
559
        $changedValues = [];
560
        try {
561
            $currentProcessorValue = $this->configurationManager->getLocalConfigurationValueByPath('GFX/processor');
562
        } catch (MissingArrayPathException $e) {
563
            $currentProcessorValue = $this->configurationManager->getDefaultConfigurationValueByPath('GFX/processor');
564
        }
565
566
        try {
567
            $currentProcessorMaskValue = $this->configurationManager->getLocalConfigurationValueByPath('GFX/processor_allowTemporaryMasksAsPng');
568
        } catch (MissingArrayPathException $e) {
569
            $currentProcessorMaskValue = $this->configurationManager->getDefaultConfigurationValueByPath('GFX/processor_allowTemporaryMasksAsPng');
570
        }
571
572
        try {
573
            $currentProcessorEffectsValue = $this->configurationManager->getLocalConfigurationValueByPath('GFX/processor_effects');
574
        } catch (MissingArrayPathException $e) {
575
            $currentProcessorEffectsValue = $this->configurationManager->getDefaultConfigurationValueByPath('GFX/processor_effects');
576
        }
577
578
        if ((string)$currentProcessorValue !== '') {
579
            if (!is_bool($currentProcessorEffectsValue)) {
580
                $changedValues['GFX/processor_effects'] = (int)$currentProcessorEffectsValue > 0;
581
            }
582
583
            if ($currentProcessorMaskValue != 0) {
584
                $changedValues['GFX/processor_allowTemporaryMasksAsPng'] = 0;
585
            }
586
        }
587
        if (!empty($changedValues)) {
588
            $this->configurationManager->setLocalConfigurationValuesByPathValuePairs($changedValues);
589
            $this->throwConfigurationChangedException();
590
        }
591
    }
592
593
    /**
594
     * Migrate the definition of the image processor from the configuration value
595
     * im_version_5 to the setting processor.
596
     *
597
     * @throws ConfigurationChangedException
598
     */
599
    protected function migrateImageProcessorSetting()
600
    {
601
        $changedSettings = [];
602
        $settingsToRename = [
603
            'GFX/im' => 'GFX/processor_enabled',
604
            'GFX/im_version_5' => 'GFX/processor',
605
            'GFX/im_v5effects' => 'GFX/processor_effects',
606
            'GFX/im_path' => 'GFX/processor_path',
607
            'GFX/im_path_lzw' => 'GFX/processor_path_lzw',
608
            'GFX/im_mask_temp_ext_gif' => 'GFX/processor_allowTemporaryMasksAsPng',
609
            'GFX/im_noScaleUp' => 'GFX/processor_allowUpscaling',
610
            'GFX/im_noFramePrepended' => 'GFX/processor_allowFrameSelection',
611
            'GFX/im_stripProfileCommand' => 'GFX/processor_stripColorProfileCommand',
612
            'GFX/im_useStripProfileByDefault' => 'GFX/processor_stripColorProfileByDefault',
613
            'GFX/colorspace' => 'GFX/processor_colorspace',
614
        ];
615
616
        foreach ($settingsToRename as $oldPath => $newPath) {
617
            try {
618
                $value = $this->configurationManager->getLocalConfigurationValueByPath($oldPath);
619
                $this->configurationManager->setLocalConfigurationValueByPath($newPath, $value);
620
                $changedSettings[$oldPath] = true;
621
            } catch (MissingArrayPathException $e) {
622
                // If an exception is thrown, the value is not set in LocalConfiguration
623
                $changedSettings[$oldPath] = false;
624
            }
625
        }
626
627
        if (!empty($changedSettings['GFX/im_version_5'])) {
628
            $currentProcessorValue = $this->configurationManager->getLocalConfigurationValueByPath('GFX/im_version_5');
629
            $newProcessorValue = $currentProcessorValue === 'gm' ? 'GraphicsMagick' : 'ImageMagick';
630
            $this->configurationManager->setLocalConfigurationValueByPath('GFX/processor', $newProcessorValue);
631
        }
632
633
        if (!empty($changedSettings['GFX/im_noScaleUp'])) {
634
            $currentProcessorValue = $this->configurationManager->getLocalConfigurationValueByPath('GFX/im_noScaleUp');
635
            $newProcessorValue = !$currentProcessorValue;
636
            $this->configurationManager->setLocalConfigurationValueByPath(
637
                'GFX/processor_allowUpscaling',
638
                $newProcessorValue
639
            );
640
        }
641
642
        if (!empty($changedSettings['GFX/im_noFramePrepended'])) {
643
            $currentProcessorValue = $this->configurationManager->getLocalConfigurationValueByPath('GFX/im_noFramePrepended');
644
            $newProcessorValue = !$currentProcessorValue;
645
            $this->configurationManager->setLocalConfigurationValueByPath(
646
                'GFX/processor_allowFrameSelection',
647
                $newProcessorValue
648
            );
649
        }
650
651
        if (!empty($changedSettings['GFX/im_mask_temp_ext_gif'])) {
652
            $currentProcessorValue = $this->configurationManager->getLocalConfigurationValueByPath('GFX/im_mask_temp_ext_gif');
653
            $newProcessorValue = !$currentProcessorValue;
654
            $this->configurationManager->setLocalConfigurationValueByPath(
655
                'GFX/processor_allowTemporaryMasksAsPng',
656
                $newProcessorValue
657
            );
658
        }
659
660
        if (!empty(array_filter($changedSettings))) {
661
            $this->configurationManager->removeLocalConfigurationKeysByPath(array_keys($changedSettings));
662
            $this->throwConfigurationChangedException();
663
        }
664
    }
665
666
    /**
667
     * Throw exception after configuration change to trigger a redirect.
668
     *
669
     * @throws ConfigurationChangedException
670
     */
671
    protected function throwConfigurationChangedException()
672
    {
673
        throw new ConfigurationChangedException(
674
            'Configuration updated, reload needed',
675
            1379024938
676
        );
677
    }
678
679
    /**
680
     * Migrate the configuration value thumbnails_png to a boolean value.
681
     *
682
     * @throws ConfigurationChangedException
683
     */
684
    protected function migrateThumbnailsPngSetting()
685
    {
686
        $changedValues = [];
687
        try {
688
            $currentThumbnailsPngValue = $this->configurationManager->getLocalConfigurationValueByPath('GFX/thumbnails_png');
689
        } catch (MissingArrayPathException $e) {
690
            $currentThumbnailsPngValue = $this->configurationManager->getDefaultConfigurationValueByPath('GFX/thumbnails_png');
691
        }
692
693
        if (is_int($currentThumbnailsPngValue) && $currentThumbnailsPngValue > 0) {
694
            $changedValues['GFX/thumbnails_png'] = true;
695
        }
696
        if (!empty($changedValues)) {
697
            $this->configurationManager->setLocalConfigurationValuesByPathValuePairs($changedValues);
698
            $this->throwConfigurationChangedException();
699
        }
700
    }
701
702
    /**
703
     * Migrate the configuration setting BE/lockSSL to boolean if set in the LocalConfiguration.php file
704
     *
705
     * @throws ConfigurationChangedException
706
     */
707
    protected function migrateLockSslSetting()
708
    {
709
        try {
710
            $currentOption = $this->configurationManager->getLocalConfigurationValueByPath('BE/lockSSL');
711
            // check if the current option is an integer/string and if it is active
712
            if (!is_bool($currentOption) && (int)$currentOption > 0) {
713
                $this->configurationManager->setLocalConfigurationValueByPath('BE/lockSSL', true);
714
                $this->throwConfigurationChangedException();
715
            }
716
        } catch (MissingArrayPathException $e) {
717
            // no change inside the LocalConfiguration.php found, so nothing needs to be modified
718
        }
719
    }
720
721
    /**
722
     * Move the database connection settings to a "Default" connection
723
     *
724
     * @throws ConfigurationChangedException
725
     */
726
    protected function migrateDatabaseConnectionSettings()
727
    {
728
        $confManager = $this->configurationManager;
729
730
        $newSettings = [];
731
        $removeSettings = [];
732
733
        try {
734
            $value = $confManager->getLocalConfigurationValueByPath('DB/username');
735
            $removeSettings[] = 'DB/username';
736
            $newSettings['DB/Connections/Default/user'] = $value;
737
        } catch (MissingArrayPathException $e) {
738
            // Old setting does not exist, do nothing
739
        }
740
741
        try {
742
            $value = $confManager->getLocalConfigurationValueByPath('DB/password');
743
            $removeSettings[] = 'DB/password';
744
            $newSettings['DB/Connections/Default/password'] = $value;
745
        } catch (MissingArrayPathException $e) {
746
            // Old setting does not exist, do nothing
747
        }
748
749
        try {
750
            $value = $confManager->getLocalConfigurationValueByPath('DB/host');
751
            $removeSettings[] = 'DB/host';
752
            $newSettings['DB/Connections/Default/host'] = $value;
753
        } catch (MissingArrayPathException $e) {
754
            // Old setting does not exist, do nothing
755
        }
756
757
        try {
758
            $value = $confManager->getLocalConfigurationValueByPath('DB/port');
759
            $removeSettings[] = 'DB/port';
760
            $newSettings['DB/Connections/Default/port'] = $value;
761
        } catch (MissingArrayPathException $e) {
762
            // Old setting does not exist, do nothing
763
        }
764
765
        try {
766
            $value = $confManager->getLocalConfigurationValueByPath('DB/socket');
767
            $removeSettings[] = 'DB/socket';
768
            // Remove empty socket connects
769
            if (!empty($value)) {
770
                $newSettings['DB/Connections/Default/unix_socket'] = $value;
771
            }
772
        } catch (MissingArrayPathException $e) {
773
            // Old setting does not exist, do nothing
774
        }
775
776
        try {
777
            $value = $confManager->getLocalConfigurationValueByPath('DB/database');
778
            $removeSettings[] = 'DB/database';
779
            $newSettings['DB/Connections/Default/dbname'] = $value;
780
        } catch (MissingArrayPathException $e) {
781
            // Old setting does not exist, do nothing
782
        }
783
784
        try {
785
            $value = (bool)$confManager->getLocalConfigurationValueByPath('SYS/dbClientCompress');
786
            $removeSettings[] = 'SYS/dbClientCompress';
787
            if ($value) {
788
                $newSettings['DB/Connections/Default/driverOptions'] = [
789
                    'flags' => MYSQLI_CLIENT_COMPRESS,
790
                ];
791
            }
792
        } catch (MissingArrayPathException $e) {
793
            // Old setting does not exist, do nothing
794
        }
795
796
        try {
797
            $value = (bool)$confManager->getLocalConfigurationValueByPath('SYS/no_pconnect');
798
            $removeSettings[] = 'SYS/no_pconnect';
799
            if (!$value) {
800
                $newSettings['DB/Connections/Default/persistentConnection'] = true;
801
            }
802
        } catch (MissingArrayPathException $e) {
803
            // Old setting does not exist, do nothing
804
        }
805
806
        try {
807
            $value = $confManager->getLocalConfigurationValueByPath('SYS/setDBinit');
808
            $removeSettings[] = 'SYS/setDBinit';
809
            $newSettings['DB/Connections/Default/initCommands'] = $value;
810
        } catch (MissingArrayPathException $e) {
811
            // Old setting does not exist, do nothing
812
        }
813
814
        try {
815
            $confManager->getLocalConfigurationValueByPath('DB/Connections/Default/charset');
816
        } catch (MissingArrayPathException $e) {
817
            // If there is no charset option yet, add it.
818
            $newSettings['DB/Connections/Default/charset'] = 'utf8';
819
        }
820
821
        try {
822
            $confManager->getLocalConfigurationValueByPath('DB/Connections/Default/driver');
823
        } catch (MissingArrayPathException $e) {
824
            // Use the mysqli driver by default if no value has been provided yet
825
            $newSettings['DB/Connections/Default/driver'] = 'mysqli';
826
        }
827
828
        // Add new settings and remove old ones
829
        if (!empty($newSettings)) {
830
            $confManager->setLocalConfigurationValuesByPathValuePairs($newSettings);
831
        }
832
        if (!empty($removeSettings)) {
833
            $confManager->removeLocalConfigurationKeysByPath($removeSettings);
834
        }
835
836
        // Throw redirect if something was changed
837
        if (!empty($newSettings) || !empty($removeSettings)) {
838
            $this->throwConfigurationChangedException();
839
        }
840
    }
841
842
    /**
843
     * Migrate the configuration setting DB/Connections/Default/charset to 'utf8' as
844
     * 'utf-8' is not supported by all MySQL versions.
845
     *
846
     * @throws ConfigurationChangedException
847
     */
848
    protected function migrateDatabaseConnectionCharset()
849
    {
850
        $confManager = $this->configurationManager;
851
        try {
852
            $driver = $confManager->getLocalConfigurationValueByPath('DB/Connections/Default/driver');
853
            $charset = $confManager->getLocalConfigurationValueByPath('DB/Connections/Default/charset');
854
            if (in_array($driver, ['mysqli', 'pdo_mysql', 'drizzle_pdo_mysql'], true) && $charset === 'utf-8') {
855
                $confManager->setLocalConfigurationValueByPath('DB/Connections/Default/charset', 'utf8');
856
                $this->throwConfigurationChangedException();
857
            }
858
        } catch (MissingArrayPathException $e) {
859
            // no incompatible charset configuration found, so nothing needs to be modified
860
        }
861
    }
862
863
    /**
864
     * Migrate the configuration setting DB/Connections/Default/driverOptions to array type.
865
     *
866
     * @throws ConfigurationChangedException
867
     */
868
    protected function migrateDatabaseDriverOptions()
869
    {
870
        $confManager = $this->configurationManager;
871
        try {
872
            $options = $confManager->getLocalConfigurationValueByPath('DB/Connections/Default/driverOptions');
873
            if (!is_array($options)) {
874
                $confManager->setLocalConfigurationValueByPath(
875
                    'DB/Connections/Default/driverOptions',
876
                    ['flags' => (int)$options]
877
                );
878
                $this->throwConfigurationChangedException();
879
            }
880
        } catch (MissingArrayPathException $e) {
881
            // no driver options found, nothing needs to be modified
882
        }
883
    }
884
885
    /**
886
     * Migrate the configuration setting BE/lang/debug if set in the LocalConfiguration.php file
887
     *
888
     * @throws ConfigurationChangedException
889
     */
890
    protected function migrateLangDebug()
891
    {
892
        $confManager = $this->configurationManager;
893
        try {
894
            $currentOption = $confManager->getLocalConfigurationValueByPath('BE/lang/debug');
895
            // check if the current option is set and boolean
896
            if (isset($currentOption) && is_bool($currentOption)) {
897
                $confManager->setLocalConfigurationValueByPath('BE/languageDebug', $currentOption);
898
                $this->throwConfigurationChangedException();
899
            }
900
        } catch (MissingArrayPathException $e) {
901
            // no change inside the LocalConfiguration.php found, so nothing needs to be modified
902
        }
903
    }
904
905
    /**
906
     * Migrate single cache hash related options under "FE" into "FE/cacheHash"
907
     *
908
     * @throws ConfigurationChangedException
909
     */
910
    protected function migrateCacheHashOptions()
911
    {
912
        $confManager = $this->configurationManager;
913
        $removeSettings = [];
914
        $newSettings = [];
915
916
        try {
917
            $value = $confManager->getLocalConfigurationValueByPath('FE/cHashOnlyForParameters');
918
            $removeSettings[] = 'FE/cHashOnlyForParameters';
919
            $newSettings['FE/cacheHash/cachedParametersWhiteList'] = GeneralUtility::trimExplode(',', $value, true);
920
        } catch (MissingArrayPathException $e) {
921
            // Migration done already
922
        }
923
924
        try {
925
            $value = $confManager->getLocalConfigurationValueByPath('FE/cHashExcludedParameters');
926
            $removeSettings[] = 'FE/cHashExcludedParameters';
927
            $newSettings['FE/cacheHash/excludedParameters'] = GeneralUtility::trimExplode(',', $value, true);
928
        } catch (MissingArrayPathException $e) {
929
            // Migration done already
930
        }
931
932
        try {
933
            $value = $confManager->getLocalConfigurationValueByPath('FE/cHashRequiredParameters');
934
            $removeSettings[] = 'FE/cHashRequiredParameters';
935
            $newSettings['FE/cacheHash/requireCacheHashPresenceParameters'] = GeneralUtility::trimExplode(',', $value, true);
936
        } catch (MissingArrayPathException $e) {
937
            // Migration done already
938
        }
939
940
        try {
941
            $value = $confManager->getLocalConfigurationValueByPath('FE/cHashExcludedParametersIfEmpty');
942
            $removeSettings[] = 'FE/cHashExcludedParametersIfEmpty';
943
            if (trim($value) === '*') {
944
                $newSettings['FE/cacheHash/excludeAllEmptyParameters'] = true;
945
            } else {
946
                $newSettings['FE/cacheHash/excludedParametersIfEmpty'] = GeneralUtility::trimExplode(',', $value, true);
947
            }
948
        } catch (MissingArrayPathException $e) {
949
            // Migration done already
950
        }
951
952
        // Add new settings and remove old ones
953
        if (!empty($newSettings)) {
954
            $confManager->setLocalConfigurationValuesByPathValuePairs($newSettings);
955
        }
956
        if (!empty($removeSettings)) {
957
            $confManager->removeLocalConfigurationKeysByPath($removeSettings);
958
        }
959
960
        // Throw redirect if something was changed
961
        if (!empty($newSettings) || !empty($removeSettings)) {
962
            $this->throwConfigurationChangedException();
963
        }
964
    }
965
966
    /**
967
     * Migrate SYS/exceptionalErrors to not contain E_USER_DEPRECATED
968
     *
969
     * @throws ConfigurationChangedException
970
     */
971
    protected function migrateExceptionErrors()
972
    {
973
        $confManager = $this->configurationManager;
974
        try {
975
            $currentOption = (int)$confManager->getLocalConfigurationValueByPath('SYS/exceptionalErrors');
976
            // make sure E_USER_DEPRECATED is not part of the exceptionalErrors
977
            if ($currentOption & E_USER_DEPRECATED) {
978
                $confManager->setLocalConfigurationValueByPath('SYS/exceptionalErrors', $currentOption & ~E_USER_DEPRECATED);
979
                $this->throwConfigurationChangedException();
980
            }
981
        } catch (MissingArrayPathException $e) {
982
            // no change inside the LocalConfiguration.php found, so nothing needs to be modified
983
        }
984
    }
985
986
    /**
987
     * Migrate SYS/displayErrors to not contain 2
988
     *
989
     * @throws ConfigurationChangedException
990
     */
991
    protected function migrateDisplayErrorsSetting()
992
    {
993
        $confManager = $this->configurationManager;
994
        try {
995
            $currentOption = (int)$confManager->getLocalConfigurationValueByPath('SYS/displayErrors');
996
            // make sure displayErrors is set to 2
997
            if ($currentOption === 2) {
998
                $confManager->setLocalConfigurationValueByPath('SYS/displayErrors', -1);
999
                $this->throwConfigurationChangedException();
1000
            }
1001
        } catch (MissingArrayPathException $e) {
1002
            // no change inside the LocalConfiguration.php found, so nothing needs to be modified
1003
        }
1004
    }
1005
1006
    /**
1007
     * Migrate salted passwords extension configuration settings to BE/passwordHashing and FE/passwordHashing
1008
     *
1009
     * @throws ConfigurationChangedException
1010
     */
1011
    protected function migrateSaltedPasswordsSettings()
1012
    {
1013
        $confManager = $this->configurationManager;
1014
        $configsToRemove = [];
1015
        try {
1016
            $extensionConfiguration = (array)$confManager->getLocalConfigurationValueByPath('EXTENSIONS/saltedpasswords');
1017
            $configsToRemove[] = 'EXTENSIONS/saltedpasswords';
1018
        } catch (MissingArrayPathException $e) {
1019
            $extensionConfiguration = [];
1020
        }
1021
        // Migration already done
1022
        if (empty($extensionConfiguration)) {
1023
            return;
1024
        }
1025
        // Upgrade to best available hash method. This is only done once since that code will no longer be reached
1026
        // after first migration because extConf and EXTENSIONS array entries are gone then. Thus, a manual selection
1027
        // to some different hash mechanism will not be touched again after first upgrade.
1028
        // Phpass is always available, so we have some last fallback if the others don't kick in
1029
        $okHashMethods = [
1030
            Argon2iPasswordHash::class,
1031
            Argon2idPasswordHash::class,
1032
            BcryptPasswordHash::class,
1033
            Pbkdf2PasswordHash::class,
1034
            PhpassPasswordHash::class,
1035
        ];
1036
        $newMethods = [];
1037
        foreach (['BE', 'FE'] as $mode) {
1038
            foreach ($okHashMethods as $className) {
1039
                /** @var PasswordHashInterface $instance */
1040
                $instance = GeneralUtility::makeInstance($className);
1041
                if ($instance->isAvailable()) {
1042
                    $newMethods[$mode] = $className;
1043
                    break;
1044
                }
1045
            }
1046
        }
1047
        // We only need to write to LocalConfiguration if method is different than Argon2i from DefaultConfiguration
1048
        $newConfig = [];
1049
        if ($newMethods['BE'] !== Argon2iPasswordHash::class) {
1050
            $newConfig['BE/passwordHashing/className'] = $newMethods['BE'];
1051
        }
1052
        if ($newMethods['FE'] !== Argon2iPasswordHash::class) {
1053
            $newConfig['FE/passwordHashing/className'] = $newMethods['FE'];
1054
        }
1055
        if (!empty($newConfig)) {
1056
            $confManager->setLocalConfigurationValuesByPathValuePairs($newConfig);
1057
        }
1058
        $confManager->removeLocalConfigurationKeysByPath($configsToRemove);
1059
        $this->throwConfigurationChangedException();
1060
    }
1061
1062
    /**
1063
     * Renames all SYS[caching][cache] configuration names to names without the prefix "cache_".
1064
     * see #88366
1065
     */
1066
    protected function migrateCachingFrameworkCaches()
1067
    {
1068
        $confManager = $this->configurationManager;
1069
        try {
1070
            $cacheConfigurations = (array)$confManager->getLocalConfigurationValueByPath('SYS/caching/cacheConfigurations');
1071
            $newConfig = [];
1072
            $hasBeenModified = false;
1073
            foreach ($cacheConfigurations as $identifier => $cacheConfiguration) {
1074
                if (strpos($identifier, 'cache_') === 0) {
1075
                    $identifier = substr($identifier, 6);
1076
                    $hasBeenModified = true;
1077
                }
1078
                $newConfig[$identifier] = $cacheConfiguration;
1079
            }
1080
1081
            if ($hasBeenModified) {
1082
                $confManager->setLocalConfigurationValueByPath('SYS/caching/cacheConfigurations', $newConfig);
1083
                $this->throwConfigurationChangedException();
1084
            }
1085
        } catch (MissingArrayPathException $e) {
1086
            // no change inside the LocalConfiguration.php found, so nothing needs to be modified
1087
        }
1088
    }
1089
1090
    /**
1091
     * Migrates "mail" to "sendmail" as "mail" (PHP's built-in mail() method) is not supported anymore
1092
     * with Symfony components.
1093
     * See #88643
1094
     */
1095
    protected function migrateMailSettingsToSendmail()
1096
    {
1097
        $confManager = $this->configurationManager;
1098
        try {
1099
            $transport = $confManager->getLocalConfigurationValueByPath('MAIL/transport');
1100
            if ($transport === 'mail') {
1101
                $confManager->setLocalConfigurationValueByPath('MAIL/transport', 'sendmail');
1102
                $confManager->setLocalConfigurationValueByPath('MAIL/transport_sendmail_command', (string)@ini_get('sendmail_path'));
1103
                $this->throwConfigurationChangedException();
1104
            }
1105
        } catch (MissingArrayPathException $e) {
1106
            // no change inside the LocalConfiguration.php found, so nothing needs to be modified
1107
        }
1108
    }
1109
1110
    /**
1111
     * Migrates MAIL/transport_smtp_encrypt to a boolean value
1112
     * See #91070, #90295, #88643 and https://github.com/symfony/symfony/commit/5b8c4676d059
1113
     */
1114
    protected function migrateMailSmtpEncryptSetting()
1115
    {
1116
        $confManager = $this->configurationManager;
1117
        try {
1118
            $transport = $confManager->getLocalConfigurationValueByPath('MAIL/transport');
1119
            if ($transport === 'smtp') {
1120
                $encrypt = $confManager->getLocalConfigurationValueByPath('MAIL/transport_smtp_encrypt');
1121
                if (is_string($encrypt)) {
1122
                    // SwiftMailer used 'tls' as identifier to connect with STARTTLS via SMTP (as usually used with port 587).
1123
                    // See https://github.com/swiftmailer/swiftmailer/blob/v5.4.10/lib/classes/Swift/Transport/EsmtpTransport.php#L144
1124
                    if ($encrypt === 'tls') {
1125
                        // With TYPO3 v10 the MAIL/transport_smtp_encrypt option is passed as constructor parameter $tls to
1126
                        // Symfony\Component\Mailer\Transport\Smtp\EsmtpTransport
1127
                        // $tls = true instructs to start a SMTPS connection – that means SSL/TLS via SMTPS, not STARTTLS via SMTP.
1128
                        // That means symfony/mailer will use STARTTLS when $tls = false or ($tls = null with port != 465) is passed.
1129
                        // Actually symfony/mailer will use STARTTLS by default now.
1130
                        // Due to the misleading name (transport_smtp_encrypt) we avoid to set the option to false, but rather remove it.
1131
                        // Note: symfony/mailer provides no way to enforce STARTTLS usage, see https://github.com/symfony/symfony/commit/5b8c4676d059
1132
                        $confManager->removeLocalConfigurationKeysByPath(['MAIL/transport_smtp_encrypt']);
1133
                    } else {
1134
                        $confManager->setLocalConfigurationValueByPath('MAIL/transport_smtp_encrypt', true);
1135
                    }
1136
                    $this->throwConfigurationChangedException();
1137
                }
1138
            }
1139
        } catch (MissingArrayPathException $e) {
1140
            // no change inside the LocalConfiguration.php found, so nothing needs to be modified
1141
        }
1142
    }
1143
}
1144