Change implementation of the Smtp server (#3202)

* Change implementation of the Smtp server

* Update BTCPayServer/Services/Mails/EmailSettings.cs

Co-authored-by: Andrew Camilleri <evilkukka@gmail.com>

Co-authored-by: Andrew Camilleri <evilkukka@gmail.com>
This commit is contained in:
Nicolas Dorier 2021-12-15 21:30:46 +09:00 committed by GitHub
parent ece5401121
commit ac099aa513
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 61 additions and 60 deletions

View file

@ -2724,7 +2724,6 @@ namespace BTCPayServer.Tests
Password = "admin@admin.com", Password = "admin@admin.com",
Port = 1234, Port = 1234,
Server = "admin.com", Server = "admin.com",
EnableSSL = true
}); });
Assert.Equal("admin@admin.com",(await Assert.IsType<ServerEmailSender>(await emailSenderFactory.GetEmailSender()).GetEmailSettings()).Login); Assert.Equal("admin@admin.com",(await Assert.IsType<ServerEmailSender>(await emailSenderFactory.GetEmailSender()).GetEmailSettings()).Login);
Assert.Equal("admin@admin.com",(await Assert.IsType<StoreEmailSender>(await emailSenderFactory.GetEmailSender(acc.StoreId)).GetEmailSettings()).Login); Assert.Equal("admin@admin.com",(await Assert.IsType<StoreEmailSender>(await emailSenderFactory.GetEmailSender(acc.StoreId)).GetEmailSettings()).Login);
@ -2739,8 +2738,7 @@ namespace BTCPayServer.Tests
Login = "store@store.com", Login = "store@store.com",
Password = "store@store.com", Password = "store@store.com",
Port = 1234, Port = 1234,
Server = "store.com", Server = "store.com"
EnableSSL = true
}), "")); }), ""));
Assert.Equal("store@store.com",(await Assert.IsType<StoreEmailSender>(await emailSenderFactory.GetEmailSender(acc.StoreId)).GetEmailSettings()).Login); Assert.Equal("store@store.com",(await Assert.IsType<StoreEmailSender>(await emailSenderFactory.GetEmailSender(acc.StoreId)).GetEmailSettings()).Login);

View file

@ -56,6 +56,7 @@
<PackageReference Include="Fido2.AspNet" Version="2.0.1" /> <PackageReference Include="Fido2.AspNet" Version="2.0.1" />
<PackageReference Include="HtmlSanitizer" Version="5.0.372" /> <PackageReference Include="HtmlSanitizer" Version="5.0.372" />
<PackageReference Include="LNURL" Version="0.0.15" /> <PackageReference Include="LNURL" Version="0.0.15" />
<PackageReference Include="MailKit" Version="3.0.0" />
<PackageReference Include="McMaster.NETCore.Plugins.Mvc" Version="1.4.0" /> <PackageReference Include="McMaster.NETCore.Plugins.Mvc" Version="1.4.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Filter" Version="1.1.2" /> <PackageReference Include="Microsoft.Extensions.Logging.Filter" Version="1.1.2" />
<PackageReference Include="Microsoft.NetCore.Analyzers" Version="3.3.2"> <PackageReference Include="Microsoft.NetCore.Analyzers" Version="3.3.2">

View file

@ -32,6 +32,7 @@ using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.AspNetCore.Routing; using Microsoft.AspNetCore.Routing;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using MimeKit;
using NBitcoin; using NBitcoin;
using NBitcoin.DataEncoders; using NBitcoin.DataEncoders;
using Renci.SshNet; using Renci.SshNet;
@ -1025,10 +1026,11 @@ namespace BTCPayServer.Controllers
TempData[WellKnownTempData.ErrorMessage] = "Required fields missing"; TempData[WellKnownTempData.ErrorMessage] = "Required fields missing";
return View(model); return View(model);
} }
using (var client = model.Settings.CreateSmtpClient()) using (var client = await model.Settings.CreateSmtpClient())
using (var message = model.Settings.CreateMailMessage(new MailAddress(model.TestEmail), "BTCPay test", "BTCPay test")) using (var message = model.Settings.CreateMailMessage(new MailboxAddress(model.TestEmail, model.TestEmail), "BTCPay test", "BTCPay test", false))
{ {
await client.SendMailAsync(message); await client.SendAsync(message);
await client.DisconnectAsync(true);
} }
TempData[WellKnownTempData.SuccessMessage] = "Email sent to " + model.TestEmail + ", please, verify you received it"; TempData[WellKnownTempData.SuccessMessage] = "Email sent to " + model.TestEmail + ", please, verify you received it";
} }

