/// <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> /// 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); } }