using AsbCloudApp.Exceptions; using AsbCloudApp.Services; using Microsoft.Extensions.Configuration; using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Net.Mail; using System.Threading; using System.Threading.Tasks; using AsbCloudInfrastructure.Background; namespace AsbCloudInfrastructure.Services { public class EmailService : IEmailService { private readonly BackgroundWorker backgroundWorker; private readonly bool IsConfigured; private readonly string sender; private readonly string smtpServer; private readonly string smtpPassword; public EmailService(BackgroundWorker backgroundWorker, IConfiguration configuration) { sender = configuration.GetValue("email:sender", string.Empty); smtpPassword = configuration.GetValue("email:password", string.Empty); smtpServer = configuration.GetValue("email:smtpServer", string.Empty); var configError = string.IsNullOrEmpty(sender) || string.IsNullOrEmpty(smtpPassword) || string.IsNullOrEmpty(smtpServer); IsConfigured = !configError; this.backgroundWorker = backgroundWorker; } public void EnqueueSend(string address, string subject, string htmlBody) => EnqueueSend(new List { address }, subject, htmlBody); public void EnqueueSend(IEnumerable addresses, string subject, string htmlBody) { if (!IsConfigured) { Trace.TraceWarning("smtp is not configured"); return; } var workId = MakeWorkId(addresses, subject, htmlBody); if (!backgroundWorker.Contains(workId)) { var workAction = MakeEmailSendWorkAction(addresses, subject, htmlBody); var work = new WorkBase(workId, workAction); backgroundWorker.Push(work); } } private Func MakeEmailSendWorkAction(IEnumerable addresses, string subject, string htmlBody) { var mailAddresses = new List(); foreach (var address in addresses) { if (MailAddress.TryCreate(address, out MailAddress? mailAddress)) mailAddresses.Add(mailAddress); else Trace.TraceWarning($"Mail {address} is not correct."); } if (!mailAddresses.Any()) throw new ArgumentException($"No valid email found. List:[{string.Join(',', addresses)}]", nameof(addresses)); if (string.IsNullOrEmpty(subject)) throw new ArgumentInvalidException($"{nameof(subject)} should be set", nameof(subject)); var workAction = async (string id, IServiceProvider serviceProvider, CancellationToken token) => { var from = new MailAddress(sender); var message = new MailMessage { From = from }; foreach (var mailAddress in mailAddresses) message.To.Add(mailAddress); message.BodyEncoding = System.Text.Encoding.UTF8; message.Body = htmlBody; message.Subject = subject; message.IsBodyHtml = true; using var client = new SmtpClient(smtpServer); client.EnableSsl = true; client.UseDefaultCredentials = false; client.Credentials = new System.Net.NetworkCredential(sender, smtpPassword); await client.SendMailAsync(message, token); Trace.TraceInformation($"Send email to {string.Join(',', addresses)} subj:{subject} html body count {htmlBody.Length}"); }; return workAction; } private static string MakeWorkId(IEnumerable addresses, string subject, string content) { var hash = GetHashCode(addresses); hash ^= subject.GetHashCode(); hash ^= content.GetHashCode(); return hash.ToString("x"); } private static int GetHashCode(IEnumerable strings) { int hash = -1397075115; var enumerator = strings.GetEnumerator(); while (enumerator.MoveNext()) hash ^= enumerator.Current.GetHashCode(); return hash; } } }