-
Notifications
You must be signed in to change notification settings - Fork 123
Feature/1350/fix email settings not working with ssl #4294
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| using Microsoft.AspNetCore.Http; | ||
| using System.Collections.Generic; | ||
|
|
||
| namespace Shesha.Email.Dtos | ||
| { | ||
| public class EmailWithAttachmentsDto: SendTestEmailInput | ||
| { | ||
| public string From { get; set; } | ||
| public List<IFormFile> Attachments { get; set; } = []; | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,240 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| using MailKit.Net.Smtp; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| using MailKit.Security; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| using MimeKit; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| using MimeKit.Utils; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| using NUglify; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion | 🟠 Major Remove unused import.
-using NUglify;📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| using Shesha.Configuration; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| using System; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| using System.IO; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| using System.Linq; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| using System.Net; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| using System.Net.Mail; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| using System.Text; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| using System.Threading; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| using System.Threading.Tasks; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| using SmtpClient = MailKit.Net.Smtp.SmtpClient; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| namespace Shesha.Email | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| internal static class MailKitEmailHelper | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public static MimeMessage ConvertToMimeMessage(MailMessage mail) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (mail == null) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| throw new ArgumentNullException(nameof(mail)); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| var message = new MimeMessage(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (mail.From != null) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| message.From.Add(CreateMailboxAddress(mail.From)); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| foreach (var address in mail.To.Cast<MailAddress>()) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| message.To.Add(CreateMailboxAddress(address)); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| foreach (var address in mail.CC.Cast<MailAddress>()) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| message.Cc.Add(CreateMailboxAddress(address)); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| foreach (var address in mail.Bcc.Cast<MailAddress>()) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| message.Bcc.Add(CreateMailboxAddress(address)); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| message.Subject = mail.Subject ?? string.Empty; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| foreach (var headerKey in mail.Headers.AllKeys) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| var value = mail.Headers[headerKey]; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!string.IsNullOrEmpty(headerKey) && !string.IsNullOrEmpty(value)) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| message.Headers.Replace(headerKey, value); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| var builder = new BodyBuilder(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| var htmlView = mail.AlternateViews | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .Cast<AlternateView>() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .FirstOrDefault(v => string.Equals(v.ContentType.MediaType.Split('/')[0], "text", StringComparison.OrdinalIgnoreCase) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| && string.Equals(v.ContentType.MediaType.Split('/')[1], "html", StringComparison.OrdinalIgnoreCase)); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (htmlView != null) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| builder.HtmlBody = ReadAlternateView(htmlView); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| AddLinkedResources(builder, htmlView); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| else if (mail.IsBodyHtml) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| builder.HtmlBody = mail.Body; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| else | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| builder.TextBody = mail.Body; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| foreach (var alternateView in mail.AlternateViews.Cast<AlternateView>()) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (alternateView == htmlView) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| continue; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| var alternateViewMediaType = alternateView.ContentType.MediaType; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| var mediaTypeMain = alternateViewMediaType.Split('/')[0]; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| var mediaTypeSub = alternateViewMediaType.Split('/')[1]; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (string.Equals(mediaTypeMain, "text", StringComparison.OrdinalIgnoreCase) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| && string.Equals(mediaTypeSub, "plain", StringComparison.OrdinalIgnoreCase)) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| builder.TextBody = ReadAlternateView(alternateView); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| foreach (var attachment in mail.Attachments.Cast<Attachment>()) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| builder.Attachments.Add(CreateAttachmentPart(attachment)); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| message.Body = builder.ToMessageBody(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return message; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public static async Task SendAsync(MimeMessage message, SmtpSettings settings, CancellationToken cancellationToken = default) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (settings == null) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| throw new ArgumentNullException(nameof(settings)); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| using var smtpClient = new SmtpClient(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| await smtpClient.ConnectAsync(settings.Host, settings.Port, GetSecureSocketOption(settings), cancellationToken).ConfigureAwait(false); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| var credential = CreateCredential(settings); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (credential != null) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| await smtpClient.AuthenticateAsync(credential, cancellationToken).ConfigureAwait(false); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| await smtpClient.SendAsync(message, cancellationToken).ConfigureAwait(false); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| await smtpClient.DisconnectAsync(true, cancellationToken).ConfigureAwait(false); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public static void Send(MimeMessage message, SmtpSettings settings, CancellationToken cancellationToken = default) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (settings == null) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| throw new ArgumentNullException(nameof(settings)); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| using var smtpClient = new SmtpClient(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| smtpClient.Connect(settings.Host, settings.Port, GetSecureSocketOption(settings), cancellationToken); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| var credential = CreateCredential(settings); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (credential != null) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| smtpClient.Authenticate(credential, cancellationToken); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| smtpClient.Send(message, cancellationToken); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| smtpClient.Disconnect(true, cancellationToken); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private static void AddLinkedResources(BodyBuilder builder, AlternateView view) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| foreach (var resource in view.LinkedResources.Cast<LinkedResource>()) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| builder.LinkedResources.Add(CreateLinkedResource(resource)); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private static MimePart CreateAttachmentPart(Attachment attachment) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| var contentType = attachment.ContentType.MediaType; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| var mediaType = contentType.Split('/')[0] ?? "application"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| var mediaSubType = contentType.Split('/')[1] ?? "octet-stream"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| var mimePart = new MimePart(mediaType, mediaSubType) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Content = new MimeContent(attachment.ContentStream), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ContentDisposition = new ContentDisposition(ContentDisposition.Attachment), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ContentTransferEncoding = ContentEncoding.Base64, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| FileName = attachment.Name | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // The created MemoryStream is now owned by MimeContent, which will dispose it when disposed. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return mimePart; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private static MimeEntity CreateLinkedResource(LinkedResource resource) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| var contentType = resource.ContentType.MediaType; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| var mediaType = contentType.Split('/')[0] ?? "application"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| var mediaSubType = contentType.Split('/')[1] ?? "octet-stream"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| using MemoryStream memoryStream = CopyToMemoryStream(resource.ContentStream); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| var mimePart = new MimePart(mediaType, mediaSubType) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Content = new MimeContent(memoryStream), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ContentDisposition = new ContentDisposition(ContentDisposition.Inline), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ContentTransferEncoding = ContentEncoding.Base64, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ContentId = string.IsNullOrWhiteSpace(resource.ContentId) ? MimeUtils.GenerateMessageId() : resource.ContentId, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| FileName = resource.ContentId | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // The created MemoryStream is now owned by MimeContent, which will dispose it when disposed. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return mimePart; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+166
to
+183
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Critical: The private static MimeEntity CreateLinkedResource(LinkedResource resource)
{
var contentType = resource.ContentType.MediaType;
var mediaType = contentType.Split('/')[0] ?? "application";
var mediaSubType = contentType.Split('/')[1] ?? "octet-stream";
- using MemoryStream memoryStream = CopyToMemoryStream(resource.ContentStream);
+ MemoryStream memoryStream = CopyToMemoryStream(resource.ContentStream);
var mimePart = new MimePart(mediaType, mediaSubType)
{
Content = new MimeContent(memoryStream),
ContentDisposition = new ContentDisposition(ContentDisposition.Inline),
ContentTransferEncoding = ContentEncoding.Base64,
ContentId = string.IsNullOrWhiteSpace(resource.ContentId) ? MimeUtils.GenerateMessageId() : resource.ContentId,
FileName = resource.ContentId
};
// The created MemoryStream is now owned by MimeContent, which will dispose it when disposed.
return mimePart;
}📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private static MemoryStream CopyToMemoryStream(Stream stream) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| var memoryStream = new MemoryStream(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (stream.CanSeek) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| stream.Position = 0; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| stream.CopyTo(memoryStream); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| memoryStream.Position = 0; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return memoryStream; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private static MailboxAddress CreateMailboxAddress(MailAddress address) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return string.IsNullOrEmpty(address.DisplayName) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ? MailboxAddress.Parse(address.Address) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| : new MailboxAddress(address.DisplayName, address.Address); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private static string ReadAlternateView(AlternateView view) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| var encoding = !string.IsNullOrWhiteSpace(view.ContentType.CharSet) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ? Encoding.GetEncoding(view.ContentType.CharSet) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| : Encoding.UTF8; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (view.ContentStream.CanSeek) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| view.ContentStream.Position = 0; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| using var reader = new StreamReader(view.ContentStream, encoding, detectEncodingFromByteOrderMarks: true, leaveOpen: true); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| var content = reader.ReadToEnd(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (view.ContentStream.CanSeek) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| view.ContentStream.Position = 0; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return content; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private static NetworkCredential? CreateCredential(SmtpSettings settings) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (string.IsNullOrWhiteSpace(settings.UserName)) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return null; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return string.IsNullOrWhiteSpace(settings.Domain) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ? new NetworkCredential(settings.UserName, settings.Password) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| : new NetworkCredential(settings.UserName, settings.Password, settings.Domain); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private static SecureSocketOptions GetSecureSocketOption(SmtpSettings settings) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!settings.EnableSsl) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return SecureSocketOptions.None; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return settings.Port == 465 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ? SecureSocketOptions.SslOnConnect | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| : SecureSocketOptions.StartTls; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+230
to
+238
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧹 Nitpick | 🔵 Trivial Consider adding explicit SSL mode configuration for edge cases. The port-based heuristic (465 = implicit SSL, others = StartTls) is reasonable for common configurations, but some SMTP servers may have non-standard setups. Consider adding an explicit 🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3,13 +3,13 @@ | |
| using Shesha.Configuration; | ||
| using Shesha.Configuration.Email; | ||
| using Shesha.Domain; | ||
| using Shesha.Email; | ||
| using Shesha.Email.Dtos; | ||
| using Shesha.Notifications.Dto; | ||
| using Shesha.Notifications.MessageParticipants; | ||
| using Shesha.Utilities; | ||
| using System; | ||
| using System.Collections.Generic; | ||
| using System.Net; | ||
| using System.Net.Mail; | ||
| using System.Threading.Tasks; | ||
|
|
||
|
|
@@ -86,10 +86,9 @@ private async Task SendEmailAsync(MailMessage mail) | |
| { | ||
| try | ||
| { | ||
| using (var smtpClient = await GetSmtpClientAsync()) | ||
| { | ||
| smtpClient.Send(mail); | ||
| } | ||
| using var mimeMessage = MailKitEmailHelper.ConvertToMimeMessage(mail); | ||
| var smtpSettings = await _emailSettings.SmtpSettings.GetValueAsync(); | ||
| await MailKitEmailHelper.SendAsync(mimeMessage, smtpSettings); | ||
|
Comment on lines
+89
to
+91
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧹 Nitpick | 🔵 Trivial Reorder operations to fail fast before expensive conversion. The private async Task SendEmailAsync(MailMessage mail)
{
try
{
- using var mimeMessage = MailKitEmailHelper.ConvertToMimeMessage(mail);
var smtpSettings = await _emailSettings.SmtpSettings.GetValueAsync();
+ using var mimeMessage = MailKitEmailHelper.ConvertToMimeMessage(mail);
await MailKitEmailHelper.SendAsync(mimeMessage, smtpSettings);
}🤖 Prompt for AI Agents |
||
| } | ||
| catch (Exception ex) | ||
| { | ||
|
|
@@ -98,28 +97,6 @@ private async Task SendEmailAsync(MailMessage mail) | |
| } | ||
| } | ||
|
|
||
| private async Task<SmtpClient> GetSmtpClientAsync() | ||
| { | ||
| var smtpSettings = await _emailSettings.SmtpSettings.GetValueAsync(); | ||
| return GetSmtpClient(smtpSettings); | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Returns SmtpClient configured according to the current application settings | ||
| /// </summary> | ||
| private SmtpClient GetSmtpClient(SmtpSettings smtpSettings) | ||
| { | ||
| var client = new SmtpClient(smtpSettings.Host, smtpSettings.Port) | ||
| { | ||
| EnableSsl = smtpSettings.EnableSsl, | ||
| Credentials = string.IsNullOrWhiteSpace(smtpSettings.Domain) | ||
| ? new NetworkCredential(smtpSettings.UserName, smtpSettings.Password) | ||
| : new NetworkCredential(smtpSettings.UserName, smtpSettings.Password, smtpSettings.Domain) | ||
| }; | ||
|
|
||
| return client; | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// | ||
| /// </summary> | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -161,12 +161,13 @@ | |
| <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> | ||
| </PackageReference> | ||
| <PackageReference Include="DocumentFormat.OpenXml" Version="3.0.0" /> | ||
| <PackageReference Include="Hangfire.Core" Version="1.8.6" /> | ||
| <PackageReference Include="IDisposableAnalyzers" Version="4.0.8"> | ||
| <PrivateAssets>all</PrivateAssets> | ||
| <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> | ||
| </PackageReference> | ||
| <PackageReference Include="MediaTypeMap.Core" Version="2.3.3" /> | ||
| <PackageReference Include="Hangfire.Core" Version="1.8.6" /> | ||
| <PackageReference Include="IDisposableAnalyzers" Version="4.0.8"> | ||
| <PrivateAssets>all</PrivateAssets> | ||
| <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> | ||
| </PackageReference> | ||
| <PackageReference Include="MailKit" Version="4.7.0" /> | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🌐 Web query:
💡 Result: No — MailKit 4.7.0 is not the latest. The current NuGet latest version is 4.14.1 (install with: dotnet add package MailKit --version 4.14.1). [1] Sources: 🌐 Web query:
💡 Result: Summary — notable 2024–2025 issues affecting MailKit (or its dependencies)
If you want, I can:
Sources: Citations:
Upgrade MailKit from 4.7.0 to 4.14.1 to address security vulnerabilities. MailKit 4.7.0 is outdated; the current NuGet latest is 4.14.1. More critically, MailKit 4.7.0 depends on a vulnerable version of MimeKit that is affected by a Denial-of-Service vulnerability (GHSA-gmc6-fwg3-75m5) caused by System.Formats.Asn1 ASN.1 parsing, which was patched in MimeKit 4.7.1. Additionally, CVE-2024-38095 impacts ASN.1 operations used in S/MIME processing. Upgrade to MailKit 4.14.1 immediately. 🤖 Prompt for AI Agents |
||
| <PackageReference Include="MediaTypeMap.Core" Version="2.3.3" /> | ||
| <PackageReference Include="Microsoft.AspNetCore.Mvc.Versioning" Version="5.0.0" /> | ||
| <PackageReference Include="Microsoft.VisualStudio.Threading.Analyzers" Version="17.13.2"> | ||
| <PrivateAssets>all</PrivateAssets> | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion | 🟠 Major
Add nullable annotation to
Fromproperty.The project has
<Nullable>enable</Nullable>. TheFromproperty should be marked asstring?if it can be null/empty, or initialized with a default value if required.public class EmailWithAttachmentsDto: SendTestEmailInput { - public string From { get; set; } + public string? From { get; set; } public List<IFormFile> Attachments { get; set; } = []; }📝 Committable suggestion
🤖 Prompt for AI Agents