diff --git a/Src/SmtpServer/ISmtpServerOptions.cs b/Src/SmtpServer/ISmtpServerOptions.cs index b0c1daa..01a6266 100644 --- a/Src/SmtpServer/ISmtpServerOptions.cs +++ b/Src/SmtpServer/ISmtpServerOptions.cs @@ -42,5 +42,12 @@ public interface ISmtpServerOptions /// The size of the buffer that is read from each call to the underlying network client. /// int NetworkBufferSize { get; } + + /// + /// Gets the custom SMTP greeting message that the server sends immediately after a client connects, + /// typically as the initial "220" response. The message can be dynamically generated based on the session context. + /// If not set, a default greeting will be used (e.g., "220 mail.example.com ESMTP ready"). + /// + Func CustomSmtpGreeting { get; } } } diff --git a/Src/SmtpServer/SmtpServerOptionsBuilder.cs b/Src/SmtpServer/SmtpServerOptionsBuilder.cs index 307ff8f..7516728 100644 --- a/Src/SmtpServer/SmtpServerOptionsBuilder.cs +++ b/Src/SmtpServer/SmtpServerOptionsBuilder.cs @@ -24,6 +24,7 @@ public ISmtpServerOptions Build() MaxAuthenticationAttempts = 3, NetworkBufferSize = 128, CommandWaitTimeout = TimeSpan.FromMinutes(5), + CustomSmtpGreeting = null, }; _setters.ForEach(setter => setter(serverOptions)); @@ -157,6 +158,23 @@ public SmtpServerOptionsBuilder CommandWaitTimeout(TimeSpan value) return this; } + /// + /// Sets the custom SMTP greeting message sent to the client upon connection, + /// typically returned as the initial "220" response. + /// + /// + /// A delegate that returns the greeting message to send to the client, + /// based on the (e.g., client IP, TLS state). + /// Example: ctx => $"220 {sessionContext.ServerOptions.ServerName} ESMTP ready" + /// + /// An OptionsBuilder to continue building on. + public SmtpServerOptionsBuilder CustomGreetingMessage(Func smtpGreetingFunc) + { + _setters.Add(options => options.CustomSmtpGreeting = smtpGreetingFunc); + + return this; + } + #region SmtpServerOptions class SmtpServerOptions : ISmtpServerOptions @@ -200,6 +218,13 @@ class SmtpServerOptions : ISmtpServerOptions /// The size of the buffer that is read from each call to the underlying network client. /// public int NetworkBufferSize { get; set; } + + /// + /// Gets or sets the custom greeting message sent by the server in response to the initial SMTP connection. + /// This message is returned after the client connects and before any commands are issued (e.g., "220 mail.example.com v1.0 ESMTP ready"). + /// If not set, a default greeting will be used. + /// + public Func CustomSmtpGreeting { get; set; } } #endregion diff --git a/Src/SmtpServer/SmtpSession.cs b/Src/SmtpServer/SmtpSession.cs index 292b7f5..dfc8461 100644 --- a/Src/SmtpServer/SmtpSession.cs +++ b/Src/SmtpServer/SmtpSession.cs @@ -1,20 +1,21 @@ -using System; -using System.Threading; -using System.Threading.Tasks; -using SmtpServer.Protocol; -using System.Reflection; +using SmtpServer.ComponentModel; using SmtpServer.IO; -using System.IO.Pipelines; +using SmtpServer.Protocol; using SmtpServer.StateMachine; -using SmtpServer.ComponentModel; +using System; using System.Buffers; using System.Collections.Generic; +using System.IO.Pipelines; +using System.Reflection; +using System.Threading; +using System.Threading.Tasks; namespace SmtpServer { internal sealed class SmtpSession { const string BufferKey = "SmtpSession:Buffer"; + static readonly Version AssemblyVersion = typeof(SmtpSession).GetTypeInfo().Assembly.GetName().Version; readonly SmtpStateMachine _stateMachine; readonly SmtpSessionContext _context; @@ -188,9 +189,15 @@ static async Task ExecuteAsync(SmtpCommand command, SmtpSessionContext con /// A task which performs the operation. ValueTask OutputGreetingAsync(CancellationToken cancellationToken) { - var version = typeof(SmtpSession).GetTypeInfo().Assembly.GetName().Version; - - _context.Pipe.Output.WriteLine($"220 {_context.ServerOptions.ServerName} v{version} ESMTP ready"); + if (_context.ServerOptions.CustomSmtpGreeting is null) + { + var serverVersion = AssemblyVersion; + _context.Pipe.Output.WriteLine($"220 {_context.ServerOptions.ServerName} v{serverVersion} ESMTP ready"); + } + else + { + _context.Pipe.Output.WriteLine(_context.ServerOptions.CustomSmtpGreeting(_context)); + } return _context.Pipe.Output.FlushAsync(cancellationToken); }