Browse Source

Migrate to mailkit.

pull/613/head
Sebastian 5 years ago
parent
commit
a2fc908f9f
  1. 4
      backend/extensions/Squidex.Extensions/Actions/Email/EmailAction.cs
  2. 60
      backend/extensions/Squidex.Extensions/Actions/Email/EmailActionHandler.cs
  3. 3
      backend/src/Squidex.Infrastructure/Email/IEmailSender.cs
  4. 80
      backend/src/Squidex.Infrastructure/Email/SmtpEmailSender.cs
  5. 1
      backend/src/Squidex.Infrastructure/Squidex.Infrastructure.csproj

4
backend/extensions/Squidex.Extensions/Actions/Email/EmailAction.cs

@ -31,10 +31,6 @@ namespace Squidex.Extensions.Actions.Email
[DataType(DataType.Text)]
public int ServerPort { get; set; }
[Display(Name = "Use SSL", Description = "Specify whether the SMPT client uses Secure Sockets Layer (SSL) to encrypt the connection.")]
[DataType(DataType.Text)]
public bool ServerUseSsl { get; set; }
[LocalizedRequired]
[Display(Name = "Username", Description = "The username for the SMTP server.")]
[DataType(DataType.Text)]

60
backend/extensions/Squidex.Extensions/Actions/Email/EmailActionHandler.cs

@ -5,12 +5,11 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using System.Net;
using System.Net.Mail;
using System.Net.Sockets;
using System.Threading;
using System.Threading.Tasks;
using MailKit.Net.Smtp;
using MimeKit;
using MimeKit.Text;
using Squidex.Domain.Apps.Core.HandleRules;
using Squidex.Domain.Apps.Core.Rules.EnrichedEvents;
@ -28,7 +27,6 @@ namespace Squidex.Extensions.Actions.Email
var ruleJob = new EmailJob
{
ServerHost = action.ServerHost,
ServerUseSsl = action.ServerUseSsl,
ServerPassword = action.ServerPassword,
ServerPort = action.ServerPort,
ServerUsername = await FormatAsync(action.ServerUsername, @event),
@ -45,47 +43,31 @@ namespace Squidex.Extensions.Actions.Email
protected override async Task<Result> ExecuteJobAsync(EmailJob job, CancellationToken ct = default)
{
await CheckConnectionAsync(job, ct);
using (var client = new SmtpClient(job.ServerHost, job.ServerPort)
using (var smtpClient = new SmtpClient())
{
Credentials = new NetworkCredential(
job.ServerUsername,
job.ServerPassword),
await smtpClient.ConnectAsync(job.ServerHost, job.ServerPort, cancellationToken: ct);
EnableSsl = job.ServerUseSsl
})
{
using (ct.Register(client.SendAsyncCancel))
{
await client.SendMailAsync(
job.MessageFrom,
job.MessageTo,
job.MessageSubject,
job.MessageBody,
ct);
}
}
await smtpClient.AuthenticateAsync(job.ServerUsername, job.ServerPassword, ct);
return Result.Complete();
}
var smtpMessage = new MimeMessage();
private static async Task CheckConnectionAsync(EmailJob job, CancellationToken ct)
{
using (var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp))
{
var tcs = new TaskCompletionSource<IAsyncResult>();
smtpMessage.From.Add(MailboxAddress.Parse(
job.MessageFrom));
socket.BeginConnect(job.ServerHost, job.ServerPort, tcs.SetResult, null);
smtpMessage.To.Add(MailboxAddress.Parse(
job.MessageTo));
using (ct.Register(() =>
smtpMessage.Body = new TextPart(TextFormat.Html)
{
tcs.TrySetException(new OperationCanceledException($"Failed to establish a connection to {job.ServerHost}:{job.ServerPort}"));
}))
{
await tcs.Task;
}
Text = job.MessageBody
};
smtpMessage.Subject = job.MessageSubject;
await smtpClient.SendAsync(smtpMessage, ct);
}
return Result.Complete();
}
}
@ -99,8 +81,6 @@ namespace Squidex.Extensions.Actions.Email
public string ServerPassword { get; set; }
public bool ServerUseSsl { get; set; }
public string MessageFrom { get; set; }
public string MessageTo { get; set; }

3
backend/src/Squidex.Infrastructure/Email/IEmailSender.cs

