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); } } }