Backend/e-suite.Service.Mail/e-suite.Service.Mail/MailService.cs

189 lines
8.2 KiB
C#

using System.Net;
using System.Net.Mail;
using System.Net.Mime;
using e_suite.API.Common.exceptions;
using e_suite.API.Common.repository;
using e_suite.Database.Audit;
using e_suite.Database.Audit.AuditEngine;
using e_suite.Service.Mail.Facade;
using e_suite.Service.Mail.Helper;
using eSuite.Core.MailService;
using Microsoft.Extensions.Configuration;
namespace e_suite.Service.Mail;
public class MailService : IMailService
{
private readonly IConfiguration _configuration;
private readonly IUserManagerRepository _userManagerRepository;
private readonly IMailServiceRepository _mailServiceRepository;
private readonly ISmtpClientFactory _smtpClientFactory;
const long SunStrategyDomainId = 1;
public MailService(IConfiguration configuration, IUserManagerRepository userManagerRepository, IMailServiceRepository mailServiceRepository, ISmtpClientFactory smtpClientFactory)
{
_configuration = configuration;
_userManagerRepository = userManagerRepository;
_mailServiceRepository = mailServiceRepository;
_smtpClientFactory = smtpClientFactory;
}
public async Task RequestEMailAsync(MailRequest emailRequest, CancellationToken cancellationToken)
{
var username = _configuration.GetConfigValue("MAIL_USERNAME", "Smtp:Username", string.Empty);
var host = _configuration.GetConfigValue("MAIL_SERVER", "Smtp:Server", "defaultmailserver");
var port = _configuration.GetConfigValue("MAIL_PORT", "Smtp:Port", 25);
var fromAddress = _configuration.GetConfigValue("MAIL_ADDRESS", "Smtp:FromAddress", "defaultFromAddress");
var fromDisplayName = _configuration.GetConfigValue("MAIL_DISPLAY_NAME","Smtp:FromDisplayName", "eSuite MailService");
foreach (var address in emailRequest.To)
{
var message = new MailMessage
{
Body = null,
BodyEncoding = null,
BodyTransferEncoding = TransferEncoding.QuotedPrintable,
DeliveryNotificationOptions = DeliveryNotificationOptions.None,
From = new MailAddress(fromAddress!, fromDisplayName),
HeadersEncoding = null,
IsBodyHtml = false,
Priority = MailPriority.Normal,
Sender = null!,
Subject = null,
SubjectEncoding = null,
};
var user = await _userManagerRepository.GetUserByEmail(address.Email, cancellationToken) ??
throw new NotFoundException($"Unable to find user for e-mail {address.Email}");
message.To.Add(new MailAddress(address.Email, address.DisplayName));
await PrepareMessageBody(message, emailRequest, user.DomainId, cancellationToken);
using var client = _smtpClientFactory.CreateSmtpClient(host, port);
if (!string.IsNullOrEmpty(username))
{
var password = _configuration.GetConfigValue("MAIL_PASSWORD", "Smtp:Password", string.Empty);
client.Credentials = new NetworkCredential(username, password);
}
client.EnableSsl = _configuration.GetValue("Smtp:EnableSSL", false);
client.Timeout = _configuration.GetValue("Smtp:timeout", 30000);
#pragma warning disable SYSLIB0014
//There is no option be to use the ServicePointManager at the moment.
ServicePointManager.ServerCertificateValidationCallback = CertificateValidationCallBack!;
#pragma warning restore SYSLIB0014
try
{
await client.SendMailAsync(message, cancellationToken);
var auditUserDetails = new AuditUserDetails
{
UserDisplayName = "Mail service"
};
var fields = new Dictionary<string, Change>
{
["Address"] = new()
{
NewValue = address.Email,
},
["DisplayName"] = new()
{
NewValue = address.DisplayName
},
["EmailType"] = new()
{
NewValue = emailRequest.EmailType
},
["Subject"] = new()
{
NewValue = message.Subject
}
};
await _userManagerRepository.AddAdhocAuditEntry(auditUserDetails, AuditType.EmailSent, fields, cancellationToken);
}
catch (Exception e)
{
Console.WriteLine(e);
throw;
}
}
}
private async Task PrepareMessageBody(MailMessage message, MailRequest emailRequest, long domainId,
CancellationToken cancellationToken)
{
message.IsBodyHtml = true;
var mailTemplate =
await _mailServiceRepository.GetMailTemplate(domainId, emailRequest.EmailType, cancellationToken) ?? await _mailServiceRepository.GetMailTemplate(SunStrategyDomainId, emailRequest.EmailType,
cancellationToken) ??
throw new NotImplementedException($"{emailRequest.EmailType} not implemented");
message.Subject = mailTemplate.Subject;
message.Body = $"<html><head></head><body>{mailTemplate.TemplateDefinition}<body></html>";
SubstituteParameters(message, emailRequest);
}
private static void SubstituteParameters(MailMessage message, MailRequest emailRequest)
{
foreach (var parameter in emailRequest.Parameters)
message.Body = message.Body.Replace($"${parameter.Key}", parameter.Value, StringComparison.InvariantCultureIgnoreCase);
}
/// <summary>
/// https://docs.microsoft.com/en-us/previous-versions/office/developer/exchange-server-2010/dd633677(v=exchg.80)?redirectedfrom=MSDN
/// </summary>
/// <param name="sender"></param>
/// <param name="certificate"></param>
/// <param name="chain"></param>
/// <param name="sslPolicyErrors"></param>
/// <returns></returns>
private static bool CertificateValidationCallBack(
object sender,
System.Security.Cryptography.X509Certificates.X509Certificate certificate,
System.Security.Cryptography.X509Certificates.X509Chain chain,
System.Net.Security.SslPolicyErrors sslPolicyErrors)
{
// If the certificate is a valid, signed certificate, return true.
if (sslPolicyErrors == System.Net.Security.SslPolicyErrors.None) return true;
// If there are errors in the certificate chain, look at each error to determine the cause.
if ((sslPolicyErrors & System.Net.Security.SslPolicyErrors.RemoteCertificateChainErrors) != 0)
{
if (chain != null && chain.ChainStatus != null)
foreach (var status in chain.ChainStatus)
if (certificate.Subject == certificate.Issuer &&
status.Status == System.Security.Cryptography.X509Certificates.X509ChainStatusFlags
.UntrustedRoot)
{
// Self-signed certificates with an untrusted root are valid.
continue;
}
else
{
if (status.Status != System.Security.Cryptography.X509Certificates.X509ChainStatusFlags
.NoError)
// If there are any other errors in the certificate chain, the certificate is invalid,
// so the method returns false.
return false;
}
// When processing reaches this line, the only errors in the certificate chain are
// untrusted root errors for self-signed certificates. These certificates are valid
// for default Exchange server installations, so return true.
return true;
}
else
{
// In all other cases, return false.
return false;
}
}
}