diff --git a/src/OrchardCore.Modules/OrchardCore.Email/ViewModels/SmtpSettingsViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Email/ViewModels/SmtpSettingsViewModel.cs index 741c838b675..7d27792e686 100644 --- a/src/OrchardCore.Modules/OrchardCore.Email/ViewModels/SmtpSettingsViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Email/ViewModels/SmtpSettingsViewModel.cs @@ -1,8 +1,12 @@ +using System; +using System.Collections.Generic; using System.ComponentModel.DataAnnotations; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Localization; namespace OrchardCore.Email.ViewModels { - public class SmtpSettingsViewModel + public class SmtpSettingsViewModel : IValidatableObject { [Required(AllowEmptyStrings = false)] public string To { get; set; } @@ -18,5 +22,16 @@ public class SmtpSettingsViewModel public string Subject { get; set; } public string Body { get; set; } + + public IEnumerable Validate(ValidationContext validationContext) + { + var emailAddressValidator = validationContext.GetService(); + var S = validationContext.GetService>(); + + if (!String.IsNullOrWhiteSpace(Sender) && !emailAddressValidator.Validate(Sender)) + { + yield return new ValidationResult(S["Invalid Email."], new[] { nameof(Sender) }); + } + } } } diff --git a/src/OrchardCore/OrchardCore.Email.Core/Services/SmtpService.cs b/src/OrchardCore/OrchardCore.Email.Core/Services/SmtpService.cs index 8cc15618114..d9c62210c67 100644 --- a/src/OrchardCore/OrchardCore.Email.Core/Services/SmtpService.cs +++ b/src/OrchardCore/OrchardCore.Email.Core/Services/SmtpService.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.IO; using System.Net.Security; using System.Security.Cryptography.X509Certificates; @@ -71,7 +72,14 @@ public async Task SendAsync(MailMessage message) message.From = senderAddress; } - var mimeMessage = FromMailMessage(message); + var errors = new List(); + + var mimeMessage = FromMailMessage(message, errors); + + if (errors.Count > 0) + { + return SmtpResult.Failed(errors.ToArray()); + } if (mimeMessage.From.Count == 0 && mimeMessage.Cc.Count == 0 && mimeMessage.Bcc.Count == 0) { @@ -94,7 +102,7 @@ public async Task SendAsync(MailMessage message) } catch (Exception ex) { - result = SmtpResult.Failed(S["An error occurred while sending an email: '{0}'", ex.Message]); + result = SmtpResult.Failed(S["An error occurred while sending an email: '{0}'", ex.Message]); } result.Response = response; @@ -102,7 +110,7 @@ public async Task SendAsync(MailMessage message) return result; } - private MimeMessage FromMailMessage(MailMessage message) + private MimeMessage FromMailMessage(MailMessage message, IList errors) { var submitterAddress = String.IsNullOrWhiteSpace(message.Sender) ? _options.DefaultSender @@ -112,42 +120,78 @@ private MimeMessage FromMailMessage(MailMessage message) if (!String.IsNullOrEmpty(submitterAddress)) { - mimeMessage.Sender = MailboxAddress.Parse(submitterAddress); + if (MailboxAddress.TryParse(submitterAddress, out var mailBox)) + { + mimeMessage.Sender = mailBox; + + } + else + { + errors.Add(S["Invalid email address: '{0}'", submitterAddress]); + } } - if (!string.IsNullOrWhiteSpace(message.From)) + if (!String.IsNullOrWhiteSpace(message.From)) { - foreach (var address in message.From.Split(EmailsSeparator, StringSplitOptions.RemoveEmptyEntries)) + foreach (var address in message.From.Split(EmailsSeparator, StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries)) { - mimeMessage.From.Add(MailboxAddress.Parse(address)); + if (MailboxAddress.TryParse(address, out var mailBox)) + { + mimeMessage.From.Add(mailBox); + } + else + { + errors.Add(S["Invalid email address: '{0}'", address]); + } } } - if (!string.IsNullOrWhiteSpace(message.To)) + if (!String.IsNullOrWhiteSpace(message.To)) { - foreach (var address in message.To.Split(EmailsSeparator, StringSplitOptions.RemoveEmptyEntries)) + foreach (var address in message.To.Split(EmailsSeparator, StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries)) { - mimeMessage.To.Add(MailboxAddress.Parse(address)); + if (MailboxAddress.TryParse(address, out var mailBox)) + { + mimeMessage.To.Add(mailBox); + } + else + { + errors.Add(S["Invalid email address: '{0}'", address]); + } } } - if (!string.IsNullOrWhiteSpace(message.Cc)) + if (!String.IsNullOrWhiteSpace(message.Cc)) { - foreach (var address in message.Cc.Split(EmailsSeparator, StringSplitOptions.RemoveEmptyEntries)) + foreach (var address in message.Cc.Split(EmailsSeparator, StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries)) { - mimeMessage.Cc.Add(MailboxAddress.Parse(address)); + if (MailboxAddress.TryParse(address, out var mailBox)) + { + mimeMessage.Cc.Add(mailBox); + } + else + { + errors.Add(S["Invalid email address: '{0}'", address]); + } } } - if (!string.IsNullOrWhiteSpace(message.Bcc)) + if (!String.IsNullOrWhiteSpace(message.Bcc)) { - foreach (var address in message.Bcc.Split(EmailsSeparator, StringSplitOptions.RemoveEmptyEntries)) + foreach (var address in message.Bcc.Split(EmailsSeparator, StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries)) { - mimeMessage.Bcc.Add(MailboxAddress.Parse(address)); + if (MailboxAddress.TryParse(address, out var mailBox)) + { + mimeMessage.Bcc.Add(mailBox); + } + else + { + errors.Add(S["Invalid email address: '{0}'", address]); + } } } - if (string.IsNullOrWhiteSpace(message.ReplyTo)) + if (String.IsNullOrWhiteSpace(message.ReplyTo)) { foreach (var address in mimeMessage.From) { @@ -158,7 +202,14 @@ private MimeMessage FromMailMessage(MailMessage message) { foreach (var address in message.ReplyTo.Split(EmailsSeparator, StringSplitOptions.RemoveEmptyEntries)) { - mimeMessage.ReplyTo.Add(MailboxAddress.Parse(address)); + if (MailboxAddress.TryParse(address, out var mailBox)) + { + mimeMessage.ReplyTo.Add(mailBox); + } + else + { + errors.Add(S["Invalid email address: '{0}'", address]); + } } } @@ -223,57 +274,49 @@ private async Task SendOnlineMessageAsync(MimeMessage message) if (!_options.AutoSelectEncryption) { - switch (_options.EncryptionMethod) + secureSocketOptions = _options.EncryptionMethod switch { - case SmtpEncryptionMethod.None: - secureSocketOptions = SecureSocketOptions.None; - break; - case SmtpEncryptionMethod.SSLTLS: - secureSocketOptions = SecureSocketOptions.SslOnConnect; - break; - case SmtpEncryptionMethod.STARTTLS: - secureSocketOptions = SecureSocketOptions.StartTls; - break; - default: - break; - } + SmtpEncryptionMethod.None => SecureSocketOptions.None, + SmtpEncryptionMethod.SSLTLS => SecureSocketOptions.SslOnConnect, + SmtpEncryptionMethod.STARTTLS => SecureSocketOptions.StartTls, + _ => SecureSocketOptions.Auto, + }; } - using (var client = new SmtpClient()) - { - client.ServerCertificateValidationCallback = CertificateValidationCallback; + using var client = new SmtpClient(); - await OnMessageSendingAsync(client, message); + client.ServerCertificateValidationCallback = CertificateValidationCallback; - await client.ConnectAsync(_options.Host, _options.Port, secureSocketOptions); + await OnMessageSendingAsync(client, message); - if (_options.RequireCredentials) + await client.ConnectAsync(_options.Host, _options.Port, secureSocketOptions); + + if (_options.RequireCredentials) + { + if (_options.UseDefaultCredentials) { - if (_options.UseDefaultCredentials) - { - // There's no notion of 'UseDefaultCredentials' in MailKit, so empty credentials is passed in - await client.AuthenticateAsync(String.Empty, String.Empty); - } - else if (!String.IsNullOrWhiteSpace(_options.UserName)) - { - await client.AuthenticateAsync(_options.UserName, _options.Password); - } + // There's no notion of 'UseDefaultCredentials' in MailKit, so empty credentials is passed in + await client.AuthenticateAsync(String.Empty, String.Empty); } - - if (!String.IsNullOrEmpty( _options.ProxyHost)) + else if (!String.IsNullOrWhiteSpace(_options.UserName)) { - client.ProxyClient = new Socks5Client(_options.ProxyHost, _options.ProxyPort); + await client.AuthenticateAsync(_options.UserName, _options.Password); } + } + + if (!String.IsNullOrEmpty(_options.ProxyHost)) + { + client.ProxyClient = new Socks5Client(_options.ProxyHost, _options.ProxyPort); + } - var response = await client.SendAsync(message); + var response = await client.SendAsync(message); - await client.DisconnectAsync(true); + await client.DisconnectAsync(true); - return response; - } + return response; } - private async Task SendOfflineMessage(MimeMessage message, string pickupDirectory) + private static async Task SendOfflineMessage(MimeMessage message, string pickupDirectory) { var mailPath = Path.Combine(pickupDirectory, Guid.NewGuid().ToString() + EmailExtension); await message.WriteToAsync(mailPath, CancellationToken.None);