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