Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
cosullivan committed Dec 27, 2018
1 parent c99a7e7 commit 01c158e
Show file tree
Hide file tree
Showing 22 changed files with 191 additions and 115 deletions.
6 changes: 4 additions & 2 deletions Src/SampleApp/Examples/SecureServerExample.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,11 @@ public static void Run()
var options = new SmtpServerOptionsBuilder()
.ServerName("SmtpServer SampleApp")
.Certificate(CreateCertificate())
.AllowUnsecureAuthentication(false)
.Endpoint(builder =>
builder
.Port(9025, true)
.AllowUnsecureAuthentication(false))
.UserAuthenticator(new SampleUserAuthenticator())
.Port(9025, true)
.Build();

var server = new SmtpServer.SmtpServer(options);
Expand Down
8 changes: 5 additions & 3 deletions Src/SampleApp/Examples/SessionContextExample.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,12 @@ public static void Run()

var options = new SmtpServerOptionsBuilder()
.ServerName("SmtpServer SampleApp")
.Port(9025)
.UserAuthenticator(new AuthenticationHandler())
.AllowUnsecureAuthentication()
.AuthenticationRequired()
.Endpoint(builder =>
builder
.AllowUnsecureAuthentication()
.AuthenticationRequired()
.Port(9025))
.Build();

var server = new SmtpServer.SmtpServer(options);
Expand Down
5 changes: 5 additions & 0 deletions Src/SmtpServer.Tests/MailClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,11 @@ public static MimeMessage Message(
public static SmtpClient Client(string host = "localhost", int port = 9025)
{
var client = new SmtpClient();

client.Connected += (sender, args) =>
{

};

client.Connect("localhost", 9025);

Expand Down
2 changes: 1 addition & 1 deletion Src/SmtpServer.Tests/SmtpParserTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ public void CanMakeHelo()
// assert
Assert.True(result);
Assert.True(command is HeloCommand);
Assert.Equal("abc-1-def.mail.com", ((HeloCommand)command).Domain);
Assert.Equal("abc-1-def.mail.com", ((HeloCommand)command).DomainOrAddress);
}

[Theory]
Expand Down
5 changes: 3 additions & 2 deletions Src/SmtpServer.Tests/SmtpServer.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@
<Reference Include="BouncyCastle.Crypto, Version=1.8.2.0, Culture=neutral, PublicKeyToken=0e99375e54769942">
<HintPath>..\packages\BouncyCastle.1.8.2\lib\BouncyCastle.Crypto.dll</HintPath>
</Reference>
<Reference Include="MailKit, Version=2.1.0.0, Culture=neutral, PublicKeyToken=4e064fe7c44a8f1b, processorArchitecture=MSIL">
<HintPath>..\packages\MailKit.2.1.1\lib\net45\MailKit.dll</HintPath>
<Reference Include="MailKit, Version=1.16.0.0, Culture=neutral, PublicKeyToken=4e064fe7c44a8f1b, processorArchitecture=MSIL">
<HintPath>..\packages\MailKit.1.16.1\lib\net451\MailKit.dll</HintPath>
</Reference>
<Reference Include="MimeKit, Version=2.1.0.0, Culture=neutral, PublicKeyToken=bede1c8a46c66814, processorArchitecture=MSIL">
<HintPath>..\packages\MimeKit.2.1.1\lib\net45\MimeKit.dll</HintPath>
Expand Down Expand Up @@ -74,6 +74,7 @@
<Compile Include="ByteArrayTokenReaderTests.cs" />
</ItemGroup>
<ItemGroup>
<None Include="app.config" />
<None Include="packages.config">
<SubType>Designer</SubType>
</None>
Expand Down
59 changes: 36 additions & 23 deletions Src/SmtpServer.Tests/SmtpServerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using System.Threading;
using System.Threading.Tasks;
using MailKit;
using MailKit.Net.Smtp;
using SmtpServer.Mail;
using SmtpServer.Tests.Mocks;
using Xunit;
Expand Down Expand Up @@ -76,7 +77,7 @@ public void CanAuthenticateUser()
return true;
});

