/// <summary> /// Send response to client. /// </summary> /// <param name="output">socket output stream</param> /// <param name="smtpResponse">Response to send</param> private void SendResponse(StreamWriter output, SmtpResponse smtpResponse) { if (smtpResponse.Code <= 0) { return; } output.WriteLine(smtpResponse.Code + " " + smtpResponse.Message); output.Flush(); }
/// <summary> /// Handle an SMTP transaction, i.e. all activity between initial connect and QUIT command. /// </summary> /// <param name="output">output stream</param> /// <param name="input">input stream</param> /// <returns>List of received SmtpMessages</returns> private void HandleSmtpTransaction(StreamWriter output, TextReader input) { // Initialize the state machine SmtpState smtpState = SmtpState.CONNECT; SmtpRequest smtpRequest = new SmtpRequest(SmtpActionType.CONNECT, String.Empty, smtpState); // Execute the connection request SmtpResponse smtpResponse = smtpRequest.Execute(); // Send initial response SendResponse(output, smtpResponse); smtpState = smtpResponse.NextState; SmtpMessage msg = new SmtpMessage(); while (smtpState != SmtpState.CONNECT) { string line = input.ReadLine(); if (line == null) { break; } // Create request from client input and current state SmtpRequest request = SmtpRequest.CreateRequest(line, smtpState); // Execute request and create response object SmtpResponse response = request.Execute(); // Move to next internal state smtpState = response.NextState; // Store input in message msg.Store(response, request.Params); // If message reception is complete save it if (smtpState == SmtpState.QUIT) { // Remove the last carriage return and new line string mimeMessage = msg.RawMessage; byte[] messageBytes = Encoding.ASCII.GetBytes(mimeMessage); Message message = new Message(messageBytes, true); _receivedMail.Enqueue(message.ToMailMessage()); msg = new SmtpMessage(); } // Send reponse to client after we have stored the email, so when asking for the recived mail list it will be there // (this was not the way it was done before) SendResponse(output, response); } }
/// <summary> /// Update the headers or body depending on the SmtpResponse object and line of input. /// </summary> /// <param name="response">SmtpResponse object</param> /// <param name="commandData">remainder of input line after SMTP command has been removed</param> internal void Store(SmtpResponse response, string commandData) { if (commandData == null) { return; } if (SmtpState.DATA_HDR == response.NextState) { _rawMessageBuilder.AppendLine(commandData); int headerNameEnd = commandData.IndexOf(":", StringComparison.Ordinal); if (headerNameEnd >= 0) { string name = commandData.Substring(0, (headerNameEnd) - (0)).Trim(); string valueRenamed = commandData.Substring(headerNameEnd + 1).Trim(); // We use the Add method instead of [] because we can have multiple values for a name _headers.Add(name, valueRenamed); } } else if (SmtpState.DATA_BODY == response.NextState) { // The end of the headers is signified by a newline if (_body.Length == 0) { _rawMessageBuilder.Append(Environment.NewLine); } if (_bodyLineCount > 0) { _rawMessageBuilder.Append(Cr); } _rawMessageBuilder.Append(commandData); if (_bodyLineCount > 0) { _body.Append(Cr); } _body.Append(commandData); _bodyLineCount++; //if (!IsMultiPartMessage) return; //Match boundaryMatch = Regex.Match(body.ToString(), @"boundary=""?(?<boundary>[^"";\r\n]+)""?;?", RegexOptions.IgnoreCase); //_BaseBoundary = boundaryMatch.Groups["boundary"].Value; } }