/// <summary>
        /// Executes specified actions.
        /// </summary>
        /// <param name="dvActions">Dataview what contains actions to be executed.</param>
        /// <param name="server">Reference to owner virtual server.</param>
        /// <param name="message">Recieved message.</param>
        /// <param name="sender">MAIL FROM: command value.</param>
        /// <param name="to">RCPT TO: commands values.</param>
        public GlobalMessageRuleActionResult DoActions(DataView dvActions, VirtualServer server, Stream message, string sender, string[] to)
        {
            // TODO: get rid of MemoryStream, move to Stream

            //    bool   messageChanged = false;
            bool   deleteMessage = false;
            string storeFolder   = null;
            string errorText     = null;

            // Loop actions
            foreach (DataRowView drV in dvActions)
            {
                GlobalMessageRuleAction_enum action = (GlobalMessageRuleAction_enum)drV["ActionType"];
                byte[] actionData = (byte[])drV["ActionData"];

                // Reset stream position
                message.Position = 0;

                #region AutoResponse

                /* Description: Sends specified autoresponse message to sender.
                 *  Action data structure:
                 *      <ActionData>
                 *          <From></From>
                 *          <Message></Message>
                 *      </ActionData>
                 */

                if (action == GlobalMessageRuleAction_enum.AutoResponse)
                {
                    XmlTable table = new XmlTable("ActionData");
                    table.Parse(actionData);

                    string smtp_from   = table.GetValue("From");
                    string responseMsg = table.GetValue("Message");

                    // See if we have header field X-LS-MailServer-AutoResponse, never answer to auto response.
                    MIME_h_Collection header = new MIME_h_Collection(new MIME_h_Provider());
                    header.Parse(new SmartStream(message, false));
                    if (header.Contains("X-LS-MailServer-AutoResponse"))
                    {
                        // Just skip
                    }
                    else
                    {
                        Mail_Message autoresponseMessage = Mail_Message.ParseFromByte(System.Text.Encoding.Default.GetBytes(responseMsg));

                        // Add header field 'X-LS-MailServer-AutoResponse:'
                        autoresponseMessage.Header.Add(new MIME_h_Unstructured("X-LS-MailServer-AutoResponse", ""));
                        // Update message date
                        autoresponseMessage.Date = DateTime.Now;

                        // Set To: if not explicity set
                        if (autoresponseMessage.To == null || autoresponseMessage.To.Count == 0)
                        {
                            if (autoresponseMessage.To == null)
                            {
                                Mail_t_AddressList t = new Mail_t_AddressList();
                                t.Add(new Mail_t_Mailbox(null, sender));
                                autoresponseMessage.To = t;
                            }
                            else
                            {
                                autoresponseMessage.To.Add(new Mail_t_Mailbox(null, sender));
                            }
                        }
                        // Update Subject: variables, if any
                        if (autoresponseMessage.Subject != null)
                        {
                            if (header.Contains("Subject"))
                            {
                                autoresponseMessage.Subject = autoresponseMessage.Subject.Replace("#SUBJECT", header.GetFirst("Subject").ValueToString().Trim());
                            }
                        }

                        // Sender missing, we can't send auto response.
                        if (string.IsNullOrEmpty(sender))
                        {
                            continue;
                        }

                        server.ProcessAndStoreMessage(smtp_from, new string[] { sender }, new MemoryStream(autoresponseMessage.ToByte(new MIME_Encoding_EncodedWord(MIME_EncodedWordEncoding.Q, Encoding.UTF8), Encoding.UTF8)), null);
                    }
                }

                #endregion

                #region Delete Message

                /* Description: Deletes message.
                 *  Action data structure:
                 *      <ActionData>
                 *      </ActionData>
                 */

                else if (action == GlobalMessageRuleAction_enum.DeleteMessage)
                {
                    XmlTable table = new XmlTable("ActionData");
                    table.Parse(actionData);

                    deleteMessage = true;
                }

                #endregion

                #region ExecuteProgram

                /* Description: Executes specified program.
                 *  Action data structure:
                 *      <ActionData>
                 *          <Program></Program>
                 *          <Arguments></Arguments>
                 *      </ActionData>
                 */

                else if (action == GlobalMessageRuleAction_enum.ExecuteProgram)
                {
                    XmlTable table = new XmlTable("ActionData");
                    table.Parse(actionData);

                    System.Diagnostics.ProcessStartInfo pInfo = new System.Diagnostics.ProcessStartInfo();
                    pInfo.FileName       = table.GetValue("Program");
                    pInfo.Arguments      = table.GetValue("Arguments");
                    pInfo.CreateNoWindow = true;
                    System.Diagnostics.Process.Start(pInfo);
                }

                #endregion

                #region ForwardToEmail

                /* Description: Forwards email to specified email.
                 *  Action data structure:
                 *      <ActionData>
                 *          <Email></Email>
                 *      </ActionData>
                 */

                else if (action == GlobalMessageRuleAction_enum.ForwardToEmail)
                {
                    XmlTable table = new XmlTable("ActionData");
                    table.Parse(actionData);

                    // See If message has X-LS-MailServer-ForwardedTo: and equals to "Email".
                    // If so, then we have cross reference forward, don't forward that message
                    MIME_h_Collection header = new MIME_h_Collection(new MIME_h_Provider());
                    header.Parse(new SmartStream(message, false));
                    bool forwardedAlready = false;
                    if (header.Contains("X-LS-MailServer-ForwardedTo"))
                    {
                        foreach (MIME_h headerField in header["X-LS-MailServer-ForwardedTo"])
                        {
                            if (headerField.ValueToString().Trim() == table.GetValue("Email"))
                            {
                                forwardedAlready = true;
                                break;
                            }
                        }
                    }

                    // Reset stream position
                    message.Position = 0;

                    if (forwardedAlready)
                    {
                        // Just skip
                    }
                    else
                    {
                        // Add header field 'X-LS-MailServer-ForwardedTo:'
                        MemoryStream msFwMessage = new MemoryStream();
                        byte[]       fwField     = System.Text.Encoding.Default.GetBytes("X-LS-MailServer-ForwardedTo: " + table.GetValue("Email") + "\r\n");
                        msFwMessage.Write(fwField, 0, fwField.Length);
                        SCore.StreamCopy(message, msFwMessage);

                        server.ProcessAndStoreMessage(sender, new string[] { table.GetValue("Email") }, msFwMessage, null);
                    }
                }

                #endregion

                #region ForwardToHost

                /* Description: Forwards email to specified host.
                 *              All RCPT TO: recipients are preserved.
                 *  Action data structure:
                 *      <ActionData>
                 *          <Host></Host>
                 *          <Port></Port>
                 *      </ActionData>
                 */

                else if (action == GlobalMessageRuleAction_enum.ForwardToHost)
                {
                    XmlTable table = new XmlTable("ActionData");
                    table.Parse(actionData);

                    foreach (string t in to)
                    {
                        message.Position = 0;
                        server.RelayServer.StoreRelayMessage(
                            Guid.NewGuid().ToString(),
                            null,
                            message,
                            HostEndPoint.Parse(table.GetValue("Host") + ":" + table.GetValue("Port")),
                            sender,
                            t,
                            null,
                            SMTP_DSN_Notify.NotSpecified,
                            SMTP_DSN_Ret.NotSpecified
                            );
                    }
                    message.Position = 0;
                }

                #endregion

                #region StoreToDiskFolder

                /* Description: Stores message to specified disk folder.
                 *  Action data structure:
                 *      <ActionData>
                 *          <Folder></Folder>
                 *      </ActionData>
                 */

                else if (action == GlobalMessageRuleAction_enum.StoreToDiskFolder)
                {
                    XmlTable table = new XmlTable("ActionData");
                    table.Parse(actionData);

                    string folder = table.GetValue("Folder");
                    if (!folder.EndsWith("\\"))
                    {
                        folder += "\\";
                    }

                    if (Directory.Exists(folder))
                    {
                        using (FileStream fs = File.Create(folder + DateTime.Now.ToString("ddMMyyyyHHmmss") + "_" + Guid.NewGuid().ToString().Replace('-', '_').Substring(0, 8) + ".eml")){
                            SCore.StreamCopy(message, fs);
                        }
                    }
                    else
                    {
                        // TODO: log error somewhere
                    }
                }

                #endregion

                #region StoreToIMAPFolder

                /* Description: Stores message to specified IMAP folder.
                 *  Action data structure:
                 *      <ActionData>
                 *          <Folder></Folder>
                 *      </ActionData>
                 */

                else if (action == GlobalMessageRuleAction_enum.StoreToIMAPFolder)
                {
                    XmlTable table = new XmlTable("ActionData");
                    table.Parse(actionData);
                    storeFolder = table.GetValue("Folder");
                }

                #endregion

                #region AddHeaderField

                /* Description: Add specified header field to message main header.
                 *  Action data structure:
                 *      <ActionData>
                 *          <HeaderFieldName></HeaderFieldName>
                 *          <HeaderFieldValue></HeaderFieldValue>
                 *      </ActionData>
                 */

                else if (action == GlobalMessageRuleAction_enum.AddHeaderField)
                {
                    XmlTable table = new XmlTable("ActionData");
                    table.Parse(actionData);

                    Mail_Message mime = Mail_Message.ParseFromStream(message);
                    mime.Header.Add(new MIME_h_Unstructured(table.GetValue("HeaderFieldName"), table.GetValue("HeaderFieldValue")));
                    message.SetLength(0);
                    mime.ToStream(message, new MIME_Encoding_EncodedWord(MIME_EncodedWordEncoding.Q, Encoding.UTF8), Encoding.UTF8);

                    //  messageChanged = true;
                }

                #endregion

                #region RemoveHeaderField

                /* Description: Removes specified header field from message mian header.
                 *  Action data structure:
                 *      <ActionData>
                 *          <HeaderFieldName></HeaderFieldName>
                 *      </ActionData>
                 */

                else if (action == GlobalMessageRuleAction_enum.RemoveHeaderField)
                {
                    XmlTable table = new XmlTable("ActionData");
                    table.Parse(actionData);

                    Mail_Message mime = Mail_Message.ParseFromStream(message);
                    mime.Header.RemoveAll(table.GetValue("HeaderFieldName"));
                    message.SetLength(0);
                    mime.ToStream(message, new MIME_Encoding_EncodedWord(MIME_EncodedWordEncoding.Q, Encoding.UTF8), Encoding.UTF8);

                    //    messageChanged = true;
                }

                #endregion

                #region SendErrorToClient

                /* Description: Sends error to currently connected client. NOTE: Error text may contain ASCII printable chars only and maximum length is 500.
                 *  Action data structure:
                 *      <ActionData>
                 *          <ErrorText></ErrorText>
                 *      </ActionData>
                 */

                else if (action == GlobalMessageRuleAction_enum.SendErrorToClient)
                {
                    XmlTable table = new XmlTable("ActionData");
                    table.Parse(actionData);

                    errorText = table.GetValue("ErrorText");
                }

                #endregion

                #region StoreToFTPFolder

                /* Description: Stores message to specified FTP server folder.
                 *  Action data structure:
                 *      <ActionData>
                 *          <Server></Server>
                 *          <Port></Server>
                 *          <User></User>
                 *          <Password></Password>
                 *          <Folder></Folder>
                 *      </ActionData>
                 */

                else if (action == GlobalMessageRuleAction_enum.StoreToFTPFolder)
                {
                    XmlTable table = new XmlTable("ActionData");
                    table.Parse(actionData);

                    _MessageRuleAction_FTP_AsyncSend ftpSend = new _MessageRuleAction_FTP_AsyncSend(
                        table.GetValue("Server"),
                        Convert.ToInt32(table.GetValue("Port")),
                        table.GetValue("User"),
                        table.GetValue("Password"),
                        table.GetValue("Folder"),
                        message,
                        DateTime.Now.ToString("ddMMyyyyHHmmss") + "_" + Guid.NewGuid().ToString().Replace('-', '_').Substring(0, 8) + ".eml"
                        );
                }

                #endregion

                #region PostToNNTPNewsGroup

                /* Description: Posts message to specified NNTP newsgroup.
                 *  Action data structure:
                 *      <ActionData>
                 *          <Server></Server>
                 *          <Port></Server>
                 *          <User></User>
                 *          <Password></Password>
                 *          <Newsgroup></Newsgroup>
                 *      </ActionData>
                 */

                else if (action == GlobalMessageRuleAction_enum.PostToNNTPNewsGroup)
                {
                    XmlTable table = new XmlTable("ActionData");
                    table.Parse(actionData);

                    // Add header field "Newsgroups: newsgroup", NNTP server demands it.
                    Mail_Message mime = Mail_Message.ParseFromStream(message);
                    if (!mime.Header.Contains("Newsgroups:"))
                    {
                        mime.Header.Add(new MIME_h_Unstructured("Newsgroups:", table.GetValue("Newsgroup")));
                    }

                    _MessageRuleAction_NNTP_Async nntp = new _MessageRuleAction_NNTP_Async(
                        table.GetValue("Server"),
                        Convert.ToInt32(table.GetValue("Port")),
                        table.GetValue("Newsgroup"),
                        new MemoryStream(mime.ToByte(new MIME_Encoding_EncodedWord(MIME_EncodedWordEncoding.Q, Encoding.UTF8), Encoding.UTF8))
                        );
                }

                #endregion

                #region PostToHTTP

                /* Description: Posts message to specified page via HTTP.
                 *  Action data structure:
                 *      <ActionData>
                 *          <URL></URL>
                 *          <FileName></FileName>
                 *      </ActionData>
                 */

                else if (action == GlobalMessageRuleAction_enum.PostToHTTP)
                {
                    XmlTable table = new XmlTable("ActionData");
                    table.Parse(actionData);

                    _MessageRuleAction_HTTP_Async http = new _MessageRuleAction_HTTP_Async(
                        table.GetValue("URL"),
                        message
                        );
                }

                #endregion
            }

            return(new GlobalMessageRuleActionResult(deleteMessage, storeFolder, errorText));
        }
        /// <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);
            }
        }
