This class implements SMTP session. Defined RFC 5321.
Inheritance: LumiSoft.Net.TCP.TCP_ServerSession
        /// <summary>
        /// Filters sender.
        /// </summary>
        /// <param name="from">Sender.</param>
        /// <param name="api">Reference to server API.</param>
        /// <param name="session">Reference to SMTP session.</param>
        /// <param name="errorText">Filtering error text what is returned to client. ASCII text, 100 chars maximum.</param>
        /// <returns>Returns true if sender is ok or false if rejected.</returns>
        public bool Filter(string from,IMailServerApi api,SMTP_Session session,out string errorText)
        {
            errorText = "";
            string ip = session.RemoteEndPoint.Address.ToString();

            Dns_Client dns = new Dns_Client();

            bool ok = false;

            // Don't check PTR for authenticated session and LAN IP ranges
            if(session.Authenticated || ip.StartsWith("127.0.0.1") || ip.StartsWith("10.") || ip.StartsWith("192.168")){
                return true;
            }

            DnsServerResponse reponse = dns.Query(ip,QTYPE.PTR);
            if(reponse.ResponseCode == RCODE.NO_ERROR){
                foreach(PTR_Record rec in reponse.GetPTRRecords()){
                    if(rec.DomainName.ToLower() == session.EhloName.ToLower()){
                        ok = true;
                        break;
                    }
                }
            }

            if(!ok){
                errorText = "Bad EHLO/HELO name, you must have valid DNS PTR record for your EHLO name and IP.";
            }

            return ok;
        }
 /// <summary>
 /// Default constructor.
 /// </summary>
 /// <param name="session">Reference to calling SMTP session.</param>
 /// <param name="errorText">Gets errors what happened on storing message or null if no errors.</param>
 /// <param name="messageStream">Gets message stream where messages was stored. Stream postions is End of Stream, where message storing ended.</param>
 public MessageStoringCompleted_eArgs(SMTP_Session session,string errorText,Stream messageStream)
 {
     m_pSession       = session;
     m_ErrorText      = errorText;
     m_pMessageStream = messageStream;
     m_pCustomReply   = new SmtpServerReply();
 }
		/// <summary>
		/// Default constructor.
		/// </summary>
		/// <param name="session">Reference to pop3 session.</param>
		/// <param name="userName">Username.</param>
		/// <param name="passwData">Password data.</param>
		/// <param name="data">Authentication specific data(as tag).</param>
		/// <param name="authType">Authentication type.</param>
		public AuthUser_EventArgs(SMTP_Session session,string userName,string passwData,string data,AuthType authType)
		{
			m_pSession  = session;
			m_UserName  = userName;
			m_PasswData = passwData;
			m_Data      = data;
			m_AuthType  = authType;
		}
Exemple #4
0
        /// <summary>
        /// Default constructor.
        /// </summary>
        /// <param name="session">Owner SMTP server session.</param>
        /// <exception cref="ArgumentNullException">Is raised when <b>session</b> is null reference.</exception>
        public SMTP_e_Message(SMTP_Session session)
        {
            if(session == null){
                throw new ArgumentNullException("session");
            }

            m_pSession = session;
        }
        /// <summary>
        /// Default constructor.
        /// </summary>
        /// <param name="session">Owner SMTP server session.</param>
        /// <param name="reply">SMTP server reply.</param>
        /// <exception cref="ArgumentNullException">Is raised when <b>session</b> or <b>reply</b> is null reference.</exception>
        public SMTP_e_Started(SMTP_Session session,SMTP_Reply reply)
        {
            if(session == null){
                throw new ArgumentNullException("session");
            }
            if(reply == null){
                throw new ArgumentNullException("reply");
            }

            m_pSession = session;
            m_pReply   = reply;
        }
        /// <summary>
        /// Filters message.
        /// </summary>
        /// <param name="messageStream">Message stream which to filter.</param>
        /// <param name="filteredStream">Filtered stream.</param>
        /// <param name="sender">Senders email address.</param>
        /// <param name="recipients">Recipients email addresses.</param>
        /// <param name="api">Access to server API.</param>
        /// <param name="session">Reference to SMTP session.</param>
        /// <param name="errorText">Filtering error text what is returned to client. ASCII text, 500 chars maximum.</param>
        public FilterResult Filter(Stream messageStream,out Stream filteredStream,string sender,string[] recipients,IMailServerApi api,SMTP_Session session,out string errorText)
        {
            errorText = "";
            filteredStream = messageStream;

            try{
                messageStream.Position = 0;

            //		long pos = messageStream.Position;
                string headers = MimeUtils.ParseHeaders(messageStream).ToLower();
            //		messageStream.Position = pos;

                //--- Check required header fields ---------//
                bool headersOk = true;
                if(headers.IndexOf("from:") == -1){
                    errorText = "Required From: header field is missing !";
                    headersOk = false;
                }
                else if(headers.IndexOf("to:") == -1){
                    errorText = "Required To: header field is missing !";
                    headersOk = false;
                }
                else if(headers.IndexOf("subject:") == -1){
                    errorText = "Required Subject: header field is missing !";
                    headersOk = false;
                }
                //------------------------------------------//

                // Check invalid <CR> or <LF> in headers. Header may not contain <CR> without <LF>,
                // <CRLF> must be in pairs.
                if(headers.Replace("\r\n","").IndexOf("\r") > -1 || headers.Replace("\r\n","").IndexOf("\n") > -1){
                    errorText = "Message contains invalid  <CR> or <LF> combinations !";
                    headersOk = false;
                }
                //-------------------------------------------------------------------------------//

                if(!headersOk){
                    return FilterResult.Error;
                }
            }
            catch{
            }

            // Reset stream position
            messageStream.Position = 0;

            return FilterResult.Store;
        }
        /// <summary>
        /// Default constructor.
        /// </summary>
        /// <param name="session">Owner SMTP server session.</param>
        /// <param name="to">RCPT TO: value.</param>
        /// <param name="reply">SMTP server reply.</param>
        /// <exception cref="ArgumentNullException">Is raised when <b>session</b>, <b>to</b> or <b>reply</b> is null reference.</exception>
        public SMTP_e_RcptTo(SMTP_Session session,SMTP_RcptTo to,SMTP_Reply reply)
        {
            if(session == null){
                throw new ArgumentNullException("session");
            }
            if(to == null){
                throw new ArgumentNullException("from");
            }
            if(reply == null){
                throw new ArgumentNullException("reply");
            }

            m_pSession = session;
            m_pRcptTo  = to;
            m_pReply   = reply;
        }
        /// <summary>
        /// Default constructor.
        /// </summary>
        /// <param name="session">Owner SMTP server session.</param>
        /// <param name="stream">Message stream.</param>
        /// <param name="reply">SMTP server reply.</param>
        /// <exception cref="ArgumentNullException">Is raised when <b>session</b>, <b>stream</b> or <b>reply</b> is null reference.</exception>
        public SMTP_e_MessageStored(SMTP_Session session,Stream stream,SMTP_Reply reply)
        {
            if(session == null){
                throw new ArgumentNullException("session");
            }
            if(stream == null){
                throw new ArgumentNullException("stream");
            }
            if(reply == null){
                throw new ArgumentNullException("reply");
            }

            m_pSession = session;
            m_pStream  = stream;
            m_pReply   = reply;
        }
        /// <summary>
        /// Default constructor.
        /// </summary>
        /// <param name="session">Owner SMTP server session.</param>
        /// <param name="from">MAIL FROM: value.</param>
        /// <param name="reply">SMTP server reply.</param>
        /// <exception cref="ArgumentNullException">Is raised when <b>session</b>, <b>from</b> or <b>reply</b> is null reference.</exception>
        public SMTP_e_MailFrom(SMTP_Session session,SMTP_MailFrom from,SMTP_Reply reply)
        {
            if(session == null){
                throw new ArgumentNullException("session");
            }
            if(from == null){
                throw new ArgumentNullException("from");
            }
            if(reply == null){
                throw new ArgumentNullException("reply");
            }

            m_pSession  = session;
            m_pMailFrom = from;
            m_pReply    = reply;
        }
