/// <summary> /// Reads SMTP server single or multiline response. /// </summary> /// <param name="op">Asynchronous operation.</param> /// <returns>Returns true if aynchronous operation is pending (The <see cref="ReadResponseAsyncOP.CompletedAsync"/> event is raised upon completion of the operation). /// Returns false if operation completed synchronously.</returns> /// <exception cref="ObjectDisposedException">Is raised when this object is disposed and and this method is accessed.</exception> /// <exception cref="ArgumentNullException">Is raised when <b>op</b> is null reference.</exception> private bool ReadResponseAsync(ReadResponseAsyncOP op) { if(this.IsDisposed){ throw new ObjectDisposedException(this.GetType().Name); } if(op == null){ throw new ArgumentNullException("op"); } if(op.State != AsyncOP_State.WaitingForStart){ throw new ArgumentException("Invalid argument 'op' state, 'op' must be in 'AsyncOP_State.WaitingForStart' state.","op"); } return op.Start(this); }
/// <summary> /// Is called when SMTP server HELO command response reading has completed. /// </summary> /// <param name="op">Asynchronous operation.</param> /// <exception cref="ArgumentNullException">Is raised when <b>op</b> is null reference.</exception> private void HeloReadResponseCompleted(ReadResponseAsyncOP op) { if(op == null){ throw new ArgumentNullException("op"); } try{ if(op.Error != null){ m_pException = op.Error; m_pSmtpClient.LogAddException("Exception: " + m_pException.Message,m_pException); } else{ m_pReplyLines = op.ReplyLines; // HELO succeeded. if(m_pReplyLines[0].ReplyCode == 250){ /* RFC 5321 4.1.1.1. helo = "HELO" SP Domain CRLF helo-ok-rsp = "250" SP Domain [ SP helo-greet ] CRLF */ m_pSmtpClient.m_RemoteHostName = m_pReplyLines[0].Text.Split(new char[]{' '},2)[0]; m_pSmtpClient.m_IsEsmtpSupported = true; List<string> esmtpFeatures = new List<string>(); foreach(SMTP_t_ReplyLine line in m_pReplyLines){ esmtpFeatures.Add(line.Text); } m_pSmtpClient.m_pEsmtpFeatures = esmtpFeatures; } // HELO failed. else{ m_pException = new SMTP_ClientException(op.ReplyLines); m_pSmtpClient.LogAddException("Exception: " + m_pException.Message,m_pException); } } } catch(Exception x){ m_pException = x; m_pSmtpClient.LogAddException("Exception: " + m_pException.Message,m_pException); } op.Dispose(); SetState(AsyncOP_State.Completed); }
/// <summary> /// Is called when SMTP server greeting reading has completed. /// </summary> /// <param name="op">Asynchronous operation.</param> /// <param name="connectCallback">Callback to be called to complete connect operation.</param> private void ReadServerGreetingCompleted(ReadResponseAsyncOP op,CompleteConnectCallback connectCallback) { Exception error = null; try{ // Greeting reading failed, we are done. if(op.Error != null){ error = op.Error; } // Greeting reading succeded. else{ /* RFC 5321 4.2. Greeting = ( "220 " (Domain / address-literal) [ SP textstring ] CRLF ) / ( "220-" (Domain / address-literal) [ SP textstring ] CRLF *( "220-" [ textstring ] CRLF ) "220" [ SP textstring ] CRLF ) */ // SMTP server accepted connection, get greeting text. if(op.ReplyLines[0].ReplyCode == 220){ StringBuilder greetingText = new StringBuilder(); foreach(SMTP_t_ReplyLine line in op.ReplyLines){ greetingText.AppendLine(line.Text); } m_GreetingText = greetingText.ToString(); m_pEsmtpFeatures = new List<string>(); m_pRecipients = new List<string>(); } // SMTP server rejected connection. else{ error = new SMTP_ClientException(op.ReplyLines); } } } catch(Exception x){ error = x; } // Complete TCP_Client connect operation. connectCallback(error); }
/// <summary> /// Is called when SMTP server EHLO command response reading has completed. /// </summary> /// <param name="op">Asynchronous operation.</param> /// <exception cref="ArgumentNullException">Is raised when <b>op</b> is null reference.</exception> private void EhloReadResponseCompleted(ReadResponseAsyncOP op) { if(op == null){ throw new ArgumentNullException("op"); } try{ if(op.Error != null){ m_pException = op.Error; m_pSmtpClient.LogAddException("Exception: " + m_pException.Message,m_pException); SetState(AsyncOP_State.Completed); } else{ m_pReplyLines = op.ReplyLines; // EHLO succeeded. if(m_pReplyLines[0].ReplyCode == 250){ /* RFC 5321 4.1.1.1. ehlo = "EHLO" SP ( Domain / address-literal ) CRLF ehlo-ok-rsp = ( "250" SP Domain [ SP ehlo-greet ] CRLF ) / ( "250-" Domain [ SP ehlo-greet ] CRLF *( "250-" ehlo-line CRLF ) "250" SP ehlo-line CRLF ) */ m_pSmtpClient.m_RemoteHostName = m_pReplyLines[0].Text.Split(new char[]{' '},2)[0]; m_pSmtpClient.m_IsEsmtpSupported = true; List<string> esmtpFeatures = new List<string>(); foreach(SMTP_t_ReplyLine line in m_pReplyLines){ esmtpFeatures.Add(line.Text); } m_pSmtpClient.m_pEsmtpFeatures = esmtpFeatures; SetState(AsyncOP_State.Completed); } // EHLO failed, try HELO(EHLO may be disabled or not supported) else{ /* RFC 5321 4.1.1.1. helo = "HELO" SP Domain CRLF helo-ok-rsp = "250" SP Domain [ SP helo-greet ] CRLF */ // Log. m_pSmtpClient.LogAddText("EHLO failed, will try HELO."); byte[] buffer = Encoding.UTF8.GetBytes("HELO " + m_HostName + "\r\n"); // Log m_pSmtpClient.LogAddWrite(buffer.Length,"HELO " + m_HostName); // Start command sending. m_pSmtpClient.TcpStream.BeginWrite(buffer,0,buffer.Length,this.HeloCommandSendingCompleted,null); } } } catch(Exception x){ m_pException = x; m_pSmtpClient.LogAddException("Exception: " + m_pException.Message,m_pException); SetState(AsyncOP_State.Completed); } op.Dispose(); }
/// <summary> /// Is called when NOOP command response reading has completed. /// </summary> /// <param name="op">Asynchronous operation.</param> /// <exception cref="ArgumentNullException">Is raised when <b>op</b> is null reference.</exception> private void NoopReadResponseCompleted(ReadResponseAsyncOP op) { if(op == null){ throw new ArgumentNullException("op"); } try{ if(op.Error != null){ m_pException = op.Error; m_pSmtpClient.LogAddException("Exception: " + m_pException.Message,m_pException); } else{ // NOOP succeeded. if(op.ReplyLines[0].ReplyCode == 250){ /* RFC 5321 4.1.1.9. noop = "NOOP" [ SP String ] CRLF noop-resp = "250 OK" CRLF */ // Do nothing. } // NOOP failed. else{ m_pException = new SMTP_ClientException(op.ReplyLines); m_pSmtpClient.LogAddException("Exception: " + m_pException.Message,m_pException); } } } catch(Exception x){ m_pException = x; m_pSmtpClient.LogAddException("Exception: " + x.Message,x); } op.Dispose(); SetState(AsyncOP_State.Completed); }
/// <summary> /// This method is called when TCP client has sucessfully connected. /// </summary> /// <param name="callback">Callback to be called to complete connect operation.</param> protected override void OnConnected(CompleteConnectCallback callback) { // Read SMTP server greeting response. ReadResponseAsyncOP readGreetingOP = new ReadResponseAsyncOP(); readGreetingOP.CompletedAsync += delegate(object s,EventArgs<ReadResponseAsyncOP> e){ ReadServerGreetingCompleted(readGreetingOP,callback); }; if(!ReadResponseAsync(readGreetingOP)){ ReadServerGreetingCompleted(readGreetingOP,callback); } }
/// <summary> /// Is called when SMTP server DATA command final response reading has completed. /// </summary> /// <param name="op">Asynchronous operation.</param> /// <exception cref="ArgumentNullException">Is raised when <b>op</b> is null reference.</exception> private void DataReadFinalResponseCompleted(ReadResponseAsyncOP op) { if(op == null){ throw new ArgumentNullException("op"); } try{ if(op.Error != null){ m_pException = op.Error; m_pSmtpClient.LogAddException("Exception: " + m_pException.Message,m_pException); SetState(AsyncOP_State.Completed); } else{ // DATA command failed, only 2xx response is success. if(op.ReplyLines[0].ReplyCode < 200 || op.ReplyLines[0].ReplyCode > 299){ m_pException = new SMTP_ClientException(op.ReplyLines); } SetState(AsyncOP_State.Completed); } } catch(Exception x){ m_pException = x; m_pSmtpClient.LogAddException("Exception: " + m_pException.Message,m_pException); SetState(AsyncOP_State.Completed); } op.Dispose(); }
/// <summary> /// Is called when SMTP server response reading has completed. /// </summary> /// <param name="op">Asynchronous operation.</param> private void AuthReadResponseCompleted(ReadResponseAsyncOP op) { try{ // Continue authenticating. if(op.ReplyLines[0].ReplyCode == 334){ // 334 base64Data, we need to decode it. byte[] serverResponse = Convert.FromBase64String(op.ReplyLines[0].Text); byte[] clientResponse = m_pSASL.Continue(serverResponse); // We need just send SASL returned auth-response as base64. byte[] buffer = Encoding.UTF8.GetBytes(Convert.ToBase64String(clientResponse) + "\r\n"); // Log m_pSmtpClient.LogAddWrite(buffer.Length,Convert.ToBase64String(clientResponse)); // Start auth-data sending. m_pSmtpClient.TcpStream.BeginWrite(buffer,0,buffer.Length,this.AuthCommandSendingCompleted,null); } // Authentication suceeded. else if(op.ReplyLines[0].ReplyCode == 235){ m_pSmtpClient.m_pAuthdUserIdentity = new GenericIdentity(m_pSASL.UserName,m_pSASL.Name); SetState(AsyncOP_State.Completed); } // Authentication rejected. else{ m_pException = new SMTP_ClientException(op.ReplyLines); SetState(AsyncOP_State.Completed); } } catch(Exception x){ m_pException = x; m_pSmtpClient.LogAddException("Exception: " + x.Message,x); SetState(AsyncOP_State.Completed); } }
/// <summary> /// Is called when SMTP server DATA command initial response reading has completed. /// </summary> /// <param name="op">Asynchronous operation.</param> /// <exception cref="ArgumentNullException">Is raised when <b>op</b> is null reference.</exception> private void DataReadResponseCompleted(ReadResponseAsyncOP op) { if(op == null){ throw new ArgumentNullException("op"); } try{ if(op.Error != null){ m_pException = op.Error; m_pSmtpClient.LogAddException("Exception: " + m_pException.Message,m_pException); SetState(AsyncOP_State.Completed); } else{ // DATA command succeeded. if(op.ReplyLines[0].ReplyCode == 354){ // Start sending message. SmartStream.WritePeriodTerminatedAsyncOP sendMsgOP = new SmartStream.WritePeriodTerminatedAsyncOP(m_pStream); sendMsgOP.CompletedAsync += delegate(object s,EventArgs<SmartStream.WritePeriodTerminatedAsyncOP> e){ DataMsgSendingCompleted(sendMsgOP); }; if(!m_pSmtpClient.TcpStream.WritePeriodTerminatedAsync(sendMsgOP)){ DataMsgSendingCompleted(sendMsgOP); } } // DATA command failed. else{ m_pException = new SMTP_ClientException(op.ReplyLines); SetState(AsyncOP_State.Completed); } } } catch(Exception x){ m_pException = x; m_pSmtpClient.LogAddException("Exception: " + m_pException.Message,m_pException); SetState(AsyncOP_State.Completed); } op.Dispose(); }
/// <summary> /// Is called when DATA command message sending has completed. /// </summary> /// <param name="op">Asynchronous operation.</param> /// <exception cref="ArgumentNullException">Is raised when <b>op</b> is null reference.</exception> private void DataMsgSendingCompleted(SmartStream.WritePeriodTerminatedAsyncOP op) { if(op == null){ throw new ArgumentNullException("op"); } try{ if(op.Error != null){ m_pException = op.Error; m_pSmtpClient.LogAddException("Exception: " + m_pException.Message,m_pException); SetState(AsyncOP_State.Completed); } else{ // Log m_pSmtpClient.LogAddWrite(op.BytesWritten,"Sent message " + op.BytesWritten + " bytes."); // Read DATA command final response. ReadResponseAsyncOP readResponseOP = new ReadResponseAsyncOP(); readResponseOP.CompletedAsync += delegate(object s,EventArgs<ReadResponseAsyncOP> e){ DataReadFinalResponseCompleted(readResponseOP); }; if(!m_pSmtpClient.ReadResponseAsync(readResponseOP)){ DataReadFinalResponseCompleted(readResponseOP); } } } catch(Exception x){ m_pException = x; m_pSmtpClient.LogAddException("Exception: " + m_pException.Message,m_pException); SetState(AsyncOP_State.Completed); } op.Dispose(); }
/// <summary> /// Is called when SMTP server BDAT command response reading has completed. /// </summary> /// <param name="op">Asynchronous operation.</param> /// <exception cref="ArgumentNullException">Is raised when <b>op</b> is null reference.</exception> private void BdatReadResponseCompleted(ReadResponseAsyncOP op) { if(op == null){ throw new ArgumentNullException("op"); } try{ if(op.Error != null){ m_pException = op.Error; m_pSmtpClient.LogAddException("Exception: " + m_pException.Message,m_pException); SetState(AsyncOP_State.Completed); } else{ // BDAT succeeded. if(op.ReplyLines[0].ReplyCode == 250){ // We have sent whole message, we are done. if(m_BdatBytesInBuffer == 0){ SetState(AsyncOP_State.Completed); return; } // Send next BDAT data-chunk. else{ // Start reading next message data-block. m_pStream.BeginRead(m_pBdatBuffer,0,m_pBdatBuffer.Length,this.BdatChunkReadingCompleted,null); } } // BDAT failed. else{ m_pException = new SMTP_ClientException(op.ReplyLines); SetState(AsyncOP_State.Completed); } } } catch(Exception x){ m_pException = x; m_pSmtpClient.LogAddException("Exception: " + m_pException.Message,m_pException); SetState(AsyncOP_State.Completed); } op.Dispose(); }
/// <summary> /// Is called when SMTP server RCPT command response reading has completed. /// </summary> /// <param name="op">Asynchronous operation.</param> /// <exception cref="ArgumentNullException">Is raised when <b>op</b> is null reference.</exception> private void RcptReadResponseCompleted(ReadResponseAsyncOP op) { if(op == null){ throw new ArgumentNullException("op"); } try{ if(op.Error != null){ m_pException = op.Error; m_pSmtpClient.LogAddException("Exception: " + m_pException.Message,m_pException); } else{ // RCPT succeeded. if(op.ReplyLines[0].ReplyCode == 250){ if(!m_pSmtpClient.m_pRecipients.Contains(m_To)){ m_pSmtpClient.m_pRecipients.Add(m_To); } } // RCPT failed. else{ m_pException = new SMTP_ClientException(op.ReplyLines); m_pSmtpClient.LogAddException("Exception: " + m_pException.Message,m_pException); } } } catch(Exception x){ m_pException = x; if(m_pSmtpClient != null){ m_pSmtpClient.LogAddException("Exception: " + x.Message,x); } } op.Dispose(); SetState(AsyncOP_State.Completed); }
/// <summary> /// Is called when SMTP server MAIL command response reading has completed. /// </summary> /// <param name="op">Asynchronous operation.</param> /// <exception cref="ArgumentNullException">Is raised when <b>op</b> is null reference.</exception> private void MailReadResponseCompleted(ReadResponseAsyncOP op) { if(op == null){ throw new ArgumentNullException("op"); } try{ if(op.Error != null){ m_pException = op.Error; m_pSmtpClient.LogAddException("Exception: " + m_pException.Message,m_pException); } else{ // MAIL succeeded. if(op.ReplyLines[0].ReplyCode == 250){ m_pSmtpClient.m_MailFrom = m_MailFrom; } // MAIL failed. else{ m_pException = new SMTP_ClientException(op.ReplyLines); m_pSmtpClient.LogAddException("Exception: " + m_pException.Message,m_pException); } } } catch(Exception x){ m_pException = x; m_pSmtpClient.LogAddException("Exception: " + m_pException.Message,m_pException); } op.Dispose(); SetState(AsyncOP_State.Completed); }
/// <summary> /// Is called when STARTTLS command sending has finished. /// </summary> /// <param name="ar">Asynchronous result.</param> private void StartTlsCommandSendingCompleted(IAsyncResult ar) { try{ m_pSmtpClient.TcpStream.EndWrite(ar); // Read SMTP server response. ReadResponseAsyncOP readResponseOP = new ReadResponseAsyncOP(); readResponseOP.CompletedAsync += delegate(object s,EventArgs<ReadResponseAsyncOP> e){ StartTlsReadResponseCompleted(readResponseOP); }; if(!m_pSmtpClient.ReadResponseAsync(readResponseOP)){ StartTlsReadResponseCompleted(readResponseOP); } } catch(Exception x){ m_pException = x; m_pSmtpClient.LogAddException("Exception: " + x.Message,x); SetState(AsyncOP_State.Completed); } }
/// <summary> /// Is called when SMTP server RSET command response reading has completed. /// </summary> /// <param name="op">Asynchronous operation.</param> /// <exception cref="ArgumentNullException">Is raised when <b>op</b> is null reference.</exception> private void RsetReadResponseCompleted(ReadResponseAsyncOP op) { if(op == null){ throw new ArgumentNullException("op"); } try{ if(op.Error != null){ m_pException = op.Error; m_pSmtpClient.LogAddException("Exception: " + m_pException.Message,m_pException); } else{ // RSET succeeded. if(op.ReplyLines[0].ReplyCode == 250){ /* RFC 5321 4.1.1.9. rset = "RSET" CRLF rset-resp = "250 OK" CRLF */ // Do nothing. } // RSET failed. else{ m_pException = new SMTP_ClientException(op.ReplyLines); m_pSmtpClient.LogAddException("Exception: " + m_pException.Message,m_pException); } } } catch(Exception x){ m_pException = x; m_pSmtpClient.LogAddException("Exception: " + x.Message,x); } SetState(AsyncOP_State.Completed); }
/// <summary> /// Is called when STARTTLS command response reading has completed. /// </summary> /// <param name="op">Asynchronous operation.</param> /// <exception cref="ArgumentNullException">Is raised when <b>op</b> is null reference.</exception> private void StartTlsReadResponseCompleted(ReadResponseAsyncOP op) { if(op == null){ throw new ArgumentNullException("op"); } try{ if(op.Error != null){ m_pException = op.Error; } else{ // STARTTLS accepted. if(op.ReplyLines[0].ReplyCode == 220){ /* RFC 3207 4. The format for the STARTTLS command is: STARTTLS with no parameters. After the client gives the STARTTLS command, the server responds with one of the following reply codes: 220 Ready to start TLS 501 Syntax error (no parameters allowed) 454 TLS not available due to temporary reason */ // Log m_pSmtpClient.LogAddText("Starting TLS handshake."); SwitchToSecureAsyncOP switchSecureOP = new SwitchToSecureAsyncOP(m_pCertCallback); switchSecureOP.CompletedAsync += delegate(object s,EventArgs<SwitchToSecureAsyncOP> e){ SwitchToSecureCompleted(switchSecureOP); }; if(!m_pSmtpClient.SwitchToSecureAsync(switchSecureOP)){ SwitchToSecureCompleted(switchSecureOP); } } // STARTTLS failed. else{ m_pException = new SMTP_ClientException(op.ReplyLines); } } } catch(Exception x){ m_pException = x; } op.Dispose(); if(m_pException != null){ m_pSmtpClient.LogAddException("Exception: " + m_pException.Message,m_pException); SetState(AsyncOP_State.Completed); } }
/// <summary> /// Is called when HELO command sending has finished. /// </summary> /// <param name="ar">Asynchronous result.</param> private void HeloCommandSendingCompleted(IAsyncResult ar) { try{ m_pSmtpClient.TcpStream.EndWrite(ar); // Read HELO command response. ReadResponseAsyncOP readResponseOP = new ReadResponseAsyncOP(); readResponseOP.CompletedAsync += delegate(object s,EventArgs<ReadResponseAsyncOP> e){ HeloReadResponseCompleted(readResponseOP); }; if(!m_pSmtpClient.ReadResponseAsync(readResponseOP)){ HeloReadResponseCompleted(readResponseOP); } } catch(Exception x){ m_pException = x; if(m_pSmtpClient != null){ m_pSmtpClient.LogAddException("Exception: " + m_pException.Message,m_pException); } SetState(AsyncOP_State.Completed); } }