/// <summary> /// Parses header field from the specified value. /// </summary> /// <param name="value">Header field value. Header field name must be included. For example: 'Content-Type: text/plain'.</param> /// <returns>Returns parsed header field.</returns> /// <exception cref="ArgumentNullException">Is raised when <b>value</b> is null reference.</exception> /// <exception cref="ParseException">Is raised when header field parsing errors.</exception> public static Mail_h_AddressList Parse(string value) { if (value == null) { throw new ArgumentNullException("value"); } string[] name_value = value.Split(new char[] { ':' }, 2); if (name_value.Length != 2) { throw new ParseException("Invalid header field value '" + value + "'."); } /* RFC 5322 3.4. * address = mailbox / group * mailbox = name-addr / addr-spec * name-addr = [display-name] angle-addr * angle-addr = [CFWS] "<" addr-spec ">" [CFWS] / obs-angle-addr * group = display-name ":" [group-list] ";" [CFWS] * display-name = phrase * mailbox-list = (mailbox *("," mailbox)) / obs-mbox-list * address-list = (address *("," address)) / obs-addr-list * group-list = mailbox-list / CFWS / obs-group-list */ Mail_h_AddressList retVal = new Mail_h_AddressList(name_value[0], Mail_t_AddressList.Parse(name_value[1].Trim())); retVal.m_ParseValue = value; retVal.m_pAddresses.AcceptChanges(); return(retVal); }
/// <summary> /// Default constructor. /// </summary> /// <param name="fieldName">Header field name. For example: "To".</param> /// <param name="values">Addresses collection.</param> /// <exception cref="ArgumentNullException">Is raised when <b>filedName</b> or <b>values</b> is null reference.</exception> /// <exception cref="ArgumentException">Is raised when any of the arguments has invalid value.</exception> public Mail_h_AddressList(string fieldName,Mail_t_AddressList values) { if(fieldName == null){ throw new ArgumentNullException("fieldName"); } if(fieldName == string.Empty){ throw new ArgumentException("Argument 'fieldName' value must be specified."); } if(values == null){ throw new ArgumentNullException("values"); } m_Name = fieldName; m_pAddresses = values; }
/// <summary> /// Default constructor. /// </summary> /// <param name="fieldName">Header field name. For example: "To".</param> /// <param name="values">Addresses collection.</param> /// <exception cref="ArgumentNullException">Is raised when <b>filedName</b> or <b>values</b> is null reference.</exception> /// <exception cref="ArgumentException">Is raised when any of the arguments has invalid value.</exception> public Mail_h_AddressList(string fieldName, Mail_t_AddressList values) { if (fieldName == null) { throw new ArgumentNullException("fieldName"); } if (fieldName == string.Empty) { throw new ArgumentException("Argument 'fieldName' value must be specified."); } if (values == null) { throw new ArgumentNullException("values"); } m_Name = fieldName; m_pAddresses = values; }
/// <summary> /// Parses <b>address-list</b> from specified string value. /// </summary> /// <param name="value">The <b>address-list</b> string value.</param> /// <returns></returns> /// <exception cref="ArgumentNullException">Is raised when <b>value</b> is null reference.</exception> /// <exception cref="ParseException">Is raised when <b>value</b> is not valid <b>address-list</b> value.</exception> public static Mail_t_AddressList Parse(string value) { if (value == null) { throw new ArgumentNullException("value"); } /* RFC 5322 3.4. * address = mailbox / group * mailbox = name-addr / addr-spec * name-addr = [display-name] angle-addr * angle-addr = [CFWS] "<" addr-spec ">" [CFWS] / obs-angle-addr * group = display-name ":" [group-list] ";" [CFWS] * display-name = phrase * mailbox-list = (mailbox *("," mailbox)) / obs-mbox-list * address-list = (address *("," address)) / obs-addr-list * group-list = mailbox-list / CFWS / obs-group-list */ MIME_Reader r = new MIME_Reader(value); Mail_t_AddressList retVal = new Mail_t_AddressList(); while (true) { string word = r.QuotedReadToDelimiter(new char[] { ',', '<', ':' }); // We processed all data. if (word == null && r.Available == 0) { break; } // group else if (r.Peek(true) == ':') { Mail_t_Group group = new Mail_t_Group(word != null ? MIME_Encoding_EncodedWord.DecodeS(TextUtils.UnQuoteString(word)) : null); // Consume ':' r.Char(true); while (true) { word = r.QuotedReadToDelimiter(new char[] { ',', '<', ':', ';' }); // We processed all data. if ((word == null && r.Available == 0) || r.Peek(false) == ';') { break; } // In valid address list value. else if (word == string.Empty) { throw new ParseException("Invalid address-list value '" + value + "'."); } // name-addr else if (r.Peek(true) == '<') { group.Members.Add(new Mail_t_Mailbox(word != null ? MIME_Encoding_EncodedWord.DecodeS(TextUtils.UnQuoteString(word)) : null, r.ReadParenthesized())); } // addr-spec else { group.Members.Add(new Mail_t_Mailbox(null, word)); } // We reached at the end of group. if (r.Peek(true) == ';') { r.Char(true); break; } // We have more addresses. if (r.Peek(true) == ',') { r.Char(false); } } retVal.Add(group); } // name-addr else if (r.Peek(true) == '<') { retVal.Add(new Mail_t_Mailbox(word != null ? MIME_Encoding_EncodedWord.DecodeS(TextUtils.UnQuoteString(word.Trim())) : null, r.ReadParenthesized())); } // addr-spec else { retVal.Add(new Mail_t_Mailbox(null, word)); } // We have more addresses. if (r.Peek(true) == ',') { r.Char(false); } } return(retVal); }
/// <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); }
/// <summary> /// Parses <b>address-list</b> from specified string value. /// </summary> /// <param name="value">The <b>address-list</b> string value.</param> /// <returns></returns> /// <exception cref="ArgumentNullException">Is raised when <b>value</b> is null reference.</exception> /// <exception cref="ParseException">Is raised when <b>value</b> is not valid <b>address-list</b> value.</exception> public static Mail_t_AddressList Parse(string value) { if(value == null){ throw new ArgumentNullException("value"); } /* RFC 5322 3.4. address = mailbox / group mailbox = name-addr / addr-spec name-addr = [display-name] angle-addr angle-addr = [CFWS] "<" addr-spec ">" [CFWS] / obs-angle-addr group = display-name ":" [group-list] ";" [CFWS] display-name = phrase mailbox-list = (mailbox *("," mailbox)) / obs-mbox-list address-list = (address *("," address)) / obs-addr-list group-list = mailbox-list / CFWS / obs-group-list */ MIME_Reader r = new MIME_Reader(value); Mail_t_AddressList retVal = new Mail_t_AddressList(); while(true){ string word = r.QuotedReadToDelimiter(new char[]{',','<',':'}); // We processed all data. if(word == null && r.Available == 0){ break; } // group else if(r.Peek(true) == ':'){ Mail_t_Group group = new Mail_t_Group(word != null ? MIME_Encoding_EncodedWord.DecodeS(TextUtils.UnQuoteString(word)) : null); // Consume ':' r.Char(true); while(true){ word = r.QuotedReadToDelimiter(new char[]{',','<',':',';'}); // We processed all data. if((word == null && r.Available == 0) || r.Peek(false) == ';'){ break; } // In valid address list value. else if(word == string.Empty){ throw new ParseException("Invalid address-list value '" + value + "'."); } // name-addr else if(r.Peek(true) == '<'){ group.Members.Add(new Mail_t_Mailbox(word != null ? MIME_Encoding_EncodedWord.DecodeS(TextUtils.UnQuoteString(word)) : null,r.ReadParenthesized())); } // addr-spec else{ group.Members.Add(new Mail_t_Mailbox(null,word)); } // We reached at the end of group. if(r.Peek(true) == ';'){ r.Char(true); break; } // We have more addresses. if(r.Peek(true) == ','){ r.Char(false); } } retVal.Add(group); } // name-addr else if(r.Peek(true) == '<'){ retVal.Add(new Mail_t_Mailbox(word != null ? MIME_Encoding_EncodedWord.DecodeS(TextUtils.UnQuoteString(word.Trim())) : null,r.ReadParenthesized())); } // addr-spec else{ retVal.Add(new Mail_t_Mailbox(null,word)); } // We have more addresses. if(r.Peek(true) == ','){ r.Char(false); } } return retVal; }