@ -5,12 +5,13 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System.Threading;
using System.Threading.Tasks;
namespace Squidex.Infrastructure.Email
{
public interface IEmailSender
{
Task SendAsync(string recipient, string subject, string body);
Task SendAsync(string recipient, string subject, string body, CancellationToken ct = default);
}
}

80
backend/src/Squidex.Infrastructure/Email/SmtpEmailSender.cs

@ -5,15 +5,14 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using System.Diagnostics.CodeAnalysis;
using System.Net;
using System.Net.Mail;
using System.Net.Sockets;
using System.Threading;
using System.Threading.Tasks;
using MailKit.Net.Smtp;
using Microsoft.Extensions.ObjectPool;
using Microsoft.Extensions.Options;
using MimeKit;
using MimeKit.Text;
namespace Squidex.Infrastructure.Email
{
@ -23,56 +22,42 @@ namespace Squidex.Infrastructure.Email
private readonly SmtpOptions options;
private readonly ObjectPool<SmtpClient> clientPool;
internal sealed class SmtpClientPolicy : PooledObjectPolicy<SmtpClient>
{
private readonly SmtpOptions options;
public SmtpClientPolicy(SmtpOptions options)
{
this.options = options;
}
public override SmtpClient Create()
{
return new SmtpClient(options.Server, options.Port)
{
Credentials = new NetworkCredential(
options.Username,
options.Password),
EnableSsl = options.EnableSsl,
Timeout = options.Timeout
};
}
public override bool Return(SmtpClient obj)
{
return true;
}
}
public SmtpEmailSender(IOptions<SmtpOptions> options)
{
Guard.NotNull(options, nameof(options));
this.options = options.Value;
clientPool = new DefaultObjectPoolProvider().Create(new SmtpClientPolicy(options.Value));
clientPool = new DefaultObjectPoolProvider().Create(new DefaultPooledObjectPolicy<SmtpClient>());
}
public async Task SendAsync(string recipient, string subject, string body)
public async Task SendAsync(string recipient, string subject, string body, CancellationToken ct = default)
{
var smtpClient = clientPool.Get();
try
{
using (var cts = new CancellationTokenSource(options.Timeout))
using (var timeout = new CancellationTokenSource(options.Timeout))
{
using (var combined = CancellationTokenSource.CreateLinkedTokenSource(ct, timeout.Token))
{
await CheckConnectionAsync(cts.Token);
await EnsureConnectedAsync(smtpClient, combined.Token);
var smtpMessage = new MimeMessage();
smtpMessage.From.Add(MailboxAddress.Parse(
options.Sender));
smtpMessage.To.Add(MailboxAddress.Parse(
recipient));
using (cts.Token.Register(smtpClient.SendAsyncCancel))
smtpMessage.Body = new TextPart(TextFormat.Html)
{
await smtpClient.SendMailAsync(options.Sender, recipient, subject, body);
Text = body
};
smtpMessage.Subject = subject;
await smtpClient.SendAsync(smtpMessage, ct);
}
}
}
@ -82,21 +67,16 @@ namespace Squidex.Infrastructure.Email
}
}
private async Task CheckConnectionAsync(CancellationToken ct)
private async Task EnsureConnectedAsync(SmtpClient smtpClient, CancellationToken ct)
{
using (var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp))
if (!smtpClient.IsConnected)
{
var tcs = new TaskCompletionSource<IAsyncResult>();
socket.BeginConnect(options.Server, options.Port, tcs.SetResult, null);
await smtpClient.ConnectAsync(options.Server, options.Port, cancellationToken: ct);
}
using (ct.Register(() =>
{
tcs.TrySetException(new OperationCanceledException($"Failed to establish a connection to {options.Server}:{options.Port}"));
}))
if (!smtpClient.IsAuthenticated)
{
await tcs.Task;
}
await smtpClient.AuthenticateAsync(options.Username, options.Password, ct);
}
}
}

1
backend/src/Squidex.Infrastructure/Squidex.Infrastructure.csproj

@ -14,6 +14,7 @@
<None Remove="NewFolder\**" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="MailKit" Version="2.10.0" />
<PackageReference Include="McMaster.NETCore.Plugins" Version="1.3.1" />
<PackageReference Include="Microsoft.Extensions.Caching.Abstractions" Version="5.0.0" />
<PackageReference Include="Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions" Version="5.0.1" />

Loading…
Cancel
Save