Пример #3
0
        /// <summary>
        /// Executes specified actions.
        /// </summary>
        /// <param name="dvActions">Dataview what contains actions to be executed.</param>
        /// <param name="server">Reference to owner virtual server.</param>
        /// <param name="message">Recieved message.</param>
        /// <param name="sender">MAIL FROM: command value.</param>
        /// <param name="to">RCPT TO: commands values.</param>
        public GlobalMessageRuleActionResult DoActions(DataView dvActions,VirtualServer server,Stream message,string sender,string[] to)
        {
            // TODO: get rid of MemoryStream, move to Stream

      //    bool   messageChanged = false;
            bool   deleteMessage  = false;
            string storeFolder    = null;
            string errorText      = null;

            // Loop actions
            foreach(DataRowView drV in dvActions){               
                GlobalMessageRuleAction_enum action     = (GlobalMessageRuleAction_enum)drV["ActionType"];
                byte[]                       actionData = (byte[])drV["ActionData"];

                // Reset stream position
                message.Position = 0;
            
                #region AutoResponse

                /* Description: Sends specified autoresponse message to sender.
                    Action data structure:
                        <ActionData>
                            <From></From>
                            <Message></Message>
                        </ActionData>
                */

                if(action == GlobalMessageRuleAction_enum.AutoResponse){
                    XmlTable table = new XmlTable("ActionData");
                    table.Parse(actionData);

                    string smtp_from   = table.GetValue("From");
                    string responseMsg = table.GetValue("Message");

                    // See if we have header field X-LS-MailServer-AutoResponse, never answer to auto response.
                    MIME_h_Collection header = new MIME_h_Collection(new MIME_h_Provider());
                    header.Parse(new SmartStream(message,false));
                    if(header.Contains("X-LS-MailServer-AutoResponse")){
                        // Just skip
                    }
                    else{
                        Mail_Message autoresponseMessage = Mail_Message.ParseFromByte(System.Text.Encoding.Default.GetBytes(responseMsg));

                        // Add header field 'X-LS-MailServer-AutoResponse:'
                        autoresponseMessage.Header.Add(new MIME_h_Unstructured("X-LS-MailServer-AutoResponse",""));
                        // Update message date
                        autoresponseMessage.Date = DateTime.Now;
                       
                        // Set To: if not explicity set
                        if(autoresponseMessage.To == null || autoresponseMessage.To.Count == 0){
                            if(autoresponseMessage.To == null){                            
                                Mail_t_AddressList t = new Mail_t_AddressList();
                                t.Add(new Mail_t_Mailbox(null,sender));                        
                                autoresponseMessage.To = t; 
                            }
                            else{
                                autoresponseMessage.To.Add(new Mail_t_Mailbox(null,sender));
                            }
                        }
                        // Update Subject: variables, if any
                        if(autoresponseMessage.Subject != null){
                            if(header.Contains("Subject")){
                                autoresponseMessage.Subject = autoresponseMessage.Subject.Replace("#SUBJECT",header.GetFirst("Subject").ValueToString().Trim());
                            }
                        }
                                            
                        server.ProcessAndStoreMessage(smtp_from,new string[]{sender},new MemoryStream(autoresponseMessage.ToByte(new MIME_Encoding_EncodedWord(MIME_EncodedWordEncoding.Q,Encoding.UTF8),Encoding.UTF8)),null);
                    }
                }

                #endregion

                #region Delete Message

                /* Description: Deletes message.
                    Action data structure:
                        <ActionData>
                        </ActionData>
                */

                else if(action == GlobalMessageRuleAction_enum.DeleteMessage){
                    XmlTable table = new XmlTable("ActionData");
                    table.Parse(actionData);

                    deleteMessage = true;                
                }

                #endregion

                #region ExecuteProgram

                /* Description: Executes specified program.
                    Action data structure:
                        <ActionData>
                            <Program></Program>
                            <Arguments></Arguments>
                        </ActionData>
                */

                else if(action == GlobalMessageRuleAction_enum.ExecuteProgram){
                    XmlTable table = new XmlTable("ActionData");
                    table.Parse(actionData);

                    System.Diagnostics.ProcessStartInfo pInfo = new System.Diagnostics.ProcessStartInfo();
                    pInfo.FileName = table.GetValue("Program");
                    pInfo.Arguments = table.GetValue("Arguments");
                    pInfo.CreateNoWindow = true;
                    System.Diagnostics.Process.Start(pInfo);
                }

                #endregion

                #region ForwardToEmail

                /* Description: Forwards email to specified email.
                    Action data structure:
                        <ActionData>
                            <Email></Email>
                        </ActionData>
                */

                else if(action == GlobalMessageRuleAction_enum.ForwardToEmail){
                    XmlTable table = new XmlTable("ActionData");
                    table.Parse(actionData);

                    // See If message has X-LS-MailServer-ForwardedTo: and equals to "Email".
                    // If so, then we have cross reference forward, don't forward that message
                    MIME_h_Collection header = new MIME_h_Collection(new MIME_h_Provider());
                    header.Parse(new SmartStream(message,false));
                    bool forwardedAlready = false;
                    if(header.Contains("X-LS-MailServer-ForwardedTo")){
                        foreach(MIME_h headerField in header["X-LS-MailServer-ForwardedTo"]){
                            if(headerField.ValueToString().Trim() == table.GetValue("Email")){
                                forwardedAlready = true;
                                break;
                            }
                        }
                    }

                    // Reset stream position
                    message.Position = 0;

                    if(forwardedAlready){
                        // Just skip
                    }
                    else{
                        // Add header field 'X-LS-MailServer-ForwardedTo:'
                        MemoryStream msFwMessage = new MemoryStream();
                        byte[] fwField = System.Text.Encoding.Default.GetBytes("X-LS-MailServer-ForwardedTo: " + table.GetValue("Email") + "\r\n");
                        msFwMessage.Write(fwField,0,fwField.Length);
                        SCore.StreamCopy(message,msFwMessage);

                        server.ProcessAndStoreMessage(sender,new string[]{table.GetValue("Email")},msFwMessage,null);
                    }
                }

                #endregion

                #region ForwardToHost

                /* Description: Forwards email to specified host.
                                All RCPT TO: recipients are preserved.
                    Action data structure:
                        <ActionData>
                            <Host></Host>
                            <Port></Port>
                        </ActionData>
                */

                else if(action == GlobalMessageRuleAction_enum.ForwardToHost){
                    XmlTable table = new XmlTable("ActionData");
                    table.Parse(actionData);

                    foreach(string t in to){
                        message.Position = 0;
                        server.RelayServer.StoreRelayMessage(
                            null,
                            Guid.NewGuid().ToString(),
                            message,
                            HostEndPoint.Parse(table.GetValue("Host") + ":" + table.GetValue("Port")),
                            sender,
                            t,
                            null,
                            SMTP_DSN_Notify.NotSpecified,
                            SMTP_DSN_Ret.NotSpecified
                        );
                    }
                    message.Position = 0; // TODO: does it later that needed there, must do in called place instead ?
                }

                #endregion

                #region StoreToDiskFolder

                /* Description: Stores message to specified disk folder.
                    Action data structure:
                        <ActionData>
                            <Folder></Folder>
                        </ActionData>
                */

                else if(action == GlobalMessageRuleAction_enum.StoreToDiskFolder){
                    XmlTable table = new XmlTable("ActionData");
                    table.Parse(actionData);

                    string folder = table.GetValue("Folder");
                    if(!folder.EndsWith("\\")){
                        folder += "\\";
                    }

                    if(Directory.Exists(folder)){
                        using(FileStream fs = File.Create(folder + DateTime.Now.ToString("ddMMyyyyHHmmss") + "_" + Guid.NewGuid().ToString().Replace('-','_').Substring(0,8) + ".eml")){
                            SCore.StreamCopy(message,fs);
                        }
                    }
                    else{
                        // TODO: log error somewhere
                    }
                }

                #endregion

                #region StoreToIMAPFolder

                /* Description: Stores message to specified IMAP folder.
                    Action data structure:
                        <ActionData>
                            <Folder></Folder>
                        </ActionData>
                */

                else if(action == GlobalMessageRuleAction_enum.StoreToIMAPFolder){
                    XmlTable table = new XmlTable("ActionData");
                    table.Parse(actionData);
                    storeFolder = table.GetValue("Folder");
                }

                #endregion

                #region AddHeaderField

                /* Description: Add specified header field to message main header.
                    Action data structure:
                        <ActionData>
                            <HeaderFieldName></HeaderFieldName>
                            <HeaderFieldValue></HeaderFieldValue>
                        </ActionData>
                */

                else if(action == GlobalMessageRuleAction_enum.AddHeaderField){
                    XmlTable table = new XmlTable("ActionData");
                    table.Parse(actionData);

                    Mail_Message mime = Mail_Message.ParseFromStream(message);
                    mime.Header.Add(new MIME_h_Unstructured(table.GetValue("HeaderFieldName"),table.GetValue("HeaderFieldValue")));
                    message.SetLength(0);
                    mime.ToStream(message,new MIME_Encoding_EncodedWord(MIME_EncodedWordEncoding.Q,Encoding.UTF8),Encoding.UTF8);

                //  messageChanged = true;                    
                }

                #endregion

                #region RemoveHeaderField

                /* Description: Removes specified header field from message mian header.
                    Action data structure:
                        <ActionData>
                            <HeaderFieldName></HeaderFieldName>
                        </ActionData>
                */

                else if(action == GlobalMessageRuleAction_enum.RemoveHeaderField){
                    XmlTable table = new XmlTable("ActionData");
                    table.Parse(actionData);

                    Mail_Message mime = Mail_Message.ParseFromStream(message);
                    mime.Header.RemoveAll(table.GetValue("HeaderFieldName"));
                    message.SetLength(0);
                    mime.ToStream(message,new MIME_Encoding_EncodedWord(MIME_EncodedWordEncoding.Q,Encoding.UTF8),Encoding.UTF8);

                //    messageChanged = true;
                }

                #endregion

                #region SendErrorToClient

                /* Description: Sends error to currently connected client. NOTE: Error text may contain ASCII printable chars only and maximum length is 500.
                    Action data structure:
                        <ActionData>
                            <ErrorText></ErrorText>
                        </ActionData>
                */

                else if(action == GlobalMessageRuleAction_enum.SendErrorToClient){
                    XmlTable table = new XmlTable("ActionData");
                    table.Parse(actionData);

                    errorText = table.GetValue("ErrorText");
                }

                #endregion

                #region StoreToFTPFolder

                /* Description: Stores message to specified FTP server folder.
                    Action data structure:
                        <ActionData>
                            <Server></Server>
                            <Port></Server>
                            <User></User>
                            <Password></Password>
                            <Folder></Folder>
                        </ActionData>
                */

                else if(action == GlobalMessageRuleAction_enum.StoreToFTPFolder){
                    XmlTable table = new XmlTable("ActionData");
                    table.Parse(actionData);

                    _MessageRuleAction_FTP_AsyncSend ftpSend = new _MessageRuleAction_FTP_AsyncSend(
                        table.GetValue("Server"),
                        Convert.ToInt32(table.GetValue("Port")),
                        table.GetValue("User"),
                        table.GetValue("Password"),
                        table.GetValue("Folder"),
                        message,
                        DateTime.Now.ToString("ddMMyyyyHHmmss") + "_" + Guid.NewGuid().ToString().Replace('-','_').Substring(0,8) + ".eml"
                    );
                }

                #endregion

                #region PostToNNTPNewsGroup

                /* Description: Posts message to specified NNTP newsgroup.
                    Action data structure:
                        <ActionData>
                            <Server></Server>
                            <Port></Server>
                            <User></User>
                            <Password></Password>
                            <Newsgroup></Newsgroup>
                        </ActionData>
                */

                else if(action == GlobalMessageRuleAction_enum.PostToNNTPNewsGroup){
                    XmlTable table = new XmlTable("ActionData");
                    table.Parse(actionData);

                    // Add header field "Newsgroups: newsgroup", NNTP server demands it.                    
                    Mail_Message mime = Mail_Message.ParseFromStream(message);
                    if(!mime.Header.Contains("Newsgroups:")){
                        mime.Header.Add(new MIME_h_Unstructured("Newsgroups:",table.GetValue("Newsgroup")));
                    }

                    _MessageRuleAction_NNTP_Async nntp = new _MessageRuleAction_NNTP_Async(
                        table.GetValue("Server"),
                        Convert.ToInt32(table.GetValue("Port")),
                        table.GetValue("Newsgroup"),
                        new MemoryStream(mime.ToByte(new MIME_Encoding_EncodedWord(MIME_EncodedWordEncoding.Q,Encoding.UTF8),Encoding.UTF8))
                     );

                }

                #endregion

                #region PostToHTTP

                /* Description: Posts message to specified page via HTTP.
                    Action data structure:
                        <ActionData>
                            <URL></URL>
                            <FileName></FileName>
                        </ActionData>
                */

                else if(action == GlobalMessageRuleAction_enum.PostToHTTP){
                    XmlTable table = new XmlTable("ActionData");
                    table.Parse(actionData);

                    _MessageRuleAction_HTTP_Async http = new _MessageRuleAction_HTTP_Async(
                        table.GetValue("URL"),
                        message
                    );
                }

                #endregion

            }

            return new GlobalMessageRuleActionResult(deleteMessage,storeFolder,errorText);
        }
