Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
172 changes: 89 additions & 83 deletions Olive.Email/Email.Sending/MailMessageCreator.cs
Original file line number Diff line number Diff line change
@@ -1,121 +1,127 @@
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Configuration;
using System.Collections.Generic;
using System.Linq;
using System.Net.Mail;
using System.Net.Mime;
using System.Threading.Tasks;

namespace Olive.Email
namespace Olive.Email;

/// <summary>
/// Creates <see cref="MailMessage"/> instances based on an <see cref="IEmailMessage"/>.
/// </summary>
public interface IMailMessageCreator
{
Task<MailMessage> Create(IEmailMessage message);
}

/// <summary>
/// Default implementation of <see cref="IMailMessageCreator"/>.
/// </summary>
public sealed class MailMessageCreator : IMailMessageCreator
{
public interface IMailMessageCreator
private readonly EmailConfiguration _config;
private readonly IEmailAttachmentSerializer _attachmentSerializer;

public MailMessageCreator(IConfiguration config, IEmailAttachmentSerializer attachmentSerializer)
{
Task<MailMessage> Create(IEmailMessage message);
ArgumentNullException.ThrowIfNull(config);
_config = config.GetSection("Email").Get<EmailConfiguration>() ?? new EmailConfiguration();
_attachmentSerializer = attachmentSerializer ?? throw new ArgumentNullException(nameof(attachmentSerializer));
}

class MailMessageCreator : IMailMessageCreator
public async Task<MailMessage> Create(IEmailMessage message)
{
EmailConfiguration Config;
ArgumentNullException.ThrowIfNull(message);

public MailMessageCreator(IConfiguration config, IEmailAttachmentSerializer attachmentSerializer)
var result = new MailMessage
{
Config = config.GetSection("Email").Get<EmailConfiguration>();
AttachmentSerializer = attachmentSerializer;
}
Subject = message.Subject.Or("[NO SUBJECT]").Remove("\r", "\n"),
Body = message.Body,
From = CreateFrom(message)
};

public IEmailAttachmentSerializer AttachmentSerializer { get; }
result.Headers.Add(Constants.EMAIL_MESSAGE_ID_HEADER_KEY, message.GetId().ToString());

public async Task<MailMessage> Create(IEmailMessage message)
{
var result = new MailMessage
{
Subject = message.Subject.Or("[NO SUBJECT]").Remove("\r", "\n"),
Body = message.Body,
From = CreateFrom(message)
};
foreach (var recipient in GetEffectiveRecipients(message.To))
result.To.Add(recipient);

result.Headers
.Add(Constants.EMAIL_MESSAGE_ID_HEADER_KEY, message.GetId().ToString());
foreach (var recipient in GetEffectiveRecipients(message.Cc + _config.AutoAddCc.WithPrefix(",")))
result.CC.Add(recipient);

GetEffectiveRecipients(message.To).Do(x => result.To.Add(x));
GetEffectiveRecipients(message.Cc + Config.AutoAddCc.WithPrefix(",")).Do(x => result.CC.Add(x));
GetEffectiveRecipients(message.Bcc + Config.AutoAddBcc.WithPrefix(",")).Do(x => result.Bcc.Add(x));
foreach (var recipient in GetEffectiveRecipients(message.Bcc + _config.AutoAddBcc.WithPrefix(",")))
result.Bcc.Add(recipient);

result.ReplyToList.Add(CreateReplyTo(message));
result.ReplyToList.Add(CreateReplyTo(message));

result.AlternateViews.AddRange(GetBodyViews(message));
result.Attachments.AddRange(await AttachmentSerializer.Extract(message));
result.AlternateViews.AddRange(GetBodyViews(message));
result.Attachments.AddRange(await _attachmentSerializer.Extract(message));

return result;
}
return result;
}

MailAddress CreateFrom(IEmailMessage message)
{
var address = message.FromAddress.Or(Config.From?.Address);
var name = message.FromName.Or(Config.From?.Name);
return new MailAddress(address, name);
}
private MailAddress CreateFrom(IEmailMessage message) =>
new(message.FromAddress.Or(_config.From?.Address),
message.FromName.Or(_config.From?.Name));