View file

@ -5,6 +5,7 @@ using BTCPayServer.Data;
using BTCPayServer.Models.ServerViewModels; using BTCPayServer.Models.ServerViewModels;
using BTCPayServer.Services.Mails; using BTCPayServer.Services.Mails;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using MimeKit;
namespace BTCPayServer.Controllers namespace BTCPayServer.Controllers
{ {
@ -41,9 +42,10 @@ namespace BTCPayServer.Controllers
TempData[WellKnownTempData.ErrorMessage] = "Required fields missing"; TempData[WellKnownTempData.ErrorMessage] = "Required fields missing";
return View(model); return View(model);
} }
var client = model.Settings.CreateSmtpClient(); using var client = await model.Settings.CreateSmtpClient();
var message = model.Settings.CreateMailMessage(new MailAddress(model.TestEmail), "BTCPay test", "BTCPay test"); var message = model.Settings.CreateMailMessage(new MailboxAddress(model.TestEmail, model.TestEmail), "BTCPay test", "BTCPay test", false);
await client.SendMailAsync(message); await client.SendAsync(message);
await client.DisconnectAsync(true);
TempData[WellKnownTempData.SuccessMessage] = "Email sent to " + model.TestEmail + ", please, verify you received it"; TempData[WellKnownTempData.SuccessMessage] = "Email sent to " + model.TestEmail + ", please, verify you received it";
} }
catch (Exception ex) catch (Exception ex)

View file

@ -3,6 +3,7 @@ using System.Net.Mail;
using System.Threading.Tasks; using System.Threading.Tasks;
using BTCPayServer.Logging; using BTCPayServer.Logging;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using MimeKit;
using NBitcoin; using NBitcoin;
namespace BTCPayServer.Services.Mails namespace BTCPayServer.Services.Mails
@ -29,18 +30,11 @@ namespace BTCPayServer.Services.Mails
Logs.Configuration.LogWarning("Should have sent email, but email settings are not configured"); Logs.Configuration.LogWarning("Should have sent email, but email settings are not configured");
return; return;
} }
using (var smtp = emailSettings.CreateSmtpClient()) using (var smtp = await emailSettings.CreateSmtpClient())
{ {
var mail = emailSettings.CreateMailMessage(new MailAddress(email), subject, message); var mail = emailSettings.CreateMailMessage(new MailboxAddress(email, email), subject, message, true);
mail.IsBodyHtml = true; await smtp.SendAsync(mail, cancellationToken);
try await smtp.DisconnectAsync(true, cancellationToken);
{
await smtp.SendMailAsync(mail).WithCancellation(cancellationToken);
}
catch (OperationCanceledException) when (cancellationToken.IsCancellationRequested)
{
smtp.SendAsyncCancel();
}
} }
}, TimeSpan.Zero); }, TimeSpan.Zero);
} }

View file

