using System.Net.Security;
using System.Security.Cryptography.X509Certificates;
using Elsa.Email.Contracts;
using Elsa.Email.Options;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using MimeKit;
using SmtpClient = MailKit.Net.Smtp.SmtpClient;
namespace Elsa.Email.Services;
///
/// A MailKit implementation of .
///
public class MailKitSmtpService : ISmtpService
{
private readonly SmtpOptions _options;
private readonly ILogger _logger;
///
/// Constructor.
///
public MailKitSmtpService(
IOptions options,
ILogger logger
)
{
_options = options.Value;
_logger = logger;
}
///
/// Sends the specified message.
///
public async Task SendAsync(MimeMessage message, CancellationToken cancellationToken) => await SendMessage(message, cancellationToken);
private async Task SendMessage(MimeMessage message, CancellationToken cancellationToken)
{
using var client = new SmtpClient();
client.ServerCertificateValidationCallback = CertificateValidationCallback;
await client.ConnectAsync(_options.Host, _options.Port, _options.SecureSocketOptions, cancellationToken);
if (_options.RequireCredentials)
await client.AuthenticateAsync(_options.UserName ?? "", _options.Password ?? "", cancellationToken);
await client.SendAsync(message, cancellationToken);
await client.DisconnectAsync(true, cancellationToken);
}
private bool CertificateValidationCallback(object sender, X509Certificate? certificate, X509Chain? chain, SslPolicyErrors sslPolicyErrors)
{
if (sslPolicyErrors == SslPolicyErrors.None)
return true;
_logger.LogError(
"SMTP Server's certificate {CertificateSubject} issued by {CertificateIssuer} with thumbprint {CertificateThumbprint} and expiration date {CertificateExpirationDate} is considered invalid with {SslPolicyErrors} policy errors",
certificate?.Subject,
certificate?.Issuer,
certificate?.GetCertHashString(),
certificate?.GetExpirationDateString(),
sslPolicyErrors);
if (!sslPolicyErrors.HasFlag(SslPolicyErrors.RemoteCertificateChainErrors) || chain?.ChainStatus == null)
return false;
foreach (var chainStatus in chain.ChainStatus)
_logger.LogError("Status: {Status} - {StatusInformation}", chainStatus.Status, chainStatus.StatusInformation);
return false;
}
}