/// <summary> /// Dns relay session constructor. /// </summary> /// <param name="server">Owner relay server.</param> /// <param name="realyItem">Relay item.</param> /// <exception cref="ArgumentNullException">Is raised when <b>server</b> or <b>realyItem</b> is null.</exception> /// <exception cref="ArgumentException">Is raised when any of the arguments has invalid value.</exception> internal Relay_Session(Relay_Server server,Relay_QueueItem realyItem) { if(server == null){ throw new ArgumentNullException("server"); } if(realyItem == null){ throw new ArgumentNullException("realyItem"); } m_pServer = server; m_pRelayItem = realyItem; m_SessionID = Guid.NewGuid().ToString(); m_SessionCreateTime = DateTime.Now; m_pTargets = new List<Relay_Target>(); m_pSmtpClient = new SMTP_Client(); }
/// <summary> /// Sends message by using specified smart host. /// </summary> /// <param name="localHost">Host name which is reported to SMTP server.</param> /// <param name="host">Host name or IP address.</param> /// <param name="port">Host port.</param> /// <param name="ssl">Specifies if connected via SSL.</param> /// <param name="userName">SMTP server user name. This value may be null, then authentication not used.</param> /// <param name="password">SMTP server password.</param> /// <param name="from">Sender email what is reported to SMTP server.</param> /// <param name="to">Recipients email addresses.</param> /// <param name="message">Raw message to send.</param> /// <exception cref="ArgumentNullException">Is raised when argument <b>host</b>,<b>from</b>,<b>to</b> or <b>stream</b> is null.</exception> /// <exception cref="ArgumentException">Is raised when any of the method arguments has invalid value.</exception> /// <exception cref="SMTP_ClientException">Is raised when SMTP server returns error.</exception> public static void QuickSendSmartHost(string localHost,string host,int port,bool ssl,string userName,string password,string from,string[] to,Stream message) { if(host == null){ throw new ArgumentNullException("host"); } if(host == ""){ throw new ArgumentException("Argument 'host' value may not be empty."); } if(port < 1){ throw new ArgumentException("Argument 'port' value must be >= 1."); } if(from == null){ throw new ArgumentNullException("from"); } if(from != "" && !SMTP_Utils.IsValidAddress(from)){ throw new ArgumentException("Argument 'from' has invalid value."); } if(to == null){ throw new ArgumentNullException("to"); } if(to.Length == 0){ throw new ArgumentException("Argument 'to' must contain at least 1 recipient."); } foreach(string t in to){ if(!SMTP_Utils.IsValidAddress(t)){ throw new ArgumentException("Argument 'to' has invalid value '" + t + "'."); } } if(message == null){ throw new ArgumentNullException("message"); } using(SMTP_Client smtp = new SMTP_Client()){ smtp.Connect(host,port,ssl); smtp.EhloHelo(localHost != null ? localHost : Dns.GetHostName()); if(!string.IsNullOrEmpty(userName)){ smtp.Auth(smtp.AuthGetStrongestMethod(userName,password)); } smtp.MailFrom(from,-1); foreach(string t in to){ smtp.RcptTo(t); } smtp.SendMessage(message); } }
/// <summary> /// Starts operation processing. /// </summary> /// <param name="owner">Owner SMTP client.</param> /// <returns>Returns true if asynchronous operation in progress or false if operation completed synchronously.</returns> /// <exception cref="ArgumentNullException">Is raised when <b>owner</b> is null reference.</exception> internal bool Start(SMTP_Client owner) { if(owner == null){ throw new ArgumentNullException("owner"); } m_pSmtpClient = owner; SetState(AsyncOP_State.Active); try{ /* RFC 4954 4. The AUTH Command. AUTH mechanism [initial-response] Arguments: mechanism: A string identifying a [SASL] authentication mechanism. initial-response: An optional initial client response. If present, this response MUST be encoded as described in Section 4 of [BASE64] or contain a single character "=". */ if(m_pSASL.SupportsInitialResponse){ byte[] buffer = Encoding.UTF8.GetBytes("AUTH " + m_pSASL.Name + " " + Convert.ToBase64String(m_pSASL.Continue(null)) + "\r\n"); // Log m_pSmtpClient.LogAddWrite(buffer.Length,Encoding.UTF8.GetString(buffer).TrimEnd()); // Start command sending. m_pSmtpClient.TcpStream.BeginWrite(buffer,0,buffer.Length,this.AuthCommandSendingCompleted,null); } else{ byte[] buffer = Encoding.UTF8.GetBytes("AUTH " + m_pSASL.Name + "\r\n"); // Log m_pSmtpClient.LogAddWrite(buffer.Length,"AUTH " + m_pSASL.Name); // Start command sending. m_pSmtpClient.TcpStream.BeginWrite(buffer,0,buffer.Length,this.AuthCommandSendingCompleted,null); } } catch(Exception x){ m_pException = x; m_pSmtpClient.LogAddException("Exception: " + x.Message,x); SetState(AsyncOP_State.Completed); } // Set flag rise CompletedAsync event flag. The event is raised when async op completes. // If already completed sync, that flag has no effect. lock(m_pLock){ m_RiseCompleted = true; return m_State == AsyncOP_State.Active; } }
/// <summary> /// Is called when RCPT command has completed. /// </summary> /// <param name="op">Asynchronous operation.</param> /// <exception cref="ArgumentNullException">Is raised when <b>op</b> is null reference.</exception> private void RcptCommandCompleted(SMTP_Client.RcptToAsyncOP op) { if(op == null){ throw new ArgumentNullException("op"); } try{ if(op.Error != null){ Dispose(op.Error); } else{ // Start sending message. SMTP_Client.SendMessageAsyncOP sendMsgOP = new SMTP_Client.SendMessageAsyncOP(m_pRelayItem.MessageStream,false); sendMsgOP.CompletedAsync += delegate(object s,EventArgs<SMTP_Client.SendMessageAsyncOP> e){ MessageSendingCompleted(sendMsgOP); }; if(!m_pSmtpClient.SendMessageAsync(sendMsgOP)){ MessageSendingCompleted(sendMsgOP); } } } catch(Exception x){ Dispose(x); } }
/// <summary> /// Is called when 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 MessageSendingCompleted(SMTP_Client.SendMessageAsyncOP op) { if(op == null){ throw new ArgumentNullException("op"); } try{ if(op.Error != null){ Dispose(op.Error); } // Message sent sucessfully. else{ Dispose(null); } } catch(Exception x){ Dispose(x); } op.Dispose(); }
/// <summary> /// Is called when MAIL command has completed. /// </summary> /// <param name="op">Asynchronous operation.</param> /// <exception cref="ArgumentNullException">Is raised when <b>op</b> is null reference.</exception> private void MailCommandCompleted(SMTP_Client.MailFromAsyncOP op) { if(op == null){ throw new ArgumentNullException("op"); } try{ if(op.Error != null){ Dispose(op.Error); } else{ SMTP_Client.RcptToAsyncOP rcptOP = new SMTP_Client.RcptToAsyncOP( this.To, IsDsnSupported() ? m_pRelayItem.DSN_Notify : SMTP_DSN_Notify.NotSpecified, IsDsnSupported() ? m_pRelayItem.OriginalRecipient : null ); rcptOP.CompletedAsync += delegate(object s,EventArgs<SMTP_Client.RcptToAsyncOP> e){ RcptCommandCompleted(rcptOP); }; if(!m_pSmtpClient.RcptToAsync(rcptOP)){ RcptCommandCompleted(rcptOP); } } } catch(Exception x){ Dispose(x); } }
/// <summary> /// Is called when EHLO/HELO command has completed. /// </summary> /// <param name="op">Asynchronous operation.</param> /// <exception cref="ArgumentNullException">Is raised when <b>op</b> is null reference.</exception> private void EhloCommandCompleted(SMTP_Client.EhloHeloAsyncOP op) { if(op == null){ throw new ArgumentNullException("op"); } try{ if(op.Error != null){ Dispose(op.Error); } else{ // Start TLS requested, start switching to secure. if(!m_pSmtpClient.IsSecureConnection && m_pActiveTarget.SslMode == SslMode.TLS){ SMTP_Client.StartTlsAsyncOP startTlsOP = new SMTP_Client.StartTlsAsyncOP(null); startTlsOP.CompletedAsync += delegate(object s,EventArgs<SMTP_Client.StartTlsAsyncOP> e){ StartTlsCommandCompleted(startTlsOP); }; if(!m_pSmtpClient.StartTlsAsync(startTlsOP)){ StartTlsCommandCompleted(startTlsOP); } } // Authentication requested, start authenticating. else if(!string.IsNullOrEmpty(m_pActiveTarget.UserName)){ SMTP_Client.AuthAsyncOP authOP = new SMTP_Client.AuthAsyncOP(m_pSmtpClient.AuthGetStrongestMethod(m_pActiveTarget.UserName,m_pActiveTarget.Password)); authOP.CompletedAsync += delegate(object s,EventArgs<SMTP_Client.AuthAsyncOP> e){ AuthCommandCompleted(authOP); }; if(!m_pSmtpClient.AuthAsync(authOP)){ AuthCommandCompleted(authOP); } } // Start MAIL command. else{ long messageSize = -1; try{ messageSize = m_pRelayItem.MessageStream.Length - m_pRelayItem.MessageStream.Position; } catch{ // Stream doesn't support seeking. } SMTP_Client.MailFromAsyncOP mailOP = new SMTP_Client.MailFromAsyncOP( this.From, messageSize, IsDsnSupported() ? m_pRelayItem.DSN_Ret : SMTP_DSN_Ret.NotSpecified, IsDsnSupported() ? m_pRelayItem.EnvelopeID : null ); mailOP.CompletedAsync += delegate(object s,EventArgs<SMTP_Client.MailFromAsyncOP> e){ MailCommandCompleted(mailOP); }; if(!m_pSmtpClient.MailFromAsync(mailOP)){ MailCommandCompleted(mailOP); } } } } catch(Exception x){ Dispose(x); } }
/// <summary> /// Is called when AUTH command has completed. /// </summary> /// <param name="op">Asynchronous operation.</param> /// <exception cref="ArgumentNullException">Is raised when <b>op</b> is null reference.</exception> private void AuthCommandCompleted(SMTP_Client.AuthAsyncOP op) { if(op == null){ throw new ArgumentNullException("op"); } try{ if(op.Error != null){ Dispose(op.Error); } else{ long messageSize = -1; try{ messageSize = m_pRelayItem.MessageStream.Length - m_pRelayItem.MessageStream.Position; } catch{ // Stream doesn't support seeking. } SMTP_Client.MailFromAsyncOP mailOP = new SMTP_Client.MailFromAsyncOP( this.From, messageSize, IsDsnSupported() ? m_pRelayItem.DSN_Ret : SMTP_DSN_Ret.NotSpecified, IsDsnSupported() ? m_pRelayItem.EnvelopeID : null ); mailOP.CompletedAsync += delegate(object s,EventArgs<SMTP_Client.MailFromAsyncOP> e){ MailCommandCompleted(mailOP); }; if(!m_pSmtpClient.MailFromAsync(mailOP)){ MailCommandCompleted(mailOP); } } } catch(Exception x){ Dispose(x); } }
/// <summary> /// Completes relay session and does clean up. This method is thread-safe. /// </summary> /// <param name="exception">Exception happened or null if relay completed successfully.</param> public void Dispose(Exception exception) { try{ lock(this){ if(m_IsDisposed){ return; } try{ m_pServer.OnSessionCompleted(this,exception); } catch{ } m_pServer.Sessions.Remove(this); m_IsDisposed = true; m_pLocalBindInfo = null; m_pRelayItem = null; m_pSmartHosts = null; if(m_pSmtpClient != null){ m_pSmtpClient.Dispose(); m_pSmtpClient = null; } m_pTargets = null; if(m_pActiveTarget != null){ m_pServer.RemoveIpUsage(m_pActiveTarget.Target.Address); m_pActiveTarget = null; } m_pServer = null; } } catch(Exception x){ if(m_pServer != null){ m_pServer.OnError(x); } } }
/// <summary> /// Is called when STARTTLS command has completed. /// </summary> /// <param name="op">Asynchronous operation.</param> /// <exception cref="ArgumentNullException">Is raised when <b>op</b> is null reference.</exception> private void StartTlsCommandCompleted(SMTP_Client.StartTlsAsyncOP op) { if(op == null){ throw new ArgumentNullException("op"); } try{ if(op.Error != null){ Dispose(op.Error); } else{ // Do EHLO/HELO. SMTP_Client.EhloHeloAsyncOP ehloOP = new SMTP_Client.EhloHeloAsyncOP(null); ehloOP.CompletedAsync += delegate(object s,EventArgs<SMTP_Client.EhloHeloAsyncOP> e){ EhloCommandCompleted(ehloOP); }; if(!m_pSmtpClient.EhloHeloAsync(ehloOP)){ EhloCommandCompleted(ehloOP); } } } catch(Exception x){ Dispose(x); } }