Пример #4
0
        /// <summary>
        /// Processes and stores message.
        /// </summary>
        /// <param name="envelopeID">Envelope ID_(MAIL FROM: ENVID).</param>
        /// <param name="sender">Mail from.</param>
        /// <param name="ret">Specifies what parts of message are returned in DSN report.</param>
        /// <param name="recipients">Message recipients.</param>
        /// <param name="msgStream">Message stream. Stream position must be there where message begins.</param>
        /// <param name="e">Event data.</param>
        /// <exception cref="ArgumentNullException">Is raised when <b>recipients</b> or <b>msgStream</b> is nulll reference.</exception>
		public void ProcessAndStoreMessage(string envelopeID,string sender,SMTP_DSN_Ret ret,SMTP_RcptTo[] recipients,Stream msgStream,SMTP_e_MessageStored e)
		{
            if(recipients == null){
                throw new ArgumentNullException("recipients");
            }
            if(msgStream == null){
                throw new ArgumentNullException("msgStream");
            }

            /* Message processing.
                *) Message filters.
                *) Global message rules.
                *) Process recipients.
            */

            List<SMTP_RcptTo> dsn_Delivered = new List<SMTP_RcptTo>();

            string[] to = new string[recipients.Length];
            for(int i=0;i<to.Length;i++){
                to[i] = recipients[i].Mailbox;
            }
         
			#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,to,m_pApi,session,out errorText);
                    if(result == FilterResult.DontStore){
                        // Just skip messge, act as message is stored
                        e.Reply = new SMTP_Reply(552,"Requested mail action aborted: Message discarded by server filter.");
						return; 
                    }
                    else if(result == FilterResult.Error){
                        if(e != null){
                            e.Reply = new SMTP_Reply(552,"Requested mail action aborted: " + 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.Reply = new SMTP_Reply(552,"Requested mail action aborted: Message discarded by server filter.");
                        return;
                    }
                }
                catch(Exception x){
                    // Filtering failed, log error and allow message through.
                    OnError(x);
                }
			}
			//---------------------------------------------------------------//
			#endregion

            #region Global Message Rules
  
            filteredMsgStream.Position = 0;
                         
            Mail_Message mime = null;
            try{
                mime = Mail_Message.ParseFromStream(filteredMsgStream);
            }
            // Invalid message syntax, block such message.
            catch{
                e.Reply = new SMTP_Reply(552,"Requested mail action aborted: Message has invalid structure/syntax.");
                               
                try{
                    if(!Directory.Exists(this.MailStorePath + "Unparseable")){
                        Directory.CreateDirectory(this.MailStorePath + "Unparseable");
                    }
                                
                    using(FileStream fs = File.Create(this.MailStorePath + "Unparseable\\" + Guid.NewGuid().ToString().Replace("-","") + ".eml")){
                        filteredMsgStream.Position = 0;
                        Net_Utils.StreamCopy(filteredMsgStream,fs,32000);
                    }
                }
                catch{
                }

                return;
            }

            //--- 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.Reply = new SMTP_Reply(552,"Requested mail action aborted: " + smtpErrorText);
                return;
            }

            // Just don't store message
            if(deleteMessage){
                return;
            }
            
            // Reset stream position
            filteredMsgStream.Position = 0;
            //--- End of Global Rules -------------------------------------------------------------------//

            #endregion
            
            #region Process recipients
                  
            HashSet<string> processedItems = new HashSet<string>();

            Queue<SMTP_RcptTo> recipientsQueue = new Queue<SMTP_RcptTo>();
            // Queue current recipients for processing.
            foreach(SMTP_RcptTo recipient in recipients){
                recipientsQueue.Enqueue(recipient);
            }

            while(recipientsQueue.Count > 0){
                /* Process order
                    *) Local user
                    *) Local address
                    *) Local mailing list address
                    *) Route
                    *) Relay
                */

                SMTP_RcptTo recipient = recipientsQueue.Dequeue();

                // Check if we already have processed this item. Skip dublicate items.
                // This method also avoids loops when 2 recipients reference each other.
                if(processedItems.Contains(recipient.Mailbox)){
                    continue;
                }
                processedItems.Add(recipient.Mailbox);
                               

                #region Local user

                if(recipient.Mailbox.IndexOf('@') == -1 && m_pApi.UserExists(recipient.Mailbox)){
                    // Add user to processed list.
                    processedItems.Add(recipient.Mailbox);

                    // Delivery status notification(DSN) requested to this user.
                    if((recipient.Notify & SMTP_DSN_Notify.Success) != 0){
                        dsn_Delivered.Add(recipient);
                    }

                    ProcessUserMsg(sender,recipient.Mailbox,recipient.Mailbox,storeFolder,filteredMsgStream,e);

                    continue;
                }

                #endregion

                #region Local address

                string localUser = m_pApi.MapUser(recipient.Mailbox);
                if(localUser != null){
                    // Add user to processed list.
                    processedItems.Add(localUser);

                    // Delivery status notification(DSN) requested to this user.
                    if((recipient.Notify & SMTP_DSN_Notify.Success) != 0){
                        dsn_Delivered.Add(recipient);
                    }

                    ProcessUserMsg(sender,recipient.Mailbox,localUser,storeFolder,filteredMsgStream,e);
                }

                #endregion

                #region Mailing list address

                else if(m_pApi.MailingListExists(recipient.Mailbox)){
                    // Delivery status notification(DSN) requested to this user.
                    if((recipient.Notify & SMTP_DSN_Notify.Success) != 0){
                        dsn_Delivered.Add(recipient);
                    }

                    Queue<string> processQueue = new Queue<string>();
                    processQueue.Enqueue(recipient.Mailbox);

                    // Loop while there are mailing lists or nested mailing list available
                    while(processQueue.Count > 0){
                        string mailingList = processQueue.Dequeue(); 
                          
                        // 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)){
                                        recipientsQueue.Enqueue(new SMTP_RcptTo(serverAddress,SMTP_DSN_Notify.NotSpecified,null));                                        
                                    }
                                }
                            }
                            // 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)){
                                        recipientsQueue.Enqueue(new SMTP_RcptTo(user,SMTP_DSN_Notify.NotSpecified,null));                                        
                                    }
                                }
                                // Member is user
                                else if(m_pApi.UserExists(member)){
                                    recipientsQueue.Enqueue(new SMTP_RcptTo(member,SMTP_DSN_Notify.NotSpecified,null));                                    
                                }
                                // Unknown member, skip it.
                                else{
                                }
                            }
                            // Member is nested mailing list
                            else if(m_pApi.MailingListExists(member)){
                                processQueue.Enqueue(member);                                
                            }
                            // Member is normal email address
                            else{
                                recipientsQueue.Enqueue(new SMTP_RcptTo(member,SMTP_DSN_Notify.NotSpecified,null));                                
                            }					
					    }
                    }
                }

                #endregion

                else{
                    bool isRouted = false;

                    #region Check Routing

                    foreach(DataRowView drRoute in m_pApi.GetRoutes()){
                        // We have matching route
                        if(Convert.ToBoolean(drRoute["Enabled"]) && SCore.IsAstericMatch(drRoute["Pattern"].ToString(),recipient.Mailbox)){
                            string           description = drRoute["Action"].ToString();    
                            RouteAction_enum action      = (RouteAction_enum)Convert.ToInt32(drRoute["Action"]);
                            byte[]           actionData  = (byte[])drRoute["ActionData"];

                            #region RouteToEmail

                            if(action == RouteAction_enum.RouteToEmail){
                                XmlTable table = new XmlTable("ActionData");
                                table.Parse(actionData);

                                // Add email to process queue.
                                recipientsQueue.Enqueue(new SMTP_RcptTo(table.GetValue("EmailAddress"),SMTP_DSN_Notify.NotSpecified,null));

                                // Log
                                if(e != null){
                                    e.Session.LogAddText("Route '[" + description + "]: " + drRoute["Pattern"].ToString() + "' routed to email '" + table.GetValue("EmailAddress") + "'.");
                                }
                            }

                            #endregion

                            #region RouteToHost

                            else if(action == RouteAction_enum.RouteToHost){
                                XmlTable table = new XmlTable("ActionData");
                                table.Parse(actionData);  

                                msgStream.Position = 0;

                                // Route didn't match, so we have relay message.
                                this.RelayServer.StoreRelayMessage(                    
                                    Guid.NewGuid().ToString(),
                                    envelopeID,
                                    msgStream,
                                    HostEndPoint.Parse(table.GetValue("Host") + ":" + table.GetValue("Port")),
                                    sender,
                                    recipient.Mailbox,
                                    recipient.ORCPT,
                                    recipient.Notify,
                                    ret
                                );
                             
                                // Log
                                if(e != null){
                                    e.Session.LogAddText("Route '[" + description + "]: " + drRoute["Pattern"].ToString() + "' routed to host '" + table.GetValue("Host") + ":" + table.GetValue("Port") + "'.");
                                }
                            }

                            #endregion

                            #region RouteToMailbox

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

                                ProcessUserMsg(sender,recipient.Mailbox,table.GetValue("Mailbox"),storeFolder,filteredMsgStream,e);

                                // Log
                                if(e != null){
                                    e.Session.LogAddText("Route '[" + description + "]: " + drRoute["Pattern"].ToString() + "' routed to user '" + table.GetValue("Mailbox") + "'.");
                                }
                            }

                            #endregion
                             
                            isRouted = true;
                            break;
                        }
                    }

                   #endregion

                    // Route didn't match, so we have relay message.
                    if(!isRouted){
                        filteredMsgStream.Position = 0;

                        this.RelayServer.StoreRelayMessage(                    
                            Guid.NewGuid().ToString(),
                            envelopeID,
                            filteredMsgStream,
                            null,
                            sender,
                            recipient.Mailbox,
                            recipient.ORCPT,
                            recipient.Notify,
                            ret
                        );
                    }
                }
            }

            #endregion

            
            #region DSN "delivered"

            // Send DSN for requested recipients.
            if(dsn_Delivered.Count > 0 && !string.IsNullOrEmpty(sender)){
                try{
                    string dsn_to = "";
                    for(int i=0;i<dsn_Delivered.Count;i++){
                        if(i == (dsn_Delivered.Count - 1)){
                            dsn_to += dsn_Delivered[i].Mailbox;
                        }
                        else{
                            dsn_to += dsn_Delivered[i].Mailbox + "; ";
                        }
                    }

                    string reportingMTA = "";
                    if(e != null && !string.IsNullOrEmpty(e.Session.LocalHostName)){
                        reportingMTA = e.Session.LocalHostName;
                    }
                    else{
                        reportingMTA = System.Net.Dns.GetHostName();
                    }

                    ServerReturnMessage messageTemplate = null;
                    if(messageTemplate == null){
                        string bodyRtf = "" +
                        "{\\rtf1\\ansi\\ansicpg1257\\deff0\\deflang1061{\\fonttbl{\\f0\\froman\\fcharset0 Times New Roman;}{\\f1\froman\\fcharset186{\\*\\fname Times New Roman;}Times New Roman Baltic;}{\\f2\fswiss\\fcharset186{\\*\\fname Arial;}Arial Baltic;}}\r\n" +
                        "{\\colortbl ;\\red0\\green128\\blue0;\\red128\\green128\\blue128;}\r\n" +
                        "{\\*\\generator Msftedit 5.41.21.2508;}\\viewkind4\\uc1\\pard\\sb100\\sa100\\lang1033\\f0\\fs24\\par\r\n" +
                        "Your message WAS SUCCESSFULLY DELIVERED to:\\line\\lang1061\\f1\\tab\\cf1\\lang1033\\b\\f0 " + dsn_to + "\\line\\cf0\\b0 and you explicitly requested a delivery status notification on success.\\par\\par\r\n" +
                        "\\cf2 Your original message\\lang1061\\f1 /header\\lang1033\\f0  is attached to this e-mail\\lang1061\\f1 .\\lang1033\\f0\\par\\r\\n" +
                        "\\cf0\\line\\par\r\n" +
                        "\\pard\\lang1061\\f2\\fs20\\par\r\n" +
                        "}\r\n";

                        messageTemplate = new ServerReturnMessage("DSN SUCCESSFULLY DELIVERED: " + mime.Subject,bodyRtf);
                    }

                    string rtf = messageTemplate.BodyTextRtf;

                    Mail_Message dsnMsg = new Mail_Message();
                    dsnMsg.MimeVersion = "1.0";
                    dsnMsg.Date = DateTime.Now;
                    dsnMsg.From = new Mail_t_MailboxList();
                    dsnMsg.From.Add(new Mail_t_Mailbox("Mail Delivery Subsystem","postmaster@local"));
                    dsnMsg.To = new Mail_t_AddressList();
                    dsnMsg.To.Add(new Mail_t_Mailbox(null,sender));
                    dsnMsg.Subject = messageTemplate.Subject;

                    //--- multipart/report -------------------------------------------------------------------------------------------------
                    MIME_h_ContentType contentType_multipartReport = new MIME_h_ContentType(MIME_MediaTypes.Multipart.report);            
                    contentType_multipartReport.Parameters["report-type"] = "delivery-status";
                    contentType_multipartReport.Param_Boundary = Guid.NewGuid().ToString().Replace('-','.');
                    MIME_b_MultipartReport multipartReport = new MIME_b_MultipartReport(contentType_multipartReport);
                    dsnMsg.Body = multipartReport;

                        //--- multipart/alternative -----------------------------------------------------------------------------------------
                        MIME_Entity entity_multipart_alternative = new MIME_Entity();
                        MIME_h_ContentType contentType_multipartAlternative = new MIME_h_ContentType(MIME_MediaTypes.Multipart.alternative);
                        contentType_multipartAlternative.Param_Boundary = Guid.NewGuid().ToString().Replace('-','.');
                        MIME_b_MultipartAlternative multipartAlternative = new MIME_b_MultipartAlternative(contentType_multipartAlternative);
                        entity_multipart_alternative.Body = multipartAlternative;
                        multipartReport.BodyParts.Add(entity_multipart_alternative);

                            //--- text/plain ---------------------------------------------------------------------------------------------------
                            MIME_Entity entity_text_plain = new MIME_Entity();
                            MIME_b_Text text_plain = new MIME_b_Text(MIME_MediaTypes.Text.plain);
                            entity_text_plain.Body = text_plain;
                            text_plain.SetText(MIME_TransferEncodings.QuotedPrintable,Encoding.UTF8,SCore.RtfToText(rtf));
                            multipartAlternative.BodyParts.Add(entity_text_plain);

                            //--- text/html -----------------------------------------------------------------------------------------------------
                            MIME_Entity entity_text_html = new MIME_Entity();
                            MIME_b_Text text_html = new MIME_b_Text(MIME_MediaTypes.Text.html);
                            entity_text_html.Body = text_html;
                            text_html.SetText(MIME_TransferEncodings.QuotedPrintable,Encoding.UTF8,SCore.RtfToHtml(rtf));
                            multipartAlternative.BodyParts.Add(entity_text_html);

                        //--- message/delivery-status
                        MIME_Entity entity_message_deliveryStatus = new MIME_Entity();                        
                        MIME_b_MessageDeliveryStatus body_message_deliveryStatus = new MIME_b_MessageDeliveryStatus();
                        entity_message_deliveryStatus.Body = body_message_deliveryStatus;
                        multipartReport.BodyParts.Add(entity_message_deliveryStatus);
            
                        //--- per-message-fields ----------------------------------------------------------------------------
                        MIME_h_Collection messageFields = body_message_deliveryStatus.MessageFields;
                        if(!string.IsNullOrEmpty(envelopeID)){
                            messageFields.Add(new MIME_h_Unstructured("Original-Envelope-Id",envelopeID));
                        }
                        messageFields.Add(new MIME_h_Unstructured("Arrival-Date",MIME_Utils.DateTimeToRfc2822(DateTime.Now)));
                        if(e != null && !string.IsNullOrEmpty(e.Session.EhloHost)){
                            messageFields.Add(new MIME_h_Unstructured("Received-From-MTA","dns;" + e.Session.EhloHost));
                        }
                        messageFields.Add(new MIME_h_Unstructured("Reporting-MTA","dns;" + reportingMTA));
                        //---------------------------------------------------------------------------------------------------

                        foreach(SMTP_RcptTo r in dsn_Delivered){
                            //--- per-recipient-fields --------------------------------------------------------------------------
                            MIME_h_Collection recipientFields = new MIME_h_Collection(new MIME_h_Provider());
                            if(r.ORCPT != null){
                                recipientFields.Add(new MIME_h_Unstructured("Original-Recipient",r.ORCPT));
                            }
                            recipientFields.Add(new MIME_h_Unstructured("Final-Recipient","rfc822;" + r.Mailbox));
                            recipientFields.Add(new MIME_h_Unstructured("Action","delivered"));
                            recipientFields.Add(new MIME_h_Unstructured("Status","2.0.0"));
                            body_message_deliveryStatus.RecipientBlocks.Add(recipientFields);
                            //---------------------------------------------------------------------------------------------------
                        }
                                            
                        //--- message/rfc822
                        if(mime != null){
                            MIME_Entity entity_message_rfc822 = new MIME_Entity();
                            MIME_b_MessageRfc822 body_message_rfc822 = new MIME_b_MessageRfc822();
                            entity_message_rfc822.Body = body_message_rfc822;
                            if(ret == SMTP_DSN_Ret.FullMessage){
                                body_message_rfc822.Message = mime;
                            }
                            else{
                                MemoryStream ms = new MemoryStream();
                                mime.Header.ToStream(ms,null,null);
                                ms.Position = 0;
                                body_message_rfc822.Message = Mail_Message.ParseFromStream(ms);
                            }
                            multipartReport.BodyParts.Add(entity_message_rfc822);
                        }

                    using(MemoryStream strm = new MemoryStream()){
					    dsnMsg.ToStream(strm,new MIME_Encoding_EncodedWord(MIME_EncodedWordEncoding.Q,Encoding.UTF8),Encoding.UTF8);
					    ProcessAndStoreMessage("",new string[]{sender},strm,null);
				    }
                }
                catch(Exception x){
                    Error.DumpError(this.Name,x);
                }
            }

            #endregion
        }