@ -1,7 +1,10 @@
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
using System.Net; using System.Net;
using System.Net.Mail;
using Newtonsoft.Json; using Newtonsoft.Json;
using MailKit.Net.Smtp;
using MimeKit;
using System.Threading.Tasks;
using System.Threading;
namespace BTCPayServer.Services.Mails namespace BTCPayServer.Services.Mails
{ {
@ -41,42 +44,47 @@ namespace BTCPayServer.Services.Mails
get; set; get; set;
} }
[Display(Name = "Enable SSL")]
public bool EnableSSL
{
get; set;
}
public bool IsComplete() public bool IsComplete()
{ {
return !string.IsNullOrWhiteSpace(Server) &&
Port is int &&
!string.IsNullOrWhiteSpace(Login) &&
!string.IsNullOrWhiteSpace(Password);
}
public MimeMessage CreateMailMessage(MailboxAddress to, string subject, string message, bool isHtml)
{
var bodyBuilder = new BodyBuilder();
if (isHtml)
{
bodyBuilder.HtmlBody = message;
}
else
{
bodyBuilder.TextBody = message;
}
return new MimeMessage(
from : new[] { new MailboxAddress(From, !string.IsNullOrWhiteSpace(FromDisplay) ? From : FromDisplay) },
to: new[] { to },
subject,
bodyBuilder.ToMessageBody());
}
public async Task<SmtpClient> CreateSmtpClient()
{
SmtpClient client = new SmtpClient();
using var connectCancel = new CancellationTokenSource(10000);
try try
{ {
using var smtp = CreateSmtpClient(); await client.ConnectAsync(Server, Port.Value, MailKit.Security.SecureSocketOptions.Auto, connectCancel.Token);
return true; await client.AuthenticateAsync(Login, Password, connectCancel.Token);
} }
catch { } catch
return false;
}
public MailMessage CreateMailMessage(MailAddress to, string subject, string message)
{
return new MailMessage(
from: new MailAddress(From, FromDisplay),
to: to)
{ {
Subject = subject, client.Dispose();
Body = message throw;
}; }
}
public SmtpClient CreateSmtpClient()
{
SmtpClient client = new SmtpClient(Server, Port.Value);
client.EnableSsl = EnableSSL;
client.UseDefaultCredentials = false;
client.Credentials = new NetworkCredential(Login, Password);
client.DeliveryMethod = SmtpDeliveryMethod.Network;
client.Timeout = 10000;
return client; return client;
} }
} }

View file

@ -9,11 +9,11 @@
Quick Fill Quick Fill
</button> </button>
<div class="dropdown-menu" aria-labelledby="QuickFillDropdownToggle"> <div class="dropdown-menu" aria-labelledby="QuickFillDropdownToggle">
<a class="dropdown-item" href="" data-server="smtp.gmail.com" data-port="587" data-enablessl="true">Gmail.com</a> <a class="dropdown-item" href="" data-server="smtp.gmail.com" data-port="587">Gmail.com</a>
<a class="dropdown-item" href="" data-server="mail.yahoo.com" data-port="587" data-enablessl="true">Yahoo.com</a> <a class="dropdown-item" href="" data-server="mail.yahoo.com" data-port="587">Yahoo.com</a>
<a class="dropdown-item" href="" data-server="smtp.mailgun.org" data-port="587" data-enablessl="true">Mailgun</a> <a class="dropdown-item" href="" data-server="smtp.mailgun.org" data-port="587">Mailgun</a>
<a class="dropdown-item" href="" data-server="smtp.office365.com" data-port="587" data-enablessl="true">Office365</a> <a class="dropdown-item" href="" data-server="smtp.office365.com" data-port="587">Office365</a>
<a class="dropdown-item" href="" data-server="smtp.sendgrid.net" data-port="587" data-enablessl="true">SendGrid</a> <a class="dropdown-item" href="" data-server="smtp.sendgrid.net" data-port="587">SendGrid</a>
</div> </div>
</div> </div>
</div> </div>
@ -75,10 +75,6 @@
</div> </div>
} }
</div> </div>
<div class="form-group">
<label asp-for="Settings.EnableSSL" class="form-label"></label>
<input asp-for="Settings.EnableSSL" type="checkbox" data-fill="enablessl" class="btcpay-toggle ms-2" />
</div>
<input asp-for="PasswordSet" type="hidden"/> <input asp-for="PasswordSet" type="hidden"/>
<button type="submit" class="btn btn-primary" name="command" value="Save">Save</button> <button type="submit" class="btn btn-primary" name="command" value="Save">Save</button>
</div> </div>