/// <summary> /// Checks if specified message matches to specified criteria. /// </summary> /// <param name="matchExpression">Match expression.</param> /// <param name="mailFrom">SMTP MAIL FROM: command email value.</param> /// <param name="rcptTo">SMTP RCPT TO: command email values.</param> /// <param name="smtpSession">SMTP current session.</param> /// <param name="mime">Message to match.</param> /// <param name="messageSize">Message size in bytes.</param> /// <returns>Returns true if message matches to specified criteria.</returns> public bool Match(string matchExpression, string mailFrom, string[] rcptTo, SMTP_Session smtpSession, Mail_Message mime, int messageSize) { LumiSoft.Net.StringReader r = new LumiSoft.Net.StringReader(matchExpression); return(Match(false, r, mailFrom, rcptTo, smtpSession, mime, messageSize)); }
/// <summary> /// Checks if specified message matches to specified criteria. /// </summary> /// <param name="syntaxCheckOnly">Specifies if syntax check is only done. If true no matching is done.</param> /// <param name="r">Match expression reader what contains match expression.</param> /// <param name="mailFrom">SMTP MAIL FROM: command email value.</param> /// <param name="rcptTo">SMTP RCPT TO: command email values.</param> /// <param name="smtpSession">SMTP current session.</param> /// <param name="mime">Message to match.</param> /// <param name="messageSize">Message size in bytes.</param> /// <returns>Returns true if message matches to specified criteria.</returns> private bool Match(bool syntaxCheckOnly, LumiSoft.Net.StringReader r, string mailFrom, string[] rcptTo, SMTP_Session smtpSession, Mail_Message mime, int messageSize) { /* Possible keywords order * At first there can be NOT,parethesized or matcher * After NOT, parethesized or matcher * After matcher, AND or OR * After OR, NOT,parethesized or matcher * After AND, NOT,parethesized or matcher * After parethesized, NOT or matcher */ PossibleClauseItem possibleClauseItems = PossibleClauseItem.Parenthesizes | PossibleClauseItem.NOT | PossibleClauseItem.Matcher; bool lastMatchValue = false; // Empty string passed r.ReadToFirstChar(); if (r.Available == 0) { throw new Exception("Invalid syntax: '" + ClauseItemsToString(possibleClauseItems) + "' expected !"); } // Parse while there are expressions or get error while (r.Available > 0) { r.ReadToFirstChar(); // Syntax check must consider that there is alwas match !!! if (syntaxCheckOnly) { lastMatchValue = true; } #region () Groupped matchers // () Groupped matchers if (r.StartsWith("(")) { lastMatchValue = Match(syntaxCheckOnly, new LumiSoft.Net.StringReader(r.ReadParenthesized()), mailFrom, rcptTo, smtpSession, mime, messageSize); possibleClauseItems = PossibleClauseItem.Parenthesizes | PossibleClauseItem.Matcher | PossibleClauseItem.NOT; } #endregion #region AND clause // AND clause else if (r.StartsWith("and", false)) { // See if AND allowed if ((possibleClauseItems & PossibleClauseItem.AND) == 0) { throw new Exception("Invalid syntax: '" + ClauseItemsToString(possibleClauseItems) + "' expected !"); } // Last match value is false, no need to check next conditions if (!lastMatchValue) { return(false); } // Remove AND r.ReadWord(); r.ReadToFirstChar(); lastMatchValue = Match(syntaxCheckOnly, r, mailFrom, rcptTo, smtpSession, mime, messageSize); possibleClauseItems = PossibleClauseItem.Parenthesizes | PossibleClauseItem.Matcher | PossibleClauseItem.NOT; } #endregion #region OR clause // OR clause else if (r.StartsWith("or", false)) { // See if OR allowed if ((possibleClauseItems & PossibleClauseItem.OR) == 0) { throw new Exception("Invalid syntax: '" + ClauseItemsToString(possibleClauseItems) + "' expected !"); } // Remove OR r.ReadWord(); r.ReadToFirstChar(); // Last match value is false, then we need to check next condition. // Otherwise OR is matched already, just eat next matcher. if (lastMatchValue) { // Skip next clause Match(syntaxCheckOnly, r, mailFrom, rcptTo, smtpSession, mime, messageSize); } else { lastMatchValue = Match(syntaxCheckOnly, r, mailFrom, rcptTo, smtpSession, mime, messageSize); } possibleClauseItems = PossibleClauseItem.Parenthesizes | PossibleClauseItem.Matcher | PossibleClauseItem.NOT; } #endregion #region NOT clause // NOT clause else if (r.StartsWith("not", false)) { // See if NOT allowed if ((possibleClauseItems & PossibleClauseItem.NOT) == 0) { throw new Exception("Invalid syntax: '" + ClauseItemsToString(possibleClauseItems) + "' expected !"); } // Remove NOT r.ReadWord(); r.ReadToFirstChar(); // Just reverse match result value lastMatchValue = !Match(syntaxCheckOnly, r, mailFrom, rcptTo, smtpSession, mime, messageSize); possibleClauseItems = PossibleClauseItem.Parenthesizes | PossibleClauseItem.Matcher; } #endregion else { // See if matcher allowed if ((possibleClauseItems & PossibleClauseItem.Matcher) == 0) { throw new Exception("Invalid syntax: '" + ClauseItemsToString(possibleClauseItems) + "' expected ! \r\n\r\n Near: '" + r.OriginalString.Substring(0, r.Position) + "'"); } // 1) matchsource // 2) keyword // Read match source string word = r.ReadWord(); if (word == null) { throw new Exception("Invalid syntax: matcher is missing !"); } word = word.ToLower(); string[] matchSourceValues = new string[] {}; #region smtp.mail_from // SMTP command MAIL FROM: value. // smtp.mail_from if (word == "smtp.mail_from") { if (!syntaxCheckOnly) { matchSourceValues = new string[] { mailFrom }; } } #endregion #region smtp.rcpt_to // SMTP command RCPT TO: values. // smtp.mail_to else if (word == "smtp.rcpt_to") { if (!syntaxCheckOnly) { matchSourceValues = rcptTo; } } #endregion #region smtp.ehlo // SMTP command EHLO/HELO: value. // smtp.ehlo else if (word == "smtp.ehlo") { if (!syntaxCheckOnly) { matchSourceValues = new string[] { smtpSession.EhloHost }; } } #endregion #region smtp.authenticated // Specifies if SMTP session is authenticated. // smtp.authenticated else if (word == "smtp.authenticated") { if (!syntaxCheckOnly) { if (smtpSession != null) { matchSourceValues = new string[] { smtpSession.IsAuthenticated.ToString() }; } } } #endregion #region smtp.user // SMTP authenticated user name. Empy string "" if not authenticated. // smtp.user else if (word == "smtp.user") { if (!syntaxCheckOnly) { if (smtpSession != null && smtpSession.AuthenticatedUserIdentity != null) { matchSourceValues = new string[] { smtpSession.AuthenticatedUserIdentity.Name }; } } } #endregion #region smtp.remote_ip // SMTP session connected client IP address. // smtp.remote_ip else if (word == "smtp.remote_ip") { if (!syntaxCheckOnly) { if (smtpSession != null) { matchSourceValues = new string[] { smtpSession.RemoteEndPoint.Address.ToString() }; } } } #endregion #region message.size // Message size in bytes. // message.size else if (word == "message.size") { if (!syntaxCheckOnly) { matchSourceValues = new string[] { messageSize.ToString() }; } } #endregion #region message.header <SP> "HeaderFieldName:" // Message main header header field. If multiple header fields, then all are checked. // message.header <SP> "HeaderFieldName:" else if (word == "message.header") { string headerFieldName = r.ReadWord(); if (headerFieldName == null) { throw new Exception("Match source MainHeaderField HeaderFieldName is missing ! Syntax:{MainHeaderField <SP> \"HeaderFieldName:\"}"); } if (!syntaxCheckOnly) { if (mime.Header.Contains(headerFieldName)) { MIME_h[] fields = mime.Header[headerFieldName]; matchSourceValues = new string[fields.Length]; for (int i = 0; i < matchSourceValues.Length; i++) { matchSourceValues[i] = fields[i].ValueToString(); } } } } #endregion #region message.all_headers <SP> "HeaderFieldName:" // Any mime entity header header field. If multiple header fields, then all are checked. // message.all_headers <SP> "HeaderFieldName:" else if (word == "message.all_headers") { string headerFieldName = r.ReadWord(); if (headerFieldName == null) { throw new Exception("Match source MainHeaderField HeaderFieldName is missing ! Syntax:{MainHeaderField <SP> \"HeaderFieldName:\"}"); } if (!syntaxCheckOnly) { List <string> values = new List <string>(); foreach (MIME_Entity entity in mime.AllEntities) { if (entity.Header.Contains(headerFieldName)) { MIME_h[] fields = entity.Header[headerFieldName]; for (int i = 0; i < fields.Length; i++) { values.Add(fields[i].ValueToString()); } } } matchSourceValues = values.ToArray(); } } #endregion #region message.body_text // Message body text. // message.body_text else if (word == "message.body_text") { if (!syntaxCheckOnly) { matchSourceValues = new string[] { mime.BodyText }; } } #endregion #region message.body_html // Message body html. // message.body_html else if (word == "message.body_html") { if (!syntaxCheckOnly) { matchSourceValues = new string[] { mime.BodyHtmlText }; } } #endregion #region message.content_md5 // Message any mime entity decoded data MD5 hash. // message.content_md5 else if (word == "message.content_md5") { if (!syntaxCheckOnly) { List <string> values = new List <string>(); foreach (MIME_Entity entity in mime.AllEntities) { try{ if (entity.Body is MIME_b_SinglepartBase) { byte[] data = ((MIME_b_SinglepartBase)entity.Body).Data; if (data != null) { System.Security.Cryptography.MD5CryptoServiceProvider md5 = new System.Security.Cryptography.MD5CryptoServiceProvider(); values.Add(System.Text.Encoding.Default.GetString(md5.ComputeHash(data))); } } } catch { // Message data parsing failed, just skip that entity md5 } } matchSourceValues = values.ToArray(); } } #endregion #region sys.date_time // System current date time. Format: yyyy.MM.dd HH:mm:ss. // sys.date_time else if (word == "sys.date_time") { if (!syntaxCheckOnly) { matchSourceValues = new string[] { DateTime.Now.ToString("dd.MM.yyyy HH:mm:ss") }; } } #endregion #region sys.date // System current date. Format: yyyy.MM.dd. // sys.date else if (word == "sys.date") { if (!syntaxCheckOnly) { matchSourceValues = new string[] { DateTime.Today.ToString("dd.MM.yyyy") }; } } #endregion #region sys.time // System current time. Format: HH:mm:ss. // sys.time else if (word == "sys.time") { if (!syntaxCheckOnly) { matchSourceValues = new string[] { DateTime.Now.ToString("HH:mm:ss") }; } } #endregion #region sys.day_of_week // Day of week. Days: sunday,monday,tuesday,wednesday,thursday,friday,saturday. // sys.day_of_week else if (word == "sys.day_of_week") { if (!syntaxCheckOnly) { matchSourceValues = new string[] { DateTime.Today.DayOfWeek.ToString() }; } } #endregion /* * // Day of month. Format: 1 - 31. If no so much days in month, then replaced with month max days. * // sys.day_of_month * else if(word == "sys.day_of_month"){ * } */ #region sys.day_of_year // Month of year. Format: 1 - 12. // sys.day_of_year else if (word == "sys.day_of_year") { if (!syntaxCheckOnly) { matchSourceValues = new string[] { DateTime.Today.ToString("M") }; } } #endregion #region Unknown // Unknown else { throw new Exception("Unknown match source '" + word + "' !"); } #endregion /* If we reach so far, then we have valid match sorce and compare value. * Just do compare. */ // Reset lastMatch result lastMatchValue = false; // Read matcher word = r.ReadWord(true, new char[] { ' ' }, true); if (word == null) { throw new Exception("Invalid syntax: operator is missing ! \r\n\r\n Near: '" + r.OriginalString.Substring(0, r.Position) + "'"); } word = word.ToLower(); #region * <SP> "astericPattern" // * <SP> "astericPattern" if (word == "*") { string val = r.ReadWord(); if (val == null) { throw new Exception("Invalid syntax: <SP> \"value\" is missing !"); } val = val.ToLower(); if (!syntaxCheckOnly) { // We check matchSourceValues when first is found foreach (string matchSourceValue in matchSourceValues) { if (SCore.IsAstericMatch(val, matchSourceValue.ToLower())) { lastMatchValue = true; break; } } } } #endregion #region !* <SP> "astericPattern" // !* <SP> "astericPattern" else if (word == "!*") { string val = r.ReadWord(); if (val == null) { throw new Exception("Invalid syntax: <SP> \"value\" is missing !"); } val = val.ToLower(); if (!syntaxCheckOnly) { // We check matchSourceValues when first is found foreach (string matchSourceValue in matchSourceValues) { if (SCore.IsAstericMatch(val, matchSourceValue.ToLower())) { lastMatchValue = false; break; } } } } #endregion #region == <SP> "value" // == <SP> "value" else if (word == "==") { string val = r.ReadWord(); if (val == null) { throw new Exception("Invalid syntax: <SP> \"value\" is missing !"); } val = val.ToLower(); if (!syntaxCheckOnly) { // We check matchSourceValues when first is found foreach (string matchSourceValue in matchSourceValues) { if (val == matchSourceValue.ToLower()) { lastMatchValue = true; break; } } } } #endregion #region != <SP> "value" // != <SP> "value" else if (word == "!=") { string val = r.ReadWord(); if (val == null) { throw new Exception("Invalid syntax: <SP> \"value\" is missing !"); } val = val.ToLower(); if (!syntaxCheckOnly) { // We check matchSourceValues when first is found, then already value equals foreach (string matchSourceValue in matchSourceValues) { if (val == matchSourceValue.ToLower()) { lastMatchValue = false; break; } lastMatchValue = true; } } } #endregion #region >= <SP> "value" // >= <SP> "value" else if (word == ">=") { string val = r.ReadWord(); if (val == null) { throw new Exception("Invalid syntax: <SP> \"value\" is missing !"); } val = val.ToLower(); if (!syntaxCheckOnly) { // We check matchSourceValues when first is found foreach (string matchSourceValue in matchSourceValues) { if (matchSourceValue.ToLower().CompareTo(val) >= 0) { lastMatchValue = true; break; } } } } #endregion #region <= <SP> "value" // <= <SP> "value" else if (word == "<=") { string val = r.ReadWord(); if (val == null) { throw new Exception("Invalid syntax: <SP> \"value\" is missing !"); } val = val.ToLower(); if (!syntaxCheckOnly) { // We check matchSourceValues when first is found foreach (string matchSourceValue in matchSourceValues) { if (matchSourceValue.ToLower().CompareTo(val) <= 0) { lastMatchValue = true; break; } } } } #endregion #region > <SP> "value" // > <SP> "value" else if (word == ">") { string val = r.ReadWord(); if (val == null) { throw new Exception("Invalid syntax: <SP> \"value\" is missing !"); } val = val.ToLower(); if (!syntaxCheckOnly) { // We check matchSourceValues when first is found foreach (string matchSourceValue in matchSourceValues) { if (matchSourceValue.ToLower().CompareTo(val) > 0) { lastMatchValue = true; break; } } } } #endregion #region < <SP> "value" // < <SP> "value" else if (word == "<") { string val = r.ReadWord(); if (val == null) { throw new Exception("Invalid syntax: <SP> \"value\" is missing !"); } val = val.ToLower(); if (!syntaxCheckOnly) { // We check matchSourceValues when first is found foreach (string matchSourceValue in matchSourceValues) { if (matchSourceValue.ToLower().CompareTo(val) < 0) { lastMatchValue = true; break; } } } } #endregion #region regex <SP> "value" // Regex <SP> "value" else if (word == "regex") { string val = r.ReadWord(); if (val == null) { throw new Exception("Invalid syntax: <SP> \"value\" is missing !"); } val = val.ToLower(); if (!syntaxCheckOnly) { // We check matchSourceValues when first is found foreach (string matchSourceValue in matchSourceValues) { if (Regex.IsMatch(val, matchSourceValue.ToLower())) { lastMatchValue = true; break; } } } } #endregion #region Unknown // Unknown else { throw new Exception("Unknown keword '" + word + "' !"); } #endregion possibleClauseItems = PossibleClauseItem.AND | PossibleClauseItem.OR; } } return(lastMatchValue); }
private void m_pTab_General_MatchExpression_TextChanged(object sender,EventArgs e) { m_pTab_General_MatchExpression.SuspendPaint = true; string text = m_pTab_General_MatchExpression.Text; int selectionStart = m_pTab_General_MatchExpression.SelectionStart; int startPos = 0; StringReader r = new StringReader(text); while(r.Available > 0){ r.ReadToFirstChar(); startPos = r.Position; string word = r.ReadWord(false); if(word == null){ break; } // We must have ()[]{}<> if(word == ""){ word = r.ReadSpecifiedLength(1); } if(word.StartsWith("\"") && word.EndsWith("\"")){ m_pTab_General_MatchExpression.SelectionStart = startPos; m_pTab_General_MatchExpression.SelectionLength = word.Length; m_pTab_General_MatchExpression.SelectionColor = Color.Brown; continue; } bool isKeyWord = false; string[] keyWords = new string[]{ "and", "or", "not" }; foreach(string keyWord in keyWords){ if(word.ToLower() == keyWord.ToLower()){ isKeyWord = true; break; } } bool isMatcher = false; string[] matchers = new string[]{ "smtp.mail_from", "smtp.rcpt_to", "smtp.ehlo", "smtp.authenticated", "smtp.user", "smtp.remote_ip", "message.size", "message.header", "message.all_headers", "message.body_text", "message.body_html", "message.content_md5", "sys.date_time", "sys.date", "sys.time", "sys.day_of_week", "sys.day_of_month", "sys.day_of_year" }; foreach(string keyWord in matchers){ if(word.ToLower() == keyWord.ToLower()){ isMatcher = true; break; } } if(isKeyWord){ m_pTab_General_MatchExpression.SelectionStart = startPos; m_pTab_General_MatchExpression.SelectionLength = word.Length; m_pTab_General_MatchExpression.SelectionColor = Color.Blue; } else if(isMatcher){ m_pTab_General_MatchExpression.SelectionStart = startPos; m_pTab_General_MatchExpression.SelectionLength = word.Length; m_pTab_General_MatchExpression.SelectionColor = Color.DarkMagenta; } else{ m_pTab_General_MatchExpression.SelectionStart = startPos; m_pTab_General_MatchExpression.SelectionLength = word.Length; m_pTab_General_MatchExpression.SelectionColor = Color.Black; } } m_pTab_General_MatchExpression.SelectionStart = selectionStart; m_pTab_General_MatchExpression.SelectionLength = 0; m_pTab_General_MatchExpression.SuspendPaint = false; }
/// <summary> /// Checks if specified message matches to specified criteria. /// </summary> /// <param name="matchExpression">Match expression.</param> /// <param name="mailFrom">SMTP MAIL FROM: command email value.</param> /// <param name="rcptTo">SMTP RCPT TO: command email values.</param> /// <param name="smtpSession">SMTP current session.</param> /// <param name="mime">Message to match.</param> /// <param name="messageSize">Message size in bytes.</param> /// <returns>Returns true if message matches to specified criteria.</returns> public bool Match(string matchExpression,string mailFrom,string[] rcptTo,SMTP_Session smtpSession,Mail_Message mime,int messageSize) { LumiSoft.Net.StringReader r = new LumiSoft.Net.StringReader(matchExpression); return Match(false,r,mailFrom,rcptTo,smtpSession,mime,messageSize); }
/// <summary> /// Parses search key from current position. /// </summary> /// <param name="reader"></param> public void Parse(StringReader reader) { //Remove spaces from string start reader.ReadToFirstChar(); if(reader.StartsWith("(")){ reader = new StringReader(reader.ReadParenthesized().Trim()); } //--- Start parsing search keys --------------// while(reader.Available > 0){ object searchKey = ParseSearchKey(reader); if(searchKey != null){ m_pSearchKeys.Add(searchKey); } } //--------------------------------------------// }
/// <summary> /// Parses SIP_Uri from SIP-URI string. /// </summary> /// <param name="value">SIP-URI string.</param> /// <returns>Returns parsed SIP_Uri object.</returns> /// <exception cref="ArgumentNullException">Raised when <b>reader</b> is null.</exception> /// <exception cref="SIP_ParseException">Raised when invalid SIP message.</exception> protected override void ParseInternal(string value) { // Syntax: sip:/sips: username@host:port *[;parameter] [?header *[&header]] if(value == null){ throw new ArgumentNullException("value"); } value = Uri.UnescapeDataString(value); if(!(value.ToLower().StartsWith("sip:") || value.ToLower().StartsWith("sips:"))){ throw new SIP_ParseException("Specified value is invalid SIP-URI !"); } StringReader r = new StringReader(value); // IsSecure this.IsSecure = r.QuotedReadToDelimiter(':').ToLower() == "sips"; // Get username if(r.SourceString.IndexOf('@') > -1){ this.User = r.QuotedReadToDelimiter('@'); } // Gets host[:port] string[] host_port = r.QuotedReadToDelimiter(new char[]{';','?'},false).Split(':'); this.Host = host_port[0]; // Optional port specified if(host_port.Length == 2){ this.Port = Convert.ToInt32(host_port[1]); } // We have parameters and/or header if(r.Available > 0){ // Get parameters string[] parameters = TextUtils.SplitQuotedString(r.QuotedReadToDelimiter('?'),';'); foreach(string parameter in parameters){ if(parameter.Trim() != ""){ string[] name_value = parameter.Trim().Split(new char[]{'='},2); if(name_value.Length == 2){ this.Parameters.Add(name_value[0],TextUtils.UnQuoteString(name_value[1])); } else{ this.Parameters.Add(name_value[0],null); } } } // We have header if(r.Available > 0){ this.m_Header = r.ReadToEnd(); } } }
/// <summary> /// Parses SearchGroup or SearchItem from reader. If reader starts with (, then parses searchGroup, otherwise SearchItem. /// </summary> /// <param name="reader"></param> /// <returns></returns> internal static object ParseSearchKey(StringReader reader) { //Remove spaces from string start reader.ReadToFirstChar(); // SearchGroup if(reader.StartsWith("(")){ SearchGroup searchGroup = new SearchGroup(); searchGroup.Parse(reader); return searchGroup; } // SearchItem else{ return SearchKey.Parse(reader); } }
/// <summary> /// Parses one search key from current position. Returns null if there isn't any search key left. /// </summary> /// <param name="reader"></param> public static SearchKey Parse(StringReader reader) { string searchKeyName = ""; object searchKeyValue = null; //Remove spaces from string start reader.ReadToFirstChar(); // Search keyname is always 1 word string word = reader.ReadWord(); if(word == null){ return null; } word = word.ToUpper().Trim(); //Remove spaces from string start reader.ReadToFirstChar(); #region ALL // ALL // All messages in the mailbox; the default initial key for ANDing. if(word == "ALL"){ searchKeyName = "ALL"; } #endregion #region ANSWERED // ANSWERED // Messages with the \Answered flag set. else if(word == "ANSWERED"){ // We internally use KEYWORD ANSWERED searchKeyName = "KEYWORD"; searchKeyValue = "ANSWERED"; } #endregion #region BCC // BCC <string> // Messages that contain the specified string in the envelope structure's BCC field. else if(word == "BCC"){ // We internally use HEADER "BCC:" "value" searchKeyName = "HEADER"; // Read <string> string val = ReadString(reader); if(val != null){ searchKeyValue = new string[]{"BCC:",TextUtils.UnQuoteString(val)}; } else{ throw new Exception("BCC <string> value is missing !"); } } #endregion #region BEFORE // BEFORE <date> // Messages whose internal date (disregarding time and timezone) is earlier than the specified date. else if(word == "BEFORE"){ searchKeyName = "BEFORE"; // Read <date> string val = reader.QuotedReadToDelimiter(' '); if(val != null){ // Parse date try{ searchKeyValue = IMAP_Utils.ParseDate(TextUtils.UnQuoteString(val)); } // Invalid date catch{ throw new Exception("Invalid BEFORE <date> value '" + val + "', valid date syntax: {dd-MMM-yyyy} !"); } } else{ throw new Exception("BEFORE <date> value is missing !"); } } #endregion #region BODY // BODY <string> // Messages that contain the specified string in the body of the message. else if(word == "BODY"){ searchKeyName = "BODY"; string val = ReadString(reader); if(val != null){ searchKeyValue = val; } else{ throw new Exception("BODY <string> value is missing !"); } } #endregion #region CC // CC <string> // Messages that contain the specified string in the envelope structure's CC field. else if(word == "CC"){ // We internally use HEADER "CC:" "value" searchKeyName = "HEADER"; // Read <string> string val = ReadString(reader); if(val != null){ searchKeyValue = new string[]{"CC:",TextUtils.UnQuoteString(val)}; } else{ throw new Exception("CC <string> value is missing !"); } } #endregion #region DELETED // DELETED // Messages with the \Deleted flag set. else if(word == "DELETED"){ // We internally use KEYWORD DELETED searchKeyName = "KEYWORD"; searchKeyValue = "DELETED"; } #endregion #region DRAFT // DRAFT // Messages with the \Draft flag set. else if(word == "DRAFT"){ // We internally use KEYWORD DRAFT searchKeyName = "KEYWORD"; searchKeyValue = "DRAFT"; } #endregion #region FLAGGED // FLAGGED // Messages with the \Flagged flag set. else if(word == "FLAGGED"){ // We internally use KEYWORD FLAGGED searchKeyName = "KEYWORD"; searchKeyValue = "FLAGGED"; } #endregion #region FROM // FROM <string> // Messages that contain the specified string in the envelope structure's FROM field. else if(word == "FROM"){ // We internally use HEADER "FROM:" "value" searchKeyName = "HEADER"; // Read <string> string val = ReadString(reader); if(val != null){ searchKeyValue = new string[]{"FROM:",TextUtils.UnQuoteString(val)}; } else{ throw new Exception("FROM <string> value is missing !"); } } #endregion #region HEADER // HEADER <field-name> <string> // Messages that have a header with the specified field-name (as // defined in [RFC-2822]) and that contains the specified string // in the text of the header (what comes after the colon). If the // string to search is zero-length, this matches all messages that // have a header line with the specified field-name regardless of // the contents. else if(word == "HEADER"){ searchKeyName = "HEADER"; // Read <field-name> string fieldName = ReadString(reader); if(fieldName != null){ fieldName = TextUtils.UnQuoteString(fieldName); } else{ throw new Exception("HEADER <field-name> value is missing !"); } // Read <string> string val = ReadString(reader); if(val != null){ searchKeyValue = new string[]{fieldName,TextUtils.UnQuoteString(val)}; } else{ throw new Exception("(HEADER <field-name>) <string> value is missing !"); } } #endregion #region KEYWORD // KEYWORD <flag> // Messages with the specified keyword flag set. else if(word == "KEYWORD"){ searchKeyName = "KEYWORD"; // Read <flag> string val = reader.QuotedReadToDelimiter(' '); if(val != null){ searchKeyValue = TextUtils.UnQuoteString(val); } else{ throw new Exception("KEYWORD <flag> value is missing !"); } } #endregion #region LARGER // LARGER <n> // Messages with an [RFC-2822] size larger than the specified number of octets. else if(word == "LARGER"){ searchKeyName = "LARGER"; // Read <n> string val = reader.QuotedReadToDelimiter(' '); if(val != null){ // Parse <n> - must be integer value try{ searchKeyValue = Convert.ToInt64(TextUtils.UnQuoteString(val)); } // Invalid <n> catch{ throw new Exception("Invalid LARGER <n> value '" + val + "', it must be numeric value !"); } } else{ throw new Exception("LARGER <n> value is missing !"); } } #endregion #region NEW // NEW // Messages that have the \Recent flag set but not the \Seen flag. // This is functionally equivalent to "(RECENT UNSEEN)". else if(word == "NEW"){ // We internally use KEYWORD RECENT searchKeyName = "KEYWORD"; searchKeyValue = "RECENT"; } #endregion #region NOT // NOT <search-key> or (<search-key> <search-key> ...)(SearchGroup) // Messages that do not match the specified search key. else if(word == "NOT"){ searchKeyName = "NOT"; object searchItem = SearchGroup.ParseSearchKey(reader); if(searchItem != null){ searchKeyValue = searchItem; } else{ throw new Exception("Required NOT <search-key> isn't specified !"); } } #endregion #region OLD // OLD // Messages that do not have the \Recent flag set. This is // functionally equivalent to "NOT RECENT" (as opposed to "NOT NEW"). else if(word == "OLD"){ // We internally use UNKEYWORD RECENT searchKeyName = "UNKEYWORD"; searchKeyValue = "RECENT"; } #endregion #region ON // ON <date> // Messages whose internal date (disregarding time and timezone) is within the specified date. else if(word == "ON"){ searchKeyName = "ON"; // Read <date> string val = reader.QuotedReadToDelimiter(' '); if(val != null){ // Parse date try{ searchKeyValue = IMAP_Utils.ParseDate(TextUtils.UnQuoteString(val)); } // Invalid date catch{ throw new Exception("Invalid ON <date> value '" + val + "', valid date syntax: {dd-MMM-yyyy} !"); } } else{ throw new Exception("ON <date> value is missing !"); } } #endregion #region OR // OR <search-key1> <search-key2> - SearckKey can be parenthesis list of keys ! // Messages that match either search key. else if(word == "OR"){ searchKeyName = "OR"; //--- <search-key1> ----------------------------------------------------// object searchKey1 = SearchGroup.ParseSearchKey(reader); if(searchKey1 == null){ throw new Exception("Required OR <search-key1> isn't specified !"); } //----------------------------------------------------------------------// //--- <search-key2> ----------------------------------------------------// object searchKey2 = SearchGroup.ParseSearchKey(reader); if(searchKey2 == null){ throw new Exception("Required (OR <search-key1>) <search-key2> isn't specified !"); } //-----------------------------------------------------------------------// searchKeyValue = new object[]{searchKey1,searchKey2}; } #endregion #region RECENT // RECENT // Messages that have the \Recent flag set. else if(word == "RECENT"){ // We internally use KEYWORD RECENT searchKeyName = "KEYWORD"; searchKeyValue = "RECENT"; } #endregion #region SEEN // SEEN // Messages that have the \Seen flag set. else if(word == "SEEN"){ // We internally use KEYWORD SEEN searchKeyName = "KEYWORD"; searchKeyValue = "SEEN"; } #endregion #region SENTBEFORE // SENTBEFORE <date> // Messages whose [RFC-2822] Date: header (disregarding time and // timezone) is earlier than the specified date. else if(word == "SENTBEFORE"){ searchKeyName = "SENTBEFORE"; // Read <date> string val = reader.QuotedReadToDelimiter(' '); if(val != null){ // Parse date try{ searchKeyValue = IMAP_Utils.ParseDate(TextUtils.UnQuoteString(val)); } // Invalid date catch{ throw new Exception("Invalid SENTBEFORE <date> value '" + val + "', valid date syntax: {dd-MMM-yyyy} !"); } } else{ throw new Exception("SENTBEFORE <date> value is missing !"); } } #endregion #region SENTON // SENTON <date> // Messages whose [RFC-2822] Date: header (disregarding time and // timezone) is within the specified date. else if(word == "SENTON"){ searchKeyName = "SENTON"; // Read <date> string val = reader.QuotedReadToDelimiter(' '); if(val != null){ // Parse date try{ searchKeyValue = IMAP_Utils.ParseDate(TextUtils.UnQuoteString(val)); } // Invalid date catch{ throw new Exception("Invalid SENTON <date> value '" + val + "', valid date syntax: {dd-MMM-yyyy} !"); } } else{ throw new Exception("SENTON <date> value is missing !"); } } #endregion #region SENTSINCE // SENTSINCE <date> // Messages whose [RFC-2822] Date: header (disregarding time and // timezone) is within or later than the specified date. else if(word == "SENTSINCE"){ searchKeyName = "SENTSINCE"; // Read <date> string val = reader.QuotedReadToDelimiter(' '); if(val != null){ // Parse date try{ searchKeyValue = IMAP_Utils.ParseDate(TextUtils.UnQuoteString(val)); } // Invalid date catch{ throw new Exception("Invalid SENTSINCE <date> value '" + val + "', valid date syntax: {dd-MMM-yyyy} !"); } } else{ throw new Exception("SENTSINCE <date> value is missing !"); } } #endregion #region SINCE // SINCE <date> // Messages whose internal date (disregarding time and timezone) // is within or later than the specified date. else if(word == "SINCE"){ searchKeyName = "SINCE"; // Read <date> string val = reader.ReadWord(); if(val != null){ // Parse date try{ searchKeyValue = IMAP_Utils.ParseDate(TextUtils.UnQuoteString(val)); } // Invalid date catch{ throw new Exception("Invalid SINCE <date> value '" + val + "', valid date syntax: {dd-MMM-yyyy} !"); } } else{ throw new Exception("SINCE <date> value is missing !"); } } #endregion #region SMALLER // SMALLER <n> // Messages with an [RFC-2822] size smaller than the specified number of octets. else if(word == "SMALLER"){ searchKeyName = "SMALLER"; // Read <n> string val = reader.QuotedReadToDelimiter(' '); if(val != null){ val = TextUtils.UnQuoteString(val); // Parse <n> - must be integer value try{ searchKeyValue = Convert.ToInt64(val); } // Invalid <n> catch{ throw new Exception("Invalid SMALLER <n> value '" + val + "', it must be numeric value !"); } } else{ throw new Exception("SMALLER <n> value is missing !"); } } #endregion #region SUBJECT // SUBJECT <string> // Messages that contain the specified string in the envelope structure's SUBJECT field. else if(word == "SUBJECT"){ // We internally use HEADER "SUBJECT:" "value" searchKeyName = "HEADER"; // Read <string> string val = ReadString(reader); if(val != null){ searchKeyValue = new string[]{"SUBJECT:",TextUtils.UnQuoteString(val)}; } else{ throw new Exception("SUBJECT <string> value is missing !"); } } #endregion #region TEXT // TEXT <string> // Messages that contain the specified string in the header or body of the message. else if(word == "TEXT"){ searchKeyName = "TEXT"; string val = ReadString(reader); if(val != null){ searchKeyValue = val; } else{ throw new Exception("TEXT <string> value is missing !"); } } #endregion #region TO // TO <string> // Messages that contain the specified string in the envelope structure's TO field. else if(word == "TO"){ // We internally use HEADER "TO:" "value" searchKeyName = "HEADER"; // Read <string> string val = ReadString(reader); if(val != null){ searchKeyValue = new string[]{"TO:",TextUtils.UnQuoteString(val)}; } else{ throw new Exception("TO <string> value is missing !"); } } #endregion #region UID // UID <sequence set> // Messages with unique identifiers corresponding to the specified // unique identifier set. Sequence set ranges are permitted. else if(word == "UID"){ searchKeyName = "UID"; // Read <sequence set> string val = reader.QuotedReadToDelimiter(' '); if(val != null){ try{ IMAP_SequenceSet sequenceSet = new IMAP_SequenceSet(); sequenceSet.Parse(TextUtils.UnQuoteString(val),long.MaxValue); searchKeyValue = sequenceSet; } catch{ throw new Exception("Invalid UID <sequence-set> value '" + val + "' !"); } } else{ throw new Exception("UID <sequence-set> value is missing !"); } } #endregion #region UNANSWERED // UNANSWERED // Messages that do not have the \Answered flag set. else if(word == "UNANSWERED"){ // We internally use UNKEYWORD SEEN searchKeyName = "UNKEYWORD"; searchKeyValue = "ANSWERED"; } #endregion #region UNDELETED // UNDELETED // Messages that do not have the \Deleted flag set. else if(word == "UNDELETED"){ // We internally use UNKEYWORD UNDELETED searchKeyName = "UNKEYWORD"; searchKeyValue = "DELETED"; } #endregion #region UNDRAFT // UNDRAFT // Messages that do not have the \Draft flag set. else if(word == "UNDRAFT"){ // We internally use UNKEYWORD UNDRAFT searchKeyName = "UNKEYWORD"; searchKeyValue = "DRAFT"; } #endregion #region UNFLAGGED // UNFLAGGED // Messages that do not have the \Flagged flag set. else if(word == "UNFLAGGED"){ // We internally use UNKEYWORD UNFLAGGED searchKeyName = "UNKEYWORD"; searchKeyValue = "FLAGGED"; } #endregion #region UNKEYWORD // UNKEYWORD <flag> // Messages that do not have the specified keyword flag set. else if(word == "UNKEYWORD"){ searchKeyName = "UNKEYWORD"; // Read <flag> string val = reader.QuotedReadToDelimiter(' '); if(val != null){ searchKeyValue = TextUtils.UnQuoteString(val); } else{ throw new Exception("UNKEYWORD <flag> value is missing !"); } } #endregion #region UNSEEN // UNSEEN // Messages that do not have the \Seen flag set. else if(word == "UNSEEN"){ // We internally use UNKEYWORD UNSEEN searchKeyName = "UNKEYWORD"; searchKeyValue = "SEEN"; } #endregion #region Unknown or SEQUENCESET // Unkown keyword or <sequence set> else{ // DUMMY palce(bad design) in IMAP. // Active keyword can be <sequence set> or bad keyword, there is now way to distinguish what is meant. // Why they don't key work SEQUENCESET <sequence set> ? // <sequence set> // Messages with message sequence numbers corresponding to the // specified message sequence number set. // Just try if it can be parsed as sequence-set try{ IMAP_SequenceSet sequenceSet = new IMAP_SequenceSet(); sequenceSet.Parse(word,long.MaxValue); searchKeyName = "SEQUENCESET"; searchKeyValue = sequenceSet; } // This isn't vaild sequnce-set value catch{ throw new Exception("Invalid search key or <sequnce-set> value '" + word + "' !"); } } #endregion // REMOVE ME: // Console.WriteLine(searchKeyName + " : " + Convert.ToString(searchKeyValue)); return new SearchKey(searchKeyName,searchKeyValue); }
/// <summary> /// Reads search-key <string> value. /// </summary> /// <param name="reader"></param> /// <returns></returns> private static string ReadString(StringReader reader) { //Remove spaces from string start reader.ReadToFirstChar(); // We must support: // word // "text" // {string_length}data(string_length) // {string_length}data(string_length) if(reader.StartsWith("{")){ // Remove { reader.ReadSpecifiedLength("{".Length); int dataLength = Convert.ToInt32(reader.QuotedReadToDelimiter('}')); return reader.ReadSpecifiedLength(dataLength); } return TextUtils.UnQuoteString(reader.QuotedReadToDelimiter(' ')); }
/// <summary> /// Parses IMAP FETCH BODYSTRUCTURE multipart entity from reader. /// </summary> /// <param name="r">Fetch reader.</param> /// <returns>Returns parsed bodystructure entity.</returns> /// <exception cref="ArgumentNullException">Is raised when <b>r</b> is null reference.</exception> public static IMAP_t_Fetch_r_i_BodyStructure_e_Multipart Parse(StringReader r) { if(r == null){ throw new ArgumentNullException("r"); } IMAP_t_Fetch_r_i_BodyStructure_e_Multipart retVal = new IMAP_t_Fetch_r_i_BodyStructure_e_Multipart(); /* RFC 3501 7.4.2. Normal fields. 1*(body-parts) - parenthesized list of body parts subtype --- extention fields Extention fields. body parameter parenthesized list body disposition body language body location */ #region Normal fields // Read child entities. while(r.Available > 0){ r.ReadToFirstChar(); if(r.StartsWith("(")){ StringReader bodyPartReader = new StringReader(r.ReadParenthesized()); bodyPartReader.ReadToFirstChar(); IMAP_t_Fetch_r_i_BodyStructure_e part = null; // multipart if(bodyPartReader.StartsWith("(")){ part = IMAP_t_Fetch_r_i_BodyStructure_e_Multipart.Parse(bodyPartReader); } // single-part else{ part = IMAP_t_Fetch_r_i_BodyStructure_e_SinglePart.Parse(bodyPartReader); } part.SetParent(retVal); retVal.m_pBodyParts.Add(part); } else{ break; } } // subtype string subtype = IMAP_Utils.ReadString(r); if(!string.IsNullOrEmpty(subtype)){ retVal.m_pContentType = new MIME_h_ContentType("multipart/" + subtype); } #endregion #region Extention field // body parameter parenthesized list r.ReadToFirstChar(); if(r.StartsWith("(")){ StringReader pramsReader = new StringReader(r.ReadParenthesized()); if(retVal.m_pContentType != null){ while(pramsReader.Available > 0){ string name = IMAP_Utils.ReadString(pramsReader); if(string.IsNullOrEmpty(name)){ break; } string value = IMAP_Utils.ReadString(pramsReader); if(value == null){ value = ""; } retVal.m_pContentType.Parameters[name] = MIME_Encoding_EncodedWord.DecodeTextS(value); } } } // NIL else{ IMAP_Utils.ReadString(r); } // body disposition - "(" string SP body-fld-param ")" / nil // body-fld-param = "(" string SP string *(SP string SP string) ")" / nil if(r.StartsWith("(")){ string disposition = IMAP_Utils.ReadString(r); if(!string.IsNullOrEmpty(disposition)){ retVal.m_pContentDisposition = new MIME_h_ContentDisposition(disposition); } r.ReadToFirstChar(); // Parse Content-Dispostion parameters. if(r.StartsWith("(")){ StringReader pramsReader = new StringReader(r.ReadParenthesized()); if(retVal.m_pContentDisposition != null){ while(pramsReader.Available > 0){ string name = IMAP_Utils.ReadString(pramsReader); if(string.IsNullOrEmpty(name)){ break; } string value = IMAP_Utils.ReadString(pramsReader); if(value == null){ value = ""; } retVal.m_pContentDisposition.Parameters[name] = MIME_Encoding_EncodedWord.DecodeTextS(value); } } } // NIL else{ IMAP_Utils.ReadString(r); } } // NIL else{ IMAP_Utils.ReadString(r); } // body language - nstring / "(" string *(SP string) ")" r.ReadToFirstChar(); if(r.StartsWith("(")){ retVal.m_Language = r.ReadParenthesized(); } else{ retVal.m_Language = IMAP_Utils.ReadString(r); } // body location - nstring retVal.m_Location = IMAP_Utils.ReadString(r); #endregion return retVal; }
/// <summary> /// Parses IMAP FETCH BODYSTRUCTURE single part entity from reader. /// </summary> /// <param name="r">Fetch reader.</param> /// <returns>Returns parsed bodystructure entity.</returns> /// <exception cref="ArgumentNullException">Is raised when <b>r</b> is null reference.</exception> public static IMAP_t_Fetch_r_i_BodyStructure_e_SinglePart Parse(StringReader r) { if(r == null){ throw new ArgumentNullException("r"); } IMAP_t_Fetch_r_i_BodyStructure_e_SinglePart retVal = new IMAP_t_Fetch_r_i_BodyStructure_e_SinglePart(); /* RFC 3501 7.4.2. Normal single part entity fields. body type body subtype body parameter parenthesized list body id body description body encoding body size - Encoded bytes count. --- extention fields Message/xxx type entity fields. body type body subtype body parameter parenthesized list body id body description body encoding body size - Encoded bytes count. --- message special fields envelope structure body structure body encoded text lines count --- extention fields Text/xxx type entity fields. body type body subtype body parameter parenthesized list body id body description body encoding body size - Encoded bytes count. --- text special fields body encoded text lines count --- extention fields Extention fields. body MD5 body disposition body language body location */ #region Common fields // body type // body subtype // body parameter parenthesized list string type = IMAP_Utils.ReadString(r); string subtype = IMAP_Utils.ReadString(r); if(!string.IsNullOrEmpty(type) && !string.IsNullOrEmpty(subtype)){ retVal.m_pContentType = new MIME_h_ContentType(type + "/" + subtype); } r.ReadToFirstChar(); // Parse Content-Type parameters if(r.StartsWith("(")){ StringReader pramsReader = new StringReader(r.ReadParenthesized()); if(retVal.m_pContentType != null){ while(pramsReader.Available > 0){ string name = IMAP_Utils.ReadString(pramsReader); if(string.IsNullOrEmpty(name)){ break; } string value = IMAP_Utils.ReadString(pramsReader); if(value == null){ value = ""; } retVal.m_pContentType.Parameters[name] = MIME_Encoding_EncodedWord.DecodeTextS(value); } } } // NIL else{ IMAP_Utils.ReadString(r); } // body id - nstring retVal.m_ContentID = IMAP_Utils.ReadString(r); // body description - nstring retVal.m_ContentDescription = IMAP_Utils.ReadString(r); // body encoding - string retVal.m_ContentTransferEncoding = IMAP_Utils.ReadString(r); // body size - Encoded bytes count. string size = IMAP_Utils.ReadString(r); if(string.IsNullOrEmpty(size)){ retVal.m_ContentSize = -1; } else{ retVal.m_ContentSize = Convert.ToInt64(size); } #endregion #region Text/xxx fields if(string.Equals("text",type,StringComparison.InvariantCultureIgnoreCase)){ // body encoded text lines count string linesCount = IMAP_Utils.ReadString(r); if(string.IsNullOrEmpty(size)){ retVal.m_LinesCount = -1; } else{ retVal.m_LinesCount = Convert.ToInt32(linesCount); } } #endregion #region Message/xxx fields if(string.Equals("message",type,StringComparison.InvariantCultureIgnoreCase)){ // envelope structure r.ReadToFirstChar(); // Read ENVELOPE if(r.StartsWith("(")){ string prams = r.ReadParenthesized(); } // NIL else{ IMAP_Utils.ReadString(r); } // body structure r.ReadToFirstChar(); // Read BODYSTRUCTURE if(r.StartsWith("(")){ string prams = r.ReadParenthesized(); } // NIL else{ IMAP_Utils.ReadString(r); } // body encoded text lines count string linesCount = IMAP_Utils.ReadString(r); if(string.IsNullOrEmpty(size)){ retVal.m_LinesCount = -1; } else{ retVal.m_LinesCount = Convert.ToInt32(linesCount); } } #endregion #region Extention fields // body MD5 - nstring retVal.m_Md5 = IMAP_Utils.ReadString(r); // body disposition - "(" string SP body-fld-param ")" / nil // body-fld-param = "(" string SP string *(SP string SP string) ")" / nil if(r.StartsWith("(")){ string disposition = IMAP_Utils.ReadString(r); if(!string.IsNullOrEmpty(disposition)){ retVal.m_pContentDisposition = new MIME_h_ContentDisposition(disposition); } r.ReadToFirstChar(); // Parse Content-Dispostion parameters. if(r.StartsWith("(")){ StringReader pramsReader = new StringReader(r.ReadParenthesized()); if(retVal.m_pContentDisposition != null){ while(pramsReader.Available > 0){ string name = IMAP_Utils.ReadString(pramsReader); if(string.IsNullOrEmpty(name)){ break; } string value = IMAP_Utils.ReadString(pramsReader); if(value == null){ value = ""; } retVal.m_pContentDisposition.Parameters[name] = MIME_Encoding_EncodedWord.DecodeTextS(value); } } } // NIL else{ IMAP_Utils.ReadString(r); } } // NIL else{ IMAP_Utils.ReadString(r); } // body language - nstring / "(" string *(SP string) ")" r.ReadToFirstChar(); if(r.StartsWith("(")){ retVal.m_Language = r.ReadParenthesized(); } else{ retVal.m_Language = IMAP_Utils.ReadString(r); } // body location - nstring retVal.m_Location = IMAP_Utils.ReadString(r); #endregion return retVal; }