/// <summary> /// Sends the welcome greeting to the client. /// </summary> private void SendWelcomeMessage(SMTPContext context) { context.WriteLine(WelcomeMessage); }
/// <summary> /// Handles the command input from the client. This /// message returns when the client issues the quit command. /// </summary> private void ProcessCommands(SMTPContext context) { bool isRunning = true; // Loop until the client quits. while (isRunning) { try { String inputLine = context.ReadLine(); if (inputLine == null) { isRunning = false; context.Close(); continue; } Log.DebugFormat(Resources.Log_ProcessCommands_ProcessCommands_Read_0, inputLine); String[] inputs = inputLine.Split(" ".ToCharArray()); var messageUnknownCommand = Resources.Protocol_MESSAGE_UNKNOWN_COMMAND_500_Command_Unrecognized; switch (inputs[0].ToLower()) { case "helo": Helo(context, inputs); break; case "rset": Rset(context); break; case "noop": context.WriteLine(Resources.Protocol_MESSAGE_OK_250_OK); break; case "quit": isRunning = false; context.WriteLine(Resources.Protocol_MESSAGE_GOODBYE_221_Goodbye); context.Close(); break; case "mail": if (inputs[1].ToLower().StartsWith("from")) { Mail(context, inputLine.Substring(inputLine.IndexOf(" ", StringComparison.Ordinal))); break; } context.WriteLine(messageUnknownCommand); break; case "rcpt": if (inputs[1].ToLower().StartsWith("to")) { Rcpt(context, inputLine.Substring(inputLine.IndexOf(" ", StringComparison.Ordinal))); break; } context.WriteLine(messageUnknownCommand); break; case "data": Data(context); break; default: context.WriteLine(messageUnknownCommand); break; } } catch (Exception exception) { Log.ErrorFormat( Resources.Log_ProcessCommands_Connection_0_Exception_occured_while_processing_commands_1, context.ConnectionId, exception, exception); context.WriteLine(Resources.Protocol_MESSAGE_SYSTEM_ERROR_554_Transaction_failed); } } }
/// <summary> /// Handle the RCPT TO:<address> command. /// </summary> private void Rcpt(SMTPContext context, string argument) { if (context.LastCommand == CommandMail || context.LastCommand == CommandRcpt) { string address = ParseAddress(argument); var messageInvalidAddress = Resources.Protocol_MESSAGE_INVALID_ADDRESS_451_Address_is_invalid; if (address != null) { try { var emailAddress = new MailAddress(address); // Check to make sure we want to accept this message. if (_recipientFilter.AcceptRecipient(context, emailAddress)) { context.Message.AddToAddress(emailAddress); context.LastCommand = CommandRcpt; context.WriteLine(Resources.Protocol_MESSAGE_OK_250_OK); if (Log.IsDebugEnabled) Log.DebugFormat(Resources.Log_Connection_0_RcptTo_address_1_accepted, context.ConnectionId, address); } else { context.WriteLine(Resources.Protocol_MESSAGE_UNKNOWN_USER_550_User_does_not_exist); if (Log.IsDebugEnabled) Log.DebugFormat( Resources.Log_Connection_0_RcptTo_address_1_rejected_Did_not_pass_Address_Filter, context.ConnectionId, address); } } catch (FormatException) { if (Log.IsDebugEnabled) Log.DebugFormat( Resources.Log_Connection_0_RcptTo_argument_1_rejected_Should_be_from_username_domain_com, context.ConnectionId, argument); context.WriteLine(messageInvalidAddress); } } else { if (Log.IsDebugEnabled) Log.DebugFormat( Resources.Log_Connection_0_RcptTo_argument_1_rejected_Should_be_from_username_domain_com, context.ConnectionId, argument); context.WriteLine(messageInvalidAddress); } } else { context.WriteLine(Resources.Protocol_MESSAGE_INVALID_COMMAND_ORDER_503_Command_not_allowed_here); } }
/// <summary> /// Handles the HELO command. /// </summary> private void Helo(SMTPContext context, IList<string> inputs) { if (context.LastCommand == -1) { if (inputs.Count == 2) { context.ClientDomain = inputs[1]; context.LastCommand = CommandHelo; context.WriteLine(HeloResponse); } else { context.WriteLine(Resources.Protocol_MESSAGE_INVALID_ARGUMENT_COUNT__501_Incorrect_number_of_arguments); } } else { context.WriteLine(Resources.Protocol_MESSAGE_INVALID_COMMAND_ORDER_503_Command_not_allowed_here); } }
private void Data(SMTPContext context) { context.WriteLine(Resources.Protocol_MESSAGE_START_DATA__354_Start_mail_input_end_with_CRLF_CRLF); SMTPMessage message = context.Message; var clientEndPoint = (IPEndPoint)context.Socket.RemoteEndPoint; var header = new StringBuilder(); header.AppendFormat(Resources.EMAIL_Received_from_0_0_1, context.ClientDomain, clientEndPoint.Address); header.AppendLine(); header.AppendFormat(Resources.EMAIL_by_0_Eric_Daugherty_s_C_Email_Server, _domain); header.AppendLine(); header.Append(" " + DateTime.Now); header.AppendLine(); message.AddData(header.ToString()); String line = context.ReadLine(); while (!line.Equals(".")) { message.AddData(line); message.AddData("\r\n"); line = context.ReadLine(); } // Spool the message _messageSpool.SpoolMessage(message); context.WriteLine(Resources.Protocol_MESSAGE_OK_250_OK); // Reset the connection. context.Reset(); }
/// <summary> /// Reset the connection state. /// </summary> private static void Rset(SMTPContext context) { if (context.LastCommand != -1) { // Dump the message and reset the context. context.Reset(); context.WriteLine(Resources.Protocol_MESSAGE_OK_250_OK); } else { context.WriteLine(Resources.Protocol_MESSAGE_INVALID_COMMAND_ORDER_503_Command_not_allowed_here); } }
/// <summary> /// Handle the MAIL FROM:<address> command. /// </summary> private static void Mail(SMTPContext context, string argument) { bool addressValid = false; if (context.LastCommand == CommandHelo) { string address = ParseAddress(argument); if (address != null) { try { var emailAddress = new MailAddress(address); context.Message.FromAddress = emailAddress; context.LastCommand = CommandMail; addressValid = true; context.WriteLine(Resources.Protocol_MESSAGE_OK_250_OK); if (Log.IsDebugEnabled) Log.DebugFormat(Resources.Log_Mail_Connection_0_MailFrom_address_1_accepted, context.ConnectionId, address); } catch (FormatException ex) { Log.Warn(Resources.Log_Invalid_E_Mail_address_detected, ex); } catch (Exception ex) { Log.Error(Resources.Log_Unknown_exception_occured, ex); } } // If the address is invalid, inform the client. if (!addressValid) { if (Log.IsDebugEnabled) Log.DebugFormat( Resources.Log_Connection_0_MailFrom_argument_1_rejected_Should_be_from_username_domain_com, context.ConnectionId, argument); context.WriteLine(Resources.Protocol_MESSAGE_INVALID_ADDRESS_451_Address_is_invalid); } } else { context.WriteLine(Resources.Protocol_MESSAGE_INVALID_COMMAND_ORDER_503_Command_not_allowed_here); } }
/// <summary> /// ProcessConnection handles a connected TCP Client /// and performs all necessary interaction with this /// client to comply with RFC821. This method is thread /// safe. /// </summary> public void ProcessConnection(Socket socket) { long currentConnectionId; // Really only need to lock on the long, but that is not // allowed. Is there a better way to do this? lock (this) { currentConnectionId = _connectionId++; } using (SMTPContext context = new SMTPContext(currentConnectionId, socket)) { try { SendWelcomeMessage(context); ProcessCommands(context); } catch (Exception exception) { Log.ErrorFormat( Resources.Log_ProcessConnection_Connection_0_Error_1, context.ConnectionId, exception); } } }