Refactor: Move email services to the email plugin directory
What changed, and why it matters
This commit is a code reorganization: it moves BTCPay Server's email-sending code from a shared 'Services/Mails' folder into the existing 'Plugins/Emails' plugin directory. File paths and namespace references are updated across the project, but the actual logic for sending email, validating settings, and checking certificates is copied almost unchanged. There is no indication this change fixes or introduces a security vulnerability.
No security action required. Treat as routine maintenance. If reviewing for regressions, verify that plugin loading/dependency injection still resolves IEmailSender and EmailSenderFactory correctly after the namespace move.
Security signals we found
No security-relevant logic changes observed
Certificate validation bypass flag (DisableCertificateCheck) preserved exactly as before
Email settings password fallback preserved exactly as before
No new input validation, authorization, or cryptographic code added
No references to CVEs, advisories, or security fixes in commit title/message
Evidence from the diff
The change is a pure refactor. Classes such as EmailSender, EmailSenderFactory, EmailSettings, IEmailSender, ServerEmailSender, and StoreEmailSender are deleted from BTCPayServer.Services.Mails and recreated under BTCPayServer.Plugins.Emails.Services. Callers and using statements are updated accordingly. The implementation details (background-job scheduling, SMTP certificate bypass flag, From-address validation, password fallback behavior) remain identical. A few unused imports are cleaned up and two helper methods are marked [Obsolete].
Changed components
BTCPayServer.Plugins.Emails (new target location for email services)BTCPayServer.Services.Mails (source location, files removed)Greenfield email API controllersUI account/manage/server/store email controllersEmail-related Razor viewsMigration startup task referencing EmailSettingsInspect captured patch +312 / −390
diff --git a/BTCPayServer.Abstractions/Extensions/ViewsRazor.cs b/BTCPayServer.Abstractions/Extensions/ViewsRazor.cs
index a182fa9..50ac21c 100644
--- a/BTCPayServer.Abstractions/Extensions/ViewsRazor.cs
+++ b/BTCPayServer.Abstractions/Extensions/ViewsRazor.cs
@@ -104,11 +104,13 @@ namespace BTCPayServer.Abstractions.Extensions
return pages.Any(page => ActivePageClass(viewData, page.ToString(), page.GetType().ToString(), id) == ACTIVE_CLASS);
}
+ [Obsolete("Use IsCategory instead")]
public static string ActiveCategoryClass<T>(this ViewDataDictionary viewData, T category, object id = null)
{
return ActiveCategoryClass(viewData, category.ToString(), id);
}
+ [Obsolete("Use IsCategory instead")]
public static string ActiveCategoryClass(this ViewDataDictionary viewData, string category, object id = null)
{
return IsCategoryActive(viewData, category, id) ? ACTIVE_CLASS : null;
diff --git a/BTCPayServer.Tests/BTCPayServerTester.cs b/BTCPayServer.Tests/BTCPayServerTester.cs
index 643ba9a..a734b15 100644
--- a/BTCPayServer.Tests/BTCPayServerTester.cs
+++ b/BTCPayServer.Tests/BTCPayServerTester.cs
@@ -10,12 +10,10 @@ using System.Threading.Tasks;
using BTCPayServer.Abstractions.Constants;
using BTCPayServer.Abstractions.Contracts;
using BTCPayServer.Configuration;
-using BTCPayServer.HostedServices;
using BTCPayServer.Hosting;
using BTCPayServer.Rating;
using BTCPayServer.Services;
using BTCPayServer.Services.Invoices;
-using BTCPayServer.Services.Mails;
using BTCPayServer.Services.Rates;
using BTCPayServer.Services.Stores;
using BTCPayServer.Tests.Logging;
@@ -27,7 +25,6 @@ using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
-using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using NBitcoin;
using NBXplorer;
diff --git a/BTCPayServer.Tests/GreenfieldAPITests.cs b/BTCPayServer.Tests/GreenfieldAPITests.cs
index 137086a..426ef21 100644
--- a/BTCPayServer.Tests/GreenfieldAPITests.cs
+++ b/BTCPayServer.Tests/GreenfieldAPITests.cs
@@ -25,7 +25,7 @@ using BTCPayServer.Rating;
using BTCPayServer.Services;
using BTCPayServer.Services.Apps;
using BTCPayServer.Services.Invoices;
-using BTCPayServer.Services.Mails;
+using BTCPayServer.Plugins.Emails.Services;
using BTCPayServer.Services.Notifications;
using BTCPayServer.Services.Notifications.Blobs;
using BTCPayServer.Services.Stores;
diff --git a/BTCPayServer.Tests/UnitTest1.cs b/BTCPayServer.Tests/UnitTest1.cs
index b521089..d5a5e30 100644
--- a/BTCPayServer.Tests/UnitTest1.cs
+++ b/BTCPayServer.Tests/UnitTest1.cs
@@ -46,7 +46,7 @@ using BTCPayServer.Services;
using BTCPayServer.Services.Apps;
using BTCPayServer.Services.Invoices;
using BTCPayServer.Services.Labels;
-using BTCPayServer.Services.Mails;
+using BTCPayServer.Plugins.Emails.Services;
using BTCPayServer.Services.Notifications;
using BTCPayServer.Services.Rates;
using BTCPayServer.Storage.Models;
diff --git a/BTCPayServer/BTCPayServer.csproj b/BTCPayServer/BTCPayServer.csproj
index e504a43..d7c4d2b 100644
--- a/BTCPayServer/BTCPayServer.csproj
+++ b/BTCPayServer/BTCPayServer.csproj
@@ -94,6 +94,7 @@
</ItemGroup>
<ItemGroup>
+ <Folder Include="Plugins\Emails\Mails\" />
<Folder Include="wwwroot\vendor\bootstrap" />
<Folder Include="wwwroot\vendor\clipboard.js\" />
<Folder Include="wwwroot\vendor\highlightjs\" />
diff --git a/BTCPayServer/Controllers/GreenField/GreenfieldServerEmailController.cs b/BTCPayServer/Controllers/GreenField/GreenfieldServerEmailController.cs
index c1fd391..3e73e1d 100644
--- a/BTCPayServer/Controllers/GreenField/GreenfieldServerEmailController.cs
+++ b/BTCPayServer/Controllers/GreenField/GreenfieldServerEmailController.cs
@@ -1,17 +1,14 @@
#nullable enable
-using System;
using System.Threading.Tasks;
using BTCPayServer.Abstractions.Constants;
using BTCPayServer.Abstractions.Extensions;
using BTCPayServer.Client;
using BTCPayServer.Client.Models;
-using BTCPayServer.Models.ServerViewModels;
using BTCPayServer.Services;
-using BTCPayServer.Services.Mails;
+using BTCPayServer.Plugins.Emails.Services;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Cors;
using Microsoft.AspNetCore.Mvc;
-using Newtonsoft.Json.Linq;
namespace BTCPayServer.Controllers.GreenField
{
@@ -30,7 +27,7 @@ namespace BTCPayServer.Controllers.GreenField
_policiesSettings = policiesSettings;
_settingsRepository = settingsRepository;
}
-
+
private ServerEmailSettingsData ToApiModel(EmailSettings email)
{
var data = email.ToData<ServerEmailSettingsData>();
@@ -52,7 +49,7 @@ namespace BTCPayServer.Controllers.GreenField
{
if (!string.IsNullOrWhiteSpace(request.From) && !MailboxAddressValidator.IsMailboxAddress(request.From))
ModelState.AddModelError(nameof(request.From), "Invalid email address");
-
+
if (!ModelState.IsValid)
return this.CreateValidationError(ModelState);
diff --git a/BTCPayServer/Controllers/GreenField/GreenfieldStoreEmailController.cs b/BTCPayServer/Controllers/GreenField/GreenfieldStoreEmailController.cs
index 409e0a8..02cc52b 100644
--- a/BTCPayServer/Controllers/GreenField/GreenfieldStoreEmailController.cs
+++ b/BTCPayServer/Controllers/GreenField/GreenfieldStoreEmailController.cs
@@ -5,7 +5,7 @@ using BTCPayServer.Abstractions.Extensions;
using BTCPayServer.Client;
using BTCPayServer.Client.Models;
using BTCPayServer.Data;
-using BTCPayServer.Services.Mails;
+using BTCPayServer.Plugins.Emails.Services;
using BTCPayServer.Services.Stores;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Cors;
@@ -72,7 +72,7 @@ namespace BTCPayServer.Controllers.GreenField
{
if (!string.IsNullOrWhiteSpace(request.From) && !MailboxAddressValidator.IsMailboxAddress(request.From))
ModelState.AddModelError(nameof(request.From), "Invalid email address");
-
+
if (!ModelState.IsValid)
return this.CreateValidationError(ModelState);
diff --git a/BTCPayServer/Controllers/GreenField/LocalBTCPayServerClient.cs b/BTCPayServer/Controllers/GreenField/LocalBTCPayServerClient.cs
index 125c7d9..3c824f3 100644
--- a/BTCPayServer/Controllers/GreenField/LocalBTCPayServerClient.cs
+++ b/BTCPayServer/Controllers/GreenField/LocalBTCPayServerClient.cs
@@ -15,7 +15,7 @@ using BTCPayServer.Data;
using BTCPayServer.Plugins.Webhooks.Controllers;
using BTCPayServer.Security;
using BTCPayServer.Security.Greenfield;
-using BTCPayServer.Services.Mails;
+using BTCPayServer.Plugins.Emails.Services;
using BTCPayServer.Services.Stores;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
diff --git a/BTCPayServer/Controllers/UIAccountController.cs b/BTCPayServer/Controllers/UIAccountController.cs
index 8e5cfea..c15848b 100644
--- a/BTCPayServer/Controllers/UIAccountController.cs
+++ b/BTCPayServer/Controllers/UIAccountController.cs
@@ -15,7 +15,7 @@ using BTCPayServer.Filters;
using BTCPayServer.Logging;
using BTCPayServer.Models.AccountViewModels;
using BTCPayServer.Services;
-using BTCPayServer.Services.Mails;
+using BTCPayServer.Plugins.Emails.Services;
using Fido2NetLib;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authorization;
diff --git a/BTCPayServer/Controllers/UIManageController.cs b/BTCPayServer/Controllers/UIManageController.cs
index b457fcf..7c1535d 100644
--- a/BTCPayServer/Controllers/UIManageController.cs
+++ b/BTCPayServer/Controllers/UIManageController.cs
@@ -11,7 +11,7 @@ using BTCPayServer.Fido2;
using BTCPayServer.Models.ManageViewModels;
using BTCPayServer.Security.Greenfield;
using BTCPayServer.Services;
-using BTCPayServer.Services.Mails;
+using BTCPayServer.Plugins.Emails.Services;
using BTCPayServer.Services.Stores;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
diff --git a/BTCPayServer/Controllers/UIServerController.Users.cs b/BTCPayServer/Controllers/UIServerController.Users.cs
index 538c504..17de893 100644
--- a/BTCPayServer/Controllers/UIServerController.Users.cs
+++ b/BTCPayServer/Controllers/UIServerController.Users.cs
@@ -8,8 +8,6 @@ using BTCPayServer.Abstractions.Models;
using BTCPayServer.Data;
using BTCPayServer.Events;
using BTCPayServer.Models.ServerViewModels;
-using BTCPayServer.Services;
-using BTCPayServer.Services.Mails;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
diff --git a/BTCPayServer/Controllers/UIServerController.cs b/BTCPayServer/Controllers/UIServerController.cs
index 6208432..3abc104 100644
--- a/BTCPayServer/Controllers/UIServerController.cs
+++ b/BTCPayServer/Controllers/UIServerController.cs
@@ -21,7 +21,7 @@ using BTCPayServer.Models.ServerViewModels;
using BTCPayServer.Models.StoreViewModels;
using BTCPayServer.Services;
using BTCPayServer.Services.Apps;
-using BTCPayServer.Services.Mails;
+using BTCPayServer.Plugins.Emails.Services;
using BTCPayServer.Services.Stores;
using BTCPayServer.Storage.Services;
using BTCPayServer.Storage.Services.Providers;
diff --git a/BTCPayServer/Controllers/UIStoresController.Users.cs b/BTCPayServer/Controllers/UIStoresController.Users.cs
index b2951ab..afbaa8f 100644
--- a/BTCPayServer/Controllers/UIStoresController.Users.cs
+++ b/BTCPayServer/Controllers/UIStoresController.Users.cs
@@ -10,12 +10,9 @@ using BTCPayServer.Data;
using BTCPayServer.Events;
using BTCPayServer.Models.StoreViewModels;
using BTCPayServer.Security;
-using BTCPayServer.Services;
-using BTCPayServer.Services.Mails;
using BTCPayServer.Services.Stores;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
-using Microsoft.AspNetCore.Mvc.Localization;
using NicolasDorier.RateLimits;
using static BTCPayServer.Services.Stores.StoreRepository;
diff --git a/BTCPayServer/Controllers/UIStoresController.cs b/BTCPayServer/Controllers/UIStoresController.cs
index 2615b81..32f1ff2 100644
--- a/BTCPayServer/Controllers/UIStoresController.cs
+++ b/BTCPayServer/Controllers/UIStoresController.cs
@@ -11,7 +11,7 @@ using BTCPayServer.Security.Bitpay;
using BTCPayServer.Services;
using BTCPayServer.Services.Apps;
using BTCPayServer.Services.Invoices;
-using BTCPayServer.Services.Mails;
+using BTCPayServer.Plugins.Emails.Services;
using BTCPayServer.Services.Rates;
using BTCPayServer.Services.Stores;
using BTCPayServer.Services.Wallets;
diff --git a/BTCPayServer/Data/StoreBlob.cs b/BTCPayServer/Data/StoreBlob.cs
index b85bdcd..cf1feae 100644
--- a/BTCPayServer/Data/StoreBlob.cs
+++ b/BTCPayServer/Data/StoreBlob.cs
@@ -7,8 +7,8 @@ using BTCPayServer.Client.JsonConverters;
using BTCPayServer.Client.Models;
using BTCPayServer.JsonConverters;
using BTCPayServer.Payments;
+using BTCPayServer.Plugins.Emails.Services;
using BTCPayServer.Rating;
-using BTCPayServer.Services.Mails;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
diff --git a/BTCPayServer/HostedServices/BitpayIPNSender.cs b/BTCPayServer/HostedServices/BitpayIPNSender.cs
index 201a458..8bcee33 100644
--- a/BTCPayServer/HostedServices/BitpayIPNSender.cs
+++ b/BTCPayServer/HostedServices/BitpayIPNSender.cs
@@ -5,12 +5,11 @@ using System.Net.Http;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
-using BTCPayServer.Client.Models;
using BTCPayServer.Events;
using BTCPayServer.Payments;
using BTCPayServer.Services;
using BTCPayServer.Services.Invoices;
-using BTCPayServer.Services.Mails;
+using BTCPayServer.Plugins.Emails.Services;
using BTCPayServer.Services.Rates;
using BTCPayServer.Services.Stores;
using Microsoft.Extensions.Hosting;
diff --git a/BTCPayServer/Hosting/BTCPayServerServices.cs b/BTCPayServer/Hosting/BTCPayServerServices.cs
index 2eefa55..a5359d7 100644
--- a/BTCPayServer/Hosting/BTCPayServerServices.cs
+++ b/BTCPayServer/Hosting/BTCPayServerServices.cs
@@ -41,7 +41,7 @@ using BTCPayServer.Services.Apps;
using BTCPayServer.Services.Fees;
using BTCPayServer.Services.Invoices;
using BTCPayServer.Services.Labels;
-using BTCPayServer.Services.Mails;
+using BTCPayServer.Plugins.Emails.Services;
using BTCPayServer.Services.Notifications;
using BTCPayServer.Services.Notifications.Blobs;
using BTCPayServer.Services.PaymentRequests;
diff --git a/BTCPayServer/Hosting/MigrationStartupTask.cs b/BTCPayServer/Hosting/MigrationStartupTask.cs
index 683520a..7dbb148 100644
--- a/BTCPayServer/Hosting/MigrationStartupTask.cs
+++ b/BTCPayServer/Hosting/MigrationStartupTask.cs
@@ -16,6 +16,7 @@ using BTCPayServer.Payments.Bitcoin;
using BTCPayServer.Payments.Lightning;
using BTCPayServer.Payouts;
using BTCPayServer.Plugins.Crowdfund;
+using BTCPayServer.Plugins.Emails.Services;
using BTCPayServer.Plugins.PointOfSale;
using BTCPayServer.Services;
using BTCPayServer.Services.Apps;
@@ -593,7 +594,7 @@ namespace BTCPayServer.Hosting
private async Task MigrateEmailServerDisableTLSCerts()
{
await using var ctx = _DBContextFactory.CreateContext();
- var serverEmailSettings = await _Settings.GetSettingAsync<Services.Mails.EmailSettings>();
+ var serverEmailSettings = await _Settings.GetSettingAsync<EmailSettings>();
if (serverEmailSettings?.Server is String server)
{
serverEmailSettings.DisableCertificateCheck = Extensions.IsLocalNetwork(server);
diff --git a/BTCPayServer/Plugins/Emails/Controllers/UIEmailControllerBase.cs b/BTCPayServer/Plugins/Emails/Controllers/UIEmailControllerBase.cs
index a1761fd..f7ad1bc 100644
--- a/BTCPayServer/Plugins/Emails/Controllers/UIEmailControllerBase.cs
+++ b/BTCPayServer/Plugins/Emails/Controllers/UIEmailControllerBase.cs
@@ -5,10 +5,8 @@ using System.Threading.Tasks;
using BTCPayServer.Abstractions.Constants;
using BTCPayServer.Abstractions.Extensions;
using BTCPayServer.Abstractions.Models;
-using BTCPayServer.Client;
using BTCPayServer.Plugins.Emails.Views;
-using BTCPayServer.Services;
-using BTCPayServer.Services.Mails;
+using BTCPayServer.Plugins.Emails.Services;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Localization;
using MimeKit;
diff --git a/BTCPayServer/Plugins/Emails/Controllers/UIEmailRuleControllerBase.cs b/BTCPayServer/Plugins/Emails/Controllers/UIEmailRuleControllerBase.cs
index 742ecbd..f22dbbb 100644
--- a/BTCPayServer/Plugins/Emails/Controllers/UIEmailRuleControllerBase.cs
+++ b/BTCPayServer/Plugins/Emails/Controllers/UIEmailRuleControllerBase.cs
@@ -10,7 +10,7 @@ using BTCPayServer.Client;
using BTCPayServer.Data;
using BTCPayServer.Plugins.Emails.Views;
using BTCPayServer.Plugins.Emails.Views.Shared;
-using BTCPayServer.Services.Mails;
+using BTCPayServer.Plugins.Emails.Services;
using Dapper;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
diff --git a/BTCPayServer/Plugins/Emails/Controllers/UIServerEmailController.cs b/BTCPayServer/Plugins/Emails/Controllers/UIServerEmailController.cs
index 9b25092..e1e963f 100644
--- a/BTCPayServer/Plugins/Emails/Controllers/UIServerEmailController.cs
+++ b/BTCPayServer/Plugins/Emails/Controllers/UIServerEmailController.cs
@@ -1,13 +1,9 @@
-using System;
-using System.ComponentModel.DataAnnotations;
using System.Threading.Tasks;
using BTCPayServer.Abstractions.Constants;
-using BTCPayServer.Abstractions.Extensions;
-using BTCPayServer.Abstractions.Models;
using BTCPayServer.Client;
using BTCPayServer.Plugins.Emails.Views;
using BTCPayServer.Services;
-using BTCPayServer.Services.Mails;
+using BTCPayServer.Plugins.Emails.Services;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Localization;
diff --git a/BTCPayServer/Plugins/Emails/Controllers/UIServerEmailRulesController.cs b/BTCPayServer/Plugins/Emails/Controllers/UIServerEmailRulesController.cs
index a8d91b7..4c9d53d 100644
--- a/BTCPayServer/Plugins/Emails/Controllers/UIServerEmailRulesController.cs
+++ b/BTCPayServer/Plugins/Emails/Controllers/UIServerEmailRulesController.cs
@@ -3,18 +3,12 @@ using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using BTCPayServer.Abstractions.Constants;
-using BTCPayServer.Abstractions.Extensions;
-using BTCPayServer.Abstractions.Models;
using BTCPayServer.Client;
-using BTCPayServer.Controllers;
using BTCPayServer.Data;
using BTCPayServer.Plugins.Emails.Views;
-using BTCPayServer.Plugins.Emails.Views.Shared;
-using BTCPayServer.Services.Mails;
+using BTCPayServer.Plugins.Emails.Services;
using Microsoft.AspNetCore.Authorization;
-using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
-using Microsoft.AspNetCore.Routing;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Localization;
diff --git a/BTCPayServer/Plugins/Emails/Controllers/UIStoreEmailRulesController.cs b/BTCPayServer/Plugins/Emails/Controllers/UIStoreEmailRulesController.cs
index f2b4d44..fc6dad7 100644
--- a/BTCPayServer/Plugins/Emails/Controllers/UIStoreEmailRulesController.cs
+++ b/BTCPayServer/Plugins/Emails/Controllers/UIStoreEmailRulesController.cs
@@ -6,7 +6,7 @@ using BTCPayServer.Abstractions.Extensions;
using BTCPayServer.Client;
using BTCPayServer.Data;
using BTCPayServer.Plugins.Emails.Views;
-using BTCPayServer.Services.Mails;
+using BTCPayServer.Plugins.Emails.Services;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Routing;
diff --git a/BTCPayServer/Plugins/Emails/Controllers/UIStoresEmailController.cs b/BTCPayServer/Plugins/Emails/Controllers/UIStoresEmailController.cs
index 065566c..d409a5b 100644
--- a/BTCPayServer/Plugins/Emails/Controllers/UIStoresEmailController.cs
+++ b/BTCPayServer/Plugins/Emails/Controllers/UIStoresEmailController.cs
@@ -1,18 +1,13 @@
-using System;
-using System.ComponentModel.DataAnnotations;
using System.Threading.Tasks;
using BTCPayServer.Abstractions.Constants;
-using BTCPayServer.Abstractions.Extensions;
-using BTCPayServer.Abstractions.Models;
using BTCPayServer.Client;
using BTCPayServer.Data;
using BTCPayServer.Plugins.Emails.Views;
-using BTCPayServer.Services.Mails;
+using BTCPayServer.Plugins.Emails.Services;
using BTCPayServer.Services.Stores;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Localization;
-using MimeKit;
namespace BTCPayServer.Plugins.Emails.Controllers;
diff --git a/BTCPayServer/Plugins/Emails/HostedServices/EmailRuleProcessorSender.cs b/BTCPayServer/Plugins/Emails/HostedServices/EmailRuleProcessorSender.cs
index 84059ed..184b126 100644
--- a/BTCPayServer/Plugins/Emails/HostedServices/EmailRuleProcessorSender.cs
+++ b/BTCPayServer/Plugins/Emails/HostedServices/EmailRuleProcessorSender.cs
@@ -7,7 +7,7 @@ using System.Threading;
using System.Threading.Tasks;
using BTCPayServer.Data;
using BTCPayServer.HostedServices;
-using BTCPayServer.Services.Mails;
+using BTCPayServer.Plugins.Emails.Services;
using Microsoft.Extensions.Logging;
using MimeKit;
using Newtonsoft.Json.Linq;
diff --git a/BTCPayServer/Plugins/Emails/HostedServices/UserEventTriggerHostedService.cs b/BTCPayServer/Plugins/Emails/HostedServices/UserEventTriggerHostedService.cs
index de90f5b..60d974d 100644
--- a/BTCPayServer/Plugins/Emails/HostedServices/UserEventTriggerHostedService.cs
+++ b/BTCPayServer/Plugins/Emails/HostedServices/UserEventTriggerHostedService.cs
@@ -8,12 +8,9 @@ using BTCPayServer.Events;
using BTCPayServer.HostedServices;
using BTCPayServer.Logging;
using BTCPayServer.Services;
-using BTCPayServer.Services.Mails;
using BTCPayServer.Services.Notifications;
using BTCPayServer.Services.Notifications.Blobs;
-using BTCPayServer.Services.Stores;
using Microsoft.AspNetCore.Identity;
-using Microsoft.Extensions.Logging;
using Newtonsoft.Json.Linq;
using QRCoder;
diff --git a/BTCPayServer/Plugins/Emails/Services/EmailSender.cs b/BTCPayServer/Plugins/Emails/Services/EmailSender.cs
new file mode 100644
index 0000000..8b2c7b9
--- /dev/null
+++ b/BTCPayServer/Plugins/Emails/Services/EmailSender.cs
@@ -0,0 +1,41 @@
+#nullable enable
+using System;
+using System.Threading.Tasks;
+using BTCPayServer.Logging;
+using BTCPayServer.Services;
+using Microsoft.Extensions.Logging;
+using MimeKit;
+
+namespace BTCPayServer.Plugins.Emails.Services;
+
+public abstract class EmailSender(IBackgroundJobClient jobClient, EventAggregator eventAggregator, Logs logs) : IEmailSender
+{
+ public EventAggregator EventAggregator { get; } = eventAggregator;
+ public Logs Logs { get; } = logs;
+
+ public void SendEmail(MailboxAddress email, string subject, string message)
+ {
+ SendEmail(new[] { email }, Array.Empty<MailboxAddress>(), Array.Empty<MailboxAddress>(), subject, message);
+ }
+
+ public void SendEmail(MailboxAddress[] email, MailboxAddress[] cc, MailboxAddress[] bcc, string subject, string message)
+ {
+ jobClient.Schedule(async cancellationToken =>
+ {
+ var emailSettings = await GetEmailSettings();
+ if (emailSettings?.IsComplete() is not true)
+ {
+ Logs.Configuration.LogWarning("Should have sent email, but email settings are not configured");
+ return;
+ }
+
+ using var smtp = await emailSettings.CreateSmtpClient();
+ var mail = emailSettings.CreateMailMessage(email, cc, bcc, subject, message, true);
+ var response = await smtp.SendAsync(mail, cancellationToken);
+ await smtp.DisconnectAsync(true, cancellationToken);
+ EventAggregator.Publish(new Events.EmailSentEvent(response, mail));
+ }, TimeSpan.Zero);
+ }
+
+ public abstract Task<EmailSettings?> GetEmailSettings();
+}
diff --git a/BTCPayServer/Plugins/Emails/Services/EmailSenderFactory.cs b/BTCPayServer/Plugins/Emails/Services/EmailSenderFactory.cs
new file mode 100644
index 0000000..45d8996
--- /dev/null
+++ b/BTCPayServer/Plugins/Emails/Services/EmailSenderFactory.cs
@@ -0,0 +1,39 @@
+#nullable enable
+using System.Threading.Tasks;
+using BTCPayServer.Logging;
+using BTCPayServer.Services;
+using BTCPayServer.Services.Stores;
+
+namespace BTCPayServer.Plugins.Emails.Services;
+
+public class EmailSenderFactory(
+ IBackgroundJobClient jobClient,
+ SettingsRepository settingsSettingsRepository,
+ EventAggregator eventAggregator,
+ ISettingsAccessor<PoliciesSettings> policiesSettings,
+ StoreRepository storeRepository,
+ Logs logs)
+{
+ public Logs Logs { get; } = logs;
+
+ public Task<IEmailSender> GetEmailSender(string? storeId = null)
+ {
+ var serverSender = new ServerEmailSender(settingsSettingsRepository, jobClient, eventAggregator, Logs);
+ if (string.IsNullOrEmpty(storeId))
+ return Task.FromResult<IEmailSender>(serverSender);
+ return Task.FromResult<IEmailSender>(new StoreEmailSender(storeRepository,
+ !policiesSettings.Settings.DisableStoresToUseServerEmailSettings ? serverSender : null, jobClient,
+ eventAggregator, storeId, Logs));
+ }
+
+ public async Task<bool> IsComplete(string? storeId = null)
+ {
+ var settings = await this.GetSettings(storeId);
+ return settings?.IsComplete() is true;
+ }
+ public async Task<EmailSettings?> GetSettings(string? storeId = null)
+ {
+ var sender = await this.GetEmailSender(storeId);
+ return await sender.GetEmailSettings();
+ }
+}
diff --git a/BTCPayServer/Plugins/Emails/Services/EmailSettings.cs b/BTCPayServer/Plugins/Emails/Services/EmailSettings.cs
new file mode 100644
index 0000000..41a255f
--- /dev/null
+++ b/BTCPayServer/Plugins/Emails/Services/EmailSettings.cs
@@ -0,0 +1,125 @@
+using System.ComponentModel.DataAnnotations;
+using System.Threading;
+using System.Threading.Tasks;
+using BTCPayServer.Client.Models;
+using BTCPayServer.Validation;
+using MailKit.Net.Smtp;
+using Microsoft.AspNetCore.Mvc.ModelBinding;
+using MimeKit;
+using Newtonsoft.Json;
+
+namespace BTCPayServer.Plugins.Emails.Services;
+
+/// <summary>
+/// Email Settings gets saved to database, and is used by both the server and store settings
+/// </summary>
+public class EmailSettings
+{
+ public string Server { get; set; }
+ public int? Port { get; set; }
+ public string Login { get; set; }
+ public string Password { get; set; }
+ public string From { get; set; }
+ public bool DisableCertificateCheck { get; set; }
+ [JsonIgnore]
+ public bool EnabledCertificateCheck
+ {
+ get => !DisableCertificateCheck;
+ set { DisableCertificateCheck = !value; }
+ }
+
+ // Conversion functions for mapping to and from the client EmailSettingsData model
+#nullable enable
+ public static EmailSettings FromData(EmailSettingsData data, string? existingPassword)
+ => new()
+ {
+ Server = data.Server,
+ Port = data.Port,
+ Login = data.Login,
+ // If login was provided but password was not, use the existing password from database
+ Password = !string.IsNullOrEmpty(data.Login) && string.IsNullOrEmpty(data.Password) ?
+ existingPassword : data.Password,
+ From = data.From,
+ DisableCertificateCheck = data.DisableCertificateCheck
+ };
+
+ public T ToData<T>() where T : EmailSettingsData, new()
+ => new T()
+ {
+ Server = Server,
+ Port = Port,
+ Login = Login,
+ PasswordSet = !string.IsNullOrEmpty(Password),
+ From = From,
+ DisableCertificateCheck = DisableCertificateCheck
+ };
+#nullable restore
+
+
+ //
+ public bool IsComplete()
+ {
+ return MailboxAddressValidator.IsMailboxAddress(From)
+ && !string.IsNullOrWhiteSpace(Server)
+ && Port != null
+ && !string.IsNullOrWhiteSpace(From)
+ && MailboxAddressValidator.IsMailboxAddress(From);
+ }
+
+ public void Validate(string prefixKey, ModelStateDictionary modelState)
+ {
+ if (!string.IsNullOrEmpty(From) && !MailboxAddressValidator.IsMailboxAddress(From))
+ {
+ modelState.AddModelError($"{prefixKey}{nameof(From)}", MailboxAddressAttribute.ErrorMessageConst);
+ }
+ }
+
+ public MimeMessage CreateMailMessage(MailboxAddress to, string subject, string message, bool isHtml) =>
+ CreateMailMessage(new[] { to }, null, null, subject, message, isHtml);
+ public MimeMessage CreateMailMessage(MailboxAddress[] to, MailboxAddress[] cc, MailboxAddress[] bcc, string subject, string message, bool isHtml)
+ {
+ var bodyBuilder = new BodyBuilder();
+ if (isHtml)
+ {
+ bodyBuilder.HtmlBody = message;
+ }
+ else
+ {
+ bodyBuilder.TextBody = message;
+ }
+
+ var mm = new MimeMessage();
+ mm.Body = bodyBuilder.ToMessageBody();
+ mm.Subject = subject;
+ mm.From.Add(MailboxAddressValidator.Parse(From));
+ mm.To.AddRange(to);
+ mm.Cc.AddRange(cc ?? System.Array.Empty<InternetAddress>());
+ mm.Bcc.AddRange(bcc ?? System.Array.Empty<InternetAddress>());
+ return mm;
+ }
+
+ public async Task<SmtpClient> CreateSmtpClient()
+ {
+ SmtpClient client = new SmtpClient();
+ using var connectCancel = new CancellationTokenSource(10000);
+ try
+ {
+ if (DisableCertificateCheck)
+ {
+ client.CheckCertificateRevocation = false;
+#pragma warning disable CA5359 // Do Not Disable Certificate Validation
+ client.ServerCertificateValidationCallback = (s, c, h, e) => true;
+#pragma warning restore CA5359 // Do Not Disable Certificate Validation
+ }
+ await client.ConnectAsync(Server, Port.Value, MailKit.Security.SecureSocketOptions.Auto, connectCancel.Token);
+ if ((client.Capabilities & SmtpCapabilities.Authentication) != 0)
+ await client.AuthenticateAsync(Login ?? string.Empty, Password ?? string.Empty, connectCancel.Token);
+ }
+ catch
+ {
+ client.Dispose();
+ throw;
+ }
+ return client;
+ }
+}
diff --git a/BTCPayServer/Plugins/Emails/Services/IEmailSender.cs b/BTCPayServer/Plugins/Emails/Services/IEmailSender.cs
new file mode 100644
index 0000000..c6a9174
--- /dev/null
+++ b/BTCPayServer/Plugins/Emails/Services/IEmailSender.cs
@@ -0,0 +1,12 @@
+#nullable enable
+using System.Threading.Tasks;
+using MimeKit;
+
+namespace BTCPayServer.Plugins.Emails.Services;
+
+public interface IEmailSender
+{
+ void SendEmail(MailboxAddress email, string subject, string message);
+ void SendEmail(MailboxAddress[] email, MailboxAddress[] cc, MailboxAddress[] bcc, string subject, string message);
+ Task<EmailSettings?> GetEmailSettings();
+}
diff --git a/BTCPayServer/Plugins/Emails/Services/ServerEmailSender.cs b/BTCPayServer/Plugins/Emails/Services/ServerEmailSender.cs
new file mode 100644
index 0000000..5081ac3
--- /dev/null
+++ b/BTCPayServer/Plugins/Emails/Services/ServerEmailSender.cs
@@ -0,0 +1,15 @@
+#nullable enable
+using System.Threading.Tasks;
+using BTCPayServer.Logging;
+using BTCPayServer.Services;
+
+namespace BTCPayServer.Plugins.Emails.Services;
+
+class ServerEmailSender(SettingsRepository settingsRepository,
+ IBackgroundJobClient backgroundJobClient,
+ EventAggregator eventAggregator,
+ Logs logs) : EmailSender(backgroundJobClient, eventAggregator, logs)
+{
+ public override Task<EmailSettings?> GetEmailSettings()
+ => settingsRepository.GetSettingAsync<EmailSettings>();
+}
diff --git a/BTCPayServer/Plugins/Emails/Services/StoreEmailSender.cs b/BTCPayServer/Plugins/Emails/Services/StoreEmailSender.cs
new file mode 100644
index 0000000..28fd8b2
--- /dev/null
+++ b/BTCPayServer/Plugins/Emails/Services/StoreEmailSender.cs
@@ -0,0 +1,48 @@
+#nullable enable
+using System;
+using System.Threading.Tasks;
+using BTCPayServer.Data;
+using BTCPayServer.Logging;
+using BTCPayServer.Services;
+using BTCPayServer.Services.Stores;
+
+namespace BTCPayServer.Plugins.Emails.Services;
+
+class StoreEmailSender(
+ StoreRepository storeRepository,
+ EmailSender? fallback,
+ IBackgroundJobClient backgroundJobClient,
+ EventAggregator eventAggregator,
+ string storeId,
+ Logs logs)
+ : EmailSender(backgroundJobClient, eventAggregator, logs)
+{
+ public StoreRepository StoreRepository { get; } = storeRepository;
+ public EmailSender? FallbackSender { get; } = fallback;
+ public string StoreId { get; } = storeId ?? throw new ArgumentNullException(nameof(storeId));
+
+ public override async Task<EmailSettings?> GetEmailSettings()
+ {
+ var store = await StoreRepository.FindStore(StoreId);
+ if (store is null)
+ return null;
+ var emailSettings = GetCustomSettings(store);
+ if (emailSettings is not null)
+ return emailSettings;
+ if (FallbackSender is not null)
+ return await FallbackSender.GetEmailSettings();
+ return null;
+ }
+ public async Task<EmailSettings?> GetCustomSettings()
+ {
+ var store = await StoreRepository.FindStore(StoreId);
+ if (store is null)
+ return null;
+ return GetCustomSettings(store);
+ }
+ EmailSettings? GetCustomSettings(StoreData store)
+ {
+ var emailSettings = store.GetStoreBlob().EmailSettings;
+ return emailSettings?.IsComplete() is true ? emailSettings : null;
+ }
+}
diff --git a/BTCPayServer/Plugins/Emails/Views/EmailsViewModel.cs b/BTCPayServer/Plugins/Emails/Views/EmailsViewModel.cs
index 3a435c9..0374470 100644
--- a/BTCPayServer/Plugins/Emails/Views/EmailsViewModel.cs
+++ b/BTCPayServer/Plugins/Emails/Views/EmailsViewModel.cs
@@ -1,5 +1,5 @@
using System.ComponentModel.DataAnnotations;
-using BTCPayServer.Services.Mails;
+using BTCPayServer.Plugins.Emails.Services;
using BTCPayServer.Validation;
namespace BTCPayServer.Plugins.Emails.Views;
diff --git a/BTCPayServer/Plugins/Subscriptions/Controllers/UIOfferingController.cs b/BTCPayServer/Plugins/Subscriptions/Controllers/UIOfferingController.cs
index 5b69ecb..30e29d7 100644
--- a/BTCPayServer/Plugins/Subscriptions/Controllers/UIOfferingController.cs
+++ b/BTCPayServer/Plugins/Subscriptions/Controllers/UIOfferingController.cs
@@ -17,7 +17,7 @@ using BTCPayServer.Plugins.Emails.Views;
using BTCPayServer.Services;
using BTCPayServer.Services.Apps;
using BTCPayServer.Services.Invoices;
-using BTCPayServer.Services.Mails;
+using BTCPayServer.Plugins.Emails.Services;
using BTCPayServer.Views.UIStoreMembership;
using Dapper;
using Microsoft.AspNetCore.Authorization;
diff --git a/BTCPayServer/Plugins/Subscriptions/Views/UISubscriberPortal/SubscriptionsViewModel.cs b/BTCPayServer/Plugins/Subscriptions/Views/UISubscriberPortal/SubscriptionsViewModel.cs
index 18fcfb0..5b8e92b 100644
--- a/BTCPayServer/Plugins/Subscriptions/Views/UISubscriberPortal/SubscriptionsViewModel.cs
+++ b/BTCPayServer/Plugins/Subscriptions/Views/UISubscriberPortal/SubscriptionsViewModel.cs
@@ -1,13 +1,7 @@
-using System;
-using System.Collections;
-using System.Collections.Generic;
-using System.Globalization;
+using System.Collections.Generic;
using BTCPayServer.Data;
using BTCPayServer.Data.Subscriptions;
using BTCPayServer.Plugins.Emails.Views;
-using BTCPayServer.Services.Mails;
-using Microsoft.AspNetCore.Mvc.Razor;
-using Microsoft.AspNetCore.Mvc.Rendering;
namespace BTCPayServer.Views.UIStoreMembership;
diff --git a/BTCPayServer/Services/Mails/EmailSender.cs b/BTCPayServer/Services/Mails/EmailSender.cs
deleted file mode 100644
index 4f71794..0000000
--- a/BTCPayServer/Services/Mails/EmailSender.cs
+++ /dev/null
@@ -1,50 +0,0 @@
-#nullable enable
-using System;
-using System.Threading.Tasks;
-using BTCPayServer.Logging;
-using Microsoft.Extensions.Logging;
-using MimeKit;
-
-namespace BTCPayServer.Services.Mails
-{
- public abstract class EmailSender : IEmailSender
- {
- public EventAggregator EventAggregator { get; }
- public Logs Logs { get; }
-
- readonly IBackgroundJobClient _JobClient;
-
- public EmailSender(IBackgroundJobClient jobClient, EventAggregator eventAggregator, Logs logs)
- {
- EventAggregator = eventAggregator;
- Logs = logs;
- _JobClient = jobClient ?? throw new ArgumentNullException(nameof(jobClient));
- }
-
- public void SendEmail(MailboxAddress email, string subject, string message)
- {
- SendEmail(new[] { email }, Array.Empty<MailboxAddress>(), Array.Empty<MailboxAddress>(), subject, message);
- }
-
- public void SendEmail(MailboxAddress[] email, MailboxAddress[] cc, MailboxAddress[] bcc, string subject, string message)
- {
- _JobClient.Schedule(async cancellationToken =>
- {
- var emailSettings = await GetEmailSettings();
- if (emailSettings?.IsComplete() is not true)
- {
- Logs.Configuration.LogWarning("Should have sent email, but email settings are not configured");
- return;
- }
-
- using var smtp = await emailSettings.CreateSmtpClient();
- var mail = emailSettings.CreateMailMessage(email, cc, bcc, subject, message, true);
- var response = await smtp.SendAsync(mail, cancellationToken);
- await smtp.DisconnectAsync(true, cancellationToken);
- EventAggregator.Publish(new Events.EmailSentEvent(response, mail));
- }, TimeSpan.Zero);
- }
-
- public abstract Task<EmailSettings?> GetEmailSettings();
- }
-}
diff --git a/BTCPayServer/Services/Mails/EmailSenderFactory.cs b/BTCPayServer/Services/Mails/EmailSenderFactory.cs
deleted file mode 100644
index f12e1f2..0000000
--- a/BTCPayServer/Services/Mails/EmailSenderFactory.cs
+++ /dev/null
@@ -1,55 +0,0 @@
-#nullable enable
-using System.Threading.Tasks;
-using BTCPayServer.HostedServices;
-using BTCPayServer.Logging;
-using BTCPayServer.Services.Stores;
-
-namespace BTCPayServer.Services.Mails
-{
- public class EmailSenderFactory
- {
- public ISettingsAccessor<PoliciesSettings> PoliciesSettings { get; }
- public Logs Logs { get; }
-
- private readonly IBackgroundJobClient _jobClient;
- private readonly SettingsRepository _settingsRepository;
- private readonly EventAggregator _eventAggregator;
- private readonly StoreRepository _storeRepository;
-
- public EmailSenderFactory(IBackgroundJobClient jobClient,
- SettingsRepository settingsSettingsRepository,
- EventAggregator eventAggregator,
- ISettingsAccessor<PoliciesSettings> policiesSettings,
- StoreRepository storeRepository,
- Logs logs)
- {
- Logs = logs;
- _jobClient = jobClient;
- _settingsRepository = settingsSettingsRepository;
- _eventAggregator = eventAggregator;
- PoliciesSettings = policiesSettings;
- _storeRepository = storeRepository;
- }
-
- public Task<IEmailSender> GetEmailSender(string? storeId = null)
- {
- var serverSender = new ServerEmailSender(_settingsRepository, _jobClient, _eventAggregator, Logs);
- if (string.IsNullOrEmpty(storeId))
- return Task.FromResult<IEmailSender>(serverSender);
- return Task.FromResult<IEmailSender>(new StoreEmailSender(_storeRepository,
- !PoliciesSettings.Settings.DisableStoresToUseServerEmailSettings ? serverSender : null, _jobClient,
- _eventAggregator, storeId, Logs));
- }
-
- public async Task<bool> IsComplete(string? storeId = null)
- {
- var settings = await this.GetSettings(storeId);
- return settings?.IsComplete() is true;
- }
- public async Task<EmailSettings?> GetSettings(string? storeId = null)
- {
- var sender = await this.GetEmailSender(storeId);
- return await sender.GetEmailSettings();
- }
- }
-}
diff --git a/BTCPayServer/Services/Mails/EmailSettings.cs b/BTCPayServer/Services/Mails/EmailSettings.cs
deleted file mode 100644
index 17671fa..0000000
--- a/BTCPayServer/Services/Mails/EmailSettings.cs
+++ /dev/null
@@ -1,126 +0,0 @@
-using System.ComponentModel.DataAnnotations;
-using System.Threading;
-using System.Threading.Tasks;
-using BTCPayServer.Client.Models;
-using BTCPayServer.Validation;
-using MailKit.Net.Smtp;
-using Microsoft.AspNetCore.Mvc.ModelBinding;
-using MimeKit;
-using Newtonsoft.Json;
-
-namespace BTCPayServer.Services.Mails
-{
- /// <summary>
- /// Email Settings gets saved to database, and is used by both the server and store settings
- /// </summary>
- public class EmailSettings
- {
- public string Server { get; set; }
- public int? Port { get; set; }
- public string Login { get; set; }
- public string Password { get; set; }
- public string From { get; set; }
- public bool DisableCertificateCheck { get; set; }
- [JsonIgnore]
- public bool EnabledCertificateCheck
- {
- get => !DisableCertificateCheck;
- set { DisableCertificateCheck = !value; }
- }
-
- // Conversion functions for mapping to and from the client EmailSettingsData model
-#nullable enable
- public static EmailSettings FromData(EmailSettingsData data, string? existingPassword)
- => new()
- {
- Server = data.Server,
- Port = data.Port,
- Login = data.Login,
- // If login was provided but password was not, use the existing password from database
- Password = !string.IsNullOrEmpty(data.Login) && string.IsNullOrEmpty(data.Password) ?
- existingPassword : data.Password,
- From = data.From,
- DisableCertificateCheck = data.DisableCertificateCheck
- };
-
- public T ToData<T>() where T : EmailSettingsData, new()
- => new T()
- {
- Server = Server,
- Port = Port,
- Login = Login,
- PasswordSet = !string.IsNullOrEmpty(Password),
- From = From,
- DisableCertificateCheck = DisableCertificateCheck
- };
-#nullable restore
-
-
- //
- public bool IsComplete()
- {
- return MailboxAddressValidator.IsMailboxAddress(From)
- && !string.IsNullOrWhiteSpace(Server)
- && Port != null
- && !string.IsNullOrWhiteSpace(From)
- && MailboxAddressValidator.IsMailboxAddress(From);
- }
-
- public void Validate(string prefixKey, ModelStateDictionary modelState)
- {
- if (!string.IsNullOrEmpty(From) && !MailboxAddressValidator.IsMailboxAddress(From))
- {
- modelState.AddModelError($"{prefixKey}{nameof(From)}", MailboxAddressAttribute.ErrorMessageConst);
- }
- }
-
- public MimeMessage CreateMailMessage(MailboxAddress to, string subject, string message, bool isHtml) =>
- CreateMailMessage(new[] { to }, null, null, subject, message, isHtml);
- public MimeMessage CreateMailMessage(MailboxAddress[] to, MailboxAddress[] cc, MailboxAddress[] bcc, string subject, string message, bool isHtml)
- {
- var bodyBuilder = new BodyBuilder();
- if (isHtml)
- {
- bodyBuilder.HtmlBody = message;
- }
- else
- {
- bodyBuilder.TextBody = message;
- }
-
- var mm = new MimeMessage();
- mm.Body = bodyBuilder.ToMessageBody();
- mm.Subject = subject;
- mm.From.Add(MailboxAddressValidator.Parse(From));
- mm.To.AddRange(to);
- mm.Cc.AddRange(cc ?? System.Array.Empty<InternetAddress>());
- mm.Bcc.AddRange(bcc ?? System.Array.Empty<InternetAddress>());
- return mm;
- }
-
- public async Task<SmtpClient> CreateSmtpClient()
- {
- SmtpClient client = new SmtpClient();
- using var connectCancel = new CancellationTokenSource(10000);
- try
- {
- if (DisableCertificateCheck)
- {
- client.CheckCertificateRevocation = false;
-#pragma warning disable CA5359 // Do Not Disable Certificate Validation
- client.ServerCertificateValidationCallback = (s, c, h, e) => true;
-#pragma warning restore CA5359 // Do Not Disable Certificate Validation
- }
- await client.ConnectAsync(Server, Port.Value, MailKit.Security.SecureSocketOptions.Auto, connectCancel.Token);
- if ((client.Capabilities & SmtpCapabilities.Authentication) != 0)
- await client.AuthenticateAsync(Login ?? string.Empty, Password ?? string.Empty, connectCancel.Token);
- }
- catch
- {
- client.Dispose();
- throw;
- }
- return client;
- }
- }
-}
diff --git a/BTCPayServer/Services/Mails/IEmailSender.cs b/BTCPayServer/Services/Mails/IEmailSender.cs
deleted file mode 100644
index 7f822d0..0000000
--- a/BTCPayServer/Services/Mails/IEmailSender.cs
+++ /dev/null
@@ -1,13 +0,0 @@
-#nullable enable
-using System.Threading.Tasks;
-using MimeKit;
-
-namespace BTCPayServer.Services.Mails
-{
- public interface IEmailSender
- {
- void SendEmail(MailboxAddress email, string subject, string message);
- void SendEmail(MailboxAddress[] email, MailboxAddress[] cc, MailboxAddress[] bcc, string subject, string message);
- Task<EmailSettings?> GetEmailSettings();
- }
-}
diff --git a/BTCPayServer/Services/Mails/ServerEmailSender.cs b/BTCPayServer/Services/Mails/ServerEmailSender.cs
deleted file mode 100644
index e0afb25..0000000
--- a/BTCPayServer/Services/Mails/ServerEmailSender.cs
+++ /dev/null
@@ -1,25 +0,0 @@
-using System;
-using System.Threading.Tasks;
-using BTCPayServer.Logging;
-
-namespace BTCPayServer.Services.Mails
-{
- class ServerEmailSender : EmailSender
- {
- public ServerEmailSender(SettingsRepository settingsRepository,
- IBackgroundJobClient backgroundJobClient,
- EventAggregator eventAggregator,
- Logs logs) : base(backgroundJobClient, eventAggregator, logs)
- {
- ArgumentNullException.ThrowIfNull(settingsRepository);
- SettingsRepository = settingsRepository;
- }
-
- public SettingsRepository SettingsRepository { get; }
-
- public override Task<EmailSettings> GetEmailSettings()
- {
- return SettingsRepository.GetSettingAsync<EmailSettings>();
- }
- }
-}
diff --git a/BTCPayServer/Services/Mails/StoreEmailSender.cs b/BTCPayServer/Services/Mails/StoreEmailSender.cs
deleted file mode 100644
index 167cbf7..0000000
--- a/BTCPayServer/Services/Mails/StoreEmailSender.cs
+++ /dev/null
@@ -1,53 +0,0 @@
-#nullable enable
-using System;
-using System.Threading.Tasks;
-using BTCPayServer.Data;
-using BTCPayServer.Logging;
-using BTCPayServer.Services.Stores;
-
-namespace BTCPayServer.Services.Mails
-{
- class StoreEmailSender : EmailSender
- {
- public StoreEmailSender(StoreRepository storeRepository,
- EmailSender? fallback,
- IBackgroundJobClient backgroundJobClient,
- EventAggregator eventAggregator,
- string storeId,
- Logs logs) : base(backgroundJobClient, eventAggregator, logs)
- {
- StoreId = storeId ?? throw new ArgumentNullException(nameof(storeId));
- StoreRepository = storeRepository;
- FallbackSender = fallback;
- }
-
- public StoreRepository StoreRepository { get; }
- public EmailSender? FallbackSender { get; }
- public string StoreId { get; }
-
- public override async Task<EmailSettings?> GetEmailSettings()
- {
- var store = await StoreRepository.FindStore(StoreId);
- if (store is null)
- return null;
- var emailSettings = GetCustomSettings(store);
- if (emailSettings is not null)
- return emailSettings;
- if (FallbackSender is not null)
- return await FallbackSender.GetEmailSettings();
- return null;
- }
- public async Task<EmailSettings?> GetCustomSettings()
- {
- var store = await StoreRepository.FindStore(StoreId);
- if (store is null)
- return null;
- return GetCustomSettings(store);
- }
- EmailSettings? GetCustomSettings(StoreData store)
- {
- var emailSettings = store.GetStoreBlob().EmailSettings;
- return emailSettings?.IsComplete() is true ? emailSettings : null;
- }
- }
-}
diff --git a/BTCPayServer/Views/UIAccount/ForgotPassword.cshtml b/BTCPayServer/Views/UIAccount/ForgotPassword.cshtml
index d5f50c3..3a8836b 100644
--- a/BTCPayServer/Views/UIAccount/ForgotPassword.cshtml
+++ b/BTCPayServer/Views/UIAccount/ForgotPassword.cshtml
@@ -1,6 +1,4 @@
-@using BTCPayServer.Services
-@using BTCPayServer.Services.Mails
-@using Microsoft.AspNetCore.Mvc.TagHelpers
+@using BTCPayServer.Plugins.Emails.Services
@model ForgotPasswordViewModel
@inject EmailSenderFactory EmailSenderFactory
@{
diff --git a/BTCPayServer/Views/UIServer/Policies.cshtml b/BTCPayServer/Views/UIServer/Policies.cshtml
index c9cfd94..bb0d06d 100644
--- a/BTCPayServer/Views/UIServer/Policies.cshtml
+++ b/BTCPayServer/Views/UIServer/Policies.cshtml
@@ -1,5 +1,5 @@
@using BTCPayServer.Services
-@using BTCPayServer.Services.Mails;
+@using BTCPayServer.Plugins.Emails.Services
@model BTCPayServer.Services.PoliciesSettings
@inject EmailSenderFactory EmailSenderFactory
@inject TransactionLinkProviders TransactionLinkProviders
Why this scored 12/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.