Exemple #10
0
        /// <summary>
        /// Default constructor.
        /// </summary>
        /// <param name="session">Owner SMTP server session.</param>
        /// <param name="domain">Ehlo/Helo domain name.</param>
        /// <param name="reply">SMTP server reply.</param>
        /// <exception cref="ArgumentNullException">Is raised when <b>session</b>, <b>domain</b> or <b>reply</b> is null reference.</exception>
        /// <exception cref="ArgumentException">Is raised when any of the arguments has invalid value.</exception>
        public SMTP_e_Ehlo(SMTP_Session session,string domain,SMTP_Reply reply)
        {
            if(session == null){
                throw new ArgumentNullException("session");
            }
            if(domain == null){
                throw new ArgumentNullException("domain");
            }
            if(domain == string.Empty){
                throw new ArgumentException("Argument 'domain' value must be sepcified.","domain");
            }
            if(reply == null){
                throw new ArgumentNullException("reply");
            }

            m_pSession = session;
            m_Domain   = domain;
            m_pReply   = reply;
        }
Exemple #11
0
        /// <summary>
		/// Filters sender.
		/// </summary>
		/// <param name="from">Sender.</param>
		/// <param name="api">Reference to server API.</param>
		/// <param name="session">Reference to SMTP session.</param>
		/// <param name="errorText">Filtering error text what is returned to client. ASCII text, 100 chars maximum.</param>
		/// <returns>Returns true if sender is ok or false if rejected.</returns>
        public bool Filter(string from, IMailServerApi api, SMTP_Session session, out string errorText)
        {
            errorText = null;
            bool ok = false;

            // Don't check authenticated users or LAN IP
            if (session.IsAuthenticated || IsPrivateIP(session.RemoteEndPoint.Address))
            {
                return true;
            }

            try
            {                
                using (new TransactionScope(TransactionScopeOption.Suppress))
                {
                    var d = new NKDC(ApplicationConnectionString, null);
                    if (!(from o in d.ContactEmailsViews where o.Email == @from select o).Any())
                    {
                        errorText = "You must be a registered user to use the email support service.";
                        WriteFilterLog("Sender:" + from + " IP:" + session.RemoteEndPoint.Address.ToString() + " unregistered.\r\n");
                        return false;
                    }
                    else
                    {
                        return true;
                    }
                }


            }
            catch (Exception ex)
            {
                WriteFilterLog(string.Format("Sender:{0} IP:{1} caused exception.\r\nEX:{2}{3}\r\n__\r\n", from, session.RemoteEndPoint.Address, ex, ex.Message));
            }

            return ok;
        }
Exemple #12
0
		/// <summary>
		/// Raises event ValidateMailboxSize.
		/// </summary>
		/// <param name="session"></param>
		/// <param name="eAddress"></param>
		/// <param name="messageSize"></param>
		/// <returns></returns>
		internal bool Validate_MailBoxSize(SMTP_Session session,string eAddress,long messageSize)
		{
			ValidateMailboxSize_EventArgs oArgs = new ValidateMailboxSize_EventArgs(session,eAddress,messageSize);
			if(this.ValidateMailboxSize != null){
				this.ValidateMailboxSize(this,oArgs);
			}

			return oArgs.IsValid;
		}
Exemple #13
0
		/// <summary>
		/// Raises event ValidateMailTo.
		/// </summary>
		/// <param name="session"></param>
		/// <param name="forward_path"></param>
		/// <param name="email"></param>
		/// <param name="authenticated"></param>
		/// <returns></returns>
		internal ValidateRecipient_EventArgs OnValidate_MailTo(SMTP_Session session,string forward_path,string email,bool authenticated) 
		{	
			ValidateRecipient_EventArgs oArg = new ValidateRecipient_EventArgs(session,email,authenticated);
			if(this.ValidateMailTo != null){
				this.ValidateMailTo(this, oArg);
			}

			return oArg;						
		}
Exemple #14
0
		/// <summary>
		/// Raises event ValidateMailFrom.
		/// </summary>
		/// <param name="session"></param>
		/// <param name="reverse_path"></param>
		/// <param name="email"></param>
		/// <returns></returns>
		internal ValidateSender_EventArgs OnValidate_MailFrom(SMTP_Session session,string reverse_path,string email) 
		{	
			ValidateSender_EventArgs oArg = new ValidateSender_EventArgs(session,email);
			if(this.ValidateMailFrom != null){
				this.ValidateMailFrom(this, oArg);
			}

			return oArg;						
		}
Exemple #15
0
		/// <summary>
		/// Raises event AuthUser.
		/// </summary>
		/// <param name="session">Reference to current smtp session.</param>
		/// <param name="userName">User name.</param>
		/// <param name="passwordData">Password compare data,it depends of authentication type.</param>
		/// <param name="data">For md5 eg. md5 calculation hash.It depends of authentication type.</param>
		/// <param name="authType">Authentication type.</param>
		/// <returns></returns>
		internal AuthUser_EventArgs OnAuthUser(SMTP_Session session,string userName,string passwordData,string data,AuthType authType)
		{
			AuthUser_EventArgs oArgs = new AuthUser_EventArgs(session,userName,passwordData,data,authType);
			if(this.AuthUser != null){
				this.AuthUser(this,oArgs);
			}

			return oArgs;
		}
