-
Notifications
You must be signed in to change notification settings - Fork 142
Fix for #1350: switch SMTP email sending to MailKit #4167
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,228 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 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 MailKit.Net.Smtp; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| using MailKit.Security; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| using MimeKit; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| using MimeKit.Utils; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| using Shesha.Configuration; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 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, "text", StringComparison.OrdinalIgnoreCase) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| && string.Equals(v.ContentType.MediaSubtype, "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; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (string.Equals(alternateView.ContentType.MediaType, "text", StringComparison.OrdinalIgnoreCase) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| && string.Equals(alternateView.ContentType.MediaSubtype, "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); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+116
to
+132
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. Add timeout configuration for synchronous SMTP operations. Similar to the async path, the synchronous SMTP operations lack timeout configuration. -public static void Send(MimeMessage message, SmtpSettings settings, CancellationToken cancellationToken = default)
+public static void Send(MimeMessage message, SmtpSettings settings, CancellationToken cancellationToken = default, int timeoutSeconds = 30)
{
if (settings == null)
throw new ArgumentNullException(nameof(settings));
- using var smtpClient = new SmtpClient();
+ using var smtpClient = new SmtpClient
+ {
+ Timeout = timeoutSeconds * 1000
+ };
smtpClient.Connect(settings.Host, settings.Port, GetSecureSocketOption(settings), cancellationToken);📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private static void AddLinkedResources(BodyBuilder builder, AlternateView view) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| foreach (var resource in view.LinkedResources.Cast<LinkedResource>()) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| builder.LinkedResources.Add(CreateLinkedResource(resource)); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private static MimeEntity CreateAttachmentPart(Attachment attachment) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| var mediaType = attachment.ContentType.MediaType ?? "application"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| var mediaSubType = attachment.ContentType.MediaSubtype ?? "octet-stream"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| var mimePart = new MimePart(mediaType, mediaSubType) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Content = new MimeContent(CopyToMemoryStream(attachment.ContentStream)), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ContentDisposition = new ContentDisposition(ContentDisposition.Attachment), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ContentTransferEncoding = ContentEncoding.Base64, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| FileName = attachment.Name | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return mimePart; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private static MimeEntity CreateLinkedResource(LinkedResource resource) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| var mediaType = resource.ContentType.MediaType ?? "application"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| var mediaSubType = resource.ContentType.MediaSubtype ?? "octet-stream"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| var mimePart = new MimePart(mediaType, mediaSubType) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Content = new MimeContent(CopyToMemoryStream(resource.ContentStream)), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ContentDisposition = new ContentDisposition(ContentDisposition.Inline), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ContentTransferEncoding = ContentEncoding.Base64, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ContentId = string.IsNullOrWhiteSpace(resource.ContentId) ? MimeUtils.GenerateMessageId() : resource.ContentId, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| FileName = resource.ContentId | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return mimePart; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 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; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
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.
Add timeout configuration for SMTP operations.
The SMTP client operations (Connect, Authenticate, Send, Disconnect) have no timeout configured, which can cause indefinite hangs if the SMTP server becomes unresponsive.
Consider adding a timeout parameter or using a reasonable default:
📝 Committable suggestion
🤖 Prompt for AI Agents