1
0
mirror of https://github.com/bitwarden/server synced 2025-12-21 10:43:44 +00:00
Files
server/src/Core/Services/Implementations/MailKitSmtpMailDeliveryService.cs
Justin Baur 2242a70e50 [PM-336] Nullable Platform & Unowned Services (#5646)
* Nullable Platform & Unowned Services

* Fix build errors

* Format
2025-04-15 12:56:58 -04:00

134 lines
4.7 KiB
C#

using System.Security.Cryptography.X509Certificates;
using Bit.Core.Platform.X509ChainCustomization;
using Bit.Core.Settings;
using Bit.Core.Utilities;
using MailKit.Net.Smtp;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using MimeKit;
namespace Bit.Core.Services;
public class MailKitSmtpMailDeliveryService : IMailDeliveryService
{
private readonly GlobalSettings _globalSettings;
private readonly ILogger<MailKitSmtpMailDeliveryService> _logger;
private readonly X509ChainOptions _x509ChainOptions;
private readonly string _replyDomain;
private readonly string _replyEmail;
public MailKitSmtpMailDeliveryService(
GlobalSettings globalSettings,
ILogger<MailKitSmtpMailDeliveryService> logger,
IOptions<X509ChainOptions> x509ChainOptions)
{
if (globalSettings.Mail.Smtp?.Host == null)
{
throw new ArgumentNullException(nameof(globalSettings.Mail.Smtp.Host));
}
if (globalSettings.Mail.ReplyToEmail == null)
{
throw new InvalidOperationException("A GlobalSettings.Mail.ReplyToEmail is required to be set up.");
}
_replyEmail = CoreHelpers.PunyEncode(globalSettings.Mail.ReplyToEmail);
if (_replyEmail.Contains("@"))
{
_replyDomain = _replyEmail.Split('@')[1];
}
_globalSettings = globalSettings;
_logger = logger;
_x509ChainOptions = x509ChainOptions.Value;
}
public async Task SendEmailAsync(Models.Mail.MailMessage message)
=> await SendEmailAsync(message, CancellationToken.None);
public async Task SendEmailAsync(Models.Mail.MailMessage message, CancellationToken cancellationToken)
{
var mimeMessage = new MimeMessage();
mimeMessage.From.Add(new MailboxAddress(_globalSettings.SiteName, _replyEmail));
mimeMessage.Subject = message.Subject;
if (!string.IsNullOrWhiteSpace(_replyDomain))
{
mimeMessage.MessageId = $"<{Guid.NewGuid()}@{_replyDomain}>";
}
foreach (var address in message.ToEmails)
{
var punyencoded = CoreHelpers.PunyEncode(address);
mimeMessage.To.Add(MailboxAddress.Parse(punyencoded));
}
if (message.BccEmails != null)
{
foreach (var address in message.BccEmails)
{
var punyencoded = CoreHelpers.PunyEncode(address);
mimeMessage.Bcc.Add(MailboxAddress.Parse(punyencoded));
}
}
var builder = new BodyBuilder();
if (!string.IsNullOrWhiteSpace(message.TextContent))
{
builder.TextBody = message.TextContent;
}
builder.HtmlBody = message.HtmlContent;
mimeMessage.Body = builder.ToMessageBody();
using (var client = new SmtpClient())
{
if (_globalSettings.Mail.Smtp.TrustServer)
{
client.ServerCertificateValidationCallback = (s, c, h, e) => true;
}
else if (_x509ChainOptions.TryGetCustomRemoteCertificateValidationCallback(out var callback))
{
client.ServerCertificateValidationCallback = (sender, cert, chain, errors) =>
{
return callback(new X509Certificate2(cert), chain, errors);
};
}
if (!_globalSettings.Mail.Smtp.StartTls && !_globalSettings.Mail.Smtp.Ssl &&
_globalSettings.Mail.Smtp.Port == 25)
{
await client.ConnectAsync(
_globalSettings.Mail.Smtp.Host,
_globalSettings.Mail.Smtp.Port,
MailKit.Security.SecureSocketOptions.None,
cancellationToken
);
}
else
{
var useSsl = _globalSettings.Mail.Smtp.Port == 587 && !_globalSettings.Mail.Smtp.SslOverride ?
false : _globalSettings.Mail.Smtp.Ssl;
await client.ConnectAsync(
_globalSettings.Mail.Smtp.Host,
_globalSettings.Mail.Smtp.Port,
useSsl,
cancellationToken
);
}
if (CoreHelpers.SettingHasValue(_globalSettings.Mail.Smtp.Username) &&
CoreHelpers.SettingHasValue(_globalSettings.Mail.Smtp.Password))
{
await client.AuthenticateAsync(
_globalSettings.Mail.Smtp.Username,
_globalSettings.Mail.Smtp.Password,
cancellationToken
);
}
await client.SendAsync(mimeMessage, cancellationToken);
await client.DisconnectAsync(true, cancellationToken);
}
}
}