Exemple #16
0
		/// <summary>
		/// Raises event ValidateIP event.
		/// </summary>
		/// <param name="session">Reference to current smtp session.</param>
		internal ValidateIP_EventArgs OnValidate_IpAddress(SMTP_Session session) 
		{	
			ValidateIP_EventArgs oArg = new ValidateIP_EventArgs(session.LocalEndPoint,session.RemoteEndPoint);
			if(this.ValidateIPAddress != null){
				this.ValidateIPAddress(this, oArg);
			}

			session.Tag = oArg.SessionTag;

			return oArg;						
		}
Exemple #17
0
            /// <summary>
            /// Is called when SMTP server "final" response sending has completed.
            /// </summary>
            private void SendFinalResponseCompleted(SMTP_Session.SendResponseAsyncOP op)
            {                 
                if(op.Error != null){
                    m_pException = op.Error;
                }

                SetState(AsyncOP_State.Completed);
                
                op.Dispose();
            }
        /// <summary>
        /// Filters message.
        /// </summary>
        /// <param name="messageStream">Message stream which to filter.</param>
        /// <param name="filteredStream">Filtered stream.</param>
        /// <param name="sender">Senders email address.</param>
        /// <param name="recipients">Recipients email addresses.</param>
        /// <param name="api">Access to server API.</param>
        /// <param name="session">Reference to SMTP session.</param>
        /// <param name="errorText">Filtering error text what is returned to client. ASCII text, 500 chars maximum.</param>
        public FilterResult Filter(Stream messageStream,out Stream filteredStream,string sender,string[] recipients,IMailServerApi api,SMTP_Session session,out string errorText)
        {
            errorText = "";

            messageStream.Position = 0;
            filteredStream = messageStream; // we don't change message content, just return same stream

            try{
                //--- Load data -----------------------
                DataSet ds = new DataSet();
                DataTable dt = ds.Tables.Add("KewWords");
                dt.Columns.Add("Cost",typeof(int));
                dt.Columns.Add("KeyWord");

                dt = ds.Tables.Add("ContentMd5");
                dt.Columns.Add("Description");
                dt.Columns.Add("EntryMd5Value");
                ds.ReadXml(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) + "\\lsSpam_db.xml");

                //--- Do mime parts data md5 hash compare ----------------
                ArrayList entries = new ArrayList();
                Mime parser = Mime.Parse(messageStream);

                System.Security.Cryptography.MD5 md5 = System.Security.Cryptography.MD5.Create();
                foreach(MimeEntity ent in parser.MimeEntities){
                    if(ent.Data != null){
                        string md5Hash = Convert.ToBase64String(md5.ComputeHash(ent.Data));

                        foreach(DataRow dr in ds.Tables["ContentMd5"].Rows){
                            // Message contains blocked content(attachment,...)
                            if(dr["EntryMd5Value"].ToString() == md5Hash){
                                WriteFilterLog(DateTime.Now.ToString() + " From:" + sender + " Subject:\"" + parser.MainEntity.Subject + "\"  Contained blocked content:hash=" + md5Hash + "\r\n");
                                return FilterResult.DontStore;
                            }
                        }
                    }
                }

                byte[] topLines = new byte[2000];
                if(messageStream.Length < 2000){
                    topLines = new byte[messageStream.Length];
                }
                messageStream.Read(topLines,0,topLines.Length);

                string lines = System.Text.Encoding.ASCII.GetString(topLines).ToLower();

                //--- Try spam keywords -----------
                int totalCost = 0;
                string keyWords = "";
                DataView dv = ds.Tables["KewWords"].DefaultView;
                dv.Sort = "Cost DESC";
                foreach(DataRowView drV in dv){
                    if(lines.IndexOf(drV.Row["KeyWord"].ToString().ToLower()) > -1){
                        totalCost += Convert.ToInt32(drV.Row["Cost"]);

                        keyWords += drV.Row["KeyWord"].ToString() + " cost:" + drV.Row["Cost"].ToString() + " ";

                        // Check that total cost isn't exceeded
                        if(totalCost > 99){
                            errorText = "Message was blocked by server and considered as SPAM !";

                            WriteFilterLog(DateTime.Now.ToString() + " From:" + sender + " Blocked KeyWords: " + keyWords + "\r\n");

                            return FilterResult.Error;
                        }
                    }
                }
                //---------------------------------

                // Reset stream position
                messageStream.Position = 0;

                return FilterResult.Store;
            }
            catch(Exception x){
                return FilterResult.DontStore;
            }
        }
 /// <summary>
 /// Checks if specified message matches to specified criteria.
 /// </summary>
 /// <param name="matchExpression">Match expression.</param>
 /// <param name="mailFrom">SMTP MAIL FROM: command email value.</param>
 /// <param name="rcptTo">SMTP RCPT TO: command email values.</param>
 /// <param name="smtpSession">SMTP current session.</param>
 /// <param name="mime">Message to match.</param>
 /// <param name="messageSize">Message size in bytes.</param>
 /// <returns>Returns true if message matches to specified criteria.</returns>
 public bool Match(string matchExpression,string mailFrom,string[] rcptTo,SMTP_Session smtpSession,Mail_Message mime,int messageSize)
 {
     LumiSoft.Net.StringReader r = new LumiSoft.Net.StringReader(matchExpression);
     return Match(false,r,mailFrom,rcptTo,smtpSession,mime,messageSize);
 }
 /// <summary>
 /// Default constructor.
 /// </summary>
 /// <param name="session">Reference to smtp session.</param>
 /// <param name="mailTo">Recipient email address.</param>
 /// <param name="authenticated">Specifies if connected user is authenticated.</param>
 public ValidateRecipient_EventArgs(SMTP_Session session,string mailTo,bool authenticated)
 {
     m_pSession      = session;
     m_MailTo        = mailTo;
     m_Authenticated = authenticated;
 }
Exemple #21
0
            /// <summary>
            /// Starts operation processing.
            /// </summary>
            /// <param name="owner">Owner SMTP session.</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>
            public bool Start(SMTP_Session owner)
            {
                if(owner == null){
                    throw new ArgumentNullException("owner");
                }

                m_pSession = owner;

                SetState(AsyncOP_State.Active);

                try{
                    // Build SMTP response.
                    StringBuilder response = new StringBuilder();
                    foreach(SMTP_t_ReplyLine replyLine in m_pReplyLines){
                        response.Append(replyLine.ToString());
                    }
                                        
                    byte[] buffer = Encoding.UTF8.GetBytes(response.ToString());

                    // Log
                    m_pSession.LogAddWrite(buffer.Length,response.ToString());

                    // Start response sending.
                    m_pSession.TcpStream.BeginWrite(buffer,0,buffer.Length,this.ResponseSendingCompleted,null);
                }
                catch(Exception x){
                    m_pException = x;
                    m_pSession.LogAddException("Exception: " + m_pException.Message,m_pException);
                    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;
                }
            }