using (CreateServer(options => options.AllowUnsecureAuthentication().UserAuthenticator(userAuthenticator)))
using (CreateServer(server => server.UserAuthenticator(userAuthenticator), endpoint => endpoint.AllowUnsecureAuthentication()))
{
// act
MailClient.Send(user: "user", password: "password");
Expand Down Expand Up @@ -105,7 +106,7 @@ public void CanFailAuthenticationEmptyUserOrPassword(string user, string passwor
return false;
});

using (CreateServer(options => options.AllowUnsecureAuthentication().UserAuthenticator(userAuthenticator)))
using (CreateServer(server => server.UserAuthenticator(userAuthenticator), endpoint => endpoint.AllowUnsecureAuthentication()))
{
// act and assert
Assert.Throws<MailKit.Security.AuthenticationException>(() => MailClient.Send(user: user, password: password));
Expand Down Expand Up @@ -183,14 +184,7 @@ public void CanReturnSmtpResponseException_DoesNotQuit()
public void CanReturnSmtpResponseException_SessionWillQuit()
{
// arrange
var mailboxFilter = new DelegatingMailboxFilter(@from =>
{
throw new SmtpResponseException(SmtpResponse.AuthenticationRequired, true);

#pragma warning disable 162
return MailboxFilterResult.Yes;
#pragma warning restore 162
});
var mailboxFilter = new DelegatingMailboxFilter(@from => throw new SmtpResponseException(SmtpResponse.AuthenticationRequired, true));

using (CreateServer(options => options.MailboxFilter(mailboxFilter)))
{
Expand All @@ -209,7 +203,7 @@ public void CanForceUserAuthentication_DoesNotThrowIfLoginIsSent()
{
var userAuthenticator = new DelegatingUserAuthenticator((user, password) => true);

using (CreateServer(options => options.AllowUnsecureAuthentication().AuthenticationRequired().UserAuthenticator(userAuthenticator)))
using (CreateServer(server => server.UserAuthenticator(userAuthenticator), endpoint => endpoint.AllowUnsecureAuthentication().AuthenticationRequired()))
{
MailClient.Send(user: "user", password: "password");
}
Expand All @@ -220,7 +214,7 @@ public void CanForceUserAuthentication_ThrowsIfLoginIsNotSent()
{
var userAuthenticator = new DelegatingUserAuthenticator((user, password) => true);

using (CreateServer(options => options.AllowUnsecureAuthentication().AuthenticationRequired().UserAuthenticator(userAuthenticator)))
using (CreateServer(server => server.UserAuthenticator(userAuthenticator), endpoint => endpoint.AllowUnsecureAuthentication().AuthenticationRequired()))
{
Assert.Throws<ServiceNotAuthenticatedException>(() => MailClient.Send());
}
Expand Down Expand Up @@ -284,12 +278,13 @@ public void ServerCanBeSecuredAndAuthenticated()
ServicePointManager.ServerCertificateValidationCallback = IgnoreCertificateValidationFailureForTestingOnly;

using (var disposable = CreateServer(
options =>
options
server =>
server
.UserAuthenticator(userAuthenticator)
.AllowUnsecureAuthentication(true)
.Certificate(CreateCertificate())
.SupportedSslProtocols(SslProtocols.Default)))
.SupportedSslProtocols(SslProtocols.Default),
endpoint =>
endpoint.AllowUnsecureAuthentication(true)))
{
ISessionContext sessionContext = null;
var sessionCreatedHandler = new EventHandler<SessionEventArgs>(
Expand Down Expand Up @@ -322,7 +317,7 @@ public void EndpointListenerWillRaiseEndPointEvents()
endpointListenerFactory.EndpointStarted += (sender, e) => { started = true; };
endpointListenerFactory.EndpointStopped += (sender, e) => { stopped = true; };

using (CreateServer(options => options.EndpointListenerFactory(endpointListenerFactory)))
using (CreateServer(server => server.EndpointListenerFactory(endpointListenerFactory)))
{
MailClient.Send();
}
Expand Down Expand Up @@ -350,22 +345,40 @@ static X509Certificate2 CreateCertificate()
/// <returns>A disposable instance which will close and release the server instance.</returns>
SmtpServerDisposable CreateServer()
{
return CreateServer(options => { });
return CreateServer(options => { }, options => { });
}

/// <summary>
/// Create a running instance of a server.
/// </summary>
/// <param name="serverConfiguration">The configuration to apply to run the server.</param>
/// <returns>A disposable instance which will close and release the server instance.</returns>
SmtpServerDisposable CreateServer(Action<SmtpServerOptionsBuilder> serverConfiguration)
{
return CreateServer(serverConfiguration, endpointConfiguration => { });
}

/// <summary>
/// Create a running instance of a server.
/// </summary>
/// <param name="configuration">The configuration to apply to run the server.</param>
/// <param name="serverConfiguration">The configuration to apply to run the server.</param>
/// <param name="endpointConfiguration">The configuration to apply to the endpoint.</param>
/// <returns>A disposable instance which will close and release the server instance.</returns>
SmtpServerDisposable CreateServer(Action<SmtpServerOptionsBuilder> configuration)
SmtpServerDisposable CreateServer(
Action<SmtpServerOptionsBuilder> serverConfiguration,
Action<EndpointDefinitionBuilder> endpointConfiguration)
{
var options = new SmtpServerOptionsBuilder()
.ServerName("localhost")
.Port(9025)
.Endpoint(
endpointBuilder =>
{
endpointBuilder.Port(9025);
endpointConfiguration(endpointBuilder);
})
.MessageStore(MessageStore);

configuration(options);
serverConfiguration(options);

var server = new SmtpServer(options.Build());
var smtpServerTask = server.StartAsync(CancellationTokenSource.Token);
Expand All @@ -384,7 +397,7 @@ SmtpServerDisposable CreateServer(Action<SmtpServerOptionsBuilder> configuration
}
});
}

/// <summary>
/// The message store that is being used to store the messages by default.
/// </summary>
Expand Down
15 changes: 15 additions & 0 deletions Src/SmtpServer.Tests/app.config
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="MimeKit" publicKeyToken="bede1c8a46c66814" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-2.1.0.0" newVersion="2.1.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="BouncyCastle.Crypto" publicKeyToken="0e99375e54769942" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-1.8.2.0" newVersion="1.8.2.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>
2 changes: 1 addition & 1 deletion Src/SmtpServer.Tests/packages.config
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="BouncyCastle" version="1.8.2" targetFramework="net461" />
<package id="MailKit" version="2.1.1" targetFramework="net461" />
<package id="MailKit" version="1.16.1" targetFramework="net461" />
<package id="MimeKit" version="2.1.1" targetFramework="net461" />
<package id="xunit" version="2.2.0" targetFramework="net462" />
<package id="xunit.abstractions" version="2.0.1" targetFramework="net462" />
Expand Down
45 changes: 45 additions & 0 deletions Src/SmtpServer/EndpointDefinitionBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,17 @@ public EndpointDefinitionBuilder Endpoint(IPEndPoint endpoint)
return this;
}

/// <summary>
/// Adds an endpoint with the given port.
/// </summary>
/// <param name="port">The port to add as the endpoint.</param>
/// <param name="isSecure">Indicates whether the port is secure by default.</param>
/// <returns>A OptionsBuilder to continue building on.</returns>
public EndpointDefinitionBuilder Port(int port, bool isSecure)
{
return Port(port).IsSecure(isSecure);
}

/// <summary>
/// Adds an endpoint with the given port.
/// </summary>
Expand All @@ -58,6 +69,30 @@ public EndpointDefinitionBuilder IsSecure(bool value)
return this;
}

/// <summary>
/// Sets a value indicating whether the client must authenticate in order to proceed.
/// </summary>
/// <param name="value">true if the client must issue an AUTH command before sending any mail, false if not.</param>
/// <returns>A OptionsBuilder to continue building on.</returns>
public EndpointDefinitionBuilder AuthenticationRequired(bool value = true)
{
_setters.Add(options => options.AuthenticationRequired = value);

return this;
}

/// <summary>
/// Sets a value indicating whether authentication should be allowed on an unsecure session.
/// </summary>
/// <param name="value">true if the AUTH command is available on an unsecure session, false if not.</param>
/// <returns>A OptionsBuilder to continue building on.</returns>
public EndpointDefinitionBuilder AllowUnsecureAuthentication(bool value = true)
{
_setters.Add(options => options.AllowUnsecureAuthentication = value);

return this;
}

#region EndpointDefinition

internal sealed class EndpointDefinition : IEndpointDefinition
Expand All @@ -71,6 +106,16 @@ internal sealed class EndpointDefinition : IEndpointDefinition
/// Indicates whether the endpoint is secure by default.
/// </summary>
public bool IsSecure { get; set; }

/// <summary>
/// Gets a value indicating whether the client must authenticate in order to proceed.
/// </summary>
public bool AuthenticationRequired { get; set; }

/// <summary>
/// Gets a value indicating whether authentication should be allowed on an unsecure session.
/// </summary>
public bool AllowUnsecureAuthentication { get; set; }
}

