/// <summary> /// Parses header field from the specified value. /// </summary> /// <param name="value">Header field value. Header field name must be included. For example: 'Return-Path: <[email protected]>'.</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_ReturnPath 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 + "'."); } Mail_h_ReturnPath retVal = new Mail_h_ReturnPath(null); MIME_Reader r = new MIME_Reader(name_value[1].Trim()); r.ToFirstChar(); // Return-Path missing <>, some server won't be honor RFC. if(!r.StartsWith("<")){ retVal.m_Address = r.ToEnd(); } else{ retVal.m_Address = r.ReadParenthesized(); } return retVal; }
/// <summary> /// Reads SMTP "Mailbox" from the specified MIME reader. /// </summary> /// <param name="reader">MIME reader.</param> /// <returns>Returns SMTP "Mailbox" or null if no SMTP mailbox available.</returns> /// <exception cref="ArgumentNullException">Is raised when <b>reader</b> is null reference.</exception> internal static string SMTP_Mailbox(MIME_Reader reader) { if(reader == null){ throw new ArgumentNullException("reader"); } // TODO: /* RFC 5321. Mailbox = Local-part "@" ( Domain / address-literal ) Local-part = Dot-string / Quoted-string ; MAY be case-sensitive Dot-string = Atom *("." Atom) */ StringBuilder retVal = new StringBuilder(); if(reader.Peek(true) == '\"'){ retVal.Append("\"" + reader.QuotedString() + "\""); } else{ retVal.Append(reader.DotAtom()); } if(reader.Peek(true) != '@'){ return null; } else{ // Eat "@". reader.Char(true); retVal.Append('@'); retVal.Append(reader.DotAtom()); } return retVal.ToString(); }
/// <summary> /// Parses <b>mailbox-list</b> from specified string value. /// </summary> /// <param name="value">The <b>mailbox-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>mailbox-list</b> value.</exception> public static Mail_t_MailboxList Parse(string value) { if(value == null){ throw new ArgumentNullException("value"); } MIME_Reader r = new MIME_Reader(value); Mail_t_MailboxList retVal = new Mail_t_MailboxList(); while(true){ string word = r.QuotedReadToDelimiter(new char[]{',','<'}); // We processed all data. if(string.IsNullOrEmpty(word) && r.Available == 0){ break; } // 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> /// Parses header field from the specified value. /// </summary> /// <param name="value">Header field value. Header field name must be included. For example: 'Sender: [email protected]'.</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_Mailbox 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 + "'."); } MIME_Reader r = new MIME_Reader(name_value[1].Trim()); string word = r.QuotedReadToDelimiter(new char[]{',','<',':'}); // Invalid value. if(word == null){ throw new ParseException("Invalid header field value '" + value + "'."); } // name-addr else if(r.Peek(true) == '<'){ Mail_h_Mailbox h = new Mail_h_Mailbox(name_value[0],new Mail_t_Mailbox(word != null ? MIME_Encoding_EncodedWord.DecodeS(TextUtils.UnQuoteString(word)) : null,r.ReadParenthesized())); h.m_ParseValue = value; return h; } // addr-spec else{ Mail_h_Mailbox h = new Mail_h_Mailbox(name_value[0],new Mail_t_Mailbox(null,word)); h.m_ParseValue = value; return h; } }
/// <summary> /// Parses RFC 2822 date-time from the specified value. /// </summary> /// <param name="value">RFC 2822 date-time string value.</param> /// <returns>Returns parsed datetime value.</returns> /// <exception cref="ArgumentNullException">Is raised when <b>value</b> is null.</exception> /// <exception cref="ArgumentException">Is raised when any of the arguments has invalid value.</exception> public static DateTime ParseRfc2822DateTime(string value) { if (value == null) { throw new ArgumentNullException(value); } /* RFC 2822 3. * date-time = [ day-of-week "," ] date FWS time [CFWS] * day-of-week = ([FWS] day-name) / obs-day-of-week * day-name = "Mon" / "Tue" / "Wed" / "Thu" / "Fri" / "Sat" / "Sun" * date = day month year * year = 4*DIGIT / obs-year * month = (FWS month-name FWS) / obs-month * month-name = "Jan" / "Feb" / "Mar" / "Apr" / "May" / "Jun" / "Jul" / "Aug" / "Sep" / "Oct" / "Nov" / "Dec" * day = ([FWS] 1*2DIGIT) / obs-day * time = time-of-day FWS zone * time-of-day = hour ":" minute [ ":" second ] * hour = 2DIGIT / obs-hour * minute = 2DIGIT / obs-minute * second = 2DIGIT / obs-second * zone = (( "+" / "-" ) 4DIGIT) / obs-zone * * The date and time-of-day SHOULD express local time. */ try{ MIME_Reader r = new MIME_Reader(value); string v = r.Atom(); // Skip optional [ day-of-week "," ] and read "day". if (v.Length == 3) { r.Char(true); v = r.Atom(); } int day = Convert.ToInt32(v); v = r.Atom().ToLower(); int month = 1; if (v == "jan") { month = 1; } else if (v == "feb") { month = 2; } else if (v == "mar") { month = 3; } else if (v == "apr") { month = 4; } else if (v == "may") { month = 5; } else if (v == "jun") { month = 6; } else if (v == "jul") { month = 7; } else if (v == "aug") { month = 8; } else if (v == "sep") { month = 9; } else if (v == "oct") { month = 10; } else if (v == "nov") { month = 11; } else if (v == "dec") { month = 12; } else { throw new ArgumentException("Invalid month-name value '" + value + "'."); } int year = Convert.ToInt32(r.Atom()); int hour = Convert.ToInt32(r.Atom()); r.Char(true); int minute = Convert.ToInt32(r.Atom()); int second = 0; // We have optional "second". if (r.Peek(true) == ':') { r.Char(true); second = Convert.ToInt32(r.Atom()); } int timeZoneMinutes = 0; v = r.Atom(); // Time zone missing. Not RFC syntax, but some servers will send such dates. if (v == null) { // Just consider time zone as 0(GMT). } // We have RFC 2822 date. For example: +2000. else if (v[0] == '+' || v[0] == '-') { if (v[0] == '+') { timeZoneMinutes = (Convert.ToInt32(v.Substring(1, 2)) * 60 + Convert.ToInt32(v.Substring(3, 2))); } else { timeZoneMinutes = -(Convert.ToInt32(v.Substring(1, 2)) * 60 + Convert.ToInt32(v.Substring(3, 2))); } } // We have RFC 822 date with abbrevated time zone name. For example: GMT. else { v = v.ToUpper(); #region time zones // Alpha Time Zone (military). if (v == "A") { timeZoneMinutes = ((01 * 60) + 00); } // Australian Central Daylight Time. else if (v == "ACDT") { timeZoneMinutes = ((10 * 60) + 30); } // Australian Central Standard Time. else if (v == "ACST") { timeZoneMinutes = ((09 * 60) + 30); } // Atlantic Daylight Time. else if (v == "ADT") { timeZoneMinutes = -((03 * 60) + 00); } // Australian Eastern Daylight Time. else if (v == "AEDT") { timeZoneMinutes = ((11 * 60) + 00); } // Australian Eastern Standard Time. else if (v == "AEST") { timeZoneMinutes = ((10 * 60) + 00); } // Alaska Daylight Time. else if (v == "AKDT") { timeZoneMinutes = -((08 * 60) + 00); } // Alaska Standard Time. else if (v == "AKST") { timeZoneMinutes = -((09 * 60) + 00); } // Atlantic Standard Time. else if (v == "AST") { timeZoneMinutes = -((04 * 60) + 00); } // Australian Western Daylight Time. else if (v == "AWDT") { timeZoneMinutes = ((09 * 60) + 00); } // Australian Western Standard Time. else if (v == "AWST") { timeZoneMinutes = ((08 * 60) + 00); } // Bravo Time Zone (millitary). else if (v == "B") { timeZoneMinutes = ((02 * 60) + 00); } // British Summer Time. else if (v == "BST") { timeZoneMinutes = ((01 * 60) + 00); } // Charlie Time Zone (millitary). else if (v == "C") { timeZoneMinutes = ((03 * 60) + 00); } // Central Daylight Time. else if (v == "CDT") { timeZoneMinutes = -((05 * 60) + 00); } // Central European Daylight Time. else if (v == "CEDT") { timeZoneMinutes = ((02 * 60) + 00); } // Central European Summer Time. else if (v == "CEST") { timeZoneMinutes = ((02 * 60) + 00); } // Central European Time. else if (v == "CET") { timeZoneMinutes = ((01 * 60) + 00); } // Central Standard Time. else if (v == "CST") { timeZoneMinutes = -((06 * 60) + 00); } // Christmas Island Time. else if (v == "CXT") { timeZoneMinutes = ((01 * 60) + 00); } // Delta Time Zone (military). else if (v == "D") { timeZoneMinutes = ((04 * 60) + 00); } // Echo Time Zone (military). else if (v == "E") { timeZoneMinutes = ((05 * 60) + 00); } // Eastern Daylight Time. else if (v == "EDT") { timeZoneMinutes = -((04 * 60) + 00); } // Eastern European Daylight Time. else if (v == "EEDT") { timeZoneMinutes = ((03 * 60) + 00); } // Eastern European Summer Time. else if (v == "EEST") { timeZoneMinutes = ((03 * 60) + 00); } // Eastern European Time. else if (v == "EET") { timeZoneMinutes = ((02 * 60) + 00); } // Eastern Standard Time. else if (v == "EST") { timeZoneMinutes = -((05 * 60) + 00); } // Foxtrot Time Zone (military). else if (v == "F") { timeZoneMinutes = (06 * 60 + 00); } // Golf Time Zone (military). else if (v == "G") { timeZoneMinutes = ((07 * 60) + 00); } // Greenwich Mean Time. else if (v == "GMT") { timeZoneMinutes = 0000; } // Hotel Time Zone (military). else if (v == "H") { timeZoneMinutes = ((08 * 60) + 00); } // India Time Zone (military). else if (v == "I") { timeZoneMinutes = ((09 * 60) + 00); } // Irish Summer Time. else if (v == "IST") { timeZoneMinutes = ((01 * 60) + 00); } // Kilo Time Zone (millitary). else if (v == "K") { timeZoneMinutes = ((10 * 60) + 00); } // Lima Time Zone (millitary). else if (v == "L") { timeZoneMinutes = ((11 * 60) + 00); } // Mike Time Zone (millitary). else if (v == "M") { timeZoneMinutes = ((12 * 60) + 00); } // Mountain Daylight Time. else if (v == "MDT") { timeZoneMinutes = -((06 * 60) + 00); } // Mountain Standard Time. else if (v == "MST") { timeZoneMinutes = -((07 * 60) + 00); } // November Time Zone (military). else if (v == "N") { timeZoneMinutes = -((01 * 60) + 00); } // Newfoundland Daylight Time. else if (v == "NDT") { timeZoneMinutes = -((02 * 60) + 30); } // Norfolk (Island) Time. else if (v == "NFT") { timeZoneMinutes = ((11 * 60) + 30); } // Newfoundland Standard Time. else if (v == "NST") { timeZoneMinutes = -((03 * 60) + 30); } // Oscar Time Zone (military). else if (v == "O") { timeZoneMinutes = -((02 * 60) + 00); } // Papa Time Zone (military). else if (v == "P") { timeZoneMinutes = -((03 * 60) + 00); } // Pacific Daylight Time. else if (v == "PDT") { timeZoneMinutes = -((07 * 60) + 00); } // Pacific Standard Time. else if (v == "PST") { timeZoneMinutes = -((08 * 60) + 00); } // Quebec Time Zone (military). else if (v == "Q") { timeZoneMinutes = -((04 * 60) + 00); } // Romeo Time Zone (military). else if (v == "R") { timeZoneMinutes = -((05 * 60) + 00); } // Sierra Time Zone (military). else if (v == "S") { timeZoneMinutes = -((06 * 60) + 00); } // Tango Time Zone (military). else if (v == "T") { timeZoneMinutes = -((07 * 60) + 00); } // Uniform Time Zone (military). else if (v == "") { timeZoneMinutes = -((08 * 60) + 00); } // Coordinated Universal Time. else if (v == "UTC") { timeZoneMinutes = 0000; } // Victor Time Zone (militray). else if (v == "V") { timeZoneMinutes = -((09 * 60) + 00); } // Whiskey Time Zone (military). else if (v == "W") { timeZoneMinutes = -((10 * 60) + 00); } // Western European Daylight Time. else if (v == "WEDT") { timeZoneMinutes = ((01 * 60) + 00); } // Western European Summer Time. else if (v == "WEST") { timeZoneMinutes = ((01 * 60) + 00); } // Western European Time. else if (v == "WET") { timeZoneMinutes = 0000; } // Western Standard Time. else if (v == "WST") { timeZoneMinutes = ((08 * 60) + 00); } // X-ray Time Zone (military). else if (v == "X") { timeZoneMinutes = -((11 * 60) + 00); } // Yankee Time Zone (military). else if (v == "Y") { timeZoneMinutes = -((12 * 60) + 00); } // Zulu Time Zone (military). else if (v == "Z") { timeZoneMinutes = 0000; } #endregion } // Convert time to UTC and then back to local. DateTime timeUTC = new DateTime(year, month, day, hour, minute, second).AddMinutes(-(timeZoneMinutes)); return(new DateTime(timeUTC.Year, timeUTC.Month, timeUTC.Day, timeUTC.Hour, timeUTC.Minute, timeUTC.Second, DateTimeKind.Utc).ToLocalTime()); } catch (Exception x) { string dymmy = x.Message; throw new ArgumentException("Argumnet 'value' value '" + value + "' is not valid RFC 822/2822 date-time string."); } }
/// <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 + "'."); } MIME_Reader r = new MIME_Reader(name_value[1]); /* 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],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){ break; } // 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.m_pAddresses.Add(group); } // name-addr else if(r.Peek(true) == '<'){ retVal.m_pAddresses.Add(new Mail_t_Mailbox(word != null ? MIME_Encoding_EncodedWord.DecodeS(TextUtils.UnQuoteString(word)) : null,r.ReadParenthesized())); } // addr-spec else{ retVal.m_pAddresses.Add(new Mail_t_Mailbox(null,word)); } // We have more addresses. if(r.Peek(true) == ','){ r.Char(false); } } retVal.m_ParseValue = value; retVal.m_pAddresses.AcceptChanges(); return retVal; }
/// <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 MIME_h_ContentType Parse(string value) { if(value == null){ throw new ArgumentNullException("value"); } // We should not have encoded words here, but some email clients do this, so encoded them if any. string valueDecoded = MIME_Encoding_EncodedWord.DecodeS(value); MIME_h_ContentType retVal = new MIME_h_ContentType(); string[] name_value = valueDecoded.Split(new char[]{':'},2); if(name_value.Length != 2){ throw new ParseException("Invalid Content-Type: header field value '" + value + "'."); } MIME_Reader r = new MIME_Reader(name_value[1]); string type = r.Token(); if(type == null){ throw new ParseException("Invalid Content-Type: header field value '" + value + "'."); } retVal.m_Type = type; if(r.Char(false) != '/'){ throw new ParseException("Invalid Content-Type: header field value '" + value + "'."); } string subtype = r.Token(); if(subtype == null){ throw new ParseException("Invalid Content-Type: header field value '" + value + "'."); } retVal.m_SubType = subtype; if(r.Available > 0){ retVal.m_pParameters.Parse(r); } retVal.m_ParseValue = value; retVal.m_IsModified = false; return retVal; }
/// <summary> /// Parses header field from the specified value. /// </summary> /// <param name="value">Header field value. Header field name must be included. For example: 'Sender: [email protected]'.</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_Received 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 + "'."); } Mail_h_Received retVal = new Mail_h_Received("a","b",DateTime.MinValue); MIME_Reader r = new MIME_Reader(name_value[1]); while(true){ string word = r.Word(); // We processed all data. if(word == null && r.Available == 0){ break; } // We have comment, just eat it. else if(r.StartsWith("(")){ r.ReadParenthesized(); } // We have date-time. else if(r.StartsWith(";")){ // Eat ';' r.Char(false); retVal.m_Time = MIME_Utils.ParseRfc2822DateTime(r.ToEnd()); } else{ // We have some unexpected char like: .,= ... . Just eat it. if(word == null){ r.Char(true); continue; } word = word.ToUpperInvariant(); if(word == "FROM"){ retVal.m_From = r.DotAtom(); r.ToFirstChar(); if(r.StartsWith("(")){ string[] parts = r.ReadParenthesized().Split(' '); if(parts.Length == 1){ if(Net_Utils.IsIPAddress(parts[0])){ retVal.m_pFrom_TcpInfo = new Mail_t_TcpInfo(IPAddress.Parse(parts[0]),null); } } else if(parts.Length == 2){ if(Net_Utils.IsIPAddress(parts[1])){ retVal.m_pFrom_TcpInfo = new Mail_t_TcpInfo(IPAddress.Parse(parts[1]),parts[0]); } } } } else if(word == "BY"){ retVal.m_By = r.DotAtom(); r.ToFirstChar(); if(r.StartsWith("(")){ string[] parts = r.ReadParenthesized().Split(' '); if(parts.Length == 1){ if(Net_Utils.IsIPAddress(parts[0])){ retVal.m_pBy_TcpInfo = new Mail_t_TcpInfo(IPAddress.Parse(parts[0]),null); } } else if(parts.Length == 2){ if(Net_Utils.IsIPAddress(parts[1])){ retVal.m_pBy_TcpInfo = new Mail_t_TcpInfo(IPAddress.Parse(parts[1]),parts[0]); } } } } else if(word == "VIA"){ retVal.m_Via = r.Word(); } else if(word == "WITH"){ retVal.m_With = r.Word(); } else if(word == "ID"){ // msg-id = [CFWS] "<" id-left "@" id-right ">" [CFWS] if(r.StartsWith("<")){ retVal.m_ID = r.ReadParenthesized(); } else{ retVal.m_ID = r.Atom(); } } else if(word == "FOR"){ r.ToFirstChar(); // path / angle-address if(r.StartsWith("<")){ retVal.m_For = r.ReadParenthesized(); } else{ string mailbox = Mail_Utils.SMTP_Mailbox(r); if(mailbox == null){ throw new ParseException("Invalid Received: For parameter value '" + r.ToEnd() + "'."); } retVal.m_For = mailbox; } } // Unknown, just eat value. else{ r.Word(); } } } retVal.m_ParseValue = value; return retVal; }
/// <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 MIME_h_ContentDisposition Parse(string value) { if(value == null){ throw new ArgumentNullException("value"); } // We should not have encoded words here, but some email clients do this, so encoded them if any. string valueDecoded = MIME_Encoding_EncodedWord.DecodeS(value); MIME_h_ContentDisposition retVal = new MIME_h_ContentDisposition(); string[] name_value = valueDecoded.Split(new char[]{':'},2); if(name_value.Length != 2){ throw new ParseException("Invalid Content-Type: header field value '" + value + "'."); } MIME_Reader r = new MIME_Reader(name_value[1]); string type = r.Token(); if(type == null){ throw new ParseException("Invalid Content-Disposition: header field value '" + value + "'."); } retVal.m_DispositionType = type.Trim(); retVal.m_pParameters.Parse(r); retVal.m_ParseValue = value; return retVal; }
/// <summary> /// Parses parameters from the specified reader. /// </summary> /// <param name="reader">MIME reader.</param> /// <exception cref="ArgumentNullException">Is raised when <b>reader</b> is null reference.</exception> public void Parse(MIME_Reader reader) { if(reader == null){ throw new ArgumentNullException("reader"); } /* RFC 2231. */ while(true){ // End os stream reached. if(reader.Peek(true) == -1){ break; } // Next parameter start, just eat that char. else if(reader.Peek(true) == ';'){ reader.Char(false); } else{ string name = reader.Token(); string value = ""; // Parameter value specified. if(reader.Peek(true) == '='){ reader.Char(false); string v = reader.Word(); // Normally value may not be null, but following case: paramName=EOS. if(v != null){ value = v; } } // RFC 2231 encoded/splitted parameter. if(name.IndexOf('*') > -1){ string[] name_x_no_x = name.Split('*'); name = name_x_no_x[0]; Encoding charset = Encoding.ASCII; StringBuilder valueBuffer = new StringBuilder(); // We must have charset'language'value. // Examples: // URL*=utf-8''test; // URL*0*=utf-8''"test"; if((name_x_no_x.Length == 2 && name_x_no_x[1] == "") || name_x_no_x.Length == 3){ string[] charset_language_value = value.Split('\''); charset = Encoding.GetEncoding(charset_language_value[0]); valueBuffer.Append(DecodeExtOctet(charset_language_value[2],charset)); } // No encoding, probably just splitted ASCII value. // Example: // URL*0="value1"; // URL*1="value2"; else{ valueBuffer.Append(value); } // Read while value continues. while(true){ // End os stream reached. if(reader.Peek(true) == -1){ break; } // Next parameter start, just eat that char. else if(reader.Peek(true) == ';'){ reader.Char(false); } else{ if(!reader.StartsWith(name + "*")){ break; } reader.Token(); // Parameter value specified. if(reader.Peek(true) == '='){ reader.Char(false); string v = reader.Word(); // Normally value may not be null, but following case: paramName=EOS. if(v != null){ valueBuffer.Append(DecodeExtOctet(v,charset)); } } } } this[name] = valueBuffer.ToString(); } // Regular parameter. else{ this[name] = value; } } } m_IsModified = false; }
/// <summary> /// Parses parameters from the specified reader. /// </summary> /// <param name="reader">MIME reader.</param> /// <exception cref="ArgumentNullException">Is raised when <b>reader</b> is null reference.</exception> public void Parse(MIME_Reader reader) { if (reader == null) { throw new ArgumentNullException("reader"); } /* RFC 2231. * Asterisks ("*") are reused to provide the indicator that language and * character set information is present and encoding is being used. A * single quote ("'") is used to delimit the character set and language * information at the beginning of the parameter value. Percent signs * ("%") are used as the encoding flag, which agrees with RFC 2047. * * Character set and language information may be combined with the * parameter continuation mechanism. For example: * * Content-Type: application/x-stuff * title*0*=us-ascii'en'This%20is%20even%20more%20 * title*1*=%2A%2A%2Afun%2A%2A%2A%20 * title*2="isn't it!" * * Note that: * * (1) Language and character set information only appear at * the beginning of a given parameter value. * * (2) Continuations do not provide a facility for using more * than one character set or language in the same * parameter value. * * (3) A value presented using multiple continuations may * contain a mixture of encoded and unencoded segments. * * (4) The first segment of a continuation MUST be encoded if * language and character set information are given. * * (5) If the first segment of a continued parameter value is * encoded the language and character set field delimiters * MUST be present even when the fields are left blank. */ KeyValueCollection <string, _ParameterBuilder> parameters = new KeyValueCollection <string, _ParameterBuilder>(); // Parse all parameter parts. string[] parameterParts = TextUtils.SplitQuotedString(reader.ToEnd(), ';'); foreach (string part in parameterParts) { if (string.IsNullOrEmpty(part)) { continue; } string[] name_value = part.Trim().Split(new char[] { '=' }, 2); string paramName = name_value[0].Trim(); string paramValue = null; if (name_value.Length == 2) { paramValue = TextUtils.UnQuoteString(MIME_Utils.UnfoldHeader(name_value[1].Trim())); } // Valueless parameter. //else{ string[] nameParts = paramName.Split('*'); int index = 0; bool encoded = nameParts.Length == 3; // Get multi value parameter index. if (nameParts.Length >= 2) { try{ index = Convert.ToInt32(nameParts[1]); } catch { } } // Single value parameter and we already have parameter with such name, skip it. if (nameParts.Length < 2 && parameters.ContainsKey(nameParts[0])) { continue; } // Parameter builder doesn't exist for the specified parameter, create it. if (!parameters.ContainsKey(nameParts[0])) { parameters.Add(nameParts[0], new _ParameterBuilder(nameParts[0])); } parameters[nameParts[0]].AddPart(index, encoded, paramValue); } // Build parameters from parts. foreach (_ParameterBuilder b in parameters) { m_pParameters.Add(b.Name, b.GetParamter()); } m_IsModified = false; }
/// <summary> /// Returns header field parameters as string. /// </summary> /// <param name="charset">Charset to use to encode 8-bit characters. Value null means parameters not encoded.</param> /// <returns>Returns header field parameters as string.</returns> public string ToString(Encoding charset) { /* RFC 2231. * If parameter conatins 8-bit byte, we need to encode parameter value * If parameter value length bigger than MIME maximum allowed line length, * we need split value. */ if (charset == null) { charset = Encoding.Default; } StringBuilder retVal = new StringBuilder(); foreach (MIME_h_Parameter parameter in this.ToArray()) { if (string.IsNullOrEmpty(parameter.Value)) { retVal.Append(";\r\n\t" + parameter.Name); } // We don't need to encode or split value. else if ((charset == null || Net_Utils.IsAscii(parameter.Value)) && parameter.Value.Length < 76) { retVal.Append(";\r\n\t" + parameter.Name + "=" + TextUtils.QuoteString(parameter.Value)); } // Use RFC 2047 to encode parameter. else if (m_EncodeRfc2047) { retVal.Append(";\r\n\t" + parameter.Name + "=" + TextUtils.QuoteString(MIME_Encoding_EncodedWord.EncodeS(MIME_EncodedWordEncoding.B, Encoding.UTF8, false, parameter.Value))); } // We need to RFC 2231 encode/split value. else { byte[] byteValue = charset.GetBytes(parameter.Value); List <string> values = new List <string>(); // Do encoding/splitting. int offset = 0; char[] valueBuff = new char[50]; foreach (byte b in byteValue) { // We need split value as RFC 2231 says. if (offset >= (50 - 3)) { values.Add(new string(valueBuff, 0, offset)); offset = 0; } // Normal char, we don't need to encode. if (MIME_Reader.IsAttributeChar((char)b)) { valueBuff[offset++] = (char)b; } // We need to encode byte as %X2. else { valueBuff[offset++] = '%'; valueBuff[offset++] = (b >> 4).ToString("X")[0]; valueBuff[offset++] = (b & 0xF).ToString("X")[0]; } } // Add pending buffer value. if (offset > 0) { values.Add(new string(valueBuff, 0, offset)); } for (int i = 0; i < values.Count; i++) { // Only fist value entry has charset and language info. if (charset != null && i == 0) { retVal.Append(";\r\n\t" + parameter.Name + "*" + i.ToString() + "*=" + charset.WebName + "''" + values[i]); } else { retVal.Append(";\r\n\t" + parameter.Name + "*" + i.ToString() + "*=" + values[i]); } } } } return(retVal.ToString()); }
/// <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 MIME_h_ContentDisposition Parse(string value) { if(value == null){ throw new ArgumentNullException("value"); } MIME_h_ContentDisposition retVal = new MIME_h_ContentDisposition(); string[] name_value = value.Split(new char[]{':'},2); if(name_value.Length != 2){ throw new ParseException("Invalid Content-Type: header field value '" + value + "'."); } MIME_Reader r = new MIME_Reader(name_value[1]); string type = r.Token(); if(type == null){ throw new ParseException("Invalid Content-Disposition: header field value '" + value + "'."); } retVal.m_DispositionType = type; retVal.m_pParameters.Parse(r); retVal.m_ParseValue = value; return retVal; }
/// <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 MIME_h_ContentType Parse(string value) { if(value == null){ throw new ArgumentNullException("value"); } MIME_h_ContentType retVal = new MIME_h_ContentType(); string[] name_value = value.Split(new char[]{':'},2); if(name_value.Length != 2){ throw new ParseException("Invalid Content-Type: header field value '" + value + "'."); } MIME_Reader r = new MIME_Reader(name_value[1]); string type = r.Token(); if(type == null){ throw new ParseException("Invalid Content-Type: header field value '" + value + "'."); } retVal.m_Type = type; if(r.Char(false) != '/'){ throw new ParseException("Invalid Content-Type: header field value '" + value + "'."); } string subtype = r.Token(); if(subtype == null){ throw new ParseException("Invalid Content-Type: header field value '" + value + "'."); } retVal.m_SubType = subtype; retVal.m_pParameters.Parse(r); retVal.m_ParseValue = value; retVal.m_IsModified = false; return retVal; }
/// <summary> /// Parses RFC 2822 date-time from the specified value. /// </summary> /// <param name="value">RFC 2822 date-time string value.</param> /// <returns>Returns parsed datetime value.</returns> /// <exception cref="ArgumentNullException">Is raised when <b>value</b> is null.</exception> /// <exception cref="ArgumentException">Is raised when any of the arguments has invalid value.</exception> public static DateTime ParseRfc2822DateTime(string value) { if(value == null){ throw new ArgumentNullException(value); } /* RFC 2822 3. * date-time = [ day-of-week "," ] date FWS time [CFWS] * day-of-week = ([FWS] day-name) / obs-day-of-week * day-name = "Mon" / "Tue" / "Wed" / "Thu" / "Fri" / "Sat" / "Sun" * date = day month year * year = 4*DIGIT / obs-year * month = (FWS month-name FWS) / obs-month * month-name = "Jan" / "Feb" / "Mar" / "Apr" / "May" / "Jun" / "Jul" / "Aug" / "Sep" / "Oct" / "Nov" / "Dec" * day = ([FWS] 1*2DIGIT) / obs-day * time = time-of-day FWS zone * time-of-day = hour ":" minute [ ":" second ] * hour = 2DIGIT / obs-hour * minute = 2DIGIT / obs-minute * second = 2DIGIT / obs-second * zone = (( "+" / "-" ) 4DIGIT) / obs-zone * * The date and time-of-day SHOULD express local time. */ try{ MIME_Reader r = new MIME_Reader(value); string v = r.Atom(); // Skip optional [ day-of-week "," ] and read "day". if(v.Length == 3){ r.Char(true); v = r.Atom(); } int day = Convert.ToInt32(v); v = r.Atom().ToLower(); int month = 1; if(v == "jan"){ month = 1; } else if(v == "feb"){ month = 2; } else if(v == "mar"){ month = 3; } else if(v == "apr"){ month = 4; } else if(v == "may"){ month = 5; } else if(v == "jun"){ month = 6; } else if(v == "jul"){ month = 7; } else if(v == "aug"){ month = 8; } else if(v == "sep"){ month = 9; } else if(v == "oct"){ month = 10; } else if(v == "nov"){ month = 11; } else if(v == "dec"){ month = 12; } else{ throw new ArgumentException("Invalid month-name value '" + value + "'."); } int year = Convert.ToInt32(r.Atom()); int hour = Convert.ToInt32(r.Atom()); r.Char(true); int minute = Convert.ToInt32(r.Atom()); int second = 0; // We have optional "second". if(r.Peek(true) == ':'){ r.Char(true); second = Convert.ToInt32(r.Atom()); } int timeZoneMinutes = 0; v = r.Atom(); // We have RFC 2822 date. For example: +2000. if(v[0] == '+' || v[0] == '-'){ if(v[0] == '+'){ timeZoneMinutes = (Convert.ToInt32(v.Substring(1,2)) * 60 + Convert.ToInt32(v.Substring(3,2))); } else{ timeZoneMinutes = -(Convert.ToInt32(v.Substring(1,2)) * 60 + Convert.ToInt32(v.Substring(3,2))); } } // We have RFC 822 date with abbrevated time zone name. For example: GMT. else{ v = v.ToUpper(); #region time zones // Alpha Time Zone (military). if(v == "A"){ timeZoneMinutes = ((01 * 60) + 00); } // Australian Central Daylight Time. else if(v == "ACDT"){ timeZoneMinutes = ((10 * 60) + 30); } // Australian Central Standard Time. else if(v == "ACST"){ timeZoneMinutes = ((09 * 60) + 30); } // Atlantic Daylight Time. else if(v == "ADT"){ timeZoneMinutes = -((03 * 60) + 00); } // Australian Eastern Daylight Time. else if(v == "AEDT"){ timeZoneMinutes = ((11 * 60) + 00); } // Australian Eastern Standard Time. else if(v == "AEST"){ timeZoneMinutes = ((10 * 60) + 00); } // Alaska Daylight Time. else if(v == "AKDT"){ timeZoneMinutes = -((08 * 60) + 00); } // Alaska Standard Time. else if(v == "AKST"){ timeZoneMinutes = -((09 * 60) + 00); } // Atlantic Standard Time. else if(v == "AST"){ timeZoneMinutes = -((04 * 60) + 00); } // Australian Western Daylight Time. else if(v == "AWDT"){ timeZoneMinutes = ((09 * 60) + 00); } // Australian Western Standard Time. else if(v == "AWST"){ timeZoneMinutes = ((08 * 60) + 00); } // Bravo Time Zone (millitary). else if(v == "B"){ timeZoneMinutes = ((02 * 60) + 00); } // British Summer Time. else if(v == "BST"){ timeZoneMinutes = ((01 * 60) + 00); } // Charlie Time Zone (millitary). else if(v == "C"){ timeZoneMinutes = ((03 * 60) + 00); } // Central Daylight Time. else if(v == "CDT"){ timeZoneMinutes = -((05 * 60) + 00); } // Central European Daylight Time. else if(v == "CEDT"){ timeZoneMinutes = ((02 * 60) + 00); } // Central European Summer Time. else if(v == "CEST"){ timeZoneMinutes = ((02 * 60) + 00); } // Central European Time. else if(v == "CET"){ timeZoneMinutes = ((01 * 60) + 00); } // Central Standard Time. else if(v == "CST"){ timeZoneMinutes = -((06 * 60) + 00); } // Christmas Island Time. else if(v == "CXT"){ timeZoneMinutes = ((01 * 60) + 00); } // Delta Time Zone (military). else if(v == "D"){ timeZoneMinutes = ((04 * 60) + 00); } // Echo Time Zone (military). else if(v == "E"){ timeZoneMinutes = ((05 * 60) + 00); } // Eastern Daylight Time. else if(v == "EDT"){ timeZoneMinutes = -((04 * 60) + 00); } // Eastern European Daylight Time. else if(v == "EEDT"){ timeZoneMinutes = ((03 * 60) + 00); } // Eastern European Summer Time. else if(v == "EEST"){ timeZoneMinutes = ((03 * 60) + 00); } // Eastern European Time. else if(v == "EET"){ timeZoneMinutes = ((02 * 60) + 00); } // Eastern Standard Time. else if(v == "EST"){ timeZoneMinutes = -((05 * 60) + 00); } // Foxtrot Time Zone (military). else if(v == "F"){ timeZoneMinutes = (06 * 60 + 00); } // Golf Time Zone (military). else if(v == "G"){ timeZoneMinutes = ((07 * 60) + 00); } // Greenwich Mean Time. else if(v == "GMT"){ timeZoneMinutes = 0000; } // Hotel Time Zone (military). else if(v == "H"){ timeZoneMinutes = ((08 * 60) + 00); } // India Time Zone (military). else if(v == "I"){ timeZoneMinutes = ((09 * 60) + 00); } // Irish Summer Time. else if(v == "IST"){ timeZoneMinutes = ((01 * 60) + 00); } // Kilo Time Zone (millitary). else if(v == "K"){ timeZoneMinutes = ((10 * 60) + 00); } // Lima Time Zone (millitary). else if(v == "L"){ timeZoneMinutes = ((11 * 60) + 00); } // Mike Time Zone (millitary). else if(v == "M"){ timeZoneMinutes = ((12 * 60) + 00); } // Mountain Daylight Time. else if(v == "MDT"){ timeZoneMinutes = -((06 * 60) + 00); } // Mountain Standard Time. else if(v == "MST"){ timeZoneMinutes = -((07 * 60) + 00); } // November Time Zone (military). else if(v == "N"){ timeZoneMinutes = -((01 * 60) + 00); } // Newfoundland Daylight Time. else if(v == "NDT"){ timeZoneMinutes = -((02 * 60) + 30); } // Norfolk (Island) Time. else if(v == "NFT"){ timeZoneMinutes = ((11 * 60) + 30); } // Newfoundland Standard Time. else if(v == "NST"){ timeZoneMinutes = -((03 * 60) + 30); } // Oscar Time Zone (military). else if(v == "O"){ timeZoneMinutes = -((02 * 60) + 00); } // Papa Time Zone (military). else if(v == "P"){ timeZoneMinutes = -((03 * 60) + 00); } // Pacific Daylight Time. else if(v == "PDT"){ timeZoneMinutes = -((07 * 60) + 00); } // Pacific Standard Time. else if(v == "PST"){ timeZoneMinutes = -((08 * 60) + 00); } // Quebec Time Zone (military). else if(v == "Q"){ timeZoneMinutes = -((04 * 60) + 00); } // Romeo Time Zone (military). else if(v == "R"){ timeZoneMinutes = -((05 * 60) + 00); } // Sierra Time Zone (military). else if(v == "S"){ timeZoneMinutes = -((06 * 60) + 00); } // Tango Time Zone (military). else if(v == "T"){ timeZoneMinutes = -((07 * 60) + 00); } // Uniform Time Zone (military). else if(v == ""){ timeZoneMinutes = -((08 * 60) + 00); } // Coordinated Universal Time. else if(v == "UTC"){ timeZoneMinutes = 0000; } // Victor Time Zone (militray). else if(v == "V"){ timeZoneMinutes = -((09 * 60) + 00); } // Whiskey Time Zone (military). else if(v == "W"){ timeZoneMinutes = -((10 * 60) + 00); } // Western European Daylight Time. else if(v == "WEDT"){ timeZoneMinutes = ((01 * 60) + 00); } // Western European Summer Time. else if(v == "WEST"){ timeZoneMinutes = ((01 * 60) + 00); } // Western European Time. else if(v == "WET"){ timeZoneMinutes = 0000; } // Western Standard Time. else if(v == "WST"){ timeZoneMinutes = ((08 * 60) + 00); } // X-ray Time Zone (military). else if(v == "X"){ timeZoneMinutes = -((11 * 60) + 00); } // Yankee Time Zone (military). else if(v == "Y"){ timeZoneMinutes = -((12 * 60) + 00); } // Zulu Time Zone (military). else if(v == "Z"){ timeZoneMinutes = 0000; } #endregion } // Convert time to UTC and then back to local. DateTime timeUTC = new DateTime(year,month,day,hour,minute,second).AddMinutes(-(timeZoneMinutes)); return new DateTime(timeUTC.Year,timeUTC.Month,timeUTC.Day,timeUTC.Hour,timeUTC.Minute,timeUTC.Second,DateTimeKind.Utc).ToLocalTime(); } catch(Exception x){ string dymmy = x.Message; throw new ArgumentException("Argumnet 'value' value '" + value + "' is not valid RFC 822/2822 date-time string."); } }
/// <summary> /// Parses parameters from the specified reader. /// </summary> /// <param name="reader">MIME reader.</param> /// <exception cref="ArgumentNullException">Is raised when <b>reader</b> is null reference.</exception> public void Parse(MIME_Reader reader) { if(reader == null){ throw new ArgumentNullException("reader"); } /* RFC 2231. Asterisks ("*") are reused to provide the indicator that language and character set information is present and encoding is being used. A single quote ("'") is used to delimit the character set and language information at the beginning of the parameter value. Percent signs ("%") are used as the encoding flag, which agrees with RFC 2047. Character set and language information may be combined with the parameter continuation mechanism. For example: Content-Type: application/x-stuff title*0*=us-ascii'en'This%20is%20even%20more%20 title*1*=%2A%2A%2Afun%2A%2A%2A%20 title*2="isn't it!" Note that: (1) Language and character set information only appear at the beginning of a given parameter value. (2) Continuations do not provide a facility for using more than one character set or language in the same parameter value. (3) A value presented using multiple continuations may contain a mixture of encoded and unencoded segments. (4) The first segment of a continuation MUST be encoded if language and character set information are given. (5) If the first segment of a continued parameter value is encoded the language and character set field delimiters MUST be present even when the fields are left blank. */ KeyValueCollection<string,_ParameterBuilder> parameters = new KeyValueCollection<string,_ParameterBuilder>(); // Parse all parameter parts. string[] parameterParts = TextUtils.SplitQuotedString(reader.ToEnd(),';'); foreach(string part in parameterParts){ if(string.IsNullOrEmpty(part)){ continue; } string[] name_value = part.Trim().Split(new char[]{'='},2); string paramName = name_value[0].Trim(); string paramValue = null; if(name_value.Length == 2){ paramValue = TextUtils.UnQuoteString(name_value[1].Trim()); } // Valueless parameter. //else{ string[] nameParts = paramName.Split('*'); int index = 0; bool encoded = nameParts.Length == 3; // Get multi value parameter index. if(nameParts.Length >= 2){ try{ index = Convert.ToInt32(nameParts[1]); } catch{ } } // Single value parameter and we already have parameter with such name, skip it. if(nameParts.Length < 2 && parameters.ContainsKey(nameParts[0])){ continue; } // Parameter builder doesn't exist for the specified parameter, create it. if(!parameters.ContainsKey(nameParts[0])){ parameters.Add(nameParts[0],new _ParameterBuilder(nameParts[0])); } parameters[nameParts[0]].AddPart(index,encoded,paramValue); } // Build parameters from parts. foreach(_ParameterBuilder b in parameters){ m_pParameters.Add(b.Name,b.GetParamter()); } m_IsModified = false; }
/// <summary> /// Parses parameters from the specified reader. /// </summary> /// <param name="reader">MIME reader.</param> /// <exception cref="ArgumentNullException">Is raised when <b>reader</b> is null reference.</exception> public void Parse(MIME_Reader reader) { if (reader == null) { throw new ArgumentNullException("reader"); } /* RFC 2231. */ while (true) { // End os stream reached. if (reader.Peek(true) == -1) { break; } // Next parameter start, just eat that char. else if (reader.Peek(true) == ';') { reader.Char(false); } else { string name = reader.Token(); string value = ""; // Parameter value specified. if (reader.Peek(true) == '=') { reader.Char(false); string v = reader.Word(); // Normally value may not be null, but following case: paramName=EOS. if (v != null) { value = v; } } // RFC 2231 encoded/splitted parameter. if (name.IndexOf('*') > -1) { string[] name_x_no_x = name.Split('*'); name = name_x_no_x[0]; Encoding charset = Encoding.ASCII; StringBuilder valueBuffer = new StringBuilder(); // We must have charset'language'value. // Examples: // URL*=utf-8''test; // URL*0*=utf-8''"test"; if ((name_x_no_x.Length == 2 && name_x_no_x[1] == "") || name_x_no_x.Length == 3) { string[] charset_language_value = value.Split('\''); charset = Encoding.GetEncoding(charset_language_value[0]); valueBuffer.Append(DecodeExtOctet(charset_language_value[2], charset)); } // No encoding, probably just splitted ASCII value. // Example: // URL*0="value1"; // URL*1="value2"; else { valueBuffer.Append(value); } // Read while value continues. while (true) { // End os stream reached. if (reader.Peek(true) == -1) { break; } // Next parameter start, just eat that char. else if (reader.Peek(true) == ';') { reader.Char(false); } else { if (!reader.StartsWith(name + "*")) { break; } reader.Token(); // Parameter value specified. if (reader.Peek(true) == '=') { reader.Char(false); string v = reader.Word(); // Normally value may not be null, but following case: paramName=EOS. if (v != null) { valueBuffer.Append(DecodeExtOctet(v, charset)); } } } } this[name] = valueBuffer.ToString(); } // Regular parameter. else { this[name] = value; } } } m_IsModified = false; }