private void Data(SmtpContext context) { context.WriteLine(MessageStartData); var messageData = context.MessageData; var clientEndPoint = (IPEndPoint)context.Socket.RemoteEndPoint; var header = new StringBuilder(); header.Append(String.Format("Received: from {0} ({0} [{1}])\r\n", context.ClientDomain, clientEndPoint.Address)); messageData.AddData(header.ToString()); var line = context.ReadLine(); while (!line.Equals(".")) { messageData.AddData(line); messageData.AddData("\r\n"); line = context.ReadLine(); } // Spool the message _handler(messageData.ParseMessage()); context.WriteLine(MessageOk); // Reset the connection. context.Reset(); }
/// <summary> /// Handle the RCPT TO:<address> command. /// </summary> private void Rcpt(SmtpContext context, string argument) { if (context.LastCommand == CommandMail || context.LastCommand == CommandRcpt) { var emailAddress = ParseAddress(argument); if (emailAddress != null) { // Check to make sure we want to accept this message. if (_recipientFilter(context, emailAddress)) { context.MessageData.AddToAddress(emailAddress); context.LastCommand = CommandRcpt; context.WriteLine(MessageOk); _logger.Debug("Connection {0}: RcptTo address: {1} accepted.", context.ConnectionId, emailAddress); } else { context.WriteLine(MessageUnknownUser); _logger.Debug("Connection {0}: RcptTo address: {1} rejected. Did not pass Address Filter.", context.ConnectionId, emailAddress); } } else { _logger.Debug("Connection {0}: RcptTo argument: {1} rejected. Should be from:<*****@*****.**>", context.ConnectionId, argument); context.WriteLine(MessageInvalidAddress); } } else { context.WriteLine(MessageInvalidCommandOrder); } }
/// <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(MessageOk); } else { context.WriteLine(MessageInvalidCommandOrder); } }
/// <summary> /// Handles the HELO command. /// </summary> private void Helo(SmtpContext context, string[] inputs) { if (context.LastCommand == -1) { if (inputs.Length == 2) { context.ClientDomain = inputs[1]; context.LastCommand = CommandHelo; context.WriteLine(HeloResponse); } else { context.WriteLine(MessageInvalidArgumentCount); } } else { context.WriteLine(MessageInvalidCommandOrder); } }
/// <summary> /// Handle the MAIL FROM:<address> command. /// </summary> private void Mail(SmtpContext context, string argument) { if (context.LastCommand == CommandHelo) { var emailAddress = ParseAddress(argument); if (emailAddress == null) { _logger.Debug("Connection {0}: MailFrom argument: {1} rejected. Should be from:<*****@*****.**>", context.ConnectionId, argument); context.WriteLine(MessageInvalidAddress); return; } context.MessageData.FromAddress = emailAddress; context.LastCommand = CommandMail; context.WriteLine(MessageOk); _logger.Debug("Connection {0}: MailFrom address: {1} accepted.", context.ConnectionId, emailAddress.ToString()); } else { context.WriteLine(MessageInvalidCommandOrder); } }
/// <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 HandleConnection(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++; } var context = new SmtpContext(currentConnectionId, socket, _logger); try { SendWelcomeMessage(context); ProcessCommands(context); } catch (Exception exception) { _logger.Error(exception, "Connection {0}: Error: {1}", context.ConnectionId, exception); } }
/// <summary> /// Handles the command input from the client. This /// message returns when the client issues the quit command. /// </summary> private void ProcessCommands(SmtpContext context) { while (true) { try { var inputLine = context.ReadLine(); if (inputLine == null) { context.Close(); return; } _logger.Debug("ProcessCommands Read: " + inputLine); var inputs = inputLine.Split(" ".ToCharArray()); switch (inputs[0].ToLower()) { case "helo": Helo(context, inputs); break; case "rset": Rset(context); break; case "noop": context.WriteLine(MessageOk); break; case "quit": context.WriteLine(MessageGoodbye); context.Close(); return; case "mail": if (inputs[1].ToLower().StartsWith("from")) { Mail(context, inputLine.Substring(inputLine.IndexOf(" "))); break; } context.WriteLine(MessageUnknownCommand); break; case "rcpt": if (inputs[1].ToLower().StartsWith("to")) { Rcpt(context, inputLine.Substring(inputLine.IndexOf(" "))); break; } context.WriteLine(MessageUnknownCommand); break; case "data": Data(context); break; default: context.WriteLine(MessageUnknownCommand); break; } } catch (Exception exception) { _logger.Error(exception, "Connection {0}: Exception occured while processing commands: {1}", context.ConnectionId, exception); context.WriteLine(MessageSystemError); } } }
/// <summary> /// Sends the welcome greeting to the client. /// </summary> private void SendWelcomeMessage(SmtpContext context) { context.WriteLine(WelcomeMessage); }
private void Data(SmtpContext context) { context.WriteLine(MessageStartData); var messageData = context.MessageData; var clientEndPoint = (IPEndPoint) context.Socket.RemoteEndPoint; var header = new StringBuilder(); header.Append(String.Format("Received: from {0} ({0} [{1}])\r\n", context.ClientDomain, clientEndPoint.Address)); messageData.AddData(header.ToString()); var line = context.ReadLine(); while(!line.Equals(".")) { messageData.AddData(line); messageData.AddData("\r\n"); line = context.ReadLine(); } // Spool the message _handler(messageData.ParseMessage()); context.WriteLine(MessageOk); // Reset the connection. context.Reset(); }
/// <summary> /// Handles the command input from the client. This /// message returns when the client issues the quit command. /// </summary> private void ProcessCommands(SmtpContext context) { while(true) { try { var inputLine = context.ReadLine(); if (inputLine == null) { context.Close(); return; } _logger.Debug("ProcessCommands Read: " + inputLine); var inputs = inputLine.Split(" ".ToCharArray()); switch(inputs[0].ToLower()) { case "helo": Helo(context, inputs); break; case "rset": Rset(context); break; case "noop": context.WriteLine(MessageOk); break; case "quit": context.WriteLine(MessageGoodbye); context.Close(); return; case "mail": if (inputs[1].ToLower().StartsWith("from")) { Mail(context, inputLine.Substring(inputLine.IndexOf(" "))); break; } context.WriteLine(MessageUnknownCommand); break; case "rcpt": if (inputs[1].ToLower().StartsWith("to")) { Rcpt(context, inputLine.Substring(inputLine.IndexOf(" "))); break; } context.WriteLine(MessageUnknownCommand); break; case "data": Data(context); break; default: context.WriteLine(MessageUnknownCommand); break; } } catch(Exception exception) { _logger.Error(exception, "Connection {0}: Exception occured while processing commands: {1}", context.ConnectionId, exception); context.WriteLine(MessageSystemError); } } }
/// <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 HandleConnection(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++; } var context = new SmtpContext(currentConnectionId, socket, _logger); try { SendWelcomeMessage(context); ProcessCommands(context); } catch(Exception exception) { _logger.Error(exception, "Connection {0}: Error: {1}", context.ConnectionId, exception); } }