#endregion
Expand Down
10 changes: 10 additions & 0 deletions Src/SmtpServer/IEndpointDefinition.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,15 @@ public interface IEndpointDefinition
/// Indicates whether the endpoint is secure by default.
/// </summary>
bool IsSecure { get; }

/// <summary>
/// Gets a value indicating whether the client must authenticate in order to proceed.
/// </summary>
bool AuthenticationRequired { get; }

/// <summary>
/// Gets a value indicating whether authentication should be allowed on an unsecure session.
/// </summary>
bool AllowUnsecureAuthentication { get; }
}
}
5 changes: 5 additions & 0 deletions Src/SmtpServer/ISessionContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@ public interface ISessionContext
/// </summary>
ISmtpServerOptions ServerOptions { get; }

/// <summary>
/// Gets the endpoint definition.
/// </summary>
IEndpointDefinition EndpointDefinition { get; }

/// <summary>
/// Gets the text stream to read from and write to.
/// </summary>
Expand Down
10 changes: 0 additions & 10 deletions Src/SmtpServer/ISmtpServerOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -55,16 +55,6 @@ public interface ISmtpServerOptions
/// </summary>
IUserAuthenticatorFactory UserAuthenticatorFactory { get; }

/// <summary>
/// Gets a value indicating whether authentication should be allowed on an unsecure session.
/// </summary>
bool AllowUnsecureAuthentication { get; }

