From a2fc908f9f916d974170007fdf057c921f6600d8 Mon Sep 17 00:00:00 2001 From: Sebastian Date: Wed, 23 Dec 2020 09:20:29 +0100 Subject: [PATCH] Migrate to mailkit. --- .../Actions/Email/EmailAction.cs | 4 - .../Actions/Email/EmailActionHandler.cs | 60 +++++-------- .../Email/IEmailSender.cs | 3 +- .../Email/SmtpEmailSender.cs | 84 +++++++------------ .../Squidex.Infrastructure.csproj | 1 + 5 files changed, 55 insertions(+), 97 deletions(-) diff --git a/backend/extensions/Squidex.Extensions/Actions/Email/EmailAction.cs b/backend/extensions/Squidex.Extensions/Actions/Email/EmailAction.cs index f6df36b78..1e68dc41f 100644 --- a/backend/extensions/Squidex.Extensions/Actions/Email/EmailAction.cs +++ b/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)] diff --git a/backend/extensions/Squidex.Extensions/Actions/Email/EmailActionHandler.cs b/backend/extensions/Squidex.Extensions/Actions/Email/EmailActionHandler.cs index 52a3d75d4..6574e8f71 100644 --- a/backend/extensions/Squidex.Extensions/Actions/Email/EmailActionHandler.cs +++ b/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 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(); + 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(() => - { - tcs.TrySetException(new OperationCanceledException($"Failed to establish a connection to {job.ServerHost}:{job.ServerPort}")); - })) + smtpMessage.Body = new TextPart(TextFormat.Html) { - 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; } diff --git a/backend/src/Squidex.Infrastructure/Email/IEmailSender.cs b/backend/src/Squidex.Infrastructure/Email/IEmailSender.cs index 6fa4e5cd8..07c30766d 100644 --- a/backend/src/Squidex.Infrastructure/Email/IEmailSender.cs +++ b/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); } } diff --git a/backend/src/Squidex.Infrastructure/Email/SmtpEmailSender.cs b/backend/src/Squidex.Infrastructure/Email/SmtpEmailSender.cs index 4cb3b8323..30ce0157c 100644 --- a/backend/src/Squidex.Infrastructure/Email/SmtpEmailSender.cs +++ b/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 clientPool; - internal sealed class SmtpClientPolicy : PooledObjectPolicy - { - 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 options) { Guard.NotNull(options, nameof(options)); this.options = options.Value; - clientPool = new DefaultObjectPoolProvider().Create(new SmtpClientPolicy(options.Value)); + clientPool = new DefaultObjectPoolProvider().Create(new DefaultPooledObjectPolicy()); } - 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)) { - await CheckConnectionAsync(cts.Token); - - using (cts.Token.Register(smtpClient.SendAsyncCancel)) + using (var combined = CancellationTokenSource.CreateLinkedTokenSource(ct, timeout.Token)) { - await smtpClient.SendMailAsync(options.Sender, recipient, subject, body); + await EnsureConnectedAsync(smtpClient, combined.Token); + + var smtpMessage = new MimeMessage(); + + smtpMessage.From.Add(MailboxAddress.Parse( + options.Sender)); + + smtpMessage.To.Add(MailboxAddress.Parse( + recipient)); + + smtpMessage.Body = new TextPart(TextFormat.Html) + { + 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(); - - 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}")); - })) - { - await tcs.Task; - } + if (!smtpClient.IsAuthenticated) + { + await smtpClient.AuthenticateAsync(options.Username, options.Password, ct); } } } diff --git a/backend/src/Squidex.Infrastructure/Squidex.Infrastructure.csproj b/backend/src/Squidex.Infrastructure/Squidex.Infrastructure.csproj index e6fc63f57..e5f41c674 100644 --- a/backend/src/Squidex.Infrastructure/Squidex.Infrastructure.csproj +++ b/backend/src/Squidex.Infrastructure/Squidex.Infrastructure.csproj @@ -14,6 +14,7 @@ +