From 17e8552c8bec437e5d71573115a85a11c71e3507 Mon Sep 17 00:00:00 2001 From: Tino Hager Date: Tue, 13 Aug 2024 10:21:52 +0200 Subject: [PATCH 01/15] Add build script --- .github/workflows/build.yml | 41 +++++++++++++++++++ Examples/SampleApp/SampleApp.csproj | 4 +- Examples/WorkerService/WorkerService.csproj | 4 +- .../SmtpServer.Benchmarks.csproj | 4 +- Src/SmtpServer.Tests/SmtpServer.Tests.csproj | 8 ++-- Src/SmtpServer.Tests/SmtpServerTests.cs | 8 ++-- 6 files changed, 55 insertions(+), 14 deletions(-) create mode 100644 .github/workflows/build.yml diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..a83b330 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,41 @@ +name: Build and Publish + +on: + workflow_dispatch: # Allow running the workflow manually from the GitHub UI + push: + paths: + - 'Src/**' + - '.github/workflows/**' + branches: [ master ] + +jobs: + build: + + runs-on: ubuntu-latest + + permissions: + contents: read + packages: write + + steps: + - uses: actions/checkout@v4 + - name: Setup .NET 8.0 + uses: actions/setup-dotnet@v4 + with: + dotnet-version: 8.0.x + - name: Restore dependencies + working-directory: ./Src + run: dotnet restore + - name: Build + working-directory: ./Src + run: dotnet build --configuration Release --no-restore + - name: Test + working-directory: ./Src + run: | + dotnet test --configuration Release --no-restore --no-build --verbosity normal + #- name: Create NuGet packages + # run: | + # dotnet pack --configuration Release --output $GITHUB_WORKSPACE/out Src/SmtpServer/SmtpServer.csproj + #- name: Push NuGet packages + # run: | + # dotnet nuget push $GITHUB_WORKSPACE/out/*.nupkg --source https://api.nuget.org/v3/index.json --api-key ${{secrets.NUGET_TOKEN}} --skip-duplicate --no-symbols \ No newline at end of file diff --git a/Examples/SampleApp/SampleApp.csproj b/Examples/SampleApp/SampleApp.csproj index f84b784..77cfe22 100644 --- a/Examples/SampleApp/SampleApp.csproj +++ b/Examples/SampleApp/SampleApp.csproj @@ -2,7 +2,7 @@ Exe - net5.0 + net8.0 @@ -15,7 +15,7 @@ - + diff --git a/Examples/WorkerService/WorkerService.csproj b/Examples/WorkerService/WorkerService.csproj index e5dfbb7..4aeeed1 100644 --- a/Examples/WorkerService/WorkerService.csproj +++ b/Examples/WorkerService/WorkerService.csproj @@ -1,12 +1,12 @@ - net5.0 + net8.0 dotnet-WorkerService-1397719A-187C-45F4-8DB3-2427A449DD89 - + diff --git a/Src/SmtpServer.Benchmarks/SmtpServer.Benchmarks.csproj b/Src/SmtpServer.Benchmarks/SmtpServer.Benchmarks.csproj index 3a94f4b..7dd835a 100644 --- a/Src/SmtpServer.Benchmarks/SmtpServer.Benchmarks.csproj +++ b/Src/SmtpServer.Benchmarks/SmtpServer.Benchmarks.csproj @@ -2,7 +2,7 @@ Exe - net5.0 + net8.0 @@ -20,7 +20,7 @@ - + diff --git a/Src/SmtpServer.Tests/SmtpServer.Tests.csproj b/Src/SmtpServer.Tests/SmtpServer.Tests.csproj index 6e8b39f..38bb095 100644 --- a/Src/SmtpServer.Tests/SmtpServer.Tests.csproj +++ b/Src/SmtpServer.Tests/SmtpServer.Tests.csproj @@ -1,14 +1,14 @@  - net5.0 + net8.0 - - - + + + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/Src/SmtpServer.Tests/SmtpServerTests.cs b/Src/SmtpServer.Tests/SmtpServerTests.cs index ff7a60f..9753d68 100644 --- a/Src/SmtpServer.Tests/SmtpServerTests.cs +++ b/Src/SmtpServer.Tests/SmtpServerTests.cs @@ -48,7 +48,7 @@ public void CanReceiveMessage() [Theory] [InlineData("Assunto teste acento çãõáéíóú", "utf-8")] - [InlineData("שלום שלום שלום", "windows-1255")] + [InlineData("שלום שלום שלום", "windows-1255", Skip = "fix this currently not working with github build job")] public void CanReceiveUnicodeMimeMessage(string text, string charset) { using (CreateServer()) @@ -242,7 +242,7 @@ public void DoesNotSecureTheSessionWhenCertificateIsEmpty() ServicePointManager.ServerCertificateValidationCallback = null; } - [Fact] + [Fact(Skip = "fix this currently not working with github build job")] public void SecuresTheSessionWhenCertificateIsSupplied() { ServicePointManager.ServerCertificateValidationCallback = IgnoreCertificateValidationFailureForTestingOnly; @@ -271,7 +271,7 @@ public void SecuresTheSessionWhenCertificateIsSupplied() ServicePointManager.ServerCertificateValidationCallback = null; } - [Fact] + [Fact(Skip = "fix this currently not working with github build job")] public void SecuresTheSessionByDefault() { ServicePointManager.ServerCertificateValidationCallback = IgnoreCertificateValidationFailureForTestingOnly; @@ -300,7 +300,7 @@ public void SecuresTheSessionByDefault() ServicePointManager.ServerCertificateValidationCallback = null; } - [Fact] + [Fact(Skip = "fix this currently not working with github build job")] public void ServerCanBeSecuredAndAuthenticated() { var userAuthenticator = new DelegatingUserAuthenticator((user, password) => true); From e3792afc0fa2a14f58e022dcd322f94b5ec5169f Mon Sep 17 00:00:00 2001 From: Tino Hager Date: Thu, 15 Aug 2024 22:09:48 +0200 Subject: [PATCH 02/15] reactivate one unit test --- Src/SmtpServer.Tests/SmtpServerTests.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Src/SmtpServer.Tests/SmtpServerTests.cs b/Src/SmtpServer.Tests/SmtpServerTests.cs index 9753d68..1ab9ee9 100644 --- a/Src/SmtpServer.Tests/SmtpServerTests.cs +++ b/Src/SmtpServer.Tests/SmtpServerTests.cs @@ -16,6 +16,8 @@ using SmtpServer.Protocol; using SmtpServer.Storage; using SmtpResponse = SmtpServer.Protocol.SmtpResponse; +using System.Text; +using SmtpServer.Tests.Helpers; namespace SmtpServer.Tests { @@ -48,9 +50,11 @@ public void CanReceiveMessage() [Theory] [InlineData("Assunto teste acento çãõáéíóú", "utf-8")] - [InlineData("שלום שלום שלום", "windows-1255", Skip = "fix this currently not working with github build job")] + [InlineData("שלום שלום שלום", "windows-1255")] public void CanReceiveUnicodeMimeMessage(string text, string charset) { + Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); + using (CreateServer()) { // act From 6c7f1fd133b23d4a2c31469fb11bb55bd82f6bcb Mon Sep 17 00:00:00 2001 From: Tino Hager Date: Thu, 15 Aug 2024 22:23:08 +0200 Subject: [PATCH 03/15] Update SmtpServerTests.cs --- Src/SmtpServer.Tests/SmtpServerTests.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/Src/SmtpServer.Tests/SmtpServerTests.cs b/Src/SmtpServer.Tests/SmtpServerTests.cs index 1ab9ee9..41f9320 100644 --- a/Src/SmtpServer.Tests/SmtpServerTests.cs +++ b/Src/SmtpServer.Tests/SmtpServerTests.cs @@ -17,7 +17,6 @@ using SmtpServer.Storage; using SmtpResponse = SmtpServer.Protocol.SmtpResponse; using System.Text; -using SmtpServer.Tests.Helpers; namespace SmtpServer.Tests { From 32ce1fac650ce2fc21fe321dc8061de8ea4d5967 Mon Sep 17 00:00:00 2001 From: Tino Hager Date: Mon, 19 Aug 2024 22:45:32 +0200 Subject: [PATCH 04/15] Reactivate all unit tests --- Src/SmtpServer.Tests/MailClient.cs | 7 +- Src/SmtpServer.Tests/SmtpServerTests.cs | 171 ++++++++++++------------ 2 files changed, 93 insertions(+), 85 deletions(-) diff --git a/Src/SmtpServer.Tests/MailClient.cs b/Src/SmtpServer.Tests/MailClient.cs index b1ee5b9..89653f5 100644 --- a/Src/SmtpServer.Tests/MailClient.cs +++ b/Src/SmtpServer.Tests/MailClient.cs @@ -1,8 +1,8 @@ -using System.Threading; -using MailKit.Net.Smtp; +using MailKit.Net.Smtp; using MailKit.Security; using MimeKit; using MimeKit.Text; +using System.Threading; namespace SmtpServer.Tests { @@ -49,7 +49,8 @@ public static MimeMessage Message( public static SmtpClientEx Client(string host = "localhost", int port = 9025, SecureSocketOptions options = SecureSocketOptions.Auto) { var client = new SmtpClientEx(); - + client.ServerCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) => true; + client.Connected += (sender, args) => { diff --git a/Src/SmtpServer.Tests/SmtpServerTests.cs b/Src/SmtpServer.Tests/SmtpServerTests.cs index 41f9320..c624990 100644 --- a/Src/SmtpServer.Tests/SmtpServerTests.cs +++ b/Src/SmtpServer.Tests/SmtpServerTests.cs @@ -1,22 +1,22 @@ -using System; +using MailKit; +using SmtpServer.Authentication; +using SmtpServer.ComponentModel; +using SmtpServer.Mail; +using SmtpServer.Net; +using SmtpServer.Protocol; +using SmtpServer.Storage; +using SmtpServer.Tests.Mocks; +using System; using System.IO; using System.Net; -using System.Net.Security; using System.Security.Authentication; +using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; +using System.Text; using System.Threading; using System.Threading.Tasks; -using MailKit; -using SmtpServer.Mail; -using SmtpServer.Tests.Mocks; using Xunit; -using SmtpServer.Authentication; -using SmtpServer.ComponentModel; -using SmtpServer.Net; -using SmtpServer.Protocol; -using SmtpServer.Storage; using SmtpResponse = SmtpServer.Protocol.SmtpResponse; -using System.Text; namespace SmtpServer.Tests { @@ -139,7 +139,8 @@ public void CanReceiveBccInMessageTransaction() } } - [Fact(Skip = "Command timeout wont work properly until https://github.com/dotnet/corefx/issues/15033")] + //[Fact(Skip = "Command timeout wont work properly until https://github.com/dotnet/corefx/issues/15033")] + [Fact] public void WillTimeoutWaitingForCommand() { using (CreateServer(c => c.CommandWaitTimeout(TimeSpan.FromSeconds(1)))) @@ -245,98 +246,83 @@ public void DoesNotSecureTheSessionWhenCertificateIsEmpty() ServicePointManager.ServerCertificateValidationCallback = null; } - [Fact(Skip = "fix this currently not working with github build job")] + [Fact] public void SecuresTheSessionWhenCertificateIsSupplied() { - ServicePointManager.ServerCertificateValidationCallback = IgnoreCertificateValidationFailureForTestingOnly; + using var disposable = CreateServer(options => options.Certificate(CreateCertificate())); - using (var disposable = CreateServer(options => options.Certificate(CreateCertificate()))) - { - var isSecure = false; - var sessionCreatedHandler = new EventHandler( - delegate (object sender, SessionEventArgs args) + var isSecure = false; + var sessionCreatedHandler = new EventHandler( + delegate (object sender, SessionEventArgs args) + { + args.Context.CommandExecuted += (_, commandArgs) => { - args.Context.CommandExecuted += (_, commandArgs) => - { - isSecure = commandArgs.Context.Pipe.IsSecure; - }; - }); + isSecure = commandArgs.Context.Pipe.IsSecure; + }; + }); - disposable.Server.SessionCreated += sessionCreatedHandler; - - MailClient.Send(); + disposable.Server.SessionCreated += sessionCreatedHandler; - disposable.Server.SessionCreated -= sessionCreatedHandler; - - Assert.True(isSecure); - } + MailClient.Send(); - ServicePointManager.ServerCertificateValidationCallback = null; + disposable.Server.SessionCreated -= sessionCreatedHandler; + + Assert.True(isSecure); } - [Fact(Skip = "fix this currently not working with github build job")] + [Fact] public void SecuresTheSessionByDefault() { - ServicePointManager.ServerCertificateValidationCallback = IgnoreCertificateValidationFailureForTestingOnly; + using var disposable = CreateServer(endpoint => endpoint.IsSecure(true).Certificate(CreateCertificate())); - using (var disposable = CreateServer(endpoint => endpoint.IsSecure(true).Certificate(CreateCertificate()))) - { - var isSecure = false; - var sessionCreatedHandler = new EventHandler( - delegate (object sender, SessionEventArgs args) + var isSecure = false; + var sessionCreatedHandler = new EventHandler( + delegate (object sender, SessionEventArgs args) + { + args.Context.CommandExecuted += (_, commandArgs) => { - args.Context.CommandExecuted += (_, commandArgs) => - { - isSecure = commandArgs.Context.Pipe.IsSecure; - }; - }); + isSecure = commandArgs.Context.Pipe.IsSecure; + }; + }); - disposable.Server.SessionCreated += sessionCreatedHandler; - - MailClient.NoOp(MailKit.Security.SecureSocketOptions.SslOnConnect); + disposable.Server.SessionCreated += sessionCreatedHandler; - disposable.Server.SessionCreated -= sessionCreatedHandler; - - Assert.True(isSecure); - } + MailClient.NoOp(MailKit.Security.SecureSocketOptions.SslOnConnect); - ServicePointManager.ServerCertificateValidationCallback = null; + disposable.Server.SessionCreated -= sessionCreatedHandler; + + Assert.True(isSecure); } - [Fact(Skip = "fix this currently not working with github build job")] + [Fact] public void ServerCanBeSecuredAndAuthenticated() { var userAuthenticator = new DelegatingUserAuthenticator((user, password) => true); - ServicePointManager.ServerCertificateValidationCallback = IgnoreCertificateValidationFailureForTestingOnly; - - using (var disposable = CreateServer( + using var disposable = CreateServer( endpoint => endpoint.AllowUnsecureAuthentication(true).Certificate(CreateCertificate()).SupportedSslProtocols(SslProtocols.Tls12), - services => services.Add(userAuthenticator))) - { - var isSecure = false; - ISessionContext sessionContext = null; - var sessionCreatedHandler = new EventHandler( - delegate (object sender, SessionEventArgs args) - { - sessionContext = args.Context; - sessionContext.CommandExecuted += (_, commandArgs) => - { - isSecure = commandArgs.Context.Pipe.IsSecure; - }; - }); + services => services.Add(userAuthenticator)); - disposable.Server.SessionCreated += sessionCreatedHandler; + var isSecure = false; + ISessionContext sessionContext = null; + var sessionCreatedHandler = new EventHandler( + delegate (object sender, SessionEventArgs args) + { + sessionContext = args.Context; + sessionContext.CommandExecuted += (_, commandArgs) => + { + isSecure = commandArgs.Context.Pipe.IsSecure; + }; + }); - MailClient.Send(user: "user", password: "password"); + disposable.Server.SessionCreated += sessionCreatedHandler; - disposable.Server.SessionCreated -= sessionCreatedHandler; + MailClient.Send(user: "user", password: "password"); - Assert.True(isSecure); - Assert.True(sessionContext.Authentication.IsAuthenticated); - } + disposable.Server.SessionCreated -= sessionCreatedHandler; - ServicePointManager.ServerCertificateValidationCallback = null; + Assert.True(isSecure); + Assert.True(sessionContext.Authentication.IsAuthenticated); } [Fact] @@ -359,17 +345,38 @@ public void EndpointListenerWillRaiseEndPointEvents() Assert.True(stopped); } - public static bool IgnoreCertificateValidationFailureForTestingOnly(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors) + public static X509Certificate2 CreateSelfSignedCertificate(string subjectName) { - return true; + var validityPeriodInYears = 1; + + using RSA rsa = RSA.Create(2048); // 2048-Bit Key + + var certificateRequest = new CertificateRequest( + $"CN={subjectName}", // Common Name (CN) + rsa, + HashAlgorithmName.SHA256, // Hash-Algorithmus + RSASignaturePadding.Pkcs1 // Padding Schema + ); + + certificateRequest.CertificateExtensions.Add( + new X509SubjectKeyIdentifierExtension(certificateRequest.PublicKey, false) + ); + + certificateRequest.CertificateExtensions.Add( + new X509BasicConstraintsExtension(true, false, 0, true) + ); + + DateTimeOffset notBefore = DateTimeOffset.UtcNow; + DateTimeOffset notAfter = notBefore.AddYears(validityPeriodInYears); + + X509Certificate2 certificate = certificateRequest.CreateSelfSigned(notBefore, notAfter); + + return new X509Certificate2(certificate.Export(X509ContentType.Pfx)); } public static X509Certificate2 CreateCertificate() { - var certificate = File.ReadAllBytes(@"C:\Users\caino\Dropbox\Documents\Cain\Programming\SmtpServer\SmtpServer.pfx"); - var password = File.ReadAllText(@"C:\Users\caino\Dropbox\Documents\Cain\Programming\SmtpServer\SmtpServerPassword.txt"); - - return new X509Certificate2(certificate, password); + return CreateSelfSignedCertificate("localhost"); } /// From 677a295a57b569c317ef9ff915417081e84b5b84 Mon Sep 17 00:00:00 2001 From: Tino Hager Date: Tue, 20 Aug 2024 08:32:44 +0200 Subject: [PATCH 05/15] Update SmtpServerTests.cs --- Src/SmtpServer.Tests/SmtpServerTests.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Src/SmtpServer.Tests/SmtpServerTests.cs b/Src/SmtpServer.Tests/SmtpServerTests.cs index c624990..1c62dc7 100644 --- a/Src/SmtpServer.Tests/SmtpServerTests.cs +++ b/Src/SmtpServer.Tests/SmtpServerTests.cs @@ -139,8 +139,7 @@ public void CanReceiveBccInMessageTransaction() } } - //[Fact(Skip = "Command timeout wont work properly until https://github.com/dotnet/corefx/issues/15033")] - [Fact] + [Fact(Skip = "Command timeout wont work properly until https://github.com/dotnet/corefx/issues/15033")] public void WillTimeoutWaitingForCommand() { using (CreateServer(c => c.CommandWaitTimeout(TimeSpan.FromSeconds(1)))) From 85d75a9e62e48dc94c431da21ccae111ad4a0bbe Mon Sep 17 00:00:00 2001 From: Tino Hager Date: Thu, 29 Aug 2024 23:29:26 +0200 Subject: [PATCH 06/15] Add Smtp session Timeout --- Src/SmtpServer/EndpointDefinitionBuilder.cs | 40 +++++++-------------- Src/SmtpServer/IEndpointDefinition.cs | 5 ++- Src/SmtpServer/Net/EndpointListener.cs | 1 - Src/SmtpServer/SmtpSession.cs | 13 ++++--- 4 files changed, 23 insertions(+), 36 deletions(-) diff --git a/Src/SmtpServer/EndpointDefinitionBuilder.cs b/Src/SmtpServer/EndpointDefinitionBuilder.cs index 6bb7672..a6a222b 100644 --- a/Src/SmtpServer/EndpointDefinitionBuilder.cs +++ b/Src/SmtpServer/EndpointDefinitionBuilder.cs @@ -18,7 +18,7 @@ public IEndpointDefinition Build() { var definition = new EndpointDefinition { - ReadTimeout = TimeSpan.FromMinutes(2), + SessionTimeout = TimeSpan.FromMinutes(2), SupportedSslProtocols = SslProtocols.Tls12, }; @@ -100,13 +100,13 @@ public EndpointDefinitionBuilder AllowUnsecureAuthentication(bool value = true) } /// - /// Sets the read timeout to apply to stream operations. + /// Sets the session timeout to apply to the session. /// - /// The timeout value to apply to read operations. + /// The timeout value to apply to the Smtp session. /// A EndpointDefinitionBuilder to continue building on. - public EndpointDefinitionBuilder ReadTimeout(TimeSpan value) + public EndpointDefinitionBuilder SessionTimeout(TimeSpan value) { - _setters.Add(options => options.ReadTimeout = value); + _setters.Add(options => options.SessionTimeout = value); return this; } @@ -149,39 +149,25 @@ public EndpointDefinitionBuilder SupportedSslProtocols(SslProtocols value) internal sealed class EndpointDefinition : IEndpointDefinition { - /// - /// The IP endpoint to listen on. - /// + /// public IPEndPoint Endpoint { get; set; } - /// - /// Indicates whether the endpoint is secure by default. - /// + /// public bool IsSecure { get; set; } - /// - /// Gets a value indicating whether the client must authenticate in order to proceed. - /// + /// public bool AuthenticationRequired { get; set; } - /// - /// Gets a value indicating whether authentication should be allowed on an unsecure session. - /// + /// public bool AllowUnsecureAuthentication { get; set; } - /// - /// The timeout on each individual buffer read. - /// - public TimeSpan ReadTimeout { get; set; } + /// + public TimeSpan SessionTimeout { get; set; } - /// - /// Gets the Server Certificate factory to use when starting a TLS session. - /// + /// public ICertificateFactory CertificateFactory { get; set; } - /// - /// The supported SSL protocols. - /// + /// public SslProtocols SupportedSslProtocols { get; set; } } diff --git a/Src/SmtpServer/IEndpointDefinition.cs b/Src/SmtpServer/IEndpointDefinition.cs index b21d289..2030731 100644 --- a/Src/SmtpServer/IEndpointDefinition.cs +++ b/Src/SmtpServer/IEndpointDefinition.cs @@ -1,7 +1,6 @@ using System; using System.Net; using System.Security.Authentication; -using System.Security.Cryptography.X509Certificates; namespace SmtpServer { @@ -28,9 +27,9 @@ public interface IEndpointDefinition bool AllowUnsecureAuthentication { get; } /// - /// The timeout on each individual buffer read. + /// The timeout of an Smtp session. /// - TimeSpan ReadTimeout { get; } + TimeSpan SessionTimeout { get; } /// /// Gets the Server Certificate factory to use when starting a TLS session. diff --git a/Src/SmtpServer/Net/EndpointListener.cs b/Src/SmtpServer/Net/EndpointListener.cs index 7316453..69e323e 100644 --- a/Src/SmtpServer/Net/EndpointListener.cs +++ b/Src/SmtpServer/Net/EndpointListener.cs @@ -43,7 +43,6 @@ public async Task GetPipeAsync(ISessionContext context, Ca context.Properties.Add(RemoteEndPointKey, tcpClient.Client.RemoteEndPoint); var stream = tcpClient.GetStream(); - stream.ReadTimeout = (int)_endpointDefinition.ReadTimeout.TotalMilliseconds; return new SecurableDuplexPipe(stream, () => { diff --git a/Src/SmtpServer/SmtpSession.cs b/Src/SmtpServer/SmtpSession.cs index 6f3a303..e976791 100644 --- a/Src/SmtpServer/SmtpSession.cs +++ b/Src/SmtpServer/SmtpSession.cs @@ -56,13 +56,16 @@ internal async Task RunAsync(CancellationToken cancellationToken) /// A task which asynchronously performs the execution. async Task ExecuteAsync(SmtpSessionContext context, CancellationToken cancellationToken) { + using var sessionReadTimeoutCancellationTokenSource = new CancellationTokenSource(context.EndpointDefinition.SessionTimeout); + using var linkedTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, sessionReadTimeoutCancellationTokenSource.Token); + var retries = _context.ServerOptions.MaxRetryCount; - while (retries-- > 0 && context.IsQuitRequested == false && cancellationToken.IsCancellationRequested == false) + while (retries-- > 0 && context.IsQuitRequested == false && linkedTokenSource.IsCancellationRequested == false) { try { - var command = await ReadCommandAsync(context, cancellationToken).ConfigureAwait(false); + var command = await ReadCommandAsync(context, linkedTokenSource.Token).ConfigureAwait(false); if (command == null) { @@ -74,7 +77,7 @@ async Task ExecuteAsync(SmtpSessionContext context, CancellationToken cancellati throw new SmtpResponseException(errorResponse); } - if (await ExecuteAsync(command, context, cancellationToken).ConfigureAwait(false)) + if (await ExecuteAsync(command, context, linkedTokenSource.Token).ConfigureAwait(false)) { _stateMachine.Transition(context); } @@ -85,7 +88,7 @@ async Task ExecuteAsync(SmtpSessionContext context, CancellationToken cancellati { context.RaiseResponseException(responseException); - await context.Pipe.Output.WriteReplyAsync(responseException.Response, cancellationToken).ConfigureAwait(false); + await context.Pipe.Output.WriteReplyAsync(responseException.Response, linkedTokenSource.Token).ConfigureAwait(false); context.IsQuitRequested = true; } @@ -95,7 +98,7 @@ async Task ExecuteAsync(SmtpSessionContext context, CancellationToken cancellati var response = CreateErrorResponse(responseException.Response, retries); - await context.Pipe.Output.WriteReplyAsync(response, cancellationToken).ConfigureAwait(false); + await context.Pipe.Output.WriteReplyAsync(response, linkedTokenSource.Token).ConfigureAwait(false); } catch (OperationCanceledException) { From ffe91bce0aa5dab868ea5746f90974de1a8735ba Mon Sep 17 00:00:00 2001 From: Tino Hager Date: Fri, 30 Aug 2024 11:25:05 +0200 Subject: [PATCH 07/15] Add unit test for session timeout --- Src/SmtpServer.Tests/RawSmtpClient.cs | 71 +++++++++++++++++++++++ Src/SmtpServer.Tests/SmtpServerTests.cs | 75 +++++++++++++++++++++++++ Src/SmtpServer/Net/EndpointListener.cs | 10 +++- 3 files changed, 154 insertions(+), 2 deletions(-) create mode 100644 Src/SmtpServer.Tests/RawSmtpClient.cs diff --git a/Src/SmtpServer.Tests/RawSmtpClient.cs b/Src/SmtpServer.Tests/RawSmtpClient.cs new file mode 100644 index 0000000..b0f2eb3 --- /dev/null +++ b/Src/SmtpServer.Tests/RawSmtpClient.cs @@ -0,0 +1,71 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Net; +using System.Net.Sockets; +using System.Text; +using System.Threading.Tasks; + +namespace SmtpServer.Tests +{ + internal class RawSmtpClient : IDisposable + { + private readonly TcpClient _tcpClient; + private NetworkStream _networkStream; + + internal RawSmtpClient(string host, int port) + { + _tcpClient = new TcpClient(); + } + + public void Dispose() + { + _networkStream?.Dispose(); + _tcpClient.Dispose(); + } + + internal async Task ConnectAsync() + { + await _tcpClient.ConnectAsync(new IPEndPoint(IPAddress.Parse("127.0.0.1"), 9025)); + _networkStream = _tcpClient.GetStream(); + + var greetingResponse = await WaitForDataAsync(); + if (greetingResponse.StartsWith("220")) + { + return true; + } + + return false; + } + + internal async Task SendCommandAsync(string command) + { + var commandData = Encoding.UTF8.GetBytes($"{command}\r\n"); + + await _networkStream.WriteAsync(commandData, 0, commandData.Length); + return await WaitForDataAsync(); + } + + internal async Task SendDataAsync(string data) + { + var mailData = Encoding.UTF8.GetBytes(data); + + await _networkStream.WriteAsync(mailData, 0, mailData.Length); + } + + internal async Task WaitForDataAsync() + { + var buffer = new byte[1024]; + int bytesRead; + + while ((bytesRead = await _networkStream.ReadAsync(buffer, 0, buffer.Length)) > 0) + { + var receivedData = Encoding.UTF8.GetString(buffer, 0, bytesRead); + return receivedData; + } + + return null; + } + } +} diff --git a/Src/SmtpServer.Tests/SmtpServerTests.cs b/Src/SmtpServer.Tests/SmtpServerTests.cs index 1c62dc7..12399b4 100644 --- a/Src/SmtpServer.Tests/SmtpServerTests.cs +++ b/Src/SmtpServer.Tests/SmtpServerTests.cs @@ -7,8 +7,10 @@ using SmtpServer.Storage; using SmtpServer.Tests.Mocks; using System; +using System.Diagnostics; using System.IO; using System.Net; +using System.Net.Sockets; using System.Security.Authentication; using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; @@ -159,6 +161,79 @@ public void WillTimeoutWaitingForCommand() } } + [Fact] + public async Task WillSessionTimeoutDuringMailDataTransmission() + { + var sessionTimeout = TimeSpan.FromSeconds(5); + var commandWaitTimeout = TimeSpan.FromSeconds(1); + + using var disposable = CreateServer( + serverOptions => serverOptions.CommandWaitTimeout(commandWaitTimeout), + endpointDefinition => endpointDefinition.SessionTimeout(sessionTimeout)); + + var stopwatch = new Stopwatch(); + stopwatch.Start(); + + using var rawSmtpClient = new RawSmtpClient("127.0.0.1", 9025); + await rawSmtpClient.ConnectAsync(); + + var response = await rawSmtpClient.SendCommandAsync("helo test"); + if (!response.StartsWith("250")) + { + Assert.Fail("helo command not successful"); + } + + response = await rawSmtpClient.SendCommandAsync("mail from:"); + if (!response.StartsWith("250")) + { + Assert.Fail("mail from command not successful"); + } + + response = await rawSmtpClient.SendCommandAsync("rcpt to:"); + if (!response.StartsWith("250")) + { + Assert.Fail("rcpt to command not successful"); + } + + response = await rawSmtpClient.SendCommandAsync("data"); + if (!response.StartsWith("354")) + { + Assert.Fail("data command not successful"); + } + + string smtpResponse = null; + + _ = Task.Run (async() => + { + smtpResponse = await rawSmtpClient.WaitForDataAsync(); + }); + + var isSessionCancelled = false; + + try + { + for (var i = 0; i < 1000; i++) + { + await rawSmtpClient.SendDataAsync("some text part "); + await Task.Delay(100); + } + } + catch (IOException) + { + isSessionCancelled = true; + stopwatch.Stop(); + } + catch (Exception exception) + { + Assert.Fail($"Wrong exception type {exception.GetType()}"); + } + + Assert.True(isSessionCancelled, "Smtp session is not cancelled"); + Assert.Equal("554 \r\n221 The session has be cancelled.\r\n", smtpResponse); + + Assert.True(stopwatch.Elapsed > sessionTimeout, "SessionTimeout not reached"); + } + [Fact] public void CanReturnSmtpResponseException_DoesNotQuit() { diff --git a/Src/SmtpServer/Net/EndpointListener.cs b/Src/SmtpServer/Net/EndpointListener.cs index 69e323e..74f0ef5 100644 --- a/Src/SmtpServer/Net/EndpointListener.cs +++ b/Src/SmtpServer/Net/EndpointListener.cs @@ -46,8 +46,14 @@ public async Task GetPipeAsync(ISessionContext context, Ca return new SecurableDuplexPipe(stream, () => { - tcpClient.Close(); - tcpClient.Dispose(); + try + { + tcpClient.Close(); + tcpClient.Dispose(); + } + catch (Exception) + { + } }); } From 86972e7abd97aa7e7f3151b703e59aad18a34a42 Mon Sep 17 00:00:00 2001 From: Tino Hager Date: Fri, 30 Aug 2024 11:26:14 +0200 Subject: [PATCH 08/15] namespace cleanup --- Src/SmtpServer.Tests/RawSmtpClient.cs | 3 --- Src/SmtpServer.Tests/SmtpServerTests.cs | 1 - 2 files changed, 4 deletions(-) diff --git a/Src/SmtpServer.Tests/RawSmtpClient.cs b/Src/SmtpServer.Tests/RawSmtpClient.cs index b0f2eb3..4e3211d 100644 --- a/Src/SmtpServer.Tests/RawSmtpClient.cs +++ b/Src/SmtpServer.Tests/RawSmtpClient.cs @@ -1,7 +1,4 @@ using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; using System.Net; using System.Net.Sockets; using System.Text; diff --git a/Src/SmtpServer.Tests/SmtpServerTests.cs b/Src/SmtpServer.Tests/SmtpServerTests.cs index 12399b4..c698723 100644 --- a/Src/SmtpServer.Tests/SmtpServerTests.cs +++ b/Src/SmtpServer.Tests/SmtpServerTests.cs @@ -10,7 +10,6 @@ using System.Diagnostics; using System.IO; using System.Net; -using System.Net.Sockets; using System.Security.Authentication; using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; From c90d97a39dbd24b22384ee72b525afdc44453e33 Mon Sep 17 00:00:00 2001 From: Tino Hager Date: Wed, 4 Sep 2024 20:39:25 +0200 Subject: [PATCH 09/15] Add summary informations, change consts to internal --- .../Authentication/UserAuthenticator.cs | 2 +- Src/SmtpServer/AuthenticationContext.cs | 2 +- .../ComponentModel/ServiceProvider.cs | 2 +- Src/SmtpServer/IO/PipeWriterExtensions.cs | 3 ++ Src/SmtpServer/IO/PipelineException.cs | 6 +++ Src/SmtpServer/ISmtpServerOptions.cs | 3 ++ Src/SmtpServer/Mail/IMailbox.cs | 6 +++ Src/SmtpServer/Mail/Mailbox.cs | 2 +- Src/SmtpServer/Net/EndPointEventArgs.cs | 3 ++ Src/SmtpServer/Net/EndpointListener.cs | 7 ++- Src/SmtpServer/Net/EndpointListenerFactory.cs | 9 ++-- .../Net/IEndpointListenerFactory.cs | 3 ++ Src/SmtpServer/Protocol/AuthCommand.cs | 2 +- Src/SmtpServer/Protocol/DataCommand.cs | 2 +- Src/SmtpServer/Protocol/EhloCommand.cs | 2 +- Src/SmtpServer/Protocol/HeloCommand.cs | 2 +- Src/SmtpServer/Protocol/MailCommand.cs | 2 +- Src/SmtpServer/Protocol/NoopCommand.cs | 2 +- Src/SmtpServer/Protocol/ProxyCommand.cs | 6 +-- Src/SmtpServer/Protocol/QuitCommand.cs | 2 +- Src/SmtpServer/Protocol/RcptCommand.cs | 2 +- Src/SmtpServer/Protocol/RsetCommand.cs | 2 +- Src/SmtpServer/Protocol/SmtpCommandFactory.cs | 3 ++ Src/SmtpServer/Protocol/SmtpResponse.cs | 51 +++++++++++++++++++ Src/SmtpServer/Protocol/StartTlsCommand.cs | 2 +- Src/SmtpServer/SessionEventArgs.cs | 3 ++ Src/SmtpServer/SessionFaultedEventArgs.cs | 3 ++ Src/SmtpServer/SmtpCommandEventArgs.cs | 3 ++ .../SmtpResponseExceptionEventArgs.cs | 3 ++ Src/SmtpServer/SmtpServer.cs | 3 ++ Src/SmtpServer/SmtpServerOptionsBuilder.cs | 3 ++ .../Storage/DelegatingMailboxFilter.cs | 3 ++ .../Storage/IMailboxFilterFactory.cs | 3 ++ Src/SmtpServer/Storage/IMessageStore.cs | 3 ++ .../Storage/IMessageStoreFactory.cs | 3 ++ Src/SmtpServer/Storage/MailboxFilter.cs | 23 +++------ Src/SmtpServer/Storage/MessageStore.cs | 20 ++------ Src/SmtpServer/Text/Token.cs | 3 ++ Src/SmtpServer/Text/TokenKind.cs | 3 ++ Src/SmtpServer/Text/TokenReader.cs | 3 ++ .../Tracing/TracingSmtpCommandVisitor.cs | 3 ++ 41 files changed, 155 insertions(+), 58 deletions(-) diff --git a/Src/SmtpServer/Authentication/UserAuthenticator.cs b/Src/SmtpServer/Authentication/UserAuthenticator.cs index 4eb55f4..3f8f160 100644 --- a/Src/SmtpServer/Authentication/UserAuthenticator.cs +++ b/Src/SmtpServer/Authentication/UserAuthenticator.cs @@ -8,7 +8,7 @@ namespace SmtpServer.Authentication /// public abstract class UserAuthenticator : IUserAuthenticator { - public static readonly IUserAuthenticator Default = new DefaultUserAuthenticator(); + internal static readonly IUserAuthenticator Default = new DefaultUserAuthenticator(); /// /// Authenticate a user account. diff --git a/Src/SmtpServer/AuthenticationContext.cs b/Src/SmtpServer/AuthenticationContext.cs index eb24b10..edd6b32 100644 --- a/Src/SmtpServer/AuthenticationContext.cs +++ b/Src/SmtpServer/AuthenticationContext.cs @@ -5,7 +5,7 @@ /// public sealed class AuthenticationContext { - public static readonly AuthenticationContext Unauthenticated = new AuthenticationContext(); + internal static readonly AuthenticationContext Unauthenticated = new AuthenticationContext(); /// /// Constructor. diff --git a/Src/SmtpServer/ComponentModel/ServiceProvider.cs b/Src/SmtpServer/ComponentModel/ServiceProvider.cs index e58ed7d..1b90d7a 100644 --- a/Src/SmtpServer/ComponentModel/ServiceProvider.cs +++ b/Src/SmtpServer/ComponentModel/ServiceProvider.cs @@ -11,7 +11,7 @@ namespace SmtpServer.ComponentModel /// public sealed class ServiceProvider : IServiceProvider { - public static readonly IServiceProvider Default = new ServiceProvider(); + internal static readonly IServiceProvider Default = new ServiceProvider(); IEndpointListenerFactory _endpointListenerFactory; IUserAuthenticatorFactory _userAuthenticatorFactory; diff --git a/Src/SmtpServer/IO/PipeWriterExtensions.cs b/Src/SmtpServer/IO/PipeWriterExtensions.cs index 5c635f1..767e12a 100644 --- a/Src/SmtpServer/IO/PipeWriterExtensions.cs +++ b/Src/SmtpServer/IO/PipeWriterExtensions.cs @@ -7,6 +7,9 @@ namespace SmtpServer.IO { + /// + /// Pipe Writer Extensions + /// public static class PipeWriterExtensions { /// diff --git a/Src/SmtpServer/IO/PipelineException.cs b/Src/SmtpServer/IO/PipelineException.cs index 377cfd1..0749600 100644 --- a/Src/SmtpServer/IO/PipelineException.cs +++ b/Src/SmtpServer/IO/PipelineException.cs @@ -2,7 +2,13 @@ namespace SmtpServer.IO { + /// + /// Pipeline Exception + /// public abstract class PipelineException : Exception { } + /// + /// Pipeline Cancelled Exception + /// public sealed class PipelineCancelledException : PipelineException { } } diff --git a/Src/SmtpServer/ISmtpServerOptions.cs b/Src/SmtpServer/ISmtpServerOptions.cs index 8b87ca3..f253484 100644 --- a/Src/SmtpServer/ISmtpServerOptions.cs +++ b/Src/SmtpServer/ISmtpServerOptions.cs @@ -3,6 +3,9 @@ namespace SmtpServer { + /// + /// Smtp Server Options Interface + /// public interface ISmtpServerOptions { /// diff --git a/Src/SmtpServer/Mail/IMailbox.cs b/Src/SmtpServer/Mail/IMailbox.cs index 4e6af65..dbfe5c4 100644 --- a/Src/SmtpServer/Mail/IMailbox.cs +++ b/Src/SmtpServer/Mail/IMailbox.cs @@ -3,6 +3,9 @@ namespace SmtpServer.Mail { + /// + /// Mailbox Interface + /// public interface IMailbox { /// @@ -16,6 +19,9 @@ public interface IMailbox string Host { get; } } + /// + /// Mailbox Extension Methods + /// public static class MailboxExtensionMethods { /// diff --git a/Src/SmtpServer/Mail/Mailbox.cs b/Src/SmtpServer/Mail/Mailbox.cs index 5310001..adc674f 100644 --- a/Src/SmtpServer/Mail/Mailbox.cs +++ b/Src/SmtpServer/Mail/Mailbox.cs @@ -7,7 +7,7 @@ namespace SmtpServer.Mail /// public sealed class Mailbox : IMailbox { - public static readonly IMailbox Empty = new Mailbox(string.Empty, string.Empty); + internal static readonly IMailbox Empty = new Mailbox(string.Empty, string.Empty); /// /// Constructor. diff --git a/Src/SmtpServer/Net/EndPointEventArgs.cs b/Src/SmtpServer/Net/EndPointEventArgs.cs index 7c24716..0adb330 100644 --- a/Src/SmtpServer/Net/EndPointEventArgs.cs +++ b/Src/SmtpServer/Net/EndPointEventArgs.cs @@ -3,6 +3,9 @@ namespace SmtpServer.Net { + /// + /// Endpoint EventArgs + /// public sealed class EndpointEventArgs : EventArgs { /// diff --git a/Src/SmtpServer/Net/EndpointListener.cs b/Src/SmtpServer/Net/EndpointListener.cs index 6ebc4c0..cf2cb8e 100644 --- a/Src/SmtpServer/Net/EndpointListener.cs +++ b/Src/SmtpServer/Net/EndpointListener.cs @@ -6,10 +6,13 @@ namespace SmtpServer.Net { + /// + /// Endpoint Listener + /// public sealed class EndpointListener : IEndpointListener { - public const string LocalEndPointKey = "EndpointListener:LocalEndPoint"; - public const string RemoteEndPointKey = "EndpointListener:RemoteEndPoint"; + internal const string LocalEndPointKey = "EndpointListener:LocalEndPoint"; + internal const string RemoteEndPointKey = "EndpointListener:RemoteEndPoint"; readonly IEndpointDefinition _endpointDefinition; readonly TcpListener _tcpListener; diff --git a/Src/SmtpServer/Net/EndpointListenerFactory.cs b/Src/SmtpServer/Net/EndpointListenerFactory.cs index da04feb..21db35b 100644 --- a/Src/SmtpServer/Net/EndpointListenerFactory.cs +++ b/Src/SmtpServer/Net/EndpointListenerFactory.cs @@ -3,6 +3,9 @@ namespace SmtpServer.Net { + /// + /// Endpoint Listener Factory + /// public class EndpointListenerFactory : IEndpointListenerFactory { internal static readonly IEndpointListenerFactory Default = new EndpointListenerFactory(); @@ -17,11 +20,7 @@ public class EndpointListenerFactory : IEndpointListenerFactory /// public event EventHandler EndpointStopped; - /// - /// Create an instance of an endpoint listener for the specified endpoint definition. - /// - /// The endpoint definition to create the listener for. - /// The endpoint listener for the specified endpoint definition. + /// public virtual IEndpointListener CreateListener(IEndpointDefinition endpointDefinition) { var tcpListener = new TcpListener(endpointDefinition.Endpoint); diff --git a/Src/SmtpServer/Net/IEndpointListenerFactory.cs b/Src/SmtpServer/Net/IEndpointListenerFactory.cs index b1c1523..2c68aef 100644 --- a/Src/SmtpServer/Net/IEndpointListenerFactory.cs +++ b/Src/SmtpServer/Net/IEndpointListenerFactory.cs @@ -1,5 +1,8 @@ namespace SmtpServer.Net { + /// + /// Endpoint Listener Factory Interface + /// public interface IEndpointListenerFactory { /// diff --git a/Src/SmtpServer/Protocol/AuthCommand.cs b/Src/SmtpServer/Protocol/AuthCommand.cs index 26b1f67..bc42665 100644 --- a/Src/SmtpServer/Protocol/AuthCommand.cs +++ b/Src/SmtpServer/Protocol/AuthCommand.cs @@ -15,7 +15,7 @@ namespace SmtpServer.Protocol /// public sealed class AuthCommand : SmtpCommand { - public const string Command = "AUTH"; + internal const string Command = "AUTH"; string _user; string _password; diff --git a/Src/SmtpServer/Protocol/DataCommand.cs b/Src/SmtpServer/Protocol/DataCommand.cs index 8a2a6ab..e28b974 100644 --- a/Src/SmtpServer/Protocol/DataCommand.cs +++ b/Src/SmtpServer/Protocol/DataCommand.cs @@ -12,7 +12,7 @@ namespace SmtpServer.Protocol /// public sealed class DataCommand : SmtpCommand { - public const string Command = "DATA"; + internal const string Command = "DATA"; /// /// Constructor. diff --git a/Src/SmtpServer/Protocol/EhloCommand.cs b/Src/SmtpServer/Protocol/EhloCommand.cs index f21c8d8..8c55ee1 100644 --- a/Src/SmtpServer/Protocol/EhloCommand.cs +++ b/Src/SmtpServer/Protocol/EhloCommand.cs @@ -12,7 +12,7 @@ namespace SmtpServer.Protocol /// public class EhloCommand : SmtpCommand { - public const string Command = "EHLO"; + internal const string Command = "EHLO"; /// /// Constructor. diff --git a/Src/SmtpServer/Protocol/HeloCommand.cs b/Src/SmtpServer/Protocol/HeloCommand.cs index b16366e..dc2948b 100644 --- a/Src/SmtpServer/Protocol/HeloCommand.cs +++ b/Src/SmtpServer/Protocol/HeloCommand.cs @@ -9,7 +9,7 @@ namespace SmtpServer.Protocol /// public class HeloCommand : SmtpCommand { - public const string Command = "HELO"; + internal const string Command = "HELO"; /// /// Constructor. diff --git a/Src/SmtpServer/Protocol/MailCommand.cs b/Src/SmtpServer/Protocol/MailCommand.cs index 185613d..bc31215 100644 --- a/Src/SmtpServer/Protocol/MailCommand.cs +++ b/Src/SmtpServer/Protocol/MailCommand.cs @@ -13,7 +13,7 @@ namespace SmtpServer.Protocol /// public sealed class MailCommand : SmtpCommand { - public const string Command = "MAIL"; + internal const string Command = "MAIL"; /// /// Constructor. diff --git a/Src/SmtpServer/Protocol/NoopCommand.cs b/Src/SmtpServer/Protocol/NoopCommand.cs index 93d10b6..1f83acc 100644 --- a/Src/SmtpServer/Protocol/NoopCommand.cs +++ b/Src/SmtpServer/Protocol/NoopCommand.cs @@ -9,7 +9,7 @@ namespace SmtpServer.Protocol /// public sealed class NoopCommand : SmtpCommand { - public const string Command = "NOOP"; + internal const string Command = "NOOP"; /// /// Constructor. diff --git a/Src/SmtpServer/Protocol/ProxyCommand.cs b/Src/SmtpServer/Protocol/ProxyCommand.cs index 407c70b..4d00850 100644 --- a/Src/SmtpServer/Protocol/ProxyCommand.cs +++ b/Src/SmtpServer/Protocol/ProxyCommand.cs @@ -11,10 +11,10 @@ namespace SmtpServer.Protocol /// public sealed class ProxyCommand : SmtpCommand { - public const string ProxySourceEndpointKey = "Proxy:ProxySourceEndpoint"; - public const string ProxyDestinationEndpointKey = "Proxy:ProxyDestinationEndpoint"; + internal const string ProxySourceEndpointKey = "Proxy:ProxySourceEndpoint"; + internal const string ProxyDestinationEndpointKey = "Proxy:ProxyDestinationEndpoint"; - public const string Command = "PROXY"; + internal const string Command = "PROXY"; /// /// Constructor. diff --git a/Src/SmtpServer/Protocol/QuitCommand.cs b/Src/SmtpServer/Protocol/QuitCommand.cs index 20a600b..b9542d9 100644 --- a/Src/SmtpServer/Protocol/QuitCommand.cs +++ b/Src/SmtpServer/Protocol/QuitCommand.cs @@ -11,7 +11,7 @@ namespace SmtpServer.Protocol /// public sealed class QuitCommand : SmtpCommand { - public const string Command = "QUIT"; + internal const string Command = "QUIT"; /// /// Constructor. diff --git a/Src/SmtpServer/Protocol/RcptCommand.cs b/Src/SmtpServer/Protocol/RcptCommand.cs index b073904..9643dcb 100644 --- a/Src/SmtpServer/Protocol/RcptCommand.cs +++ b/Src/SmtpServer/Protocol/RcptCommand.cs @@ -13,7 +13,7 @@ namespace SmtpServer.Protocol /// public sealed class RcptCommand : SmtpCommand { - public const string Command = "RCPT"; + internal const string Command = "RCPT"; /// /// Constructor. diff --git a/Src/SmtpServer/Protocol/RsetCommand.cs b/Src/SmtpServer/Protocol/RsetCommand.cs index 5c66c9e..cf9f513 100644 --- a/Src/SmtpServer/Protocol/RsetCommand.cs +++ b/Src/SmtpServer/Protocol/RsetCommand.cs @@ -9,7 +9,7 @@ namespace SmtpServer.Protocol /// public sealed class RsetCommand : SmtpCommand { - public const string Command = "RSET"; + internal const string Command = "RSET"; /// /// Constructor. diff --git a/Src/SmtpServer/Protocol/SmtpCommandFactory.cs b/Src/SmtpServer/Protocol/SmtpCommandFactory.cs index 73f1ee9..e0c0d75 100644 --- a/Src/SmtpServer/Protocol/SmtpCommandFactory.cs +++ b/Src/SmtpServer/Protocol/SmtpCommandFactory.cs @@ -4,6 +4,9 @@ namespace SmtpServer.Protocol { + /// + /// Smtp Command Factory + /// public class SmtpCommandFactory : ISmtpCommandFactory { /// diff --git a/Src/SmtpServer/Protocol/SmtpResponse.cs b/Src/SmtpServer/Protocol/SmtpResponse.cs index f7adc04..d25867f 100644 --- a/Src/SmtpServer/Protocol/SmtpResponse.cs +++ b/Src/SmtpServer/Protocol/SmtpResponse.cs @@ -5,18 +5,69 @@ /// public class SmtpResponse { + /// + /// 250 Ok + /// public static readonly SmtpResponse Ok = new SmtpResponse(SmtpReplyCode.Ok, "Ok"); + + /// + /// 220 ServiceReady + /// public static readonly SmtpResponse ServiceReady = new SmtpResponse(SmtpReplyCode.ServiceReady, "ready when you are"); + + /// + /// 550 MailboxUnavailable + /// public static readonly SmtpResponse MailboxUnavailable = new SmtpResponse(SmtpReplyCode.MailboxUnavailable, "mailbox unavailable"); + + /// + /// 553 MailboxNameNotAllowed + /// public static readonly SmtpResponse MailboxNameNotAllowed = new SmtpResponse(SmtpReplyCode.MailboxNameNotAllowed, "mailbox name not allowed"); + + /// + /// 221 ServiceClosingTransmissionChannel + /// public static readonly SmtpResponse ServiceClosingTransmissionChannel = new SmtpResponse(SmtpReplyCode.ServiceClosingTransmissionChannel, "bye"); + + /// + /// 501 SyntaxError + /// public static readonly SmtpResponse SyntaxError = new SmtpResponse(SmtpReplyCode.SyntaxError, "syntax error"); + + /// + /// 552 SizeLimitExceeded + /// public static readonly SmtpResponse SizeLimitExceeded = new SmtpResponse(SmtpReplyCode.SizeLimitExceeded, "size limit exceeded"); + + /// + /// 554 TransactionFailed + /// public static readonly SmtpResponse NoValidRecipientsGiven = new SmtpResponse(SmtpReplyCode.TransactionFailed, "no valid recipients given"); + + /// + /// 535 AuthenticationFailed + /// public static readonly SmtpResponse AuthenticationFailed = new SmtpResponse(SmtpReplyCode.AuthenticationFailed, "authentication failed"); + + /// + /// 235 AuthenticationSuccessful + /// public static readonly SmtpResponse AuthenticationSuccessful = new SmtpResponse(SmtpReplyCode.AuthenticationSuccessful, "go ahead"); + + /// + /// 554 TransactionFailed + /// public static readonly SmtpResponse TransactionFailed = new SmtpResponse(SmtpReplyCode.TransactionFailed); + + /// + /// 503 BadSequence + /// public static readonly SmtpResponse BadSequence = new SmtpResponse(SmtpReplyCode.BadSequence, "bad sequence of commands"); + + /// + /// 530 AuthenticationRequired + /// public static readonly SmtpResponse AuthenticationRequired = new SmtpResponse(SmtpReplyCode.AuthenticationRequired, "authentication required"); /// diff --git a/Src/SmtpServer/Protocol/StartTlsCommand.cs b/Src/SmtpServer/Protocol/StartTlsCommand.cs index aafaabc..17561d3 100644 --- a/Src/SmtpServer/Protocol/StartTlsCommand.cs +++ b/Src/SmtpServer/Protocol/StartTlsCommand.cs @@ -9,7 +9,7 @@ namespace SmtpServer.Protocol /// public sealed class StartTlsCommand : SmtpCommand { - public const string Command = "STARTTLS"; + internal const string Command = "STARTTLS"; /// /// Constructor. diff --git a/Src/SmtpServer/SessionEventArgs.cs b/Src/SmtpServer/SessionEventArgs.cs index 2581d49..f67be1a 100644 --- a/Src/SmtpServer/SessionEventArgs.cs +++ b/Src/SmtpServer/SessionEventArgs.cs @@ -2,6 +2,9 @@ namespace SmtpServer { + /// + /// Session EventArgs + /// public class SessionEventArgs : EventArgs { /// diff --git a/Src/SmtpServer/SessionFaultedEventArgs.cs b/Src/SmtpServer/SessionFaultedEventArgs.cs index 82a90d3..de40335 100644 --- a/Src/SmtpServer/SessionFaultedEventArgs.cs +++ b/Src/SmtpServer/SessionFaultedEventArgs.cs @@ -2,6 +2,9 @@ namespace SmtpServer { + /// + /// Session Faulted EventArgs + /// public class SessionFaultedEventArgs : SessionEventArgs { /// diff --git a/Src/SmtpServer/SmtpCommandEventArgs.cs b/Src/SmtpServer/SmtpCommandEventArgs.cs index 87f303d..cd15e67 100644 --- a/Src/SmtpServer/SmtpCommandEventArgs.cs +++ b/Src/SmtpServer/SmtpCommandEventArgs.cs @@ -2,6 +2,9 @@ namespace SmtpServer { + /// + /// Smtp Command EventArgs + /// public sealed class SmtpCommandEventArgs : SessionEventArgs { /// diff --git a/Src/SmtpServer/SmtpResponseExceptionEventArgs.cs b/Src/SmtpServer/SmtpResponseExceptionEventArgs.cs index 2dc0634..261e24a 100644 --- a/Src/SmtpServer/SmtpResponseExceptionEventArgs.cs +++ b/Src/SmtpServer/SmtpResponseExceptionEventArgs.cs @@ -2,6 +2,9 @@ namespace SmtpServer { + /// + /// Smtp Response Exception EventArgs + /// public sealed class SmtpResponseExceptionEventArgs : SessionEventArgs { /// diff --git a/Src/SmtpServer/SmtpServer.cs b/Src/SmtpServer/SmtpServer.cs index f46c034..d4c4a75 100644 --- a/Src/SmtpServer/SmtpServer.cs +++ b/Src/SmtpServer/SmtpServer.cs @@ -7,6 +7,9 @@ namespace SmtpServer { + /// + /// Smtp Server + /// public class SmtpServer { /// diff --git a/Src/SmtpServer/SmtpServerOptionsBuilder.cs b/Src/SmtpServer/SmtpServerOptionsBuilder.cs index 142cd5a..322a565 100644 --- a/Src/SmtpServer/SmtpServerOptionsBuilder.cs +++ b/Src/SmtpServer/SmtpServerOptionsBuilder.cs @@ -3,6 +3,9 @@ namespace SmtpServer { + /// + /// Smtp Server Options Builder + /// public sealed class SmtpServerOptionsBuilder { readonly List> _setters = new List>(); diff --git a/Src/SmtpServer/Storage/DelegatingMailboxFilter.cs b/Src/SmtpServer/Storage/DelegatingMailboxFilter.cs index 3974094..0dd6377 100644 --- a/Src/SmtpServer/Storage/DelegatingMailboxFilter.cs +++ b/Src/SmtpServer/Storage/DelegatingMailboxFilter.cs @@ -5,6 +5,9 @@ namespace SmtpServer.Storage { + /// + /// Delegating Mailbox Filter + /// public sealed class DelegatingMailboxFilter : MailboxFilter { static readonly Func EmptyAcceptDelegate = (context, @from) => true; diff --git a/Src/SmtpServer/Storage/IMailboxFilterFactory.cs b/Src/SmtpServer/Storage/IMailboxFilterFactory.cs index a2845d6..0bf495b 100644 --- a/Src/SmtpServer/Storage/IMailboxFilterFactory.cs +++ b/Src/SmtpServer/Storage/IMailboxFilterFactory.cs @@ -2,5 +2,8 @@ namespace SmtpServer.Storage { + /// + /// Mailbox Filter Factory Interface + /// public interface IMailboxFilterFactory : ISessionContextInstanceFactory { } } diff --git a/Src/SmtpServer/Storage/IMessageStore.cs b/Src/SmtpServer/Storage/IMessageStore.cs index 62b7743..5381493 100644 --- a/Src/SmtpServer/Storage/IMessageStore.cs +++ b/Src/SmtpServer/Storage/IMessageStore.cs @@ -5,6 +5,9 @@ namespace SmtpServer.Storage { + /// + /// Message Store Interface + /// public interface IMessageStore { /// diff --git a/Src/SmtpServer/Storage/IMessageStoreFactory.cs b/Src/SmtpServer/Storage/IMessageStoreFactory.cs index a69bc01..e893991 100644 --- a/Src/SmtpServer/Storage/IMessageStoreFactory.cs +++ b/Src/SmtpServer/Storage/IMessageStoreFactory.cs @@ -2,5 +2,8 @@ namespace SmtpServer.Storage { + /// + /// Message Store Factory Interface + /// public interface IMessageStoreFactory : ISessionContextInstanceFactory { } } diff --git a/Src/SmtpServer/Storage/MailboxFilter.cs b/Src/SmtpServer/Storage/MailboxFilter.cs index a8c3069..3f5c940 100644 --- a/Src/SmtpServer/Storage/MailboxFilter.cs +++ b/Src/SmtpServer/Storage/MailboxFilter.cs @@ -4,18 +4,14 @@ namespace SmtpServer.Storage { + /// + /// Mailbox Filter + /// public abstract class MailboxFilter : IMailboxFilter { - public static readonly IMailboxFilter Default = new DefaultMailboxFilter(); + internal static readonly IMailboxFilter Default = new DefaultMailboxFilter(); - /// - /// Returns a value indicating whether the given mailbox can be accepted as a sender. - /// - /// The session context. - /// The mailbox to test. - /// The estimated message size to accept. - /// The cancellation token. - /// Returns true if the mailbox is accepted, false if not. + /// public virtual Task CanAcceptFromAsync( ISessionContext context, IMailbox @from, @@ -25,14 +21,7 @@ public virtual Task CanAcceptFromAsync( return Task.FromResult(true); } - /// - /// Returns a value indicating whether the given mailbox can be accepted as a recipient to the given sender. - /// - /// The session context. - /// The mailbox to test. - /// The sender's mailbox. - /// The cancellation token. - /// Returns true if the mailbox can be delivered to, false if not. + /// public virtual Task CanDeliverToAsync( ISessionContext context, IMailbox to, diff --git a/Src/SmtpServer/Storage/MessageStore.cs b/Src/SmtpServer/Storage/MessageStore.cs index 4efe06e..9fe28e0 100644 --- a/Src/SmtpServer/Storage/MessageStore.cs +++ b/Src/SmtpServer/Storage/MessageStore.cs @@ -10,28 +10,14 @@ namespace SmtpServer.Storage /// public abstract class MessageStore : IMessageStore { - public static readonly IMessageStore Default = new DefaultMessageStore(); + internal static readonly IMessageStore Default = new DefaultMessageStore(); - /// - /// Save the given message to the underlying storage system. - /// - /// The session context. - /// The SMTP message transaction to store. - /// The buffer that contains the message content. - /// The cancellation token. - /// The response code to return that indicates the result of the message being saved. + /// public abstract Task SaveAsync(ISessionContext context, IMessageTransaction transaction, ReadOnlySequence buffer, CancellationToken cancellationToken); sealed class DefaultMessageStore : MessageStore { - /// - /// Save the given message to the underlying storage system. - /// - /// The session context. - /// The buffer that contains the message content. - /// The SMTP message transaction to store. - /// The cancellation token. - /// The response code to return that indicates the result of the message being saved. + /// public override Task SaveAsync(ISessionContext context, IMessageTransaction transaction, ReadOnlySequence buffer, CancellationToken cancellationToken) { return Task.FromResult(SmtpResponse.Ok); diff --git a/Src/SmtpServer/Text/Token.cs b/Src/SmtpServer/Text/Token.cs index a94df1c..534c618 100644 --- a/Src/SmtpServer/Text/Token.cs +++ b/Src/SmtpServer/Text/Token.cs @@ -4,6 +4,9 @@ namespace SmtpServer.Text { + /// + /// Token + /// [DebuggerDisplay("[{Kind}] {Text}")] public readonly ref struct Token { diff --git a/Src/SmtpServer/Text/TokenKind.cs b/Src/SmtpServer/Text/TokenKind.cs index 4ed8bac..fd9abcb 100644 --- a/Src/SmtpServer/Text/TokenKind.cs +++ b/Src/SmtpServer/Text/TokenKind.cs @@ -1,5 +1,8 @@ namespace SmtpServer.Text { + /// + /// Token Kind + /// public enum TokenKind { /// diff --git a/Src/SmtpServer/Text/TokenReader.cs b/Src/SmtpServer/Text/TokenReader.cs index 9a56448..1a45c5d 100644 --- a/Src/SmtpServer/Text/TokenReader.cs +++ b/Src/SmtpServer/Text/TokenReader.cs @@ -3,6 +3,9 @@ namespace SmtpServer.Text { + /// + /// Token Reader + /// public ref struct TokenReader { ref struct CheckpointState diff --git a/Src/SmtpServer/Tracing/TracingSmtpCommandVisitor.cs b/Src/SmtpServer/Tracing/TracingSmtpCommandVisitor.cs index b645a7f..1e2317c 100644 --- a/Src/SmtpServer/Tracing/TracingSmtpCommandVisitor.cs +++ b/Src/SmtpServer/Tracing/TracingSmtpCommandVisitor.cs @@ -6,6 +6,9 @@ namespace SmtpServer.Tracing { + /// + /// Tracing Smtp Command Visitor + /// public sealed class TracingSmtpCommandVisitor : SmtpCommandVisitor { readonly TextWriter _output; From 2996f364f538d1c48138b3be72617000f5b7fb0a Mon Sep 17 00:00:00 2001 From: Tino Hager Date: Wed, 4 Sep 2024 20:41:17 +0200 Subject: [PATCH 10/15] Revert "Add Smtp session Timeout" This reverts commit 85d75a9e62e48dc94c431da21ccae111ad4a0bbe. --- Src/SmtpServer/EndpointDefinitionBuilder.cs | 40 ++++++++++++++------- Src/SmtpServer/IEndpointDefinition.cs | 5 +-- Src/SmtpServer/Net/EndpointListener.cs | 1 + Src/SmtpServer/SmtpSession.cs | 13 +++---- 4 files changed, 36 insertions(+), 23 deletions(-) diff --git a/Src/SmtpServer/EndpointDefinitionBuilder.cs b/Src/SmtpServer/EndpointDefinitionBuilder.cs index 8061aaa..2c2137e 100644 --- a/Src/SmtpServer/EndpointDefinitionBuilder.cs +++ b/Src/SmtpServer/EndpointDefinitionBuilder.cs @@ -21,7 +21,7 @@ public IEndpointDefinition Build() { var definition = new EndpointDefinition { - SessionTimeout = TimeSpan.FromMinutes(2), + ReadTimeout = TimeSpan.FromMinutes(2), SupportedSslProtocols = SslProtocols.Tls12, }; @@ -103,13 +103,13 @@ public EndpointDefinitionBuilder AllowUnsecureAuthentication(bool value = true) } /// - /// Sets the session timeout to apply to the session. + /// Sets the read timeout to apply to stream operations. /// - /// The timeout value to apply to the Smtp session. + /// The timeout value to apply to read operations. /// A EndpointDefinitionBuilder to continue building on. - public EndpointDefinitionBuilder SessionTimeout(TimeSpan value) + public EndpointDefinitionBuilder ReadTimeout(TimeSpan value) { - _setters.Add(options => options.SessionTimeout = value); + _setters.Add(options => options.ReadTimeout = value); return this; } @@ -152,25 +152,39 @@ public EndpointDefinitionBuilder SupportedSslProtocols(SslProtocols value) internal sealed class EndpointDefinition : IEndpointDefinition { - /// + /// + /// The IP endpoint to listen on. + /// public IPEndPoint Endpoint { get; set; } - /// + /// + /// Indicates whether the endpoint is secure by default. + /// public bool IsSecure { get; set; } - /// + /// + /// Gets a value indicating whether the client must authenticate in order to proceed. + /// public bool AuthenticationRequired { get; set; } - /// + /// + /// Gets a value indicating whether authentication should be allowed on an unsecure session. + /// public bool AllowUnsecureAuthentication { get; set; } - /// - public TimeSpan SessionTimeout { get; set; } + /// + /// The timeout on each individual buffer read. + /// + public TimeSpan ReadTimeout { get; set; } - /// + /// + /// Gets the Server Certificate factory to use when starting a TLS session. + /// public ICertificateFactory CertificateFactory { get; set; } - /// + /// + /// The supported SSL protocols. + /// public SslProtocols SupportedSslProtocols { get; set; } } diff --git a/Src/SmtpServer/IEndpointDefinition.cs b/Src/SmtpServer/IEndpointDefinition.cs index a026807..5673ebe 100644 --- a/Src/SmtpServer/IEndpointDefinition.cs +++ b/Src/SmtpServer/IEndpointDefinition.cs @@ -1,6 +1,7 @@ using System; using System.Net; using System.Security.Authentication; +using System.Security.Cryptography.X509Certificates; namespace SmtpServer { @@ -30,9 +31,9 @@ public interface IEndpointDefinition bool AllowUnsecureAuthentication { get; } /// - /// The timeout of an Smtp session. + /// The timeout on each individual buffer read. /// - TimeSpan SessionTimeout { get; } + TimeSpan ReadTimeout { get; } /// /// Gets the Server Certificate factory to use when starting a TLS session. diff --git a/Src/SmtpServer/Net/EndpointListener.cs b/Src/SmtpServer/Net/EndpointListener.cs index cf2cb8e..64dacfc 100644 --- a/Src/SmtpServer/Net/EndpointListener.cs +++ b/Src/SmtpServer/Net/EndpointListener.cs @@ -46,6 +46,7 @@ public async Task GetPipeAsync(ISessionContext context, Ca context.Properties.Add(RemoteEndPointKey, tcpClient.Client.RemoteEndPoint); var stream = tcpClient.GetStream(); + stream.ReadTimeout = (int)_endpointDefinition.ReadTimeout.TotalMilliseconds; return new SecurableDuplexPipe(stream, () => { diff --git a/Src/SmtpServer/SmtpSession.cs b/Src/SmtpServer/SmtpSession.cs index 625064a..9daaec0 100644 --- a/Src/SmtpServer/SmtpSession.cs +++ b/Src/SmtpServer/SmtpSession.cs @@ -56,16 +56,13 @@ internal async Task RunAsync(CancellationToken cancellationToken) /// A task which asynchronously performs the execution. async Task ExecuteAsync(SmtpSessionContext context, CancellationToken cancellationToken) { - using var sessionReadTimeoutCancellationTokenSource = new CancellationTokenSource(context.EndpointDefinition.SessionTimeout); - using var linkedTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, sessionReadTimeoutCancellationTokenSource.Token); - var retries = _context.ServerOptions.MaxRetryCount; - while (retries-- > 0 && context.IsQuitRequested == false && linkedTokenSource.IsCancellationRequested == false) + while (retries-- > 0 && context.IsQuitRequested == false && cancellationToken.IsCancellationRequested == false) { try { - var command = await ReadCommandAsync(context, linkedTokenSource.Token).ConfigureAwait(false); + var command = await ReadCommandAsync(context, cancellationToken).ConfigureAwait(false); if (command == null) { @@ -77,7 +74,7 @@ async Task ExecuteAsync(SmtpSessionContext context, CancellationToken cancellati throw new SmtpResponseException(errorResponse); } - if (await ExecuteAsync(command, context, linkedTokenSource.Token).ConfigureAwait(false)) + if (await ExecuteAsync(command, context, cancellationToken).ConfigureAwait(false)) { _stateMachine.Transition(context); } @@ -88,7 +85,7 @@ async Task ExecuteAsync(SmtpSessionContext context, CancellationToken cancellati { context.RaiseResponseException(responseException); - await context.Pipe.Output.WriteReplyAsync(responseException.Response, linkedTokenSource.Token).ConfigureAwait(false); + await context.Pipe.Output.WriteReplyAsync(responseException.Response, cancellationToken).ConfigureAwait(false); context.IsQuitRequested = true; } @@ -98,7 +95,7 @@ async Task ExecuteAsync(SmtpSessionContext context, CancellationToken cancellati var response = CreateErrorResponse(responseException.Response, retries); - await context.Pipe.Output.WriteReplyAsync(response, linkedTokenSource.Token).ConfigureAwait(false); + await context.Pipe.Output.WriteReplyAsync(response, cancellationToken).ConfigureAwait(false); } catch (OperationCanceledException) { From 3cd2ca5a377e736bc13fe743085e232386c8a9f3 Mon Sep 17 00:00:00 2001 From: Tino Hager Date: Mon, 9 Sep 2024 13:10:44 +0200 Subject: [PATCH 11/15] cleanup --- Src/SmtpServer/IEndpointDefinition.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/Src/SmtpServer/IEndpointDefinition.cs b/Src/SmtpServer/IEndpointDefinition.cs index 8dc760f..a026807 100644 --- a/Src/SmtpServer/IEndpointDefinition.cs +++ b/Src/SmtpServer/IEndpointDefinition.cs @@ -1,7 +1,6 @@ using System; using System.Net; using System.Security.Authentication; -using System.Security.Cryptography.X509Certificates; namespace SmtpServer { From a43df9d2e9b35f80553b06c18bf721df63e853a8 Mon Sep 17 00:00:00 2001 From: Tino Hager Date: Mon, 16 Sep 2024 15:13:35 +0200 Subject: [PATCH 12/15] switch back to public add summary --- Src/SmtpServer/Authentication/UserAuthenticator.cs | 5 ++++- Src/SmtpServer/AuthenticationContext.cs | 5 ++++- Src/SmtpServer/Mail/Mailbox.cs | 7 ++++--- Src/SmtpServer/Net/EndpointListener.cs | 11 +++++++++-- Src/SmtpServer/Net/EndpointListenerFactory.cs | 5 ++++- Src/SmtpServer/Protocol/AuthCommand.cs | 5 ++++- Src/SmtpServer/Protocol/DataCommand.cs | 5 ++++- Src/SmtpServer/Protocol/EhloCommand.cs | 5 ++++- Src/SmtpServer/Protocol/HeloCommand.cs | 5 ++++- Src/SmtpServer/Protocol/MailCommand.cs | 5 ++++- Src/SmtpServer/Protocol/NoopCommand.cs | 5 ++++- Src/SmtpServer/Protocol/ProxyCommand.cs | 5 ++++- Src/SmtpServer/Protocol/QuitCommand.cs | 5 ++++- Src/SmtpServer/Protocol/RcptCommand.cs | 5 ++++- Src/SmtpServer/Protocol/RsetCommand.cs | 5 ++++- Src/SmtpServer/Protocol/StartTlsCommand.cs | 5 ++++- 16 files changed, 69 insertions(+), 19 deletions(-) diff --git a/Src/SmtpServer/Authentication/UserAuthenticator.cs b/Src/SmtpServer/Authentication/UserAuthenticator.cs index 3f8f160..7f6ad21 100644 --- a/Src/SmtpServer/Authentication/UserAuthenticator.cs +++ b/Src/SmtpServer/Authentication/UserAuthenticator.cs @@ -8,7 +8,10 @@ namespace SmtpServer.Authentication /// public abstract class UserAuthenticator : IUserAuthenticator { - internal static readonly IUserAuthenticator Default = new DefaultUserAuthenticator(); + /// + /// Default User Authenticator + /// + public static readonly IUserAuthenticator Default = new DefaultUserAuthenticator(); /// /// Authenticate a user account. diff --git a/Src/SmtpServer/AuthenticationContext.cs b/Src/SmtpServer/AuthenticationContext.cs index edd6b32..55d1e44 100644 --- a/Src/SmtpServer/AuthenticationContext.cs +++ b/Src/SmtpServer/AuthenticationContext.cs @@ -5,7 +5,10 @@ /// public sealed class AuthenticationContext { - internal static readonly AuthenticationContext Unauthenticated = new AuthenticationContext(); + /// + /// Authentication Context in the Unauthenticated state + /// + public static readonly AuthenticationContext Unauthenticated = new AuthenticationContext(); /// /// Constructor. diff --git a/Src/SmtpServer/Mail/Mailbox.cs b/Src/SmtpServer/Mail/Mailbox.cs index adc674f..5e76ecf 100644 --- a/Src/SmtpServer/Mail/Mailbox.cs +++ b/Src/SmtpServer/Mail/Mailbox.cs @@ -1,12 +1,13 @@ -using System; - -namespace SmtpServer.Mail +namespace SmtpServer.Mail { /// /// Mailbox /// public sealed class Mailbox : IMailbox { + /// + /// Empty Mailbox + /// internal static readonly IMailbox Empty = new Mailbox(string.Empty, string.Empty); /// diff --git a/Src/SmtpServer/Net/EndpointListener.cs b/Src/SmtpServer/Net/EndpointListener.cs index cf2cb8e..160b021 100644 --- a/Src/SmtpServer/Net/EndpointListener.cs +++ b/Src/SmtpServer/Net/EndpointListener.cs @@ -11,8 +11,15 @@ namespace SmtpServer.Net /// public sealed class EndpointListener : IEndpointListener { - internal const string LocalEndPointKey = "EndpointListener:LocalEndPoint"; - internal const string RemoteEndPointKey = "EndpointListener:RemoteEndPoint"; + /// + /// EndpointListener LocalEndPoint Key + /// + public const string LocalEndPointKey = "EndpointListener:LocalEndPoint"; + + /// + /// EndpointListener RemoteEndPoint Key + /// + public const string RemoteEndPointKey = "EndpointListener:RemoteEndPoint"; readonly IEndpointDefinition _endpointDefinition; readonly TcpListener _tcpListener; diff --git a/Src/SmtpServer/Net/EndpointListenerFactory.cs b/Src/SmtpServer/Net/EndpointListenerFactory.cs index 21db35b..019c8bb 100644 --- a/Src/SmtpServer/Net/EndpointListenerFactory.cs +++ b/Src/SmtpServer/Net/EndpointListenerFactory.cs @@ -8,7 +8,10 @@ namespace SmtpServer.Net /// public class EndpointListenerFactory : IEndpointListenerFactory { - internal static readonly IEndpointListenerFactory Default = new EndpointListenerFactory(); + /// + /// Default Endpoint Listener Factory + /// + public static readonly IEndpointListenerFactory Default = new EndpointListenerFactory(); /// /// Raised when an endpoint has been started. diff --git a/Src/SmtpServer/Protocol/AuthCommand.cs b/Src/SmtpServer/Protocol/AuthCommand.cs index bc42665..6fb980f 100644 --- a/Src/SmtpServer/Protocol/AuthCommand.cs +++ b/Src/SmtpServer/Protocol/AuthCommand.cs @@ -15,7 +15,10 @@ namespace SmtpServer.Protocol /// public sealed class AuthCommand : SmtpCommand { - internal const string Command = "AUTH"; + /// + /// Smtp Auth Command + /// + public const string Command = "AUTH"; string _user; string _password; diff --git a/Src/SmtpServer/Protocol/DataCommand.cs b/Src/SmtpServer/Protocol/DataCommand.cs index e28b974..3397b1c 100644 --- a/Src/SmtpServer/Protocol/DataCommand.cs +++ b/Src/SmtpServer/Protocol/DataCommand.cs @@ -12,7 +12,10 @@ namespace SmtpServer.Protocol /// public sealed class DataCommand : SmtpCommand { - internal const string Command = "DATA"; + /// + /// Smtp Data Command + /// + public const string Command = "DATA"; /// /// Constructor. diff --git a/Src/SmtpServer/Protocol/EhloCommand.cs b/Src/SmtpServer/Protocol/EhloCommand.cs index 8c55ee1..a9a483e 100644 --- a/Src/SmtpServer/Protocol/EhloCommand.cs +++ b/Src/SmtpServer/Protocol/EhloCommand.cs @@ -12,7 +12,10 @@ namespace SmtpServer.Protocol /// public class EhloCommand : SmtpCommand { - internal const string Command = "EHLO"; + /// + /// Smtp Ehlo Command + /// + public const string Command = "EHLO"; /// /// Constructor. diff --git a/Src/SmtpServer/Protocol/HeloCommand.cs b/Src/SmtpServer/Protocol/HeloCommand.cs index dc2948b..b28103e 100644 --- a/Src/SmtpServer/Protocol/HeloCommand.cs +++ b/Src/SmtpServer/Protocol/HeloCommand.cs @@ -9,7 +9,10 @@ namespace SmtpServer.Protocol /// public class HeloCommand : SmtpCommand { - internal const string Command = "HELO"; + /// + /// Smtp Helo Command + /// + public const string Command = "HELO"; /// /// Constructor. diff --git a/Src/SmtpServer/Protocol/MailCommand.cs b/Src/SmtpServer/Protocol/MailCommand.cs index bc31215..9ec7dc2 100644 --- a/Src/SmtpServer/Protocol/MailCommand.cs +++ b/Src/SmtpServer/Protocol/MailCommand.cs @@ -13,7 +13,10 @@ namespace SmtpServer.Protocol /// public sealed class MailCommand : SmtpCommand { - internal const string Command = "MAIL"; + /// + /// Smtp Mail Command + /// + public const string Command = "MAIL"; /// /// Constructor. diff --git a/Src/SmtpServer/Protocol/NoopCommand.cs b/Src/SmtpServer/Protocol/NoopCommand.cs index 1f83acc..2b1c2b4 100644 --- a/Src/SmtpServer/Protocol/NoopCommand.cs +++ b/Src/SmtpServer/Protocol/NoopCommand.cs @@ -9,7 +9,10 @@ namespace SmtpServer.Protocol /// public sealed class NoopCommand : SmtpCommand { - internal const string Command = "NOOP"; + /// + /// Smtp Noop Command + /// + public const string Command = "NOOP"; /// /// Constructor. diff --git a/Src/SmtpServer/Protocol/ProxyCommand.cs b/Src/SmtpServer/Protocol/ProxyCommand.cs index 4d00850..821a493 100644 --- a/Src/SmtpServer/Protocol/ProxyCommand.cs +++ b/Src/SmtpServer/Protocol/ProxyCommand.cs @@ -14,7 +14,10 @@ public sealed class ProxyCommand : SmtpCommand internal const string ProxySourceEndpointKey = "Proxy:ProxySourceEndpoint"; internal const string ProxyDestinationEndpointKey = "Proxy:ProxyDestinationEndpoint"; - internal const string Command = "PROXY"; + /// + /// Smtp Proxy Command + /// + public const string Command = "PROXY"; /// /// Constructor. diff --git a/Src/SmtpServer/Protocol/QuitCommand.cs b/Src/SmtpServer/Protocol/QuitCommand.cs index b9542d9..140ee21 100644 --- a/Src/SmtpServer/Protocol/QuitCommand.cs +++ b/Src/SmtpServer/Protocol/QuitCommand.cs @@ -11,7 +11,10 @@ namespace SmtpServer.Protocol /// public sealed class QuitCommand : SmtpCommand { - internal const string Command = "QUIT"; + /// + /// Smtp Quit Command + /// + public const string Command = "QUIT"; /// /// Constructor. diff --git a/Src/SmtpServer/Protocol/RcptCommand.cs b/Src/SmtpServer/Protocol/RcptCommand.cs index 9643dcb..1c1bc1b 100644 --- a/Src/SmtpServer/Protocol/RcptCommand.cs +++ b/Src/SmtpServer/Protocol/RcptCommand.cs @@ -13,7 +13,10 @@ namespace SmtpServer.Protocol /// public sealed class RcptCommand : SmtpCommand { - internal const string Command = "RCPT"; + /// + /// Smtp Rcpt Command + /// + public const string Command = "RCPT"; /// /// Constructor. diff --git a/Src/SmtpServer/Protocol/RsetCommand.cs b/Src/SmtpServer/Protocol/RsetCommand.cs index cf9f513..7c50b1b 100644 --- a/Src/SmtpServer/Protocol/RsetCommand.cs +++ b/Src/SmtpServer/Protocol/RsetCommand.cs @@ -9,7 +9,10 @@ namespace SmtpServer.Protocol /// public sealed class RsetCommand : SmtpCommand { - internal const string Command = "RSET"; + /// + /// Smtp Rset Command + /// + public const string Command = "RSET"; /// /// Constructor. diff --git a/Src/SmtpServer/Protocol/StartTlsCommand.cs b/Src/SmtpServer/Protocol/StartTlsCommand.cs index 17561d3..e09ba2a 100644 --- a/Src/SmtpServer/Protocol/StartTlsCommand.cs +++ b/Src/SmtpServer/Protocol/StartTlsCommand.cs @@ -9,7 +9,10 @@ namespace SmtpServer.Protocol /// public sealed class StartTlsCommand : SmtpCommand { - internal const string Command = "STARTTLS"; + /// + /// Smtp Start Tls Command + /// + public const string Command = "STARTTLS"; /// /// Constructor. From cb61a8725db4c4c713ec8a418bcb68366d691d05 Mon Sep 17 00:00:00 2001 From: Tino Hager Date: Mon, 16 Sep 2024 15:14:33 +0200 Subject: [PATCH 13/15] Update ServiceProvider.cs --- Src/SmtpServer/ComponentModel/ServiceProvider.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Src/SmtpServer/ComponentModel/ServiceProvider.cs b/Src/SmtpServer/ComponentModel/ServiceProvider.cs index 1b90d7a..2230b53 100644 --- a/Src/SmtpServer/ComponentModel/ServiceProvider.cs +++ b/Src/SmtpServer/ComponentModel/ServiceProvider.cs @@ -11,7 +11,10 @@ namespace SmtpServer.ComponentModel /// public sealed class ServiceProvider : IServiceProvider { - internal static readonly IServiceProvider Default = new ServiceProvider(); + /// + /// Default Service Provider + /// + public static readonly IServiceProvider Default = new ServiceProvider(); IEndpointListenerFactory _endpointListenerFactory; IUserAuthenticatorFactory _userAuthenticatorFactory; From da43d4facd49edb620f6315b4ba7d9f4ea0c5553 Mon Sep 17 00:00:00 2001 From: Tino Hager Date: Mon, 16 Sep 2024 15:15:07 +0200 Subject: [PATCH 14/15] Update Mailbox.cs --- Src/SmtpServer/Mail/Mailbox.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Src/SmtpServer/Mail/Mailbox.cs b/Src/SmtpServer/Mail/Mailbox.cs index 5e76ecf..2175c49 100644 --- a/Src/SmtpServer/Mail/Mailbox.cs +++ b/Src/SmtpServer/Mail/Mailbox.cs @@ -8,7 +8,7 @@ public sealed class Mailbox : IMailbox /// /// Empty Mailbox /// - internal static readonly IMailbox Empty = new Mailbox(string.Empty, string.Empty); + public static readonly IMailbox Empty = new Mailbox(string.Empty, string.Empty); /// /// Constructor. From 4f91c833a20047f87da0e2748e8049781ef51f77 Mon Sep 17 00:00:00 2001 From: Tino Hager Date: Mon, 16 Sep 2024 15:17:49 +0200 Subject: [PATCH 15/15] small fixes --- Src/SmtpServer/Net/EndpointListenerFactory.cs | 2 +- Src/SmtpServer/Protocol/ProxyCommand.cs | 11 +++++++++-- Src/SmtpServer/Storage/MailboxFilter.cs | 5 ++++- Src/SmtpServer/Storage/MessageStore.cs | 5 ++++- 4 files changed, 18 insertions(+), 5 deletions(-) diff --git a/Src/SmtpServer/Net/EndpointListenerFactory.cs b/Src/SmtpServer/Net/EndpointListenerFactory.cs index 019c8bb..097efca 100644 --- a/Src/SmtpServer/Net/EndpointListenerFactory.cs +++ b/Src/SmtpServer/Net/EndpointListenerFactory.cs @@ -11,7 +11,7 @@ public class EndpointListenerFactory : IEndpointListenerFactory /// /// Default Endpoint Listener Factory /// - public static readonly IEndpointListenerFactory Default = new EndpointListenerFactory(); + internal static readonly IEndpointListenerFactory Default = new EndpointListenerFactory(); /// /// Raised when an endpoint has been started. diff --git a/Src/SmtpServer/Protocol/ProxyCommand.cs b/Src/SmtpServer/Protocol/ProxyCommand.cs index 821a493..ece8daf 100644 --- a/Src/SmtpServer/Protocol/ProxyCommand.cs +++ b/Src/SmtpServer/Protocol/ProxyCommand.cs @@ -11,8 +11,15 @@ namespace SmtpServer.Protocol /// public sealed class ProxyCommand : SmtpCommand { - internal const string ProxySourceEndpointKey = "Proxy:ProxySourceEndpoint"; - internal const string ProxyDestinationEndpointKey = "Proxy:ProxyDestinationEndpoint"; + /// + /// Proxy Source Endpoint Key + /// + public const string ProxySourceEndpointKey = "Proxy:ProxySourceEndpoint"; + + /// + /// Proxy Destination Endpoint Key + /// + public const string ProxyDestinationEndpointKey = "Proxy:ProxyDestinationEndpoint"; /// /// Smtp Proxy Command diff --git a/Src/SmtpServer/Storage/MailboxFilter.cs b/Src/SmtpServer/Storage/MailboxFilter.cs index 3f5c940..940fe52 100644 --- a/Src/SmtpServer/Storage/MailboxFilter.cs +++ b/Src/SmtpServer/Storage/MailboxFilter.cs @@ -9,7 +9,10 @@ namespace SmtpServer.Storage /// public abstract class MailboxFilter : IMailboxFilter { - internal static readonly IMailboxFilter Default = new DefaultMailboxFilter(); + /// + /// Default Mailbox Filter + /// + public static readonly IMailboxFilter Default = new DefaultMailboxFilter(); /// public virtual Task CanAcceptFromAsync( diff --git a/Src/SmtpServer/Storage/MessageStore.cs b/Src/SmtpServer/Storage/MessageStore.cs index 9fe28e0..9af44d4 100644 --- a/Src/SmtpServer/Storage/MessageStore.cs +++ b/Src/SmtpServer/Storage/MessageStore.cs @@ -10,7 +10,10 @@ namespace SmtpServer.Storage /// public abstract class MessageStore : IMessageStore { - internal static readonly IMessageStore Default = new DefaultMessageStore(); + /// + /// Default Message Store + /// + public static readonly IMessageStore Default = new DefaultMessageStore(); /// public abstract Task SaveAsync(ISessionContext context, IMessageTransaction transaction, ReadOnlySequence buffer, CancellationToken cancellationToken);