Exemple #22
0
            /// <summary>
            /// Cleans up any resource being used.
            /// </summary>
            public void Dispose()
            {
                if(m_State == AsyncOP_State.Disposed){
                    return;
                }
                SetState(AsyncOP_State.Disposed);
                
                m_pException = null;
                m_pSession   = null;

                this.CompletedAsync = null;
            }
		/// <summary>
		/// Default constructor.
		/// </summary>
		/// <param name="session">Reference to smtp session.</param>
		/// <param name="eAddress">Email address of recipient.</param>
		/// <param name="messageSize">Message size.</param>
		public ValidateMailboxSize_EventArgs(SMTP_Session session,string eAddress,long messageSize)
		{
			m_pSession = session;
			m_eAddress = eAddress;
			m_MsgSize  = messageSize;
		}
Exemple #24
0
        /// <summary>
		/// Filters sender.
		/// </summary>
		/// <param name="from">Sender.</param>
		/// <param name="api">Reference to server API.</param>
		/// <param name="session">Reference to SMTP session.</param>
		/// <param name="errorText">Filtering error text what is returned to client. ASCII text, 100 chars maximum.</param>
		/// <returns>Returns true if sender is ok or false if rejected.</returns>
		public bool Filter(string from,IMailServerApi api,SMTP_Session session,out string errorText)
		{	
			errorText = null;
			bool ok = true;

			// Don't check authenticated users or LAN IP
			if(session.IsAuthenticated || IsPrivateIP(session.RemoteEndPoint.Address)){
				return true;
			}
			
			try{
				//--- Load data -----------------------
				DataSet ds = new DataSet();				
                ds.Tables.Add("General");
                ds.Tables["General"].Columns.Add("CheckHelo");
                ds.Tables["General"].Columns.Add("LogRejections");
                ds.Tables.Add("BlackListSettings");
                ds.Tables["BlackListSettings"].Columns.Add("ErrorText");
                ds.Tables.Add("BlackList");
                ds.Tables["BlackList"].Columns.Add("IP");
                ds.Tables.Add("Servers");
                ds.Tables["Servers"].Columns.Add("Cost");
                ds.Tables["Servers"].Columns.Add("Server");
                ds.Tables["Servers"].Columns.Add("DefaultRejectionText");
				
				ds.ReadXml(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) + "\\lsDNSBL_Filter_db.xml");

                bool logRejections = false;

                #region General

                if(ds.Tables["General"].Rows.Count == 1){
                    if(Convert.ToBoolean(ds.Tables["General"].Rows[0]["CheckHelo"])){            
                        DnsServerResponse response = Dns_Client.Static.Query(session.EhloHost,DNS_QType.A);
                        // If dns server connection errors, don't block.
                        if(response.ConnectionOk && response.ResponseCode != DNS_RCode.SERVER_FAILURE){
                            bool found = false;
                            foreach(DNS_rr_A a in response.GetARecords()){
                                if(session.RemoteEndPoint.Address.Equals(a.IP)){
                                    found = true;

                                    break;
                                }
                            }
                            if(!found){
                                errorText = "Not valid DNS EHLO/HELO name for your IP '" + session.EhloHost + "' !";

                                return false;
                            }
                        }
                    }
                    logRejections = ConvertEx.ToBoolean(ds.Tables["General"].Rows[0]["LogRejections"]);
                }

                #endregion

                #region Balck List

                foreach(DataRow dr in ds.Tables["BlackList"].Rows){
                    if(IsAstericMatch(dr["IP"].ToString(),session.RemoteEndPoint.Address.ToString())){
                        errorText = ds.Tables["BlackListSettings"].Rows[0]["ErrorText"].ToString();

                        return false;
                    }
                }

                #endregion

                #region DNSBL
                                               
				foreach(DataRow dr in ds.Tables["Servers"].Rows){
                    DnsServerResponse dnsResponse =  Dns_Client.Static.Query(ReverseIP(session.RemoteEndPoint.Address) + "." + dr["Server"].ToString(),DNS_QType.ANY);                    
					DNS_rr_A[] recs = dnsResponse.GetARecords();
					if(recs.Length > 0){
					    if(logRejections){
						    WriteFilterLog("Sender:" + from + " IP:" + session.RemoteEndPoint.Address.ToString() + " blocked\r\n");
                        }

                        errorText = dr["DefaultRejectionText"].ToString();
                        // Server provided return text, use it
                        if(dnsResponse.GetTXTRecords().Length > 0){
                            errorText = dnsResponse.GetTXTRecords()[0].Text;
                        }
                        if(errorText == ""){
                            errorText = "You are in '" + dr["Server"].ToString() + "' rejection list !";
                        }

						return false;
					}
                }

                #endregion                
            }
			catch{
			}

			return ok;
        }
Exemple #25
0
            /// <summary>
            /// Is called when SMTP server 354 response sending has completed.
            /// </summary>
            /// <param name="op">Asynchronous operation.</param>
            private void Send354ResponseCompleted(SMTP_Session.SendResponseAsyncOP op)
            {
                try{
                    // RFC 5321.4.4 trace info.
                    byte[] recevived = m_pSession.CreateReceivedHeader();
                    m_pSession.m_pMessageStream.Write(recevived,0,recevived.Length);
                    
                    // Create asynchronous read period-terminated opeartion.
                    SmartStream.ReadPeriodTerminatedAsyncOP readPeriodTermOP = new SmartStream.ReadPeriodTerminatedAsyncOP(
                        m_pSession.m_pMessageStream,
                        m_pSession.Server.MaxMessageSize,
                        SizeExceededAction.JunkAndThrowException
                    );
                    // This event is raised only if read period-terminated opeartion completes asynchronously.
                    readPeriodTermOP.Completed += new EventHandler<EventArgs<SmartStream.ReadPeriodTerminatedAsyncOP>>(delegate(object sender,EventArgs<SmartStream.ReadPeriodTerminatedAsyncOP> e){                
                        MessageReadingCompleted(readPeriodTermOP);
                    });
                    // Read period-terminated completed synchronously.
                    if(m_pSession.TcpStream.ReadPeriodTerminated(readPeriodTermOP,true)){
                        MessageReadingCompleted(readPeriodTermOP);
                    }
                }
                catch(Exception x){
                    m_pException = x;
                    m_pSession.LogAddException("Exception: " + m_pException.Message,m_pException);
                    SetState(AsyncOP_State.Completed);
                }

                op.Dispose();
            }
Exemple #26
0
 /// <summary>
 /// Raises event GetMessageStoreStream.
 /// </summary>
 /// <param name="session">Reference to calling SMTP session.</param>
 /// <returns></returns>
 internal GetMessageStoreStream_eArgs OnGetMessageStoreStream(SMTP_Session session)
 {
     GetMessageStoreStream_eArgs eArgs = new GetMessageStoreStream_eArgs(session);
     if(this.GetMessageStoreStream != null){
         this.GetMessageStoreStream(this,eArgs);
     }
     return eArgs;
 }
