using MailSharp.MailClient.Models;
using MimeKit;
using ModelPriority = MailSharp.MailClient.Models.MessagePriority;
namespace MailSharp.MailClient.Services;
public static class MimeMessageFactory
{
public static MimeMessage Build(Account account, ComposeModel compose, IEnumerable<(string FileName, Stream Content)> attachments) =>
Build(account, compose, attachments, []);
// inlineImages are pictures the user inserted into the editor via file upload rather than a
// URL (see compose.js's upload-image button) - the composer already rewrote each
tag's
// src to "cid:" before posting HtmlBody, so all that's needed here is attaching the actual
// bytes as an inline MimePart whose ContentId matches, so mail clients resolve the reference
// instead of showing a broken image.
public static MimeMessage Build(Account account, ComposeModel compose, IEnumerable<(string FileName, Stream Content)> attachments, IEnumerable<(string Cid, string FileName, Stream Content)> inlineImages)
{
var message = new MimeMessage();
message.From.Add(new MailboxAddress(account.DisplayName, account.EmailAddress));
AddAddresses(message.To, compose.To);
AddAddresses(message.Cc, compose.Cc);
AddAddresses(message.Bcc, compose.Bcc);
message.Subject = compose.Subject;
message.Priority = compose.Priority switch
{
ModelPriority.Low => MimeKit.MessagePriority.NonUrgent,
ModelPriority.High => MimeKit.MessagePriority.Urgent,
_ => MimeKit.MessagePriority.Normal
};
if (compose.Sensitivity != MessageSensitivity.Nothing)
{
message.Headers.Add("Sensitivity", compose.Sensitivity switch
{
MessageSensitivity.Personal => "Personal",
MessageSensitivity.Private => "Private",
MessageSensitivity.Confidential => "Company-Confidential",
_ => "Normal"
});
}
if (compose.RequestReadReceipt)
{
message.Headers.Add("Disposition-Notification-To", account.EmailAddress);
message.Headers.Add("Return-Receipt-To", account.EmailAddress);
}
var builder = new BodyBuilder { HtmlBody = compose.HtmlBody };
foreach (var (fileName, content) in attachments)
{
builder.Attachments.Add(fileName, content);
}
foreach (var (cid, fileName, content) in inlineImages)
{
var part = builder.LinkedResources.Add(fileName, content);
part.ContentId = cid;
part.ContentDisposition = new ContentDisposition(ContentDisposition.Inline);
}
message.Body = builder.ToMessageBody();
return message;
}
private static void AddAddresses(InternetAddressList list, string raw)
{
if (string.IsNullOrWhiteSpace(raw)) return;
foreach (var part in raw.Split([',', ';'], StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries))
{
if (MailboxAddress.TryParse(part, out var addr))
list.Add(addr);
}
}
}