Revision: 32
Author: tmyroadctfig
Date: 2006-05-14 01:04:49 -0700 (Sun, 14 May 2006)
ViewCVS: http://svn.sourceforge.net/nmailserver/?rev=32&view=rev
Log Message:
-----------
Committed patch from Jared Hodges - refactoring of the SMTP service.
Modified Paths:
--------------
NMail/trunk/NMail.SmtpService/Command/HeloCommand.cs
NMail/trunk/NMail.SmtpService/Command/RecipientCommand.cs
NMail/trunk/NMail.SmtpService/Command/SenderCommand.cs
NMail/trunk/NMail.SmtpService/NMail.SmtpService.csproj
NMail/trunk/NMail.SmtpService/SmtpSession.cs
NMail/trunk/NMail.SmtpService/State/AbstractSmtpState.cs
NMail/trunk/NMail.SmtpService/State/ConnectedState.cs
NMail/trunk/NMail.SmtpService/State/NegotiatedState.cs
NMail/trunk/NMail.SmtpService/State/RecipientRecievedState.cs
NMail/trunk/NMail.SmtpService/State/SenderRecievedState.cs
Added Paths:
-----------
NMail/trunk/NMail.SmtpService/Command/DataCommand.cs
NMail/trunk/NMail.SmtpService/Command/EhloCommand.cs
NMail/trunk/NMail.SmtpService/Command/ISmtpCommand.cs
NMail/trunk/NMail.SmtpService/Command/NoopCommand.cs
NMail/trunk/NMail.SmtpService/Command/QuitCommand.cs
NMail/trunk/NMail.SmtpService/Command/ResetCommand.cs
NMail/trunk/NMail.SmtpService/Command/StartTlsCommand.cs
Removed Paths:
-------------
NMail/trunk/NMail.SmtpService/Command/AbstractCommand.cs
NMail/trunk/NMail.SmtpService/State/DataState.cs
NMail/trunk/NMail.SmtpService/State/SmtpState.cs
Deleted: NMail/trunk/NMail.SmtpService/Command/AbstractCommand.cs
===================================================================
--- NMail/trunk/NMail.SmtpService/Command/AbstractCommand.cs 2006-05-14 06:36:32 UTC (rev 31)
+++ NMail/trunk/NMail.SmtpService/Command/AbstractCommand.cs 2006-05-14 08:04:49 UTC (rev 32)
@@ -1,48 +0,0 @@
-/*
- * Copyright 2004-2006 Luke Quinane and Daniel Frampton
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-using System;
-
-namespace NMail.SmtpService.Command {
- /// <summary>
- /// Provides an abstract SMTP command.
- /// </summary>
- public abstract class AbstractCommand {
- /// <summary>
- /// Creates the command from the given command string. Any parameters are
- /// extracted at this point.
- /// </summary>
- /// <param name="command">The command string.</param>
- protected AbstractCommand(string command) {
- this.command = command;
- }
-
- /// <summary>
- /// The full command string.
- /// </summary>
- private string command;
-
- /// <summary>
- /// The full command string.
- /// </summary>
- public string Command {
- get {
- return this.command;
- }
- }
- }
-}
Added: NMail/trunk/NMail.SmtpService/Command/DataCommand.cs
===================================================================
--- NMail/trunk/NMail.SmtpService/Command/DataCommand.cs (rev 0)
+++ NMail/trunk/NMail.SmtpService/Command/DataCommand.cs 2006-05-14 08:04:49 UTC (rev 32)
@@ -0,0 +1,138 @@
+/*
+ * Copyright 2004-2006 Luke Quinane, Daniel Frampton and Jared Hodges.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+using System;
+using System.Collections.Specialized;
+using System.Collections.Generic;
+using System.Text;
+using System.Globalization;
+using System.Text.RegularExpressions;
+
+using NMail.DataTypes;
+using NMail.Helper;
+using NMail.Configuration;
+using NMail.SmtpService.State;
+using NMail.SmtpService.Configuration;
+
+namespace NMail.SmtpService.Command {
+ /// <summary>
+ /// Represents the logic for handling the SMTP command DATA.
+ /// This command does not expect any arguments.
+ /// If no arguments are provided to the DATA command server will:
+ /// <list>
+ /// <item>Send a continuation response.</item>
+ /// <item>Read lines of message data until a single period is received (".")</item>
+ /// <item>Append any required headers.</item>
+ /// <item>Attempt to spool the message.</item>
+ /// <item>Send a 250 Ok response if successful.</item>
+ /// <item>The SMTP session will be transitioned to the NegotiatedState.</item>
+ /// </list>
+ /// </summary>
+ class DataCommand : ISmtpCommand {
+ #region ISmtpCommand Members
+
+ public void Execute(SmtpSession session, string[] tokens) {
+ if (tokens.Length != 1) {
+ throw new ApplicationException("Invalid command syntax");
+ }
+
+ session.Connection.OkWaitingData();
+
+ int maximumMessageSize = SmtpServiceConfiguration.Current.MaximumMessageSize;
+ IDnsClient dns = NMailConfiguration.Current.DnsClient;
+ int currentMaxReadLength = session.Connection.MaximumReadLength;
+ ByteStringBuilder messageData = new ByteStringBuilder(
+ session.MessageEncoding,
+ session.SizeEstimate,
+ maximumMessageSize);
+
+ try {
+ // Temporarily allow lines of any length
+ session.Connection.MaximumReadLength = maximumMessageSize;
+
+ // Data is a special case where we don't want to split each incoming line
+ while (true) {
+ string dataLine = session.Connection.ReadLine(
+ session.MessageEncoding,
+ SmtpServiceConfiguration.Current.Timeout);
+
+ if (dataLine == ".") {
+ break;
+ } else {
+ try {
+ // TODO: take action if a line is too long?
+
+ messageData.Append(dataLine + session.Connection.Terminator);
+ } catch (ArgumentOutOfRangeException) {
+ // Message is too large
+ session.Connection.ErrorMessageTooLarge();
+
+ // All bets are off?
+ session.Message = null;
+ session.CurrentState = new NegotiatedState(session);
+ return;
+ }
+ }
+ }
+ } finally {
+ // Reset the maximum read line length
+ session.Connection.MaximumReadLength = currentMaxReadLength;
+ }
+
+ // Parse the message headers
+ session.Message.Data = new Message(messageData.ToByteString());
+ MessageHeaders headers = session.Message.Data.Headers;
+
+ // Prepare details for the headers
+ string currentTime = DateTime.Now.ToString("r", DateTimeFormatInfo.InvariantInfo);
+ Host clientHost = dns.ResolveHost(session.ClientAddress);
+ string transport = (session.Esmtp) ? "ESMTP" : "SMTP";
+
+ // Add the received header
+ string recievedHeader = string.Format(RecievedHeader, session.ReportedHost,
+ clientHost, session.ClientAddress, SmtpServiceConfiguration.Current.VisibleHost, transport, currentTime);
+ headers.AppendStart("Recieved", recievedHeader);
+
+ // All message must have a from address and a date.
+ if (headers["From"] == null) {
+ headers["From"] = session.Message.Sender.ToString(true);
+ }
+ if (headers["Date"] == null) {
+ headers["Date"] = currentTime;
+ }
+
+ try {
+ // Attempt to spool the message
+ NMailConfiguration.Current.Router.SpoolMessage(session.Message);
+ session.Connection.Ok();
+ } catch (Exception ex) {
+ session.Log.Warn("Error spooling message.", ex);
+ session.Connection.TemporaryBadResponse(ex.Message);
+ }
+
+ session.Message = null;
+ session.CurrentState = new NegotiatedState(session);
+ }
+
+ /// <summary>
+ /// The format of the "Received:" header line that must be added to the message.
+ /// </summary>
+ public const string RecievedHeader = "from {0} ({1} [{2}])\r\n\tby NMail ({3}) with {4} {5}";
+
+ #endregion
+ }
+}
Added: NMail/trunk/NMail.SmtpService/Command/EhloCommand.cs
===================================================================
--- NMail/trunk/NMail.SmtpService/Command/EhloCommand.cs (rev 0)
+++ NMail/trunk/NMail.SmtpService/Command/EhloCommand.cs 2006-05-14 08:04:49 UTC (rev 32)
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2004-2006 Luke Quinane, Daniel Frampton and Jared Hodges.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+using NMail.SmtpService.Configuration;
+
+namespace NMail.SmtpService.Command {
+ /// <summary>
+ /// Represents the logic for handling the SMTP command EHLO.
+ /// This command expects the connecting host's name as its sole argument.
+ /// The execution of a valid EHLO command will result in the following:
+ /// <list>
+ /// <item>The session will be marked as Extended SMTP (ESMTP)</item>
+ /// <item>The hostname of the client will be attached to the session</item>
+ /// <item>A 250 response will be sent to the client, with a list of the
+ /// SMTP server's capabilities.
+ /// </item>
+ /// <item>The SMTP session will be transitioned to the NegotiatedState.</item>
+ /// </list>
+ /// </summary>
+ class EhloCommand : HeloCommand {
+ #region ISmtpCommand Members
+
+ public override void Execute(SmtpSession session, string[] tokens) {
+ ExecuteNoResponse(session, tokens);
+ session.Esmtp = true;
+ session.Connection.OkCapabilities(SmtpServiceConfiguration.Current.VisibleHost,
+ SmtpServiceConfiguration.Current.MaximumMessageSize);
+ }
+
+ #endregion
+ }
+}
Modified: NMail/trunk/NMail.SmtpService/Command/HeloCommand.cs
===================================================================
--- NMail/trunk/NMail.SmtpService/Command/HeloCommand.cs 2006-05-14 06:36:32 UTC (rev 31)
+++ NMail/trunk/NMail.SmtpService/Command/HeloCommand.cs 2006-05-14 08:04:49 UTC (rev 32)
@@ -1,5 +1,5 @@
/*
- * Copyright 2004-2006 Luke Quinane and Daniel Frampton
+ * Copyright 2004-2006 Luke Quinane, Daniel Frampton and Jared Hodges.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,40 +19,51 @@
using NMail.DataTypes;
using NMail.Helper;
+using NMail.SmtpService.Configuration;
+using NMail.SmtpService.State;
namespace NMail.SmtpService.Command {
/// <summary>
- /// Provides parsing for the HELO command.
+ /// Represents the logic for handling the SMTP command HELO.
+ /// This command expects the connecting host's name as its sole argument.
+ /// The execution of a valid HELO command will result in the following:
+ /// <list>
+ /// <item>The session will be marked as NOT Extended SMTP (ESMTP)</item>
+ /// <item>The hostname of the client will be attached to the session</item>
+ /// <item>A 250 response will be sent to the client</item>
+ /// <item>The SMTP session will be transitioned to the NegotiatedState</item>
+ /// </list>
/// </summary>
- public class HeloCommand : AbstractCommand {
- /// <summary>
- /// The extracted hostname.
- /// </summary>
- private Host hostname;
+ public class HeloCommand : ISmtpCommand {
+ #region ISmtpCommand Members
+
+ public virtual void Execute(SmtpSession session, string[] tokens) {
+ ExecuteNoResponse(session, tokens);
+ session.Connection.OkHelo(SmtpServiceConfiguration.Current.VisibleHost);
+ }
+
+ #endregion
+
/// <summary>
- /// Attempts to extract a hostname from a HELO command.
+ /// A helper that executes all command logic except for response to the client
/// </summary>
- /// <param name="command">The full command string.</param>
- public HeloCommand(string command) : base(command) {
- string[] tokens = command.Split(' ');
+ /// <param name="session"></param>
+ /// <param name="tokens"></param>
+ protected void ExecuteNoResponse(SmtpSession session, string[] tokens) {
+ Host hostname = null;
if (tokens.Length == 2) {
string hostStr = tokens[1];
hostStr = StringTokenizer.UnBracketString(hostStr);
- this.hostname = new Host(hostStr);
+ hostname = new Host(hostStr);
} else {
throw new ApplicationException("Invalid command syntax");
}
- }
- /// <summary>
- /// The extracted hostname.
- /// </summary>
- public Host Hostname {
- get {
- return this.hostname;
- }
+ session.ReportedHost = hostname;
+ session.Esmtp = false;
+ session.CurrentState = new NegotiatedState(session);
}
}
}
Added: NMail/trunk/NMail.SmtpService/Command/ISmtpCommand.cs
===================================================================
--- NMail/trunk/NMail.SmtpService/Command/ISmtpCommand.cs (rev 0)
+++ NMail/trunk/NMail.SmtpService/Command/ISmtpCommand.cs 2006-05-14 08:04:49 UTC (rev 32)
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2004-2006 Luke Quinane, Daniel Frampton and Jared Hodges.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+using System;
+
+namespace NMail.SmtpService.Command {
+ /// <summary>
+ /// Interface for SMTP commands.
+ /// </summary>
+ public interface ISmtpCommand {
+ /// <summary>
+ /// Executes the command and sends a response to the client.
+ /// </summary>
+ /// <param name="session">The current command's session, including the connection
+ /// for delivering a response</param>
+ /// <param name="tokens">The arguments supplied to the command. tokens[0] is the command
+ /// name itself</param>
+ void Execute(SmtpSession session, string [] tokens);
+ }
+}
Added: NMail/trunk/NMail.SmtpService/Command/NoopCommand.cs
===================================================================
--- NMail/trunk/NMail.SmtpService/Command/NoopCommand.cs (rev 0)
+++ NMail/trunk/NMail.SmtpService/Command/NoopCommand.cs 2006-05-14 08:04:49 UTC (rev 32)
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2004-2006 Luke Quinane, Daniel Frampton and Jared Hodges.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace NMail.SmtpService.Command {
+ /// <summary>
+ /// Represents the logic for handling the SMTP command NOOP.
+ /// </summary>
+ class NoopCommand : ISmtpCommand {
+ #region ISmtpCommand Members
+
+ public void Execute(SmtpSession session, string[] tokens) {
+ session.Connection.Ok();
+ }
+
+ #endregion
+ }
+}
Added: NMail/trunk/NMail.SmtpService/Command/QuitCommand.cs
===================================================================
--- NMail/trunk/NMail.SmtpService/Command/QuitCommand.cs (rev 0)
+++ NMail/trunk/NMail.SmtpService/Command/QuitCommand.cs 2006-05-14 08:04:49 UTC (rev 32)
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2004-2006 Luke Quinane, Daniel Frampton and Jared Hodges.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace NMail.SmtpService.Command {
+ /// <summary>
+ /// Represents the logic for handling the SMTP command QUIT.
+ /// Executing this command causes the connection to the client to be closed.
+ /// </summary>
+ class QuitCommand : ISmtpCommand {
+
+ #region ISmtpCommand Members
+
+ public void Execute(SmtpSession session, string[] tokens) {
+ session.Connection.Close();
+ }
+
+ #endregion
+ }
+}
Modified: NMail/trunk/NMail.SmtpService/Command/RecipientCommand.cs
===================================================================
--- NMail/trunk/NMail.SmtpService/Command/RecipientCommand.cs 2006-05-14 06:36:32 UTC (rev 31)
+++ NMail/trunk/NMail.SmtpService/Command/RecipientCommand.cs 2006-05-14 08:04:49 UTC (rev 32)
@@ -1,5 +1,5 @@
/*
- * Copyright 2004-2006 Luke Quinane and Daniel Frampton
+ * Copyright 2004-2006 Luke Quinane, Daniel Frampton and Jared Hodges.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,23 +18,19 @@
using System;
using NMail.DataTypes;
+using NMail.Configuration;
+using NMail.SmtpService.Configuration;
+using NMail.SmtpService.State;
namespace NMail.SmtpService.Command {
/// <summary>
/// Provides parsing for the "RCPT TO:" command.
/// </summary>
- public class RecipientCommand : AbstractCommand {
- /// <summary>
- /// The extracted recipient.
- /// </summary>
- private EmailAddress recipient;
+ public class RecipientCommand : ISmtpCommand {
+ #region ISmtpCommand Members
- /// <summary>
- /// Attempts to extract a recipient from a "RCPT TO:" command.
- /// </summary>
- /// <param name="command">The full command string.</param>
- public RecipientCommand(string command) : base(command) {
- string[] tokens = command.Split(' ');
+ public void Execute(SmtpSession session, string[] tokens) {
+ EmailAddress recipientEmail = null;
if (tokens[1].ToLower().StartsWith("to:")) {
// Extract the recipient address
@@ -43,28 +39,48 @@
if (tokens.Length != 2) {
throw new ApplicationException("Bad command syntax.");
} else {
- this.recipient = new EmailAddress(tokens[1].Remove(0, 3), true);
+ recipientEmail = new EmailAddress(tokens[1].Remove(0, 3), true);
}
} else {
if (tokens.Length != 3) {
throw new ApplicationException("Bad command syntax.");
} else {
- this.recipient = new EmailAddress(tokens[2], true);
+ recipientEmail = new EmailAddress(tokens[2], true);
}
}
} else {
// Missing the correct "TO:" part of the command
throw new ApplicationException("Bad command syntax.");
}
- }
- /// <summary>
- /// The extracted recipient.
- /// </summary>
- public EmailAddress Recipient {
- get {
- return this.recipient;
+ SmtpMessageRecipient recipient =
+ new SmtpMessageRecipient(session.Message, recipientEmail);
+
+ // Check the recipient
+ DeliveryResult result = NMailConfiguration.Current.Router.ValidateRecipient(recipient);
+ switch (result.Type) {
+ case DeliveryResultType.PermanentError:
+ session.Connection.ErrorBadRecipient(recipient);
+ session.IncrementBadRecipientCount();
+ break;
+
+ case DeliveryResultType.TemporaryError:
+ session.Connection.TemporaryBadRecipient(recipient);
+ break;
+
+ case DeliveryResultType.Success:
+ if (session.Message.RecipientCount <= SmtpServiceConfiguration.Current.RecipientLimit) {
+ session.Message.AddRecipient(recipient);
+ session.Connection.OkRecipient(recipient);
+ session.CurrentState = new RecipientRecievedState(session);
+ } else {
+ session.Connection.TemporaryBadRecipient(recipient);
+ }
+ break;
}
+
}
+
+ #endregion
}
}
Added: NMail/trunk/NMail.SmtpService/Command/ResetCommand.cs
===================================================================
--- NMail/trunk/NMail.SmtpService/Command/ResetCommand.cs (rev 0)
+++ NMail/trunk/NMail.SmtpService/Command/ResetCommand.cs 2006-05-14 08:04:49 UTC (rev 32)
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2004-2006 Luke Quinane, Daniel Frampton and Jared Hodges.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using NMail.SmtpService.State;
+
+namespace NMail.SmtpService.Command {
+ /// <summary>
+ /// Represents the logic for handling the SMTP command RSET.
+ /// This command expects no arguments.
+ /// The execution of a valid RSET command will result in the following:
+ /// <list>
+ /// <item>A 250 response will be sent to the client</item>
+ /// <item>The SMTP session will be transitioned to the either the
+ /// ConnectedState or the NegotiatedState depending on how
+ /// the command was created.</item>
+ /// </list>
+ /// </summary>
+ class ResetCommand : ISmtpCommand {
+ private bool isNegotiated;
+
+ public ResetCommand(bool isNegotiated) {
+ this.isNegotiated = isNegotiated;
+ }
+
+ #region ISmtpCommand Members
+
+ public void Execute(SmtpSession session, string[] tokens) {
+
+ if (tokens.Length == 1) {
+ // Reset state to negotiated unless we have not yet negotiated
+ if (isNegotiated) {
+ session.CurrentState = new NegotiatedState(session);
+ } else {
+ session.CurrentState = new ConnectedState(session);
+ }
+
+ session.Message = null;
+ session.Connection.Ok();
+ } else {
+ throw new ApplicationException("Invalid command syntax");
+ }
+ }
+
+ #endregion
+ }
+}
Modified: NMail/trunk/NMail.SmtpService/Command/SenderCommand.cs
===================================================================
--- NMail/trunk/NMail.SmtpService/Command/SenderCommand.cs 2006-05-14 06:36:32 UTC (rev 31)
+++ NMail/trunk/NMail.SmtpService/Command/SenderCommand.cs 2006-05-14 08:04:49 UTC (rev 32)
@@ -1,5 +1,5 @@
/*
- * Copyright 2004-2006 Luke Quinane and Daniel Frampton
+ * Copyright 2004-2006 Luke Quinane, Daniel Frampton and Jared Hodges.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,41 +19,30 @@
using System.Text;
using NMail.DataTypes;
+using NMail.SmtpService.Configuration;
+using NMail.SmtpService.State;
namespace NMail.SmtpService.Command {
/// <summary>
/// Provides parsing for the "MAIL FROM:" command.
/// </summary>
- public class SenderCommand : AbstractCommand {
- /// <summary>
- /// The extracted sender.
- /// </summary>
- private EmailAddress sender;
+ public class SenderCommand : ISmtpCommand {
+ #region ISmtpCommand Members
- /// <summary>
- /// The encoding for the message as reported by the client.
- /// </summary>
- private Encoding encoding;
+ public void Execute(SmtpSession session, string[] tokens) {
+ int sizeEstimate = 0;
+ Encoding encoding = Encoding.ASCII;
+ EmailAddress sender = null;
- /// <summary>
- /// The estimated size of the message in bytes as given by the client.
- /// </summary>
- private int sizeEstimate;
-
- public SenderCommand(string command) : base(command) {
- this.sizeEstimate = 0;
- this.encoding = Encoding.ASCII;
- string[] tokens = command.Split(' ');
-
- if (tokens[1].ToLower().StartsWith("from:")) {
+ if (tokens.Length > 1 && tokens[1].ToLower().StartsWith("from:")) {
// Handle the case where the "FROM:" and email address aren't seperated by whitespace
int i;
if (tokens[1].Length > 5) {
i = 2;
- this.sender = new EmailAddress(tokens[1].Remove(0, 5), true);
+ sender = new EmailAddress(tokens[1].Remove(0, 5), true);
} else {
i = 3;
- this.sender = new EmailAddress(tokens[2], true);
+ sender = new EmailAddress(tokens[2], true);
}
#region Parameter Parsing
@@ -66,17 +55,17 @@
} else if (parameter.StartsWith("size=")) {
// Get the size estimate
parameter = parameter.Remove(0, 5);
- this.sizeEstimate = Int32.Parse(parameter);
+ sizeEstimate = Int32.Parse(parameter);
} else if (parameter.StartsWith("body=")) {
// Get the body type
parameter = parameter.Remove(0, 5);
switch (parameter) {
case "7bit":
- this.encoding = Encoding.ASCII;
+ encoding = Encoding.ASCII;
break;
case "8bitmime":
- this.encoding = Encoding.UTF8;
+ encoding = Encoding.UTF8;
break;
default:
@@ -94,33 +83,18 @@
// Missing the correct "FROM:" part of the command
throw new ApplicationException("Bad command syntax.");
}
- }
- /// <summary>
- /// The extracted sender.
- /// </summary>
- public EmailAddress Sender {
- get {
- return this.sender;
+ if (sizeEstimate > SmtpServiceConfiguration.Current.MaximumMessageSize) {
+ session.Connection.ErrorMessageTooLarge();
+ } else {
+ session.Message.Sender = sender;
+ session.SizeEstimate = sizeEstimate;
+ session.MessageEncoding = encoding;
+ session.Connection.Ok();
+ session.CurrentState = new SenderRecievedState(session);
}
}
- /// <summary>
- /// The extracted size estimate for the message.
- /// </summary>
- public int SizeEstimate {
- get {
- return this.sizeEstimate;
- }
- }
-
- /// <summary>
- /// The extracted encoding for the message data.
- /// </summary>
- public Encoding MessageEncoding {
- get {
- return this.encoding;
- }
- }
+ #endregion
}
}
Added: NMail/trunk/NMail.SmtpService/Command/StartTlsCommand.cs
===================================================================
--- NMail/trunk/NMail.SmtpService/Command/StartTlsCommand.cs (rev 0)
+++ NMail/trunk/NMail.SmtpService/Command/StartTlsCommand.cs 2006-05-14 08:04:49 UTC (rev 32)
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2004-2006 Luke Quinane, Daniel Frampton and Jared Hodges.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace NMail.SmtpService.Command {
+ /// <summary>
+ /// Represents the logic for handling the SMTP command STARTTLS.
+ /// This command expects no arguments.
+ /// The execution of a valid STARTTLS command will result in the following:
+ /// <list>
+ /// <item>A 250 response will be sent to the client</item>
+ /// <item>TLS negotiation will begin.</item>
+ /// </list>
+ /// </summary>
+ class StartTlsCommand : ISmtpCommand {
+ #region ISmtpCommand Members
+
+ public void Execute(SmtpSession session, string[] tokens) {
+ session.Connection.Ok();
+ session.Connection.StartTlsAsServer();
+ }
+
+ #endregion
+ }
+}
Modified: NMail/trunk/NMail.SmtpService/NMail.SmtpService.csproj
===================================================================
--- NMail/trunk/NMail.SmtpService/NMail.SmtpService.csproj 2006-05-14 06:36:32 UTC (rev 31)
+++ NMail/trunk/NMail.SmtpService/NMail.SmtpService.csproj 2006-05-14 08:04:49 UTC (rev 32)
@@ -35,8 +35,7 @@
<ConfigurationOverrideFile>
</ConfigurationOverrideFile>
<DefineConstants>DEBUG;TRACE</DefineConstants>
- <DocumentationFile>
- </DocumentationFile>
+ <DocumentationFile>bin\Debug\NMail.SmtpService.XML</DocumentationFile>
<DebugSymbols>true</DebugSymbols>
<FileAlignment>4096</FileAlignment>
<NoStdLib>false</NoStdLib>
@@ -98,18 +97,22 @@
<Compile Include="AssemblyInfo.cs">
<SubType>Code</SubType>
</Compile>
- <Compile Include="Command\AbstractCommand.cs">
- <SubType>Code</SubType>
- </Compile>
+ <Compile Include="Command\DataCommand.cs" />
+ <Compile Include="Command\EhloCommand.cs" />
<Compile Include="Command\HeloCommand.cs">
<SubType>Code</SubType>
</Compile>
+ <Compile Include="Command\ISmtpCommand.cs" />
+ <Compile Include="Command\NoopCommand.cs" />
+ <Compile Include="Command\QuitCommand.cs" />
<Compile Include="Command\RecipientCommand.cs">
<SubType>Code</SubType>
</Compile>
+ <Compile Include="Command\ResetCommand.cs" />
<Compile Include="Command\SenderCommand.cs">
<SubType>Code</SubType>
</Compile>
+ <Compile Include="Command\StartTlsCommand.cs" />
<Compile Include="Configuration\SmtpServiceConfiguration.cs">
<SubType>Code</SubType>
</Compile>
@@ -128,9 +131,6 @@
<Compile Include="State\ConnectedState.cs">
<SubType>Code</SubType>
</Compile>
- <Compile Include="State\DataState.cs">
- <SubType>Code</SubType>
- </Compile>
<Compile Include="State\NegotiatedState.cs">
<SubType>Code</SubType>
</Compile>
@@ -140,9 +140,6 @@
<Compile Include="State\SenderRecievedState.cs">
<SubType>Code</SubType>
</Compile>
- <Compile Include="State\SmtpState.cs">
- <SubType>Code</SubType>
- </Compile>
<Content Include="NMail.SmtpService.build" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
@@ -152,4 +149,4 @@
<PostBuildEvent>
</PostBuildEvent>
</PropertyGroup>
-</Project>
\ No newline at end of file
+</Project>
Modified: NMail/trunk/NMail.SmtpService/SmtpSession.cs
===================================================================
--- NMail/trunk/NMail.SmtpService/SmtpSession.cs 2006-05-14 06:36:32 UTC (rev 31)
+++ NMail/trunk/NMail.SmtpService/SmtpSession.cs 2006-05-14 08:04:49 UTC (rev 32)
@@ -1,5 +1,5 @@
/*
- * Copyright 2004-2006 Luke Quinane and Daniel Frampton
+ * Copyright 2004-2006 Luke Quinane, Daniel Frampton and Jared Hodges.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -131,7 +131,7 @@
this.connection.StartIdleTimeout();
// Change to initial state and start processing commands
- this.ChangeState(SmtpState.Connected);
+ this.currentState = new ConnectedState(this);
this.CommandLoop();
} catch (Exception e) {
// Probably an IO exception, ignore...
@@ -161,47 +161,8 @@
command = this.connection.ReadLine(this.config.Timeout).Trim();
cmdTokens = command.Split(' ');
- // attempt to process the command
- switch (cmdTokens[0].ToLower()) {
- case "quit":
- this.connection.Close();
- break;
-
- case "noop":
- this.currentState.Noop(command);
- break;
-
- case "ehlo":
- this.currentState.Ehlo(command);
- break;
-
- case "helo":
- this.currentState.Helo(command);
- break;
-
- case "mail":
- this.currentState.MailFrom(command);
- break;
-
- case "rcpt":
- this.currentState.RcptTo(command);
- break;
-
- case "data":
- this.currentState.Data(command);
- break;
-
- case "rset":
- this.currentState.Rset(command);
- break;
-
- case "starttls":
- this.currentState.StartTls(command);
- break;
-
- default:
- throw new ApplicationException("Invalid command.");
- }
+ // process the command
+ this.currentState.Execute(command);
} catch (TimeoutException) {
// Timedout reading data from client
this.connection.ErrorReadTimedout();
@@ -232,32 +193,11 @@
}
/// <summary>
- /// Changes the current state for the session.
+ /// Gets or set the current state of the session
/// </summary>
- /// <param name="newState">The new state to change to.</param>
- internal void ChangeState(SmtpState newState) {
- switch (newState) {
- case SmtpState.Connected:
- this.currentState = new ConnectedState(this);
- break;
-
- case SmtpState.Negotiated:
- this.currentState = new NegotiatedState(this);
- break;
-
- case SmtpState.SenderRecieved:
- this.currentState = new SenderRecievedState(this);
- break;
-
- case SmtpState.RecipientRecieved:
- this.currentState = new RecipientRecievedState(this);
- break;
-
- case SmtpState.Data:
- // The data state is a special case.
- new DataState(this);
- break;
- }
+ internal AbstractSmtpState CurrentState {
+ get { return this.currentState; }
+ set { this.currentState = value; }
}
/// <summary>
Modified: NMail/trunk/NMail.SmtpService/State/AbstractSmtpState.cs
===================================================================
--- NMail/trunk/NMail.SmtpService/State/AbstractSmtpState.cs 2006-05-14 06:36:32 UTC (rev 31)
+++ NMail/trunk/NMail.SmtpService/State/AbstractSmtpState.cs 2006-05-14 08:04:49 UTC (rev 32)
@@ -1,5 +1,5 @@
/*
- * Copyright 2004-2006 Luke Quinane and Daniel Frampton
+ * Copyright 2004-2006 Luke Quinane, Daniel Frampton and Jared Hodges.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,16 +16,31 @@
*/
using System;
+using System.Collections.Generic;
using NMail;
using NMail.Configuration;
using NMail.DataTypes;
using NMail.SmtpService;
using NMail.SmtpService.Configuration;
+using NMail.SmtpService.Command;
namespace NMail.SmtpService.State {
/// <summary>
- /// Provides common SMTP session state functions.
+ /// <para>
+ /// Base class for SMTP session states, which provides common SMTP session
+ /// state functions. To implement a state, extend this class and add supported
+ /// commands to the <see cref="Commands">Commands</see> collection. Common
+ /// commands supported in all states have already been added.
+ /// </para>
+ /// <para>
+ /// Example (add this to your subclass constructor):
+ /// <code>
+ /// this.Commands["helo"] = new HeloCommand();
+ /// this.Commands["ehlo"] = new EhloCommand();
+ /// this.Commands["rset"] = new ResetCommand(false);
+ /// </code>
+ /// </para>
/// </summary>
public abstract class AbstractSmtpState {
/// <summary>
@@ -49,7 +64,14 @@
protected SmtpMessage message;
/// <summary>
+ /// The collection of valid commands for this state, indexed by command name
+ /// </summary>
+ private IDictionary<string, ISmtpCommand> commands;
+
+ /// <summary>
/// Creates a new instance of the state using the given session.
+ /// Adds common QUIT, NOOP, and STARTTLS commands to collection of
+ /// supported commands.
/// </summary>
/// <param name="session">The session that owns this state.</param>
protected AbstractSmtpState(SmtpSession session) {
@@ -57,79 +79,48 @@
this.message = this.session.Message;
this.messageRouter = NMailConfiguration.Current.Router;
this.config = SmtpServiceConfiguration.Current;
+
+ this.commands = new Dictionary<string, ISmtpCommand>();
+ this.Commands["quit"] = new QuitCommand();
+ this.Commands["noop"] = new NoopCommand();
+ this.Commands["starttls"] = new StartTlsCommand();
}
- #region Default Smtp Commands
/// <summary>
- /// The default action to perform for a NOOP command.
+ /// The collection of valid commands for this state.
+ /// Keys are command names (as provided by the client), in lowercase.
+ /// Values are the command to be supported by the name given as key.
/// </summary>
- /// <param name="command">The full command string.</param>
- public virtual void Noop(string command) {
- string[] tokens = command.Split(' ');
-
- if (tokens.Length == 1) {
- session.Connection.Ok();
- } else {
- throw new ApplicationException("Invalid command syntax.");
+ public IDictionary<string, ISmtpCommand> Commands {
+ get {
+ return this.commands;
}
}
/// <summary>
- /// The default action to perform for a RSET command.
+ /// Attempts to parse and execute <code>commandString</code>.
+ /// First word (delimited by space or end of line) is interpreted as
+ /// command name. If the command name corresponds to a supported command
+ /// in the <see cref="Commands">Commands</see> collection, the appropriate
+ /// command is executed. Otherwise, an <code>ApplicationException</code>
+ /// is thrown.
/// </summary>
- /// <param name="command">The full command string.</param>
- public virtual void Rset(string command) {
- throw new ApplicationException("Invalid state.");
- }
+ /// <param name="commandString"></param>
+ /// <exception cref="ApplicationException">Application Exception thrown for
+ /// invalid command syntax or an unsupported command</exception>
+ public virtual void Execute(string commandString) {
+ string [] tokens = commandString.Split(' ');
- /// <summary>
- /// The default action to perform for a HELO command.
- /// </summary>
- /// <param name="command">The full command string.</param>
- public virtual void Helo(string command) {
- throw new ApplicationException("Invalid state.");
- }
+ if (tokens.Length == 0) {
+ throw new ApplicationException("Invalid command syntax.");
+ }
- /// <summary>
- /// The default action to perform for a EHLO command.
- /// </summary>
- /// <param name="command">The full command string.</param>
- public virtual void Ehlo(string command) {
- throw new ApplicationException("Invalid state.");
- }
+ string commandName = tokens[0].ToLower();
+ if (!this.Commands.ContainsKey(commandName)) {
+ throw new ApplicationException("Invalid Command.");
+ }
- /// <summary>
- /// The default action to perform for a "MAIL FROM:" command.
- /// </summary>
- /// <param name="command">The full command string.</param>
- public virtual void MailFrom(string command) {
- throw new ApplicationException("Invalid state.");
+ this.Commands[commandName].Execute(session, tokens);
}
-
- /// <summary>
- /// The default action to perform for a "RCPT TO:" command.
- /// </summary>
- /// <param name="command">The full command string.</param>
- public virtual void RcptTo(string command) {
- throw new ApplicationException("Invalid state.");
- }
-
- /// <summary>
- /// The default action to perform for a DATA command.
- /// </summary>
- /// <param name="command">The full command string.</param>
- public virtual void Data(string command) {
- throw new ApplicationException("Invalid state.");
- }
-
- /// <summary>
- /// The default action to perform for a STARTTLS command.
- /// </summary>
- /// <param name="command">The full command string.</param>
- public virtual void StartTls(string command) {
- this.session.Connection.Ok();
- this.session.Connection.StartTlsAsServer();
- }
- #endregion
}
}
Modified: NMail/trunk/NMail.SmtpService/State/ConnectedState.cs
===================================================================
--- NMail/trunk/NMail.SmtpService/State/ConnectedState.cs 2006-05-14 06:36:32 UTC (rev 31)
+++ NMail/trunk/NMail.SmtpService/State/ConnectedState.cs 2006-05-14 08:04:49 UTC (rev 32)
@@ -1,5 +1,5 @@
/*
- * Copyright 2004-2006 Luke Quinane and Daniel Frampton
+ * Copyright 2004-2006 Luke Quinane, Daniel Frampton and Jared Hodges.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -30,46 +30,11 @@
/// </summary>
/// <param name="session">The session the owns this state.</param>
public ConnectedState(SmtpSession session) : base(session) {
+ this.Commands["helo"] = new HeloCommand();
+ this.Commands["ehlo"] = new EhloCommand();
+ this.Commands["rset"] = new ResetCommand(false);
+
this.session.Connection.Welcome(this.config.VisibleHost);
}
-
- /// <summary>
- /// The HELO command for this state.
- /// </summary>
- /// <param name="command">The full command string.</param>
- public override void Helo(string command) {
- HeloCommand heloCommand = new HeloCommand(command);
- this.session.ReportedHost = heloCommand.Hostname;
- this.session.Esmtp = false;
- this.session.Connection.OkHelo(this.config.VisibleHost);
- this.session.ChangeState(SmtpState.Negotiated);
- }
-
- /// <summary>
- /// The EHLO command for this state.
- /// </summary>
- /// <param name="command">The full command string.</param>
- public override void Ehlo(string command) {
- HeloCommand heloCommand = new HeloCommand(command);
- this.session.ReportedHost = heloCommand.Hostname;
- this.session.Esmtp = true;
- this.session.Connection.OkCapabilities(this.config.VisibleHost,
- this.config.MaximumMessageSize);
- this.session.ChangeState(SmtpState.Negotiated);
- }
-
- /// <summary>
- /// The RSET command for this state.
- /// </summary>
- /// <param name="command">The full command string.</param>
- public override void Rset(string command) {
- string[] tokens = command.Split(' ');
-
- if (tokens.Length == 1) {
- this.session.Connection.Ok();
- } else {
- throw new ApplicationException("Invalid command syntax");
- }
- }
}
}
Deleted: NMail/trunk/NMail.SmtpService/State/DataState.cs
===================================================================
--- NMail/trunk/NMail.SmtpService/State/DataState.cs 2006-05-14 06:36:32 UTC (rev 31)
+++ NMail/trunk/NMail.SmtpService/State/DataState.cs 2006-05-14 08:04:49 UTC (rev 32)
@@ -1,124 +0,0 @@
-/*
- * Copyright 2004-2006 Luke Quinane and Daniel Frampton
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-using System;
-using System.Collections.Specialized;
-using System.Text;
-using System.Globalization;
-using System.Text.RegularExpressions;
-
-using NMail.Configuration;
-using NMail.DataTypes;
-using NMail.Helper;
-
-namespace NMail.SmtpService.State {
- /// <summary>
- /// Defines command behaviour in the data state.
- /// </summary>
- public class DataState : AbstractSmtpState {
- /// <summary>
- /// The name server for this state.
- /// </summary>
- private IDnsClient dns;
-
- /// <summary>
- /// Creates a new instance of the state.
- /// </summary>
- /// <param name="session">The session the owns this state.</param>
- public DataState(SmtpSession session) : base(session) {
- int maximumMessageSize = this.config.MaximumMessageSize;
- this.dns = NMailConfiguration.Current.DnsClient;
- int currentMaxReadLength = this.session.Connection.MaximumReadLength;
- ByteStringBuilder messageData = new ByteStringBuilder(
- this.session.MessageEncoding,
- this.session.SizeEstimate,
- maximumMessageSize);
-
- try {
- // Temporarily allow lines of any length
- this.session.Connection.MaximumReadLength = maximumMessageSize;
-
- // Data is a special case where we don't want to split each incoming line
- while (true) {
- string dataLine = this.session.Connection.ReadLine(
- this.session.MessageEncoding,
- this.config.Timeout);
-
- if (dataLine == ".") {
- break;
- } else {
- try {
- // TODO: take action if a line is too long?
-
- messageData.Append(dataLine + this.session.Connection.Terminator);
- } catch (ArgumentOutOfRangeException) {
- // Message is too large
- this.session.Connection.ErrorMessageTooLarge();
-
- // All bets are off?
- this.session.Message = null;
- this.session.ChangeState(SmtpState.Negotiated);
- return;
- }
- }
- }
- } finally {
- // Reset the maximum read line length
- this.session.Connection.MaximumReadLength = currentMaxReadLength;
- }
-
- // Parse the message headers
- this.message.Data = new Message(messageData.ToByteString());
- MessageHeaders headers = this.message.Data.Headers;
-
- // Prepare details for the headers
- string currentTime = DateTime.Now.ToString("r", DateTimeFormatInfo.InvariantInfo);
- Host clientHost = dns.ResolveHost(this.session.ClientAddress);
- string transport = (this.session.Esmtp) ? "ESMTP" : "SMTP";
-
- // Add the received header
- string recievedHeader = string.Format(DataState.RecievedHeader, this.session.ReportedHost,
- clientHost, this.session.ClientAddress, this.config.VisibleHost, transport, currentTime);
- headers.AppendStart("Recieved", recievedHeader);
-
- // All message must have a from address and a date.
- if (headers["From"] == null) {
- headers["From"] = this.message.Sender.ToString(true);
- }
- if (headers["Date"] == null) {
- headers["Date"] = currentTime;
- }
-
- try {
- // Attempt to spool the message
- this.messageRouter.SpoolMessage(this.message);
- this.session.Connection.Ok();
- } catch (Exception ex) {
- this.session.Log.Warn("Error spooling message.", ex);
- this.session.Connection.TemporaryBadResponse(ex.Message);
- }
-
- this.session.Message = null;
- this.session.ChangeState(SmtpState.Negotiated);
- }
-
- /// <summary>
- /// The format of the "Received:" header line that must be added to the message.
- /// </summary>
- public const string RecievedHeader = "from {0} ({1} [{2}])\r\n\tby NMail ({3}) with {4} {5}";
- }
-}
Modified: NMail/trunk/NMail.SmtpService/State/NegotiatedState.cs
===================================================================
--- NMail/trunk/NMail.SmtpService/State/NegotiatedState.cs 2006-05-14 06:36:32 UTC (rev 31)
+++ NMail/trunk/NMail.SmtpService/State/NegotiatedState.cs 2006-05-14 08:04:49 UTC (rev 32)
@@ -1,5 +1,5 @@
/*
- * Copyright 2004-2006 Luke Quinane and Daniel Frampton
+ * Copyright 2004-2006 Luke Quinane, Daniel Frampton and Jared Hodges.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -30,41 +30,14 @@
/// </summary>
/// <param name="session">The session the owns this state.</param>
public NegotiatedState(SmtpSession session) : base(session) {
+ this.Commands["mail"] = new SenderCommand();
+ this.Commands["rset"] = new ResetCommand(true);
+
// Create a new message
this.session.Message = new SmtpMessage();
this.message = this.session.Message;
this.message.ReportedHost = this.session.ReportedHost;
this.message.SourceAddress = session.ClientAddress;
}
-
- public override void MailFrom(string command) {
- // Parse mail from, extracting sender and parameters
- SenderCommand senderCommand = new SenderCommand(command);
-
- // Check the message size is ok
- if (senderCommand.SizeEstimate > this.config.MaximumMessageSize) {
- this.session.Connection.ErrorMessageTooLarge();
- } else {
- this.message.Sender = senderCommand.Sender;
- this.session.SizeEstimate = senderCommand.SizeEstimate;
- this.session.MessageEncoding = senderCommand.MessageEncoding;
- this.session.Connection.Ok();
- this.session.ChangeState(SmtpState.SenderRecieved);
- }
- }
-
- /// <summary>
- /// The RSET command for this state.
- /// </summary>
- /// <param name="command">The full command string.</param>
- public override void Rset(string command) {
- string[] tokens = command.Split(' ');
-
- if (tokens.Length == 1) {
- this.session.Connection.Ok();
- } else {
- throw new ApplicationException("Invalid command syntax");
- }
- }
}
}
Modified: NMail/trunk/NMail.SmtpService/State/RecipientRecievedState.cs
===================================================================
--- NMail/trunk/NMail.SmtpService/State/RecipientRecievedState.cs 2006-05-14 06:36:32 UTC (rev 31)
+++ NMail/trunk/NMail.SmtpService/State/RecipientRecievedState.cs 2006-05-14 08:04:49 UTC (rev 32)
@@ -1,5 +1,5 @@
/*
- * Copyright 2004-2006 Luke Quinane and Daniel Frampton
+ * Copyright 2004-2006 Luke Quinane, Daniel Frampton and Jared Hodges.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -29,70 +29,10 @@
/// Creates a new instance of the state.
/// </summary>
/// <param name="session">The session the owns this state.</param>
- public RecipientRecievedState(SmtpSession session) : base(session) { }
-
- /// <summary>
- /// The "RCPT TO:" command for this state.
- /// </summary>
- /// <param name="command">The full command string.</param>
- public override void RcptTo(string command) {
- // Get the recipient address
- RecipientCommand recipientCommand = new RecipientCommand(command);
- SmtpMessageRecipient recipient =
- new SmtpMessageRecipient(this.message, recipientCommand.Recipient);
-
- // Check the recipient
- DeliveryResult result = this.messageRouter.ValidateRecipient(recipient);
- switch (result.Type) {
- case DeliveryResultType.PermanentError:
- this.session.Connection.ErrorBadRecipient(recipient);
- this.session.IncrementBadRecipientCount();
- break;
-
- case DeliveryResultType.TemporaryError:
- this.session.Connection.TemporaryBadRecipient(recipient);
- break;
-
- case DeliveryResultType.Success:
- if (this.message.RecipientCount <= this.config.RecipientLimit) {
- this.message.AddRecipient(recipient);
- this.session.Connection.OkRecipient(recipient);
- } else {
- this.session.Connection.TemporaryBadRecipient(recipient);
- }
- break;
- }
+ public RecipientRecievedState(SmtpSession session) : base(session) {
+ this.Commands["rcpt"] = new RecipientCommand();
+ this.Commands["data"] = new DataCommand();
+ this.Commands["rset"] = new ResetCommand(true);
}
-
- /// <summary>
- /// The DATA command for this state.
- /// </summary>
- /// <param name="command">The full command string.</param>
- public override void Data(string command) {
- string[] tokens = command.Split(' ');
-
- if (tokens.Length == 1) {
- this.session.Connection.OkWaitingData();
- this.session.ChangeState(SmtpState.Data);
- } else {
- throw new ApplicationException("Invalid command syntax");
- }
- }
-
- /// <summary>
- /// The RSET command for this state.
- /// </summary>
- /// <param name="command">The full command string.</param>
- public override void Rset(string command) {
- string[] tokens = command.Split(' ');
-
- if (tokens.Length == 1) {
- this.session.Message = null;
- this.session.Connection.Ok();
- this.session.ChangeState(SmtpState.Negotiated);
- } else {
- throw new ApplicationException("Invalid command syntax");
- }
- }
}
}
Modified: NMail/trunk/NMail.SmtpService/State/SenderRecievedState.cs
===================================================================
--- NMail/trunk/NMail.SmtpService/State/SenderRecievedState.cs 2006-05-14 06:36:32 UTC (rev 31)
+++ NMail/trunk/NMail.SmtpService/State/SenderRecievedState.cs 2006-05-14 08:04:49 UTC (rev 32)
@@ -1,5 +1,5 @@
/*
- * Copyright 2004-2006 Luke Quinane and Daniel Frampton
+ * Copyright 2004-2006 Luke Quinane, Daniel Frampton and Jared Hodges.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -31,52 +31,9 @@
/// Creates a new instance of the state.
/// </summary>
/// <param name="session">The session the owns this state.</param>
- public SenderRecievedState(SmtpSession session) : base(session) { }
-
- /// <summary>
- /// The "RCPT TO:" command for this state.
- /// </summary>
- /// <param name="command">The full command string.</param>
- public override void RcptTo(string command) {
- // Get the recipient address
- RecipientCommand recipientCommand = new RecipientCommand(command);
- SmtpMessageRecipient recipient =
- new SmtpMessageRecipient(this.message, recipientCommand.Recipient);
-
- // Check the recipient
- DeliveryResult result = this.messageRouter.ValidateRecipient(recipient);
- switch (result.Type) {
- case DeliveryResultType.PermanentError:
- this.session.Connection.ErrorBadRecipient(recipient);
- this.session.IncrementBadRecipientCount();
- break;
-
- case DeliveryResultType.TemporaryError:
- this.session.Connection.TemporaryBadRecipient(recipient);
- break;
-
- case DeliveryResultType.Success:
- this.message.AddRecipient(recipient);
- this.session.Connection.OkRecipient(recipient);
- this.session.ChangeState(SmtpState.RecipientRecieved);
- break;
- }
+ public SenderRecievedState(SmtpSession session) : base(session) {
+ this.Commands["rcpt"] = new RecipientCommand();
+ this.Commands["rset"] = new ResetCommand(true);
}
-
- /// <summary>
- /// The RSET command for this state.
- /// </summary>
- /// <param name="command">The full command string.</param>
- public override void Rset(string command) {
- string[] tokens = command.Split(' ');
-
- if (tokens.Length == 1) {
- this.session.Message = null;
- this.session.Connection.Ok();
- this.session.ChangeState(SmtpState.Negotiated);
- } else {
- throw new ApplicationException("Invalid command syntax");
- }
- }
}
}
Deleted: NMail/trunk/NMail.SmtpService/State/SmtpState.cs
===================================================================
--- NMail/trunk/NMail.SmtpService/State/SmtpState.cs 2006-05-14 06:36:32 UTC (rev 31)
+++ NMail/trunk/NMail.SmtpService/State/SmtpState.cs 2006-05-14 08:04:49 UTC (rev 32)
@@ -1,50 +0,0 @@
-/*
- * Copyright 2004-2006 Luke Quinane and Daniel Frampton
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-using System;
-
-namespace NMail.SmtpService.State {
- /// <summary>
- /// Describes the current state of an SMTP connection.
- /// </summary>
- public enum SmtpState {
- /// <summary>
- /// The client has connected but not said "HELO".
- /// </summary>
- Connected,
-
- /// <summary>
- /// The client has said "HELO" and supplied a valid hostname
- /// </summary>
- Negotiated,
-
- /// <summary>
- /// The client has given a valid sender
- /// </summary>
- SenderRecieved,
-
- /// <summary>
- /// The client has given one or more valid recipients
- /// </summary>
- RecipientRecieved,
-
- /// <summary>
- /// The client has sent a data command
- /// </summary>
- Data
- };
-}
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|