Example #1
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);
        }
Example #2
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>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="recipient"></param>
        /// <param name="userName"></param>
        /// <param name="storeFolder">Message folder where message will be stored. For example 'Inbox'.</param>
        /// <param name="msgStream"></param>
        /// <param name="e">Event data.</param>
        internal void ProcessUserMsg(string sender,string recipient,string userName,string storeFolder,Stream msgStream,MessageStoringCompleted_eArgs e)
        {
            string userID = m_pApi.GetUserID(userName);
            // This value can be null only if user deleted during this session, so just skip next actions.
            if(userID == null){
                return;
            }

            #region User Message rules

            Stream filteredMsgStream = msgStream;
            filteredMsgStream.Position = 0;

            Mime mime = null;
            try{
                mime = Mime.Parse(filteredMsgStream);
            }
            // Invalid message syntax, block such message.
            catch{
                e.ServerReply.ErrorReply = true;
                e.ServerReply.ReplyText = "Invalid message structure/syntax, message blocked !";
                return;
            }

            string[] to = new string[]{recipient};

            //--- Check User Message Rules --------------------------------------------------------------//
            bool   deleteMessage = false;
            string smtpErrorText = null;

            // Loop rules
            foreach(DataRowView drV_Rule in m_pApi.GetUserMessageRules(userName)){
                // Reset stream position
                filteredMsgStream.Position = 0;

                if(Convert.ToBoolean(drV_Rule["Enabled"])){
                    string ruleID = drV_Rule["RuleID"].ToString();
                    GlobalMessageRule_CheckNextRule_enum checkNextIf = (GlobalMessageRule_CheckNextRule_enum)(int)drV_Rule["CheckNextRuleIf"];
                    string matchExpression = drV_Rule["MatchExpression"].ToString();

                    // e may be null if server internal method call and no actual session !
                    SMTP_Session session = null;
                    if(e != null){
                        session = e.Session;
                    }
                    GlobalMessageRuleProcessor ruleEngine = new GlobalMessageRuleProcessor();
                    bool matches = ruleEngine.Match(matchExpression,sender,to,session,mime,(int)filteredMsgStream.Length);
                    if(matches){
                        // Do actions
                        GlobalMessageRuleActionResult result = ruleEngine.DoActions(
                            m_pApi.GetUserMessageRuleActions(userID,ruleID),
                            this,
                            filteredMsgStream,
                            sender,
                            to
                        );

                        if(result.DeleteMessage){
                            deleteMessage = true;
                        }
                        if(result.StoreFolder != null){
                            storeFolder = result.StoreFolder;
                        }
                        if(result.ErrorText != null){
                            smtpErrorText = result.ErrorText;
                        }
                    }

                    //--- See if we must check next rule -------------------------------------------------//
                    if(checkNextIf == GlobalMessageRule_CheckNextRule_enum.Always){
                        // Do nothing
                    }
                    else if(checkNextIf == GlobalMessageRule_CheckNextRule_enum.IfMatches && !matches){
                        break;
                    }
                    else if(checkNextIf == GlobalMessageRule_CheckNextRule_enum.IfNotMatches && matches){
                        break;
                    }
                    //------------------------------------------------------------------------------------//
                }
            }

            // Return error to connected client
            if(smtpErrorText != null){
                e.ServerReply.ErrorReply = true;
                e.ServerReply.ReplyText = smtpErrorText;
                return;
            }

            // Just don't store message
            if(deleteMessage){
                return;
            }

            // Reset stream position
            filteredMsgStream.Position = 0;
            //--- End of Global Rules -------------------------------------------------------------------//

            #endregion

            // ToDo: User message filtering
            #region User message filtering

                //--- Filter message -----------------------------------------------//
            /*		MemoryStream filteredMsgStream = msgStream;
                DataView dvFilters = m_pApi.GetFilters();
                dvFilters.RowFilter = "Enabled=true AND Type=ISmtpUserMessageFilter";
                dvFilters.Sort = "Cost";
                foreach(DataRowView drViewFilter in dvFilters){
                    string assemblyFile = drViewFilter.Row["Assembly"].ToString();
                    // File is without path probably, try to load it from filters folder
                    if(!File.Exists(assemblyFile)){
                        assemblyFile = m_SartUpPath + "\\Filters\\" + assemblyFile;
                    }

                    Assembly ass = Assembly.LoadFrom(assemblyFile);
                    Type tp = ass.GetType(drViewFilter.Row["ClassName"].ToString());
                    object filterInstance = Activator.CreateInstance(tp);
                    ISmtpMessageFilter filter = (ISmtpMessageFilter)filterInstance;

                    FilterResult result = filter.Filter(filteredMsgStream,out filteredMsgStream,sender,recipient,m_pApi);
                    switch(result)
                    {
                        case FilterResult.DontStore:
                            return; // Just skip messge, act as message is stored

                        case FilterResult.Error:
                            // ToDO: Add implementaion here or get rid of it (use exception instead ???).
                            return;
                    }
                }*/
                //---------------------------------------------------------------//

                #endregion

            try{
                m_pApi.StoreMessage("system",userName,storeFolder,filteredMsgStream,DateTime.Now,IMAP_MessageFlags.Recent);
            }
            catch{
                // Storing probably failed because there isn't such folder, just store to user inbox.
                m_pApi.StoreMessage("system",userName,"Inbox",filteredMsgStream,DateTime.Now,IMAP_MessageFlags.Recent);
            }
        }
        /// <summary>
        /// Processes and stores message.
        /// </summary>
        /// <param name="sender">Mail from.</param>
        /// <param name="recipient">Recipient to.</param>
        /// <param name="msgStream">Message stream. Stream position must be there where message begins.</param>
        /// <param name="e">Event data.</param>
        public void ProcessAndStoreMessage(string sender,string[] recipient,Stream msgStream,MessageStoringCompleted_eArgs e)
        {
            #region Global Filtering stuff

            //--- Filter message -----------------------------------------------//
            Stream filteredMsgStream = msgStream;
            DataView dvFilters = m_pApi.GetFilters();
            dvFilters.RowFilter = "Enabled=true AND Type='ISmtpMessageFilter'";
            dvFilters.Sort = "Cost";
            foreach(DataRowView drViewFilter in dvFilters){
                try{
                    filteredMsgStream.Position = 0;

                    string assemblyFile = API_Utlis.PathFix(drViewFilter.Row["Assembly"].ToString());
                    // File is without path probably, try to load it from filters folder
                    if(!File.Exists(assemblyFile)){
                        assemblyFile = API_Utlis.PathFix(m_pOwnerServer.StartupPath + "\\Filters\\" + assemblyFile);
                    }

                    Assembly ass = Assembly.LoadFrom(assemblyFile);
                    Type tp = ass.GetType(drViewFilter.Row["ClassName"].ToString());
                    object filterInstance = Activator.CreateInstance(tp);
                    ISmtpMessageFilter filter = (ISmtpMessageFilter)filterInstance;

                    string errorText = "";
                    SMTP_Session session = null;
                    if(e != null){
                        session = e.Session;
                    }
                    FilterResult result = filter.Filter(filteredMsgStream,out filteredMsgStream,sender,recipient,m_pApi,session,out errorText);
                    if(result == FilterResult.DontStore){
                        // Just skip messge, act as message is stored
                        e.ServerReply.ReplyText = "Message just discarded by filter !";
                        return;
                    }
                    else if(result == FilterResult.Error){
                        if(e != null){
                            e.ServerReply.ErrorReply = true;
                            e.ServerReply.ReplyText = errorText;
                        }
                        else{
                            // NOTE: 26.01.2006 - e maybe null if that method is called server internally and no smtp session.
                        }
                        return;
                    }

                    // Filter didn't return message stream
                    if(filteredMsgStream == null){
                        e.ServerReply.ErrorReply = true;
                        e.ServerReply.ReplyText = "Message rejected by filter";
                        return;
                    }
                }
                catch(Exception x){
                    // Filtering failed, log error and allow message through.
                    OnError(x);
                }
            }
            //---------------------------------------------------------------//
            #endregion

            #region Global Message Rules

            filteredMsgStream.Position = 0;
            Mime mime = null;
            try{
                mime = Mime.Parse(filteredMsgStream);
            }
            // Invalid message syntax, block such message.
            catch{
                e.ServerReply.ErrorReply = true;
                e.ServerReply.ReplyText = "Invalid message structure/syntax, message blocked !";
                return;
            }
            string[] to = recipient;

            //--- Check Global Message Rules --------------------------------------------------------------//
            bool   deleteMessage = false;
            string storeFolder   = "Inbox";
            string smtpErrorText = null;

            // Loop rules
            foreach(DataRowView drV_Rule in m_pApi.GetGlobalMessageRules()){
                // Reset stream position
                filteredMsgStream.Position = 0;

                if(Convert.ToBoolean(drV_Rule["Enabled"])){
                    string ruleID = drV_Rule["RuleID"].ToString();
                    GlobalMessageRule_CheckNextRule_enum checkNextIf = (GlobalMessageRule_CheckNextRule_enum)(int)drV_Rule["CheckNextRuleIf"];
                    string matchExpression = drV_Rule["MatchExpression"].ToString();

                    // e may be null if server internal method call and no actual session !
                    SMTP_Session session = null;
                    if(e != null){
                        session = e.Session;
                    }
                    GlobalMessageRuleProcessor ruleEngine = new GlobalMessageRuleProcessor();
                    bool matches = ruleEngine.Match(matchExpression,sender,to,session,mime,(int)filteredMsgStream.Length);
                    if(matches){
                        // Do actions
                        GlobalMessageRuleActionResult result = ruleEngine.DoActions(
                            m_pApi.GetGlobalMessageRuleActions(ruleID),
                            this,
                            filteredMsgStream,
                            sender,
                            to
                        );

                        if(result.DeleteMessage){
                            deleteMessage = true;
                        }
                        if(result.StoreFolder != null){
                            storeFolder = result.StoreFolder;
                        }
                        if(result.ErrorText != null){
                            smtpErrorText = result.ErrorText;
                        }
                    }

                    //--- See if we must check next rule -------------------------------------------------//
                    if(checkNextIf == GlobalMessageRule_CheckNextRule_enum.Always){
                        // Do nothing
                    }
                    else if(checkNextIf == GlobalMessageRule_CheckNextRule_enum.IfMatches && !matches){
                        break;
                    }
                    else if(checkNextIf == GlobalMessageRule_CheckNextRule_enum.IfNotMatches && matches){
                        break;
                    }
                    //------------------------------------------------------------------------------------//
                }
            }

            // Return error to connected client
            if(smtpErrorText != null){
                e.ServerReply.ErrorReply = true;
                e.ServerReply.ReplyText = smtpErrorText;
                return;
            }

            // Just don't store message
            if(deleteMessage){
                return;
            }

            // Reset stream position
            filteredMsgStream.Position = 0;
            //--- End of Global Rules -------------------------------------------------------------------//

            #endregion

            #region Construct final recipients

            // Construct final recipients, replace mailing list members with actual addresses.
            List<string> finalRecipients = new List<string>();
            foreach(string r in recipient){
                string emailAddress = r;

                // If domain isn't specified, add default domain
                if(emailAddress.IndexOf("@") == -1){
                    emailAddress += "@" + m_SMTP_DefaultDomain;
                }

                // This is mailing list address, get member email addresses
                if(m_pApi.MailingListExists(emailAddress)){
                    List<string> processedMailignLists = new List<string>();
                    Queue<string> processQueue = new Queue<string>();
                    processQueue.Enqueue(emailAddress);

                    // Loop while there are mailing lists or nested mailing list available
                    while(processQueue.Count > 0){
                        string mailingList = processQueue.Dequeue();
                        processedMailignLists.Add(mailingList);

                        // Process mailing list members
                        foreach(DataRowView drV in m_pApi.GetMailingListAddresses(mailingList)){
                            string member = drV["Address"].ToString();

                            // Member is asteric pattern matching server emails
                            if(member.IndexOf('*') > -1){
                                DataView dvServerAddresses = m_pApi.GetUserAddresses("");
                                foreach(DataRowView drvServerAddress in dvServerAddresses){
                                    string serverAddress = drvServerAddress["Address"].ToString();
                                    if(SCore.IsAstericMatch(member,serverAddress)){
                                        if(!finalRecipients.Contains(serverAddress)){
                                            finalRecipients.Add(serverAddress);
                                        }
                                    }
                                }
                            }
                            // Member is user or group, not email address
                            else if(member.IndexOf('@') == -1){
                                // Member is group, replace with actual users
                                if(m_pApi.GroupExists(member)){
                                    foreach(string user in m_pApi.GetGroupUsers(member)){
                                        if(!finalRecipients.Contains(user)){
                                            finalRecipients.Add(user);
                                        }
                                    }
                                }
                                // Member is user
                                else if(m_pApi.UserExists(member)){
                                    if(!finalRecipients.Contains(member)){
                                        finalRecipients.Add(member);
                                    }
                                }
                                // Unknown member, do nothing
                                else{
                                }
                            }
                            // Member is nested mailing list
                            else if(m_pApi.MailingListExists(member)){
                                // Don't proccess poroccessed mailing lists any more, causes infinite loop
                                if(!processedMailignLists.Contains(member)){
                                    processQueue.Enqueue(member);
                                }
                            }
                            // Member is normal email address
                            else{
                                if(!finalRecipients.Contains(member)){
                                    finalRecipients.Add(member);
                                }
                            }
                        }
                    }
                }
                // Normal email address
                else{
                    if(!finalRecipients.Contains(emailAddress)){
                        finalRecipients.Add(emailAddress);
                    }
                }
            }

            // TODO: ???
            // Map routing
            // Map local server email address to user.

            #endregion

            // Store individual message to each recipient
            foreach(string r in finalRecipients){
                ProcessRecipientMsg(sender,r,storeFolder,filteredMsgStream,e);
            }
        }
        /// <summary>
        /// Is called by SMTP server if server has completed incoming message storing.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e">Event data.</param>
        private void SMTP_Server_MessageStoringCompleted(object sender,MessageStoringCompleted_eArgs e)
        {
            string fileName = ((FileStream)e.MessageStream).Name;

            try{
                // Message stored successfully to file, process it.
                if(e.ErrorText == null){
                    e.MessageStream.Position = 0;

                    ProcessAndStoreMessage(e.Session.MailFrom,e.Session.MailTo,e.MessageStream,e);
                }
            }
            catch(Exception x){
                Error.DumpError(this.Name,x,new System.Diagnostics.StackTrace());
                e.ServerReply.ErrorReply = true;
                e.ServerReply.ReplyText = "Error storing message";
            }
            finally{
                // Close file and delete it.
                ((FileStream)e.MessageStream).Dispose();
                File.Delete(fileName);
            }
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="recipient"></param>
        /// <param name="storeFolder">Message folder where message will be stored. For example 'Inbox'.</param>
        /// <param name="msgStream"></param>
        /// <param name="e">Event data.</param>
        private void ProcessRecipientMsg(string sender,string recipient,string storeFolder,Stream msgStream,MessageStoringCompleted_eArgs e)
        {
            msgStream.Position = 0;

            string eAddress    = recipient;
            string mailbox     = "";
            bool   relay       = false;
            string forwardHost = null;

            // We have local user instead of emailaddress
            if(recipient.IndexOf('@') == -1){
                mailbox = recipient;
            }
            else{
                mailbox = m_pApi.MapUser(eAddress);
            }

            // If not valid local mailbox or relay message.
            if(mailbox == null){

                #region Check Routing

                bool routingDone = false;
                DataView dv = m_pApi.GetRoutes();
                foreach(DataRowView drV in dv){
                    // We have matching route
                    if(Convert.ToBoolean(drV["Enabled"]) && SCore.IsAstericMatch(drV["Pattern"].ToString(),eAddress)){
                        RouteAction_enum action     = (RouteAction_enum)Convert.ToInt32(drV["Action"]);
                        byte[]           actionData = (byte[])drV["ActionData"];

                        #region RouteToEmail

                        if(action == RouteAction_enum.RouteToEmail){
                            XmlTable table = new XmlTable("ActionData");
                            table.Parse(actionData);
                            eAddress = table.GetValue("EmailAddress");
                            relay = true;
                        }

                        #endregion

                        #region RouteToHost

                        else if(action == RouteAction_enum.RouteToHost){
                            XmlTable table = new XmlTable("ActionData");
                            table.Parse(actionData);
                            forwardHost = table.GetValue("Host") + ":" + table.GetValue("Port");
                            relay = true;
                        }

                        #endregion

                        #region RouteToMailbox

                        else if(action == RouteAction_enum.RouteToMailbox){
                            XmlTable table = new XmlTable("ActionData");
                            table.Parse(actionData);
                            mailbox = table.GetValue("Mailbox");
                        }

                        #endregion

                        routingDone = true;
                        break;
                    }
                }

                // Routing not done, handle message normally.
                if(!routingDone){
                    // Local message, but won't match to any mailbox. We never should reach there,
                    // this may happen if routing deleted between session, just skip that message.
                    if(m_pApi.DomainExists(eAddress)){
                        return;
                    }
                    else{
                        relay = true;
                    }
                }

                #endregion
            }

            if(relay){
                this.RelayServer.StoreRelayMessage(msgStream,forwardHost,sender,eAddress);
            }
            else{
                ProcessUserMsg(sender,eAddress,mailbox,storeFolder,msgStream,e);
            }
        }