Exemple #27
0
        /// <summary>
        /// Raises event MessageStoringCompleted.
        /// </summary>
        /// <param name="session">Reference to calling SMTP session.</param>
        /// <param name="errorText">Null if no errors, otherwise conatians error text. If errors happened that means that messageStream is incomplete.</param>
        /// <param name="messageStream">Stream where message was stored.</param>
        internal MessageStoringCompleted_eArgs OnMessageStoringCompleted(SMTP_Session session,string errorText,Stream messageStream)
        {            
            MessageStoringCompleted_eArgs eArgs = new MessageStoringCompleted_eArgs(session,errorText,messageStream);
            if(this.MessageStoringCompleted != null){
                this.MessageStoringCompleted(this,eArgs);
            }

            return eArgs;
        }
        /// <summary>
        /// Checks if specified message matches to specified criteria.
        /// </summary>
        /// <param name="syntaxCheckOnly">Specifies if syntax check is only done. If true no matching is done.</param>
        /// <param name="r">Match expression reader what contains match expression.</param>
        /// <param name="mailFrom">SMTP MAIL FROM: command email value.</param>
        /// <param name="rcptTo">SMTP RCPT TO: command email values.</param>
        /// <param name="smtpSession">SMTP current session.</param>
        /// <param name="mime">Message to match.</param>
        /// <param name="messageSize">Message size in bytes.</param>
        /// <returns>Returns true if message matches to specified criteria.</returns>
        private bool Match(bool syntaxCheckOnly,LumiSoft.Net.StringReader r,string mailFrom,string[] rcptTo,SMTP_Session smtpSession,Mail_Message mime,int messageSize)
        {             
            /* Possible keywords order
                At first there can be NOT,parethesized or matcher
                    After NOT, parethesized or matcher
                    After matcher, AND or OR
                    After OR, NOT,parethesized or matcher
                    After AND, NOT,parethesized or matcher
                    After parethesized, NOT or matcher
            */
 
            PossibleClauseItem possibleClauseItems = PossibleClauseItem.Parenthesizes | PossibleClauseItem.NOT | PossibleClauseItem.Matcher;
            bool lastMatchValue = false;

            // Empty string passed 
            r.ReadToFirstChar();
            if(r.Available == 0){
                throw new Exception("Invalid syntax: '" + ClauseItemsToString(possibleClauseItems) + "' expected !");
            }

            // Parse while there are expressions or get error
            while(r.Available > 0){
                r.ReadToFirstChar();

                // Syntax check must consider that there is alwas match !!!
                if(syntaxCheckOnly){
                    lastMatchValue = true;
                }

                #region () Groupped matchers

                // () Groupped matchers
                if(r.StartsWith("(")){
                    lastMatchValue = Match(syntaxCheckOnly,new LumiSoft.Net.StringReader(r.ReadParenthesized()),mailFrom,rcptTo,smtpSession,mime,messageSize);

                    possibleClauseItems = PossibleClauseItem.Parenthesizes | PossibleClauseItem.Matcher | PossibleClauseItem.NOT;
                }

                #endregion

                #region AND clause

                // AND clause
                else if(r.StartsWith("and",false)){
                    // See if AND allowed
                    if((possibleClauseItems & PossibleClauseItem.AND) == 0){
                        throw new Exception("Invalid syntax: '" + ClauseItemsToString(possibleClauseItems) + "' expected !");
                    }
                    
                    // Last match value is false, no need to check next conditions
                    if(!lastMatchValue){
                        return false;
                    }

                    // Remove AND
                    r.ReadWord();
                    r.ReadToFirstChar();

                    lastMatchValue = Match(syntaxCheckOnly,r,mailFrom,rcptTo,smtpSession,mime,messageSize);

                    possibleClauseItems = PossibleClauseItem.Parenthesizes | PossibleClauseItem.Matcher | PossibleClauseItem.NOT;
                }

                #endregion

                #region OR clause

                // OR clause
                else if(r.StartsWith("or",false)){
                    // See if OR allowed
                    if((possibleClauseItems & PossibleClauseItem.OR) == 0){
                        throw new Exception("Invalid syntax: '" + ClauseItemsToString(possibleClauseItems) + "' expected !");
                    }

                    // Remove OR
                    r.ReadWord();
                    r.ReadToFirstChar();

                    // Last match value is false, then we need to check next condition.
                    // Otherwise OR is matched already, just eat next matcher.
                    if(lastMatchValue){
                        // Skip next clause
                        Match(syntaxCheckOnly,r,mailFrom,rcptTo,smtpSession,mime,messageSize);
                    }
                    else{
                        lastMatchValue = Match(syntaxCheckOnly,r,mailFrom,rcptTo,smtpSession,mime,messageSize);
                    }

                    possibleClauseItems = PossibleClauseItem.Parenthesizes | PossibleClauseItem.Matcher | PossibleClauseItem.NOT;
                }

                #endregion

                #region NOT clause

                // NOT clause
                else if(r.StartsWith("not",false)){
                    // See if NOT allowed
                    if((possibleClauseItems & PossibleClauseItem.NOT) == 0){
                        throw new Exception("Invalid syntax: '" + ClauseItemsToString(possibleClauseItems) + "' expected !");
                    }

                    // Remove NOT
                    r.ReadWord();
                    r.ReadToFirstChar();

                    // Just reverse match result value
                    lastMatchValue = !Match(syntaxCheckOnly,r,mailFrom,rcptTo,smtpSession,mime,messageSize);

                    possibleClauseItems = PossibleClauseItem.Parenthesizes | PossibleClauseItem.Matcher;
                }

                #endregion

                else{
                    // See if matcher allowed
                    if((possibleClauseItems & PossibleClauseItem.Matcher) == 0){
                        throw new Exception("Invalid syntax: '" + ClauseItemsToString(possibleClauseItems) + "' expected ! \r\n\r\n Near: '" + r.OriginalString.Substring(0,r.Position) + "'");
                    }

                    // 1) matchsource
                    // 2) keyword
                
                    // Read match source
                    string word = r.ReadWord();
                    if(word == null){
                        throw new Exception("Invalid syntax: matcher is missing !");
                    }
                    word = word.ToLower();
                    string[] matchSourceValues = new string[]{};


                    #region smtp.mail_from
                    
                    // SMTP command MAIL FROM: value.
                    //  smtp.mail_from
                    if(word == "smtp.mail_from"){
                        if(!syntaxCheckOnly){
                            matchSourceValues = new string[]{mailFrom};
                        }
                    }

                    #endregion

                    #region smtp.rcpt_to

                    // SMTP command RCPT TO: values.
                    //  smtp.mail_to
                    else if(word == "smtp.rcpt_to"){
                        if(!syntaxCheckOnly){
                            matchSourceValues = rcptTo;
                        }
                    }

                    #endregion

                    #region smtp.ehlo

                    // SMTP command EHLO/HELO: value.
                    //  smtp.ehlo
                    else if(word == "smtp.ehlo"){
                        if(!syntaxCheckOnly){
                            matchSourceValues = new string[]{smtpSession.EhloHost};
                        }
                    }

                    #endregion

                    #region smtp.authenticated

                    // Specifies if SMTP session is authenticated.
                    //  smtp.authenticated
                    else if(word == "smtp.authenticated"){
                        if(!syntaxCheckOnly){
                            if(smtpSession != null){
                                matchSourceValues = new string[]{smtpSession.IsAuthenticated.ToString()};
                            }
                        }
                    }

                    #endregion

                    #region smtp.user

                    // SMTP authenticated user name. Empy string "" if not authenticated.
                    //  smtp.user
                    else if(word == "smtp.user"){
                        if(!syntaxCheckOnly){
                            if(smtpSession != null && smtpSession.AuthenticatedUserIdentity != null){
                                matchSourceValues = new string[]{smtpSession.AuthenticatedUserIdentity.Name};
                            }
                        }
                    }

                    #endregion

                    #region smtp.remote_ip

                    // SMTP session connected client IP address.
                    //  smtp.remote_ip
                    else if(word == "smtp.remote_ip"){
                        if(!syntaxCheckOnly){
                            if(smtpSession != null){
                                matchSourceValues = new string[]{smtpSession.RemoteEndPoint.Address.ToString()};
                            }
                        }
                    }

                    #endregion

                    
                    #region message.size

                    // Message size in bytes.
                    //  message.size
                    else if(word == "message.size"){
                        if(!syntaxCheckOnly){
                            matchSourceValues = new string[]{messageSize.ToString()};
                        }
                    }

                    #endregion

                    #region message.header <SP> "HeaderFieldName:"

                    // Message main header header field. If multiple header fields, then all are checked.
                    //  message.header <SP> "HeaderFieldName:"
                    else if(word == "message.header"){
                        string headerFieldName = r.ReadWord();
                        if(headerFieldName == null){
                            throw new Exception("Match source MainHeaderField HeaderFieldName is missing ! Syntax:{MainHeaderField <SP> \"HeaderFieldName:\"}");
                        }
                    
                        if(!syntaxCheckOnly){
                            if(mime.Header.Contains(headerFieldName)){
                                MIME_h[] fields = mime.Header[headerFieldName];
                                matchSourceValues = new string[fields.Length];
                                for(int i=0;i<matchSourceValues.Length;i++){
                                    matchSourceValues[i] = fields[i].ValueToString();
                                }
                            }
                        }
                    }

                    #endregion

                    #region message.all_headers <SP> "HeaderFieldName:"

                    // Any mime entity header header field. If multiple header fields, then all are checked.
                    //  message.all_headers <SP> "HeaderFieldName:"
                    else if(word == "message.all_headers"){
                        string headerFieldName = r.ReadWord();
                        if(headerFieldName == null){
                            throw new Exception("Match source MainHeaderField HeaderFieldName is missing ! Syntax:{MainHeaderField <SP> \"HeaderFieldName:\"}");
                        }

                        if(!syntaxCheckOnly){
                            List<string> values = new List<string>();
                            foreach(MIME_Entity entity in mime.AllEntities){
                                if(entity.Header.Contains(headerFieldName)){
                                    MIME_h[] fields = entity.Header[headerFieldName];
                                    for(int i=0;i<fields.Length;i++){
                                        values.Add(fields[i].ValueToString());
                                    }
                                }
                            }
                            matchSourceValues = values.ToArray();
                        }
                    }

                    #endregion

                    #region message.body_text

                    // Message body text.
                    //  message.body_text
                    else if(word == "message.body_text"){
                        if(!syntaxCheckOnly){
                            matchSourceValues = new string[]{mime.BodyText};
                        }
                    }

                    #endregion

                    #region message.body_html

                    // Message body html.
                    //  message.body_html
                    else if(word == "message.body_html"){
                        if(!syntaxCheckOnly){
                            matchSourceValues = new string[]{mime.BodyHtmlText};
                        }
                    }

                    #endregion

                    #region message.content_md5

                    // Message any mime entity decoded data MD5 hash.
                    //  message.content_md5
                    else if(word == "message.content_md5"){
                        if(!syntaxCheckOnly){
                            List<string> values = new List<string>();
                            foreach(MIME_Entity entity in mime.AllEntities){
                                try{
                                    if(entity.Body is MIME_b_SinglepartBase){
                                        byte[] data = ((MIME_b_SinglepartBase)entity.Body).Data;
                                        if(data != null){
                                            System.Security.Cryptography.MD5CryptoServiceProvider md5 = new System.Security.Cryptography.MD5CryptoServiceProvider();
                                            values.Add(System.Text.Encoding.Default.GetString(md5.ComputeHash(data)));
                                        }
                                    }
                                }
                                catch{
                                    // Message data parsing failed, just skip that entity md5
                                }
                            }
                            matchSourceValues = values.ToArray();
                        }
                    }

                    #endregion


                    #region sys.date_time

                    // System current date time. Format: yyyy.MM.dd HH:mm:ss.
                    //  sys.date_time
                    else if(word == "sys.date_time"){
                        if(!syntaxCheckOnly){
                            matchSourceValues = new string[]{DateTime.Now.ToString("dd.MM.yyyy HH:mm:ss")};
                        }
                    }

                    #endregion

                    #region sys.date

                    // System current date. Format: yyyy.MM.dd.
                    //  sys.date
                    else if(word == "sys.date"){
                        if(!syntaxCheckOnly){
                            matchSourceValues = new string[]{DateTime.Today.ToString("dd.MM.yyyy")};
                        }
                    }

                    #endregion

                    #region sys.time

                    // System current time. Format: HH:mm:ss.
                    //  sys.time
                    else if(word == "sys.time"){
                        if(!syntaxCheckOnly){
                            matchSourceValues = new string[]{DateTime.Now.ToString("HH:mm:ss")};
                        }
                    }

                    #endregion

                    #region sys.day_of_week

                    // Day of week. Days: sunday,monday,tuesday,wednesday,thursday,friday,saturday.
                    //  sys.day_of_week
                    else if(word == "sys.day_of_week"){
                        if(!syntaxCheckOnly){
                            matchSourceValues = new string[]{DateTime.Today.DayOfWeek.ToString()};
                        }
                    }

                    #endregion

                    /*
                    // Day of month. Format: 1 - 31. If no so much days in month, then replaced with month max days.
                    // sys.day_of_month
                    else if(word == "sys.day_of_month"){
                    }
*/
                    #region sys.day_of_year

                    // Month of year. Format: 1 - 12.
                    // sys.day_of_year
                    else if(word == "sys.day_of_year"){
                        if(!syntaxCheckOnly){
                            matchSourceValues = new string[]{DateTime.Today.ToString("M")};
                        }
                    }

                    #endregion

                    #region Unknown

                    // Unknown
                    else{
                        throw new Exception("Unknown match source '" + word + "' !");
                    }

                    #endregion

                    
                    /* If we reach so far, then we have valid match sorce and compare value.
                       Just do compare.
                    */

                    // Reset lastMatch result
                    lastMatchValue = false;

                    // Read matcher
                    word = r.ReadWord(true,new char[]{' '},true);
                    if(word == null){
                        throw new Exception("Invalid syntax: operator is missing ! \r\n\r\n Near: '" + r.OriginalString.Substring(0,r.Position) + "'");
                    }
                    word = word.ToLower();

                    #region * <SP> "astericPattern"

                    // * <SP> "astericPattern"
                    if(word == "*"){
                        string val = r.ReadWord();
                        if(val == null){
                            throw new Exception("Invalid syntax: <SP> \"value\" is missing !");
                        }
                        val = val.ToLower();
                        
                        if(!syntaxCheckOnly){
                            // We check matchSourceValues when first is found 
                            foreach(string matchSourceValue in matchSourceValues){                            
                                if(SCore.IsAstericMatch(val,matchSourceValue.ToLower())){
                                    lastMatchValue = true;
                                    break;
                                }
                            }
                        }
                    }

                    #endregion

                    #region !* <SP> "astericPattern"

                    // !* <SP> "astericPattern"
                    else if(word == "!*"){
                        string val = r.ReadWord();
                        if(val == null){
                            throw new Exception("Invalid syntax: <SP> \"value\" is missing !");
                        }
                        val = val.ToLower();
                        
                        if(!syntaxCheckOnly){
                            // We check matchSourceValues when first is found 
                            foreach(string matchSourceValue in matchSourceValues){                            
                                if(SCore.IsAstericMatch(val,matchSourceValue.ToLower())){
                                    lastMatchValue = false;
                                    break;
                                }
                            }
                        }
                    }

                    #endregion

                    #region == <SP> "value"

                    // == <SP> "value"
                    else if(word == "=="){
                        string val = r.ReadWord();
                        if(val == null){
                            throw new Exception("Invalid syntax: <SP> \"value\" is missing !");
                        }
                        val = val.ToLower();

                        if(!syntaxCheckOnly){
                            // We check matchSourceValues when first is found 
                            foreach(string matchSourceValue in matchSourceValues){
                                if(val == matchSourceValue.ToLower()){
                                    lastMatchValue = true;
                                    break;
                                }
                            }
                        }
                    }

                    #endregion

                    #region != <SP> "value"

                    // != <SP> "value"
                    else if(word == "!="){
                        string val = r.ReadWord();
                        if(val == null){
                            throw new Exception("Invalid syntax: <SP> \"value\" is missing !");
                        }
                        val = val.ToLower();

                        if(!syntaxCheckOnly){
                            // We check matchSourceValues when first is found, then already value equals 
                            foreach(string matchSourceValue in matchSourceValues){
                                if(val == matchSourceValue.ToLower()){
                                    lastMatchValue = false;
                                    break;
                                }
                                lastMatchValue = true;
                            }
                        }
                    }

                    #endregion

                    #region >= <SP> "value"

                    // >= <SP> "value"
                    else if(word == ">="){
                        string val = r.ReadWord();
                        if(val == null){
                            throw new Exception("Invalid syntax: <SP> \"value\" is missing !");
                        }
                        val = val.ToLower();

                        if(!syntaxCheckOnly){
                            // We check matchSourceValues when first is found 
                            foreach(string matchSourceValue in matchSourceValues){
                                if(matchSourceValue.ToLower().CompareTo(val) >= 0){
                                    lastMatchValue = true;
                                    break;
                                }
                            }
                        }
                    }

                    #endregion

                    #region <= <SP> "value"

                    // <= <SP> "value"
                    else if(word == "<="){
                        string val = r.ReadWord();
                        if(val == null){
                            throw new Exception("Invalid syntax: <SP> \"value\" is missing !");
                        }
                        val = val.ToLower();

                        if(!syntaxCheckOnly){
                            // We check matchSourceValues when first is found 
                            foreach(string matchSourceValue in matchSourceValues){
                                if(matchSourceValue.ToLower().CompareTo(val) <= 0){
                                    lastMatchValue = true;
                                    break;
                                }
                            }
                        }
                    }

                    #endregion

                    #region > <SP> "value"

                    // > <SP> "value"
                    else if(word == ">"){
                        string val = r.ReadWord();
                        if(val == null){
                            throw new Exception("Invalid syntax: <SP> \"value\" is missing !");
                        }
                        val = val.ToLower();

                        if(!syntaxCheckOnly){
                            // We check matchSourceValues when first is found 
                            foreach(string matchSourceValue in matchSourceValues){
                                if(matchSourceValue.ToLower().CompareTo(val) > 0){
                                    lastMatchValue = true;
                                    break;
                                }
                            }
                        }
                    }

                    #endregion

                    #region < <SP> "value"

                    // < <SP> "value"
                    else if(word == "<"){
                        string val = r.ReadWord();
                        if(val == null){
                            throw new Exception("Invalid syntax: <SP> \"value\" is missing !");
                        }
                        val = val.ToLower();

                        if(!syntaxCheckOnly){
                            // We check matchSourceValues when first is found 
                            foreach(string matchSourceValue in matchSourceValues){
                                if(matchSourceValue.ToLower().CompareTo(val) < 0){
                                    lastMatchValue = true;
                                    break;
                                }
                            }
                        }
                    }

                    #endregion

                    #region regex <SP> "value"

                    // Regex <SP> "value"
                    else if(word == "regex"){
                        string val = r.ReadWord();
                        if(val == null){
                            throw new Exception("Invalid syntax: <SP> \"value\" is missing !");
                        }
                        val = val.ToLower();

                        if(!syntaxCheckOnly){
                            // We check matchSourceValues when first is found 
                            foreach(string matchSourceValue in matchSourceValues){                            
                                if(Regex.IsMatch(val,matchSourceValue.ToLower())){
                                    lastMatchValue = true;
                                    break;
                                }
                            }
                        }
                    }

                    #endregion

                    #region Unknown

                    // Unknown
                    else{
                        throw new Exception("Unknown keword '" + word + "' !");
                    }

                    #endregion

                    possibleClauseItems = PossibleClauseItem.AND | PossibleClauseItem.OR;
                }
            }

            return lastMatchValue;
        }
