fix: support SMTP STARTTLS mode and NTLM auth (#5426)

* fix: support SMTP STARTTLS mode and NTLM auth

Add explicit SMTP STARTTLS configuration for 587-style connections and keep SSL/TLS as the implicit TLS mode.

Prefer PLAIN when advertised, keep LOGIN compatibility, and add NTLM as a fallback for Exchange SMTP servers that require it after STARTTLS.

* fix: respect explicit SMTP encryption mode

* fix: preserve SMTP TLS compatibility
This commit is contained in:
Benson Yan
2026-06-24 12:45:39 +08:00
committed by GitHub
parent 9fc9c8f1e3
commit 2f23a66733
27 changed files with 959 additions and 52 deletions
+57 -7
View File
@@ -91,6 +91,7 @@ const SystemSetting = () => {
EmailDomainRestrictionEnabled: '',
EmailAliasRestrictionEnabled: '',
SMTPSSLEnabled: '',
SMTPStartTLSEnabled: '',
SMTPForceAuthLogin: '',
EmailDomainWhitelist: [],
TelegramOAuthEnabled: '',
@@ -183,6 +184,7 @@ const SystemSetting = () => {
case 'EmailDomainRestrictionEnabled':
case 'EmailAliasRestrictionEnabled':
case 'SMTPSSLEnabled':
case 'SMTPStartTLSEnabled':
case 'SMTPForceAuthLogin':
case 'LinuxDOOAuthEnabled':
case 'discord.enabled':
@@ -321,6 +323,13 @@ const SystemSetting = () => {
const submitSMTP = async () => {
const options = [];
const smtpSecurityMode = inputs.SMTPSSLEnabled
? 'ssl_tls'
: inputs.SMTPStartTLSEnabled
? 'starttls'
: 'none';
const nextSMTPSSLEnabled = smtpSecurityMode === 'ssl_tls';
const nextSMTPStartTLSEnabled = smtpSecurityMode === 'starttls';
if (originInputs['SMTPServer'] !== inputs.SMTPServer) {
options.push({ key: 'SMTPServer', value: inputs.SMTPServer });
@@ -343,6 +352,15 @@ const SystemSetting = () => {
) {
options.push({ key: 'SMTPToken', value: inputs.SMTPToken });
}
if (originInputs['SMTPSSLEnabled'] !== nextSMTPSSLEnabled) {
options.push({ key: 'SMTPSSLEnabled', value: nextSMTPSSLEnabled });
}
if (originInputs['SMTPStartTLSEnabled'] !== nextSMTPStartTLSEnabled) {
options.push({
key: 'SMTPStartTLSEnabled',
value: nextSMTPStartTLSEnabled,
});
}
if (options.length > 0) {
await updateOptions(options);
@@ -691,6 +709,23 @@ const SystemSetting = () => {
}
};
const handleSMTPSecurityModeChange = async (event) => {
const mode = event && event.target ? event.target.value : event;
const nextSMTPSSLEnabled = mode === 'ssl_tls';
const nextSMTPStartTLSEnabled = mode === 'starttls';
formApiRef.current?.setValue('SMTPSSLEnabled', nextSMTPSSLEnabled);
formApiRef.current?.setValue(
'SMTPStartTLSEnabled',
nextSMTPStartTLSEnabled,
);
await updateOptions([
{ key: 'SMTPSSLEnabled', value: nextSMTPSSLEnabled },
{ key: 'SMTPStartTLSEnabled', value: nextSMTPStartTLSEnabled },
]);
};
const handlePasswordLoginConfirm = async () => {
await updateOptions([{ key: 'PasswordLoginEnabled', value: false }]);
setShowPasswordLoginConfirmModal(false);
@@ -1328,15 +1363,30 @@ const SystemSetting = () => {
/>
</Col>
<Col xs={24} sm={24} md={8} lg={8} xl={8}>
<Form.Checkbox
field='SMTPSSLEnabled'
noLabel
onChange={(e) =>
handleCheckboxChange('SMTPSSLEnabled', e)
<Text strong>{t('SMTP 加密方式')}</Text>
<Radio.Group
type='button'
value={
inputs.SMTPSSLEnabled
? 'ssl_tls'
: inputs.SMTPStartTLSEnabled
? 'starttls'
: 'none'
}
onChange={handleSMTPSecurityModeChange}
style={{ marginTop: 8, marginBottom: 8 }}
>
{t('启用SMTP SSL')}
</Form.Checkbox>
<Radio value='none'>{t('无加密')}</Radio>
<Radio value='ssl_tls'>{t('SSL/TLS')}</Radio>
<Radio value='starttls'>{t('STARTTLS')}</Radio>
</Radio.Group>
<Text
type='secondary'
size='small'
style={{ display: 'block', marginBottom: 8 }}
>
{t('请选择一种 SMTP 传输加密方式')}
</Text>
<Form.Checkbox
field='SMTPForceAuthLogin'
noLabel