diff --git a/src/Umbraco.Core/Events/SendEmailEventArgs.cs b/src/Umbraco.Core/Events/SendEmailEventArgs.cs index cfe43233cd..7ee9469b57 100644 --- a/src/Umbraco.Core/Events/SendEmailEventArgs.cs +++ b/src/Umbraco.Core/Events/SendEmailEventArgs.cs @@ -1,13 +1,13 @@ using System; -using System.Net.Mail; +using Umbraco.Core.Models; namespace Umbraco.Core.Events { public class SendEmailEventArgs : EventArgs { - public MailMessage Message { get; private set; } + public EmailMessage Message { get; } - public SendEmailEventArgs(MailMessage message) + public SendEmailEventArgs(EmailMessage message) { Message = message; } diff --git a/src/Umbraco.Core/IEmailSender.cs b/src/Umbraco.Core/IEmailSender.cs index 748b8e6b0a..aab944e04d 100644 --- a/src/Umbraco.Core/IEmailSender.cs +++ b/src/Umbraco.Core/IEmailSender.cs @@ -1,5 +1,5 @@ -using System.Net.Mail; -using System.Threading.Tasks; +using System.Threading.Tasks; +using Umbraco.Core.Models; namespace Umbraco.Core { @@ -8,7 +8,6 @@ namespace Umbraco.Core /// public interface IEmailSender { - // TODO: This would be better if MailMessage was our own abstraction! - Task SendAsync(MailMessage message); + Task SendAsync(EmailMessage message); } } diff --git a/src/Umbraco.Core/Models/EmailMessage.cs b/src/Umbraco.Core/Models/EmailMessage.cs new file mode 100644 index 0000000000..11483e1b20 --- /dev/null +++ b/src/Umbraco.Core/Models/EmailMessage.cs @@ -0,0 +1,35 @@ +using System; + +namespace Umbraco.Core.Models +{ + public class EmailMessage + { + public string From { get; } + public string To { get; } + public string Subject { get; } + public string Body { get; } + public bool IsBodyHtml { get; } + + public EmailMessage(string from, string to, string subject, string body, bool isBodyHtml) + { + if (from == null) throw new ArgumentNullException(nameof(from)); + if (from.Length == 0) throw new ArgumentException("Value cannot be empty.", nameof(from)); + + if (to == null) throw new ArgumentNullException(nameof(to)); + if (to.Length == 0) throw new ArgumentException("Value cannot be empty.", nameof(to)); + + if (subject == null) throw new ArgumentNullException(nameof(subject)); + if (subject.Length == 0) throw new ArgumentException("Value cannot be empty.", nameof(subject)); + + if (body == null) throw new ArgumentNullException(nameof(body)); + if (body.Length == 0) throw new ArgumentException("Value cannot be empty.", nameof(body)); + + From = from; + To = to; + Subject = subject; + Body = body; + + IsBodyHtml = isBodyHtml; + } + } +} diff --git a/src/Umbraco.Infrastructure/HealthCheck/NotificationMethods/EmailNotificationMethod.cs b/src/Umbraco.Infrastructure/HealthCheck/NotificationMethods/EmailNotificationMethod.cs index d925ce14bf..37a3b5b072 100644 --- a/src/Umbraco.Infrastructure/HealthCheck/NotificationMethods/EmailNotificationMethod.cs +++ b/src/Umbraco.Infrastructure/HealthCheck/NotificationMethods/EmailNotificationMethod.cs @@ -1,12 +1,11 @@ using System; -using System.Net.Mail; using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Options; using Umbraco.Core; -using Umbraco.Core.Configuration; using Umbraco.Core.Configuration.Models; using Umbraco.Core.HealthCheck; +using Umbraco.Core.Models; using Umbraco.Core.Services; using Umbraco.Infrastructure.HealthCheck; @@ -72,23 +71,19 @@ namespace Umbraco.Web.HealthCheck.NotificationMethods var subject = _textService.Localize("healthcheck/scheduledHealthCheckEmailSubject", new[] { host.ToString() }); var mailSender = new EmailSender(Options.Create(_globalSettings)); - using (var mailMessage = CreateMailMessage(subject, message)) - { - await mailSender.SendAsync(mailMessage); - } + var mailMessage = CreateMailMessage(subject, message); + await mailSender.SendAsync(mailMessage); } - private MailMessage CreateMailMessage(string subject, string message) + private EmailMessage CreateMailMessage(string subject, string message) { var to = _contentSettings.Notifications.Email; if (string.IsNullOrWhiteSpace(subject)) subject = "Umbraco Health Check Status"; - return new MailMessage(to, RecipientEmail, subject, message) - { - IsBodyHtml = message.IsNullOrWhiteSpace() == false && message.Contains("<") && message.Contains(" public IEnumerable FilterUserNotificationsByPath(IEnumerable userNotifications, string path) { - var pathParts = path.Split(new[] {','}, StringSplitOptions.RemoveEmptyEntries); + var pathParts = path.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); return userNotifications.Where(r => pathParts.InvariantContains(r.EntityId.ToString(CultureInfo.InvariantCulture))).ToList(); } @@ -406,22 +405,19 @@ namespace Umbraco.Core.Services.Implement summary.ToString()); var fromMail = _contentSettings.Notifications.Email ?? _globalSettings.Smtp.From; - // create the mail message - var mail = new MailMessage(fromMail, fromMail); - // populate the message + var subject = createSubject((mailingUser, subjectVars)); + var body = ""; + var isBodyHtml = false; - - mail.Subject = createSubject((mailingUser, subjectVars)); if (_contentSettings.Notifications.DisableHtmlEmail) { - mail.IsBodyHtml = false; - mail.Body = createBody((user: mailingUser, body: bodyVars, false)); + body = createBody((user: mailingUser, body: bodyVars, false)); } else { - mail.IsBodyHtml = true; - mail.Body = + isBodyHtml = true; + body = string.Concat(@" @@ -430,14 +426,17 @@ namespace Umbraco.Core.Services.Implement // nh, issue 30724. Due to hardcoded http strings in resource files, we need to check for https replacements here // adding the server name to make sure we don't replace external links - if (_globalSettings.UseHttps && string.IsNullOrEmpty(mail.Body) == false) + if (_globalSettings.UseHttps && string.IsNullOrEmpty(body) == false) { - string serverName = siteUri.Host; - mail.Body = mail.Body.Replace( - string.Format("http://{0}", serverName), - string.Format("https://{0}", serverName)); + var serverName = siteUri.Host; + body = body.Replace( + $"http://{serverName}", + $"https://{serverName}"); } + // create the mail message + var mail = new EmailMessage(fromMail, mailingUser.Email, subject, body, isBodyHtml); + return new NotificationRequest(mail, actionName, mailingUser.Name, mailingUser.Email); } @@ -488,7 +487,7 @@ namespace Umbraco.Core.Services.Implement private class NotificationRequest { - public NotificationRequest(MailMessage mail, string action, string userName, string email) + public NotificationRequest(EmailMessage mail, string action, string userName, string email) { Mail = mail; Action = action; @@ -496,13 +495,13 @@ namespace Umbraco.Core.Services.Implement Email = email; } - public MailMessage Mail { get; private set; } + public EmailMessage Mail { get; } - public string Action { get; private set; } + public string Action { get; } - public string UserName { get; private set; } + public string UserName { get; } - public string Email { get; private set; } + public string Email { get; } } private void Process(BlockingCollection notificationRequests) @@ -524,10 +523,6 @@ namespace Umbraco.Core.Services.Implement { _logger.LogError(ex, "An error occurred sending notification"); } - finally - { - request.Mail.Dispose(); - } } lock (Locker) { diff --git a/src/Umbraco.Infrastructure/Users/EmailSender.cs b/src/Umbraco.Infrastructure/Users/EmailSender.cs index c45a9af0d3..a58426d82f 100644 --- a/src/Umbraco.Infrastructure/Users/EmailSender.cs +++ b/src/Umbraco.Infrastructure/Users/EmailSender.cs @@ -1,13 +1,12 @@ using System; -using System.Linq; using System.Net.Mail; using System.Threading.Tasks; using Microsoft.Extensions.Options; using MimeKit; using MimeKit.Text; -using Umbraco.Core.Configuration; using Umbraco.Core.Configuration.Models; using Umbraco.Core.Events; +using Umbraco.Core.Models; using SmtpClient = MailKit.Net.Smtp.SmtpClient; namespace Umbraco.Core @@ -40,7 +39,7 @@ namespace Umbraco.Core /// Sends the message non-async /// /// - public void Send(MailMessage message) + public void Send(EmailMessage message) { if (_smtpConfigured.Value == false && _enableEvents) { @@ -70,7 +69,7 @@ namespace Umbraco.Core /// /// /// - public async Task SendAsync(MailMessage message) + public async Task SendAsync(EmailMessage message) { if (_smtpConfigured.Value == false && _enableEvents) { @@ -130,9 +129,9 @@ namespace Umbraco.Core if (handler != null) handler(null, e); } - private MimeMessage ConstructEmailMessage(MailMessage mailMessage) + private MimeMessage ConstructEmailMessage(EmailMessage mailMessage) { - var fromEmail = mailMessage.From?.Address; + var fromEmail = mailMessage.From; if(string.IsNullOrEmpty(fromEmail)) fromEmail = _globalSettings.Smtp.From; @@ -142,7 +141,7 @@ namespace Umbraco.Core From = { new MailboxAddress(fromEmail)}, Body = new TextPart(mailMessage.IsBodyHtml ? TextFormat.Html : TextFormat.Plain) { Text = mailMessage.Body } }; - messageToSend.To.AddRange(mailMessage.To.Select(x=>new MailboxAddress(x.Address))); + messageToSend.To.Add(new MailboxAddress(mailMessage.To)); return messageToSend; } diff --git a/src/Umbraco.Web.BackOffice/Controllers/AuthenticationController.cs b/src/Umbraco.Web.BackOffice/Controllers/AuthenticationController.cs index a6b350da91..511254e96b 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/AuthenticationController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/AuthenticationController.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Linq; using System.Net; -using System.Net.Mail; using System.Threading.Tasks; using Microsoft.AspNetCore.Routing; using Microsoft.AspNetCore.Authentication; @@ -310,6 +309,7 @@ namespace Umbraco.Web.BackOffice.Controllers var user = _userService.GetByEmail(model.Email); if (user != null) { + var from = _globalSettings.Smtp.From; var code = await _userManager.GeneratePasswordResetTokenAsync(identityUser); var callbackUrl = ConstructCallbackUrl(identityUser.Id, code); @@ -322,13 +322,7 @@ namespace Umbraco.Web.BackOffice.Controllers // Ensure the culture of the found user is used for the email! UmbracoUserExtensions.GetUserCulture(identityUser.Culture, _textService, _globalSettings)); - var mailMessage = new MailMessage() - { - Subject = subject, - Body = message, - IsBodyHtml = true, - To = { user.Email } - }; + var mailMessage = new EmailMessage(from, user.Email, subject, message, true); await _emailSender.SendAsync(mailMessage); diff --git a/src/Umbraco.Web.BackOffice/Controllers/UsersController.cs b/src/Umbraco.Web.BackOffice/Controllers/UsersController.cs index d45951a3df..89322ede60 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/UsersController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/UsersController.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Net; -using System.Net.Mail; using System.Runtime.Serialization; using System.Security.Cryptography; using System.Threading.Tasks; @@ -543,13 +542,7 @@ namespace Umbraco.Web.BackOffice.Controllers UmbracoUserExtensions.GetUserCulture(to.Language, _localizedTextService, _globalSettings), new[] { userDisplay.Name, from, message, inviteUri.ToString(), fromEmail }); - var mailMessage = new MailMessage() - { - Subject = emailSubject, - Body = emailBody, - IsBodyHtml = true, - To = { to.Email} - }; + var mailMessage = new EmailMessage(fromEmail, to.Email, emailSubject, emailBody, true); await _emailSender.SendAsync(mailMessage); } diff --git a/src/Umbraco.Web/Editors/AuthenticationController.cs b/src/Umbraco.Web/Editors/AuthenticationController.cs index 5265dc13cb..1ca85d919d 100644 --- a/src/Umbraco.Web/Editors/AuthenticationController.cs +++ b/src/Umbraco.Web/Editors/AuthenticationController.cs @@ -3,7 +3,6 @@ using System.Linq; using System.Net; using System.Net.Http; using System.Collections.Generic; -using System.Net.Mail; using System.Security.Principal; using System.Threading.Tasks; using System.Web;