Exemple #29
0
		/// <summary>
		/// Initialize and start new session here. Session isn't added to session list automatically, 
		/// session must add itself to server session list by calling AddSession().
		/// </summary>
		/// <param name="socket">Connected client socket.</param>
        /// <param name="bindInfo">BindInfo what accepted socket.</param>
		protected override void InitNewSession(Socket socket,IPBindInfo bindInfo)
		{
            // Check maximum conncurent connections from 1 IP.
            if(m_MaxConnectionsPerIP > 0){
                lock(this.Sessions){
                    int nSessions = 0;
                    foreach(SocketServerSession s in this.Sessions){
                        IPEndPoint ipEndpoint = s.RemoteEndPoint;
                        if(ipEndpoint != null){
                            if(ipEndpoint.Address.Equals(((IPEndPoint)socket.RemoteEndPoint).Address)){
                                nSessions++;
                            }
                        }

                        // Maimum allowed exceeded
                        if(nSessions >= m_MaxConnectionsPerIP){
                            socket.Send(System.Text.Encoding.ASCII.GetBytes("421 Maximum connections from your IP address is exceeded, try again later !\r\n"));
                            socket.Shutdown(SocketShutdown.Both);
                            socket.Close();
                            return;
                        }
                    }
                }
            }

            string   sessionID = Guid.NewGuid().ToString();
            SocketEx socketEx  = new SocketEx(socket);
            if(LogCommands){
                socketEx.Logger = new SocketLogger(socket,this.SessionLog);
				socketEx.Logger.SessionID = sessionID;
            }
			SMTP_Session session = new SMTP_Session(sessionID,socketEx,bindInfo,this);
		}