MailAddress CreateReplyTo(IEmailMessage message)
{
var address = message.ReplyToAddress
.Or(Config.ReplyTo?.Address)
.Or(message.FromAddress)
.Or(Config.From?.Address);
private MailAddress CreateReplyTo(IEmailMessage message)
{
var address = message.ReplyToAddress
.Or(_config.ReplyTo?.Address)
.Or(message.FromAddress)
.Or(_config.From?.Address);

var name = message.ReplyToName
.Or(Config.ReplyTo?.Name)
.Or(message.FromName)
.Or(Config.From?.Name);
var name = message.ReplyToName
.Or(_config.ReplyTo?.Name)
.Or(message.FromName)
.Or(_config.From?.Name);

return new MailAddress(address, name);
}
return new MailAddress(address, name);
}

string[] GetEffectiveRecipients(string to)
{
return to.OrEmpty().Split(',').Trim().Where(x => CanSendTo(x)).ToArray();
}
private IEnumerable<string> GetEffectiveRecipients(string? to) =>
to.OrEmpty().Split(',').Trim().Where(CanSendTo);

bool CanSendTo(string recipientAddress)
{
var permittedDomains = Config.Permitted.Domains.ToLowerOrEmpty().Or("geeks.ltd.uk|uat.co");
if (permittedDomains == "*") return true;
private bool CanSendTo(string recipientAddress)
{
var permittedDomains = _config.Permitted.Domains.ToLowerOrEmpty().Or("geeks.ltd.uk|uat.co");
if (permittedDomains == "*") return true;

if (permittedDomains.Split('|').Trim().Any(d => recipientAddress.TrimEnd(">").EndsWith("@" + d)))
return true;
if (permittedDomains.Split('|').Trim().Any(d => recipientAddress.TrimEnd(">").EndsWith("@" + d)))
return true;

var addresses = Config.Permitted.Addresses.ToLowerOrEmpty().Split('|').Trim().ToArray();
var addresses = _config.Permitted.Addresses.ToLowerOrEmpty().Split('|').Trim().ToArray();

return addresses.Any() && new MailAddress(recipientAddress).Address.IsAnyOf(addresses);
}
return addresses.Any() && new MailAddress(recipientAddress).Address.IsAnyOf(addresses);
}

IEnumerable<AlternateView> GetBodyViews(IEmailMessage message)
{
yield return AlternateView.CreateAlternateViewFromString(
message.Body.RemoveHtmlTags(),
private IEnumerable<AlternateView> GetBodyViews(IEmailMessage message)
{
yield return AlternateView.CreateAlternateViewFromString(
message.Body.RemoveHtmlTags(),
new ContentType("text/plain; charset=UTF-8"));

if (message.Html)
{
var htmlView = AlternateView.CreateAlternateViewFromString(
message.Body, new ContentType("text/html; charset=UTF-8"));

htmlView.LinkedResources.AddRange(AttachmentSerializer.GetLinkedResources(message));
yield return htmlView;
}
if (message.Html)
{
var htmlView = AlternateView.CreateAlternateViewFromString(
message.Body,
new ContentType("text/html; charset=UTF-8"));

if (message.VCalendarView.HasValue())
{
var calendarType = new ContentType("text/calendar");
calendarType.Parameters.Add("method", "REQUEST");
calendarType.Parameters.Add("name", "meeting.ics");
htmlView.LinkedResources.AddRange(_attachmentSerializer.GetLinkedResources(message));
yield return htmlView;
}

var calendarView = AlternateView
.CreateAlternateViewFromString(message.VCalendarView, calendarType);
if (message.VCalendarView.HasValue())
{
var calendarType = new ContentType("text/calendar");
calendarType.Parameters.Add("method", "REQUEST");
calendarType.Parameters.Add("name", "meeting.ics");

calendarView.TransferEncoding = TransferEncoding.SevenBit;
var calendarView = AlternateView.CreateAlternateViewFromString(message.VCalendarView, calendarType);
calendarView.TransferEncoding = TransferEncoding.SevenBit;

yield return calendarView;
}
yield return calendarView;
}
}
}
}