Skip to content

Commit 2e280a4

Browse files
authored
Add support for custom SMTP greeting message (cosullivan#255)
* Add support for custom SMTP greeting message Introduces a CustomGreetingMessage property to ISmtpServerOptions and SmtpServerOptionsBuilder, allowing configuration of the initial SMTP greeting sent to clients. SmtpSession now uses this custom message if set, otherwise defaults to the standard greeting with server name and version. * optimize code
1 parent 6fba0aa commit 2e280a4

File tree

3 files changed

+49
-10
lines changed

3 files changed

+49
-10
lines changed

Src/SmtpServer/ISmtpServerOptions.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,5 +42,12 @@ public interface ISmtpServerOptions
4242
/// The size of the buffer that is read from each call to the underlying network client.
4343
/// </summary>
4444
int NetworkBufferSize { get; }
45+
46+
/// <summary>
47+
/// Gets the custom SMTP greeting message that the server sends immediately after a client connects,
48+
/// typically as the initial "220" response. The message can be dynamically generated based on the session context.
49+
/// If not set, a default greeting will be used (e.g., "220 mail.example.com ESMTP ready").
50+
/// </summary>
51+
Func<ISessionContext, string> CustomSmtpGreeting { get; }
4552
}
4653
}

Src/SmtpServer/SmtpServerOptionsBuilder.cs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ public ISmtpServerOptions Build()
2424
MaxAuthenticationAttempts = 3,
2525
NetworkBufferSize = 128,
2626
CommandWaitTimeout = TimeSpan.FromMinutes(5),
27+
CustomSmtpGreeting = null,
2728
};
2829

2930
_setters.ForEach(setter => setter(serverOptions));
@@ -157,6 +158,23 @@ public SmtpServerOptionsBuilder CommandWaitTimeout(TimeSpan value)
157158
return this;
158159
}
159160

161+
/// <summary>
162+
/// Sets the custom SMTP greeting message sent to the client upon connection,
163+
/// typically returned as the initial "220" response.
164+
/// </summary>
165+
/// <param name="smtpGreetingFunc">
166+
/// A delegate that returns the greeting message to send to the client,
167+
/// based on the <see cref="ISessionContext"/> (e.g., client IP, TLS state).
168+
/// Example: <c>ctx => $"220 {sessionContext.ServerOptions.ServerName} ESMTP ready"</c>
169+
/// </param>
170+
/// <returns>An OptionsBuilder to continue building on.</returns>
171+
public SmtpServerOptionsBuilder CustomGreetingMessage(Func<ISessionContext, string> smtpGreetingFunc)
172+
{
173+
_setters.Add(options => options.CustomSmtpGreeting = smtpGreetingFunc);
174+
175+
return this;
176+
}
177+
160178
#region SmtpServerOptions
161179

162180
class SmtpServerOptions : ISmtpServerOptions
@@ -200,6 +218,13 @@ class SmtpServerOptions : ISmtpServerOptions
200218
/// The size of the buffer that is read from each call to the underlying network client.
201219
/// </summary>
202220
public int NetworkBufferSize { get; set; }
221+
222+
/// <summary>
223+
/// Gets or sets the custom greeting message sent by the server in response to the initial SMTP connection.
224+
/// This message is returned after the client connects and before any commands are issued (e.g., "220 mail.example.com v1.0 ESMTP ready").
225+
/// If not set, a default greeting will be used.
226+
/// </summary>
227+
public Func<ISessionContext, string> CustomSmtpGreeting { get; set; }
203228
}
204229

205230
#endregion

Src/SmtpServer/SmtpSession.cs

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,21 @@
1-
using System;
2-
using System.Threading;
3-
using System.Threading.Tasks;
4-
using SmtpServer.Protocol;
5-
using System.Reflection;
1+
using SmtpServer.ComponentModel;
62
using SmtpServer.IO;
7-
using System.IO.Pipelines;
3+
using SmtpServer.Protocol;
84
using SmtpServer.StateMachine;
9-
using SmtpServer.ComponentModel;
5+
using System;
106
using System.Buffers;
117
using System.Collections.Generic;
8+
using System.IO.Pipelines;
9+
using System.Reflection;
10+
using System.Threading;
11+
using System.Threading.Tasks;
1212

1313
namespace SmtpServer
1414
{
1515
internal sealed class SmtpSession
1616
{
1717
const string BufferKey = "SmtpSession:Buffer";
18+
static readonly Version AssemblyVersion = typeof(SmtpSession).GetTypeInfo().Assembly.GetName().Version;
1819

1920
readonly SmtpStateMachine _stateMachine;
2021
readonly SmtpSessionContext _context;
@@ -188,9 +189,15 @@ static async Task<bool> ExecuteAsync(SmtpCommand command, SmtpSessionContext con
188189
/// <returns>A task which performs the operation.</returns>
189190
ValueTask<FlushResult> OutputGreetingAsync(CancellationToken cancellationToken)
190191
{
191-
var version = typeof(SmtpSession).GetTypeInfo().Assembly.GetName().Version;
192-
193-
_context.Pipe.Output.WriteLine($"220 {_context.ServerOptions.ServerName} v{version} ESMTP ready");
192+
if (_context.ServerOptions.CustomSmtpGreeting is null)
193+
{
194+
var serverVersion = AssemblyVersion;
195+
_context.Pipe.Output.WriteLine($"220 {_context.ServerOptions.ServerName} v{serverVersion} ESMTP ready");
196+
}
197+
else
198+
{
199+
_context.Pipe.Output.WriteLine(_context.ServerOptions.CustomSmtpGreeting(_context));
200+
}
194201

195202
return _context.Pipe.Output.FlushAsync(cancellationToken);
196203
}

0 commit comments

Comments
 (0)