Exemple #30
0
             /// <summary>
            /// Starts operation processing.
            /// </summary>
            /// <param name="owner">Owner SMTP session.</param>
            /// <param name="cmdText">SMTP client command text.</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>
            public bool Start(SMTP_Session owner,string cmdText)
            {
                if(owner == null){
                    throw new ArgumentNullException("owner");
                }

                m_pSession  = owner;
                m_StartTime = DateTime.Now;

                SetState(AsyncOP_State.Active);

                try{
                    /* RFC 5321 4.1.1.4.
                        The receiver normally sends a 354 response to DATA, and then treats
                        the lines (strings ending in <CRLF> sequences, as described in
                        Section 2.3.7) following the command as mail data from the sender.
                        This command causes the mail data to be appended to the mail data
                        buffer.  The mail data may contain any of the 128 ASCII character
                        codes, although experience has indicated that use of control
                        characters other than SP, HT, CR, and LF may cause problems and
                        SHOULD be avoided when possible.
             
                        The custom of accepting lines ending only in <LF>, as a concession to
                        non-conforming behavior on the part of some UNIX systems, has proven
                        to cause more interoperability problems than it solves, and SMTP
                        server systems MUST NOT do this, even in the name of improved
                        robustness.  In particular, the sequence "<LF>.<LF>" (bare line
                        feeds, without carriage returns) MUST NOT be treated as equivalent to
                        <CRLF>.<CRLF> as the end of mail data indication.
             
                        Receipt of the end of mail data indication requires the server to
                        process the stored mail transaction information.  This processing
                        consumes the information in the reverse-path buffer, the forward-path
                        buffer, and the mail data buffer, and on the completion of this
                        command these buffers are cleared.  If the processing is successful,
                        the receiver MUST send an OK reply.  If the processing fails, the
                        receiver MUST send a failure reply.  The SMTP model does not allow
                        for partial failures at this point: either the message is accepted by
                        the server for delivery and a positive response is returned or it is
                        not accepted and a failure reply is returned.  In sending a positive
                        "250 OK" completion reply to the end of data indication, the receiver
                        takes full responsibility for the message (see Section 6.1).  Errors
                        that are diagnosed subsequently MUST be reported in a mail message,
                        as discussed in Section 4.4.

                        When the SMTP server accepts a message either for relaying or for
                        final delivery, it inserts a trace record (also referred to
                        interchangeably as a "time stamp line" or "Received" line) at the top
                        of the mail data.  This trace record indicates the identity of the
                        host that sent the message, the identity of the host that received
                        the message (and is inserting this time stamp), and the date and time
                        the message was received.  Relayed messages will have multiple time
                        stamp lines.  Details for formation of these lines, including their
                        syntax, is specified in Section 4.4.
                    */
                                        
                    // RFC 5321 3.1.
                    if(m_pSession.m_SessionRejected){
                        SendFinalResponse(new SMTP_t_ReplyLine(503,"Bad sequence of commands: Session rejected.",true));
                    }
                    // RFC 5321 4.1.4.
                    else if(string.IsNullOrEmpty(m_pSession.m_EhloHost)){
                        SendFinalResponse(new SMTP_t_ReplyLine(503,"Bad sequence of commands: Send EHLO/HELO first.",true));
                    }
                    // RFC 5321 4.1.4.
                    else if(m_pSession.m_pFrom == null){
                        SendFinalResponse(new SMTP_t_ReplyLine(503,"Bad sequence of commands: Send 'MAIL FROM:' first.",true));
                    }
                    // RFC 5321 4.1.4.
                    else if(m_pSession.m_pTo.Count == 0){
                        SendFinalResponse(new SMTP_t_ReplyLine(503,"Bad sequence of commands: Send 'RCPT TO:' first.",true));
                    }
                    else if(!string.IsNullOrEmpty(cmdText)){
                        SendFinalResponse(new SMTP_t_ReplyLine(500,"Command line syntax error.",true));
                    }
                    else{
                        // Get message store stream.
                        m_pSession.m_pMessageStream = m_pSession.OnGetMessageStream();
                        if(m_pSession.m_pMessageStream == null){
                            m_pSession.m_pMessageStream = new MemoryStreamEx(32000);
                        }                   
                        
                        // Send "354 Start mail input; end with <CRLF>.<CRLF>".
                        SMTP_Session.SendResponseAsyncOP sendResponseOP = new SendResponseAsyncOP(new SMTP_t_ReplyLine(354,"Start mail input; end with <CRLF>.<CRLF>",true));
                        sendResponseOP.CompletedAsync += delegate(object sender,EventArgs<SendResponseAsyncOP> e){
                            Send354ResponseCompleted(sendResponseOP);
                        };
                        if(!m_pSession.SendResponseAsync(sendResponseOP)){
                            Send354ResponseCompleted(sendResponseOP);
                        }
                    }
                }
                catch(Exception x){
                    m_pException = x;
                    m_pSession.LogAddException("Exception: " + m_pException.Message,m_pException);
                    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;
                }
            }