/// <summary>
/// Gets a value indicating whether the client must authenticate in order to proceed.
/// </summary>
bool AuthenticationRequired { get; }

/// <summary>
/// The supported SSL protocols.
/// </summary>
Expand Down
2 changes: 1 addition & 1 deletion Src/SmtpServer/Protocol/EhloCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ bool IsPlainLoginAllowed(SmtpSessionContext session)
return false;
}

return session.NetworkClient.IsSecure || Options.AllowUnsecureAuthentication;
return session.NetworkClient.IsSecure || session.EndpointDefinition.AllowUnsecureAuthentication;
}

/// <summary>
Expand Down
10 changes: 5 additions & 5 deletions Src/SmtpServer/Protocol/HeloCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@ public sealed class HeloCommand : SmtpCommand
/// Constructor.
/// </summary>
/// <param name="options">The server options.</param>
/// <param name="domain">The domain name.</param>
internal HeloCommand(ISmtpServerOptions options, string domain) : base(options)
/// <param name="domainOrAddress">The domain name.</param>
internal HeloCommand(ISmtpServerOptions options, string domainOrAddress) : base(options)
{
Domain = domain;
DomainOrAddress = domainOrAddress;
}

/// <summary>
Expand All @@ -27,7 +27,7 @@ internal HeloCommand(ISmtpServerOptions options, string domain) : base(options)
/// if the current state is to be maintained.</returns>
internal override async Task<bool> ExecuteAsync(SmtpSessionContext context, CancellationToken cancellationToken)
{
var response = new SmtpResponse(SmtpReplyCode.Ok, $"Hello {Domain}, haven't we met before?");
var response = new SmtpResponse(SmtpReplyCode.Ok, $"Hello {DomainOrAddress}, haven't we met before?");

await context.NetworkClient.ReplyAsync(response, cancellationToken).ReturnOnAnyThread();

Expand All @@ -37,6 +37,6 @@ internal override async Task<bool> ExecuteAsync(SmtpSessionContext context, Canc
/// <summary>
/// Gets the domain name.
/// </summary>
public string Domain { get; }
public string DomainOrAddress { get; }
}
}
Loading

0 comments on commit 01c158e

Please sign in to comment.