forked from umbraco/Umbraco-CMS
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathEmailSender.cs
More file actions
179 lines (159 loc) · 6.74 KB
/
Copy pathEmailSender.cs
File metadata and controls
179 lines (159 loc) · 6.74 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
// Copyright (c) Umbraco.
// See LICENSE for more details.
using MailKit.Net.Smtp;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using MimeKit;
using MimeKit.IO;
using Umbraco.Cms.Core.Configuration.Models;
using Umbraco.Cms.Core.DependencyInjection;
using Umbraco.Cms.Core.Events;
using Umbraco.Cms.Core.Mail;
using Umbraco.Cms.Core.Models.Email;
using Umbraco.Cms.Core.Notifications;
using Umbraco.Cms.Infrastructure.Extensions;
using Umbraco.Cms.Infrastructure.Mail.Interfaces;
namespace Umbraco.Cms.Infrastructure.Mail;
/// <summary>
/// A utility class for sending emails
/// </summary>
public class EmailSender : IEmailSender
{
// TODO: This should encapsulate a BackgroundTaskRunner with a queue to send these emails!
private readonly IEventAggregator _eventAggregator;
private readonly ILogger<EmailSender> _logger;
private readonly bool _notificationHandlerRegistered;
private GlobalSettings _globalSettings;
private readonly IEmailSenderClient _emailSenderClient;
[Obsolete("Please use the non-obsolete constructor. Will be removed in V17.")]
public EmailSender(
ILogger<EmailSender> logger,
IOptionsMonitor<GlobalSettings> globalSettings,
IEventAggregator eventAggregator)
: this(logger, globalSettings, eventAggregator,null, null)
{
}
[Obsolete("Please use the non-obsolete constructor. Will be removed in V17.")]
public EmailSender(
ILogger<EmailSender> logger,
IOptionsMonitor<GlobalSettings> globalSettings,
IEventAggregator eventAggregator,
INotificationHandler<SendEmailNotification>? handler1,
INotificationAsyncHandler<SendEmailNotification>? handler2)
{
_logger = logger;
_eventAggregator = eventAggregator;
_globalSettings = globalSettings.CurrentValue;
_notificationHandlerRegistered = handler1 is not null || handler2 is not null;
_emailSenderClient = StaticServiceProvider.Instance.GetRequiredService<IEmailSenderClient>();
globalSettings.OnChange(x => _globalSettings = x);
}
[ActivatorUtilitiesConstructor]
public EmailSender(
ILogger<EmailSender> logger,
IOptionsMonitor<GlobalSettings> globalSettings,
IEventAggregator eventAggregator,
IEmailSenderClient emailSenderClient,
INotificationHandler<SendEmailNotification>? handler1,
INotificationAsyncHandler<SendEmailNotification>? handler2)
{
_logger = logger;
_eventAggregator = eventAggregator;
_globalSettings = globalSettings.CurrentValue;
_notificationHandlerRegistered = handler1 is not null || handler2 is not null;
_emailSenderClient = emailSenderClient;
globalSettings.OnChange(x => _globalSettings = x);
}
/// <summary>
/// Sends the message async
/// </summary>
/// <returns></returns>
public async Task SendAsync(EmailMessage message, string emailType) =>
await SendAsyncInternal(message, emailType, false);
public async Task SendAsync(EmailMessage message, string emailType, bool enableNotification) =>
await SendAsyncInternal(message, emailType, enableNotification);
/// <summary>
/// Returns true if the application should be able to send a required application email
/// </summary>
/// <remarks>
/// We assume this is possible if either an event handler is registered or an smtp server is configured
/// or a pickup directory location is configured
/// </remarks>
public bool CanSendRequiredEmail() => _globalSettings.IsSmtpServerConfigured
|| _globalSettings.IsPickupDirectoryLocationConfigured
|| _notificationHandlerRegistered;
private async Task SendAsyncInternal(EmailMessage message, string emailType, bool enableNotification)
{
if (enableNotification)
{
var notification =
new SendEmailNotification(message.ToNotificationEmail(_globalSettings.Smtp?.From), emailType);
await _eventAggregator.PublishAsync(notification);
// if a handler handled sending the email then don't continue.
if (notification.IsHandled)
{
if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug))
{
_logger.LogDebug(
"The email sending for {Subject} was handled by a notification handler",
notification.Message.Subject);
}
return;
}
}
if (!_globalSettings.IsSmtpServerConfigured && !_globalSettings.IsPickupDirectoryLocationConfigured)
{
if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug))
{
_logger.LogDebug(
"Could not send email for {Subject}. It was not handled by a notification handler and there is no SMTP configured.",
message.Subject);
}
return;
}
if (_globalSettings.IsPickupDirectoryLocationConfigured &&
!string.IsNullOrWhiteSpace(_globalSettings.Smtp?.From))
{
// The following code snippet is the recommended way to handle PickupDirectoryLocation.
// See more https://github.com/jstedfast/MailKit/blob/master/FAQ.md#q-how-can-i-send-email-to-a-specifiedpickupdirectory
do
{
var path = Path.Combine(_globalSettings.Smtp.PickupDirectoryLocation!, Guid.NewGuid() + ".eml");
Stream stream;
try
{
stream = File.Open(path, FileMode.CreateNew);
}
catch (IOException)
{
if (File.Exists(path))
{
continue;
}
throw;
}
try
{
using (stream)
{
using var filtered = new FilteredStream(stream);
filtered.Add(new SmtpDataFilter());
FormatOptions options = FormatOptions.Default.Clone();
options.NewLineFormat = NewLineFormat.Dos;
await message.ToMimeMessage(_globalSettings.Smtp.From).WriteToAsync(options, filtered);
filtered.Flush();
return;
}
}
catch
{
File.Delete(path);
throw;
}
}
while (true);
}
await _emailSenderClient.SendAsync(message);
}
}