/// <summary> /// Converts the TNEF content into a <see cref="MimeKit.MimeMessage"/>. /// </summary> /// <remarks> /// TNEF data often contains properties that map to <see cref="MimeKit.MimeMessage"/> /// headers. TNEF data also often contains file attachments which will be /// mapped to MIME parts. /// </remarks> /// <returns>A message representing the TNEF data in MIME format.</returns> /// <exception cref="System.InvalidOperationException"> /// The <see cref="MimeKit.MimePart.Content"/> property is <c>null</c>. /// </exception> public MimeMessage ConvertToMessage() { if (Content == null) { throw new InvalidOperationException("Cannot parse null TNEF data."); } int codepage = 0; if (!string.IsNullOrEmpty(ContentType.Charset)) { if ((codepage = CharsetUtils.GetCodePage(ContentType.Charset)) == -1) { codepage = 0; } } using (var reader = new TnefReader(Content.Open(), codepage, TnefComplianceMode.Loose)) { return(ExtractTnefMessage(reader)); } }
public void TestArgumentExceptions() { var buffer = new byte[10]; Assert.Throws <ArgumentNullException> (() => CharsetUtils.ConvertToUnicode((ParserOptions)null, buffer, 0, buffer.Length)); Assert.Throws <ArgumentNullException> (() => CharsetUtils.ConvertToUnicode(ParserOptions.Default, null, 0, 0)); Assert.Throws <ArgumentOutOfRangeException> (() => CharsetUtils.ConvertToUnicode(ParserOptions.Default, buffer, -1, 0)); Assert.Throws <ArgumentOutOfRangeException> (() => CharsetUtils.ConvertToUnicode(ParserOptions.Default, buffer, 0, -1)); Assert.Throws <ArgumentNullException> (() => CharsetUtils.ConvertToUnicode((Encoding)null, buffer, 0, buffer.Length)); Assert.Throws <ArgumentNullException> (() => CharsetUtils.ConvertToUnicode(Encoding.UTF8, null, 0, 0)); Assert.Throws <ArgumentOutOfRangeException> (() => CharsetUtils.ConvertToUnicode(Encoding.UTF8, buffer, -1, 0)); Assert.Throws <ArgumentOutOfRangeException> (() => CharsetUtils.ConvertToUnicode(Encoding.UTF8, buffer, 0, -1)); Assert.Throws <ArgumentNullException> (() => CharsetUtils.GetCodePage(null)); Assert.Throws <ArgumentNullException> (() => CharsetUtils.GetEncoding(null)); Assert.Throws <ArgumentNullException> (() => CharsetUtils.GetEncoding(null, "fallback")); Assert.Throws <ArgumentNullException> (() => CharsetUtils.GetEncoding("charset", null)); Assert.Throws <ArgumentOutOfRangeException> (() => CharsetUtils.GetEncoding(-1, "fallback")); Assert.Throws <ArgumentNullException> (() => CharsetUtils.GetEncoding(28591, null)); }
/// <summary> /// Initializes a new instance of the <see cref="Parameter"/> class. /// </summary> /// <remarks> /// Creates a new parameter with the specified name and value. /// </remarks> /// <param name="charset">The character encoding.</param> /// <param name="name">The parameter name.</param> /// <param name="value">The parameter value.</param> /// <exception cref="System.ArgumentNullException"> /// <para><paramref name="charset"/> is <c>null</c>.</para> /// <para>-or-</para> /// <para><paramref name="name"/> is <c>null</c>.</para> /// <para>-or-</para> /// <para><paramref name="value"/> is <c>null</c>.</para> /// </exception> /// <exception cref="System.ArgumentException"> /// <para><paramref name="charset"/> cannot be empty.</para> /// <para>-or-</para> /// <para><paramref name="name"/> contains illegal characters.</para> /// </exception> /// <exception cref="System.NotSupportedException"> /// <paramref name="charset"/> is not supported. /// </exception> public Parameter(string charset, string name, string value) { if (charset == null) { throw new ArgumentNullException("charset"); } if (charset.Length == 0) { throw new ArgumentException("The charset name cannot be empty.", "charset"); } if (name == null) { throw new ArgumentNullException("name"); } if (name.Length == 0) { throw new ArgumentException("Parameter names are not allowed to be empty.", "name"); } for (int i = 0; i < name.Length; i++) { if (name[i] > 127 || !IsAttr((byte)name[i])) { throw new ArgumentException("Illegal characters in parameter name.", "name"); } } if (value == null) { throw new ArgumentNullException("value"); } Encoding = CharsetUtils.GetEncoding(charset); Value = value; Name = name; }
public void TestParseCodePage() { Assert.AreEqual(1201, CharsetUtils.ParseCodePage("iso10646")); Assert.AreEqual(1201, CharsetUtils.ParseCodePage("iso-10646")); Assert.AreEqual(1201, CharsetUtils.ParseCodePage("iso10646-1")); Assert.AreEqual(1201, CharsetUtils.ParseCodePage("iso-10646-1")); Assert.AreEqual(28591, CharsetUtils.ParseCodePage("iso8859-1")); Assert.AreEqual(28591, CharsetUtils.ParseCodePage("iso8859_1")); Assert.AreEqual(28591, CharsetUtils.ParseCodePage("iso-8859-1")); Assert.AreEqual(28591, CharsetUtils.ParseCodePage("iso_8859_1")); Assert.AreEqual(28591, CharsetUtils.ParseCodePage("latin1")); Assert.AreEqual(50220, CharsetUtils.ParseCodePage("iso2022-jp")); Assert.AreEqual(50220, CharsetUtils.ParseCodePage("iso-2022-jp")); Assert.AreEqual(50220, CharsetUtils.ParseCodePage("iso_2022_jp")); Assert.AreEqual(50225, CharsetUtils.ParseCodePage("iso2022-kr")); Assert.AreEqual(50225, CharsetUtils.ParseCodePage("iso-2022-kr")); Assert.AreEqual(50225, CharsetUtils.ParseCodePage("iso_2022_kr")); Assert.AreEqual(1252, CharsetUtils.ParseCodePage("windows-cp1252")); Assert.AreEqual(1252, CharsetUtils.ParseCodePage("windows-1252")); Assert.AreEqual(1252, CharsetUtils.ParseCodePage("cp-1252")); Assert.AreEqual(1252, CharsetUtils.ParseCodePage("cp1252")); Assert.AreEqual(-1, CharsetUtils.ParseCodePage("cp")); Assert.AreEqual(-1, CharsetUtils.ParseCodePage("iso")); Assert.AreEqual(-1, CharsetUtils.ParseCodePage("ibm")); Assert.AreEqual(-1, CharsetUtils.ParseCodePage("windows")); Assert.AreEqual(-1, CharsetUtils.ParseCodePage("windows-")); Assert.AreEqual(-1, CharsetUtils.ParseCodePage("iso-8859")); Assert.AreEqual(-1, CharsetUtils.ParseCodePage("iso-BB59")); Assert.AreEqual(-1, CharsetUtils.ParseCodePage("iso-8859-")); Assert.AreEqual(-1, CharsetUtils.ParseCodePage("iso-8859-A")); Assert.AreEqual(-1, CharsetUtils.ParseCodePage("iso-2022-US")); Assert.AreEqual(-1, CharsetUtils.ParseCodePage("iso-4999-1")); Assert.AreEqual(-1, CharsetUtils.ParseCodePage("iso-abcd-1")); }
/// <summary> /// Get a text preview of a stream of text in the specified charset. /// </summary> /// <remarks> /// Get a text preview of a stream of text in the specified charset. /// </remarks> /// <param name="stream">The original text stream.</param> /// <param name="charset">The charset encoding of the stream.</param> /// <returns>A string representing a shortened preview of the original text.</returns> /// <exception cref="System.ArgumentNullException"> /// <para><paramref name="stream"/> is <c>null</c>.</para> /// <para>-or-</para> /// <para><paramref name="charset"/> is <c>null</c>.</para> /// </exception> public virtual string GetPreviewText(Stream stream, string charset) { if (stream == null) { throw new ArgumentNullException(nameof(stream)); } if (charset == null) { throw new ArgumentNullException(nameof(charset)); } Encoding encoding; try { encoding = CharsetUtils.GetEncoding(charset); } catch (NotSupportedException) { encoding = CharsetUtils.UTF8; } return(GetPreviewText(stream, encoding)); }
public void TestGetMimeCharset () { Encoding encoding; Assert.AreEqual ("iso-8859-1", CharsetUtils.GetMimeCharset ("latin1")); Assert.AreEqual ("iso-8859-1", CharsetUtils.GetMimeCharset (CharsetUtils.Latin1)); Assert.AreEqual ("gibberish", CharsetUtils.GetMimeCharset ("gibberish")); Assert.AreEqual ("iso-2022-jp", CharsetUtils.GetMimeCharset (Encoding.GetEncoding (932))); Assert.AreEqual ("iso-2022-jp", CharsetUtils.GetMimeCharset (Encoding.GetEncoding (50220))); Assert.AreEqual ("iso-2022-jp", CharsetUtils.GetMimeCharset (Encoding.GetEncoding (50221))); try { encoding = Encoding.GetEncoding (50225); } catch (NotSupportedException) { encoding = null; } if (encoding != null) Assert.AreEqual ("euc-kr", CharsetUtils.GetMimeCharset (encoding)); Assert.AreEqual ("euc-kr", CharsetUtils.GetMimeCharset (Encoding.GetEncoding (949))); }
internal void Encode(FormatOptions options, StringBuilder builder, ref int lineLength, Encoding encoding) { string quoted; var method = GetEncodeMethod(options, Name, Value, out quoted); if (method == EncodeMethod.None) { quoted = Value; } if (method != EncodeMethod.Rfc2184) { if (lineLength + 2 + Name.Length + 1 + quoted.Length >= options.MaxLineLength) { builder.Append(";\n\t"); lineLength = 1; } else { builder.Append("; "); lineLength += 2; } lineLength += Name.Length + 1 + quoted.Length; builder.Append(Name); builder.Append('='); builder.Append(quoted); return; } int maxLength = options.MaxLineLength - (Name.Length + 6); var bestEncoding = GetBestEncoding(Value, encoding); var charset = CharsetUtils.GetMimeCharset(bestEncoding); var bytes = new byte[Math.Max(maxLength, 6)]; var hexbuf = new byte[bytes.Length * 3 + 3]; var encoder = bestEncoding.GetEncoder(); var chars = Value.ToCharArray(); var hex = new HexEncoder(); int index = 0, i = 0; string value, id; bool encoded; int length; do { encoded = GetNextValue(charset, encoder, hex, chars, ref index, ref bytes, ref hexbuf, maxLength, out value); length = Name.Length + (encoded ? 1 : 0) + 1 + value.Length; if (i == 0 && index == chars.Length) { if (lineLength + 2 + length >= options.MaxLineLength) { builder.Append(";\n\t"); lineLength = 1; } else { builder.Append("; "); lineLength += 2; } builder.Append(Name); if (encoded) { builder.Append('*'); } builder.Append('='); builder.Append(value); lineLength += length; return; } builder.Append(";\n\t"); lineLength = 1; id = i.ToString(); length += id.Length + 1; builder.Append(Name); builder.Append('*'); builder.Append(id); if (encoded) { builder.Append('*'); } builder.Append('='); builder.Append(value); lineLength += length; i++; } while (index < chars.Length); }
void EncodeRfc2231(FormatOptions options, StringBuilder builder, ref int lineLength, Encoding headerEncoding) { var bestEncoding = options.International ? CharsetUtils.UTF8 : GetBestEncoding(Value, encoding ?? headerEncoding); int maxLength = options.MaxLineLength - (Name.Length + 6); var charset = CharsetUtils.GetMimeCharset(bestEncoding); var encoder = (Encoder)bestEncoding.GetEncoder(); var bytes = new byte[Math.Max(maxLength, 6)]; var hexbuf = new byte[bytes.Length * 3 + 3]; var chars = Value.ToCharArray(); var hex = new HexEncoder(); int index = 0, i = 0; string value, id; bool encoded; int length; do { builder.Append(';'); lineLength++; encoded = Rfc2231GetNextValue(options, charset, encoder, hex, chars, ref index, ref bytes, ref hexbuf, maxLength, out value); length = Name.Length + (encoded ? 1 : 0) + 1 + value.Length; if (i == 0 && index == chars.Length) { if (lineLength + 1 + length >= options.MaxLineLength) { builder.Append(options.NewLine); builder.Append('\t'); lineLength = 1; } else { builder.Append(' '); lineLength++; } builder.Append(Name); if (encoded) { builder.Append('*'); } builder.Append('='); builder.Append(value); lineLength += length; return; } builder.Append(options.NewLine); builder.Append('\t'); lineLength = 1; id = i.ToString(); length += id.Length + 1; builder.Append(Name); builder.Append('*'); builder.Append(id); if (encoded) { builder.Append('*'); } builder.Append('='); builder.Append(value); lineLength += length; i++; } while (index < chars.Length); }
internal static bool TryParse(ParserOptions options, byte[] text, ref int index, int endIndex, bool throwOnError, out ParameterList paramList) { var rfc2231 = new Dictionary <string, List <NameValuePair> > (MimeUtils.OrdinalIgnoreCase); var @params = new List <NameValuePair> (); List <NameValuePair> parts; paramList = null; do { if (!ParseUtils.SkipCommentsAndWhiteSpace(text, ref index, endIndex, throwOnError)) { return(false); } if (index >= endIndex) { break; } // handle empty parameter name/value pairs if (text[index] == (byte)';') { index++; continue; } NameValuePair pair; if (!TryParseNameValuePair(options, text, ref index, endIndex, throwOnError, out pair)) { return(false); } if (!ParseUtils.SkipCommentsAndWhiteSpace(text, ref index, endIndex, throwOnError)) { return(false); } if (pair.Id.HasValue) { if (rfc2231.TryGetValue(pair.Name, out parts)) { parts.Add(pair); } else { parts = new List <NameValuePair> (); rfc2231[pair.Name] = parts; @params.Add(pair); parts.Add(pair); } } else { @params.Add(pair); } if (index >= endIndex) { break; } if (text[index] != (byte)';') { if (throwOnError) { throw new ParseException(string.Format("Invalid parameter list token at offset {0}", index), index, index); } return(false); } index++; } while (true); paramList = new ParameterList(); var hex = new HexDecoder(); foreach (var param in @params) { var method = ParameterEncodingMethod.Default; int startIndex = param.ValueStart; int length = param.ValueLength; var buffer = param.Value; Encoding encoding = null; Decoder decoder = null; Parameter parameter; string value; if (param.Id.HasValue) { method = ParameterEncodingMethod.Rfc2231; parts = rfc2231[param.Name]; parts.Sort(); value = string.Empty; for (int i = 0; i < parts.Count; i++) { startIndex = parts[i].ValueStart; length = parts[i].ValueLength; buffer = parts[i].Value; if (parts[i].Encoded) { bool flush = i + 1 >= parts.Count || !parts[i + 1].Encoded; Encoding charset; // Note: Some mail clients mistakenly quote encoded parameter values when they shouldn't if (length >= 2 && buffer[startIndex] == (byte)'"' && buffer[startIndex + length - 1] == (byte)'"') { startIndex++; length -= 2; } value += DecodeRfc2231(out charset, ref decoder, hex, buffer, startIndex, length, flush); encoding = encoding ?? charset; } else if (length >= 2 && buffer[startIndex] == (byte)'"') { var quoted = CharsetUtils.ConvertToUnicode(options, buffer, startIndex, length); value += MimeUtils.Unquote(quoted); hex.Reset(); } else if (length > 0) { value += CharsetUtils.ConvertToUnicode(options, buffer, startIndex, length); hex.Reset(); } } hex.Reset(); } else if (param.Encoded) { // Note: param value is not supposed to be quoted, but issue #239 illustrates // that this can happen in the wild. Hopefully we will not need to worry // about quoted-pairs. if (length >= 2 && buffer[startIndex] == (byte)'"') { if (buffer[startIndex + length - 1] == (byte)'"') { length--; } startIndex++; length--; } value = DecodeRfc2231(out encoding, ref decoder, hex, buffer, startIndex, length, true); method = ParameterEncodingMethod.Rfc2231; hex.Reset(); } else if (!paramList.Contains(param.Name)) { // Note: If we've got an rfc2231-encoded version of the same parameter, then // we'll want to choose that one as opposed to the ASCII variant (i.e. this one). // // While most mail clients that I know of do not send multiple parameters of the // same name, rfc6266 suggests that HTTP servers are using this approach to work // around HTTP clients that do not (yet) implement support for the rfc2231 // encoding of parameter values. Since none of the MIME specifications provide // any suggestions for dealing with this, following rfc6266 seems to make the // most sense, even though it is meant for HTTP clients and servers. int codepage = -1; if (length >= 2 && text[startIndex] == (byte)'"') { var quoted = Rfc2047.DecodeText(options, buffer, startIndex, length, out codepage); value = MimeUtils.Unquote(quoted); } else if (length > 0) { value = Rfc2047.DecodeText(options, buffer, startIndex, length, out codepage); } else { value = string.Empty; } if (codepage != -1 && codepage != 65001) { encoding = CharsetUtils.GetEncoding(codepage); method = ParameterEncodingMethod.Rfc2047; } } else { continue; } if (paramList.table.TryGetValue(param.Name, out parameter)) { parameter.Encoding = encoding; parameter.Value = value; } else if (encoding != null) { paramList.Add(encoding, param.Name, value); parameter = paramList[paramList.Count - 1]; } else { paramList.Add(param.Name, value); parameter = paramList[paramList.Count - 1]; } parameter.EncodingMethod = method; } return(true); }
internal static bool TryParse(ParserOptions options, byte[] text, ref int index, int endIndex, bool throwOnError, out ParameterList paramList) { var rfc2184 = new Dictionary <string, List <NameValuePair> > (icase); var @params = new List <NameValuePair> (); List <NameValuePair> parts; paramList = null; do { if (!ParseUtils.SkipCommentsAndWhiteSpace(text, ref index, endIndex, throwOnError)) { return(false); } if (index >= endIndex) { break; } // handle empty parameter name/value pairs if (text[index] == (byte)';') { index++; continue; } NameValuePair pair; if (!TryParseNameValuePair(text, ref index, endIndex, throwOnError, out pair)) { return(false); } if (!ParseUtils.SkipCommentsAndWhiteSpace(text, ref index, endIndex, throwOnError)) { return(false); } if (pair.Id.HasValue) { if (rfc2184.TryGetValue(pair.Name, out parts)) { parts.Add(pair); } else { parts = new List <NameValuePair> (); rfc2184[pair.Name] = parts; @params.Add(pair); parts.Add(pair); } } else { @params.Add(pair); } if (index >= endIndex) { break; } if (text[index] != (byte)';') { if (throwOnError) { throw new ParseException(string.Format("Invalid parameter list token at offset {0}", index), index, index); } return(false); } index++; } while (true); paramList = new ParameterList(); var hex = new HexDecoder(); foreach (var param in @params) { int startIndex = param.ValueStart; int length = param.ValueLength; Decoder decoder = null; string value; if (param.Id.HasValue) { parts = rfc2184[param.Name]; parts.Sort(); value = string.Empty; for (int i = 0; i < parts.Count; i++) { startIndex = parts[i].ValueStart; length = parts[i].ValueLength; if (parts[i].Encoded) { bool flush = i + 1 >= parts.Count || !parts[i + 1].Encoded; // Note: Some mail clients mistakenly quote encoded parameter values when they shouldn't if (length >= 2 && text[startIndex] == (byte)'"' && text[startIndex + length - 1] == (byte)'"') { startIndex++; length -= 2; } value += DecodeRfc2184(ref decoder, hex, text, startIndex, length, flush); } else if (length >= 2 && text[startIndex] == (byte)'"') { var quoted = CharsetUtils.ConvertToUnicode(options, text, startIndex, length); value += MimeUtils.Unquote(quoted); hex.Reset(); } else if (length > 0) { value += CharsetUtils.ConvertToUnicode(options, text, startIndex, length); hex.Reset(); } } hex.Reset(); } else if (param.Encoded) { value = DecodeRfc2184(ref decoder, hex, text, startIndex, length, true); hex.Reset(); } else if (!paramList.Contains(param.Name)) { // Note: If we've got an rfc2184-encoded version of the same parameter, then // we'll want to choose that one as opposed to the ASCII variant (i.e. this one). // // While most mail clients that I know of do not send multiple parameters of the // same name, rfc6266 suggests that HTTP servers are using this approach to work // around HTTP clients that do not (yet) implement support for the rfc2184/2231 // encoding of parameter values. Since none of the MIME specifications provide // any suggestions for dealing with this, following rfc6266 seems to make the // most sense, even though it is meant for HTTP clients and servers. if (length >= 2 && text[startIndex] == (byte)'"') { var quoted = Rfc2047.DecodeText(options, text, startIndex, length); value = MimeUtils.Unquote(quoted); } else if (length > 0) { value = Rfc2047.DecodeText(options, text, startIndex, length); } else { value = string.Empty; } } else { continue; } paramList[param.Name] = value; } return(true); }
internal static bool TryParse(ParserOptions options, byte[] text, ref int index, int endIndex, bool throwOnError, out ContentDisposition disposition) { string type; int atom; disposition = null; if (!ParseUtils.SkipCommentsAndWhiteSpace(text, ref index, endIndex, throwOnError)) { return(false); } if (index >= endIndex) { if (throwOnError) { throw new ParseException(string.Format(CultureInfo.InvariantCulture, "Expected atom token at position {0}", index), index, index); } return(false); } atom = index; if (text[index] == '"') { if (throwOnError) { throw new ParseException(string.Format(CultureInfo.InvariantCulture, "Unxpected qstring token at position {0}", atom), atom, index); } // Note: This is a work-around for broken mailers that quote the disposition value... // // See https://github.com/jstedfast/MailKit/issues/486 for details. if (!ParseUtils.SkipQuoted(text, ref index, endIndex, throwOnError)) { return(false); } type = CharsetUtils.ConvertToUnicode(options, text, atom, index - atom); type = MimeUtils.Unquote(type); if (string.IsNullOrEmpty(type)) { type = Attachment; } } else { if (!ParseUtils.SkipAtom(text, ref index, endIndex)) { if (throwOnError) { throw new ParseException(string.Format(CultureInfo.InvariantCulture, "Invalid atom token at position {0}", atom), atom, index); } // Note: this is a work-around for broken mailers that do not specify a disposition value... // // See https://github.com/jstedfast/MailKit/issues/486 for details. if (index > atom || text[index] != (byte)';') { return(false); } type = Attachment; } else { type = Encoding.ASCII.GetString(text, atom, index - atom); } } disposition = new ContentDisposition(); disposition.disposition = type; if (!ParseUtils.SkipCommentsAndWhiteSpace(text, ref index, endIndex, throwOnError)) { return(false); } if (index >= endIndex) { return(true); } if (text[index] != (byte)';') { if (throwOnError) { throw new ParseException(string.Format(CultureInfo.InvariantCulture, "Expected ';' at position {0}", index), index, index); } return(false); } index++; if (!ParseUtils.SkipCommentsAndWhiteSpace(text, ref index, endIndex, throwOnError)) { return(false); } if (index >= endIndex) { return(true); } ParameterList parameters; if (!ParameterList.TryParse(options, text, ref index, endIndex, throwOnError, out parameters)) { return(false); } disposition.Parameters = parameters; return(true); }
public void TestCharsetUtilsGetCodePage() { for (int i = 1; i < 16; i++) { int expected, codepage; string name; name = string.Format("iso-8859-{0}", i); codepage = CharsetUtils.GetCodePage(name); switch (i) { case 11: expected = 874; break; case 10: case 12: case 14: expected = -1; break; case 13: if (Path.DirectorySeparatorChar == '\\') { goto default; } expected = -1; break; default: expected = 28590 + i; break; } Assert.AreEqual(expected, codepage, "Invalid codepage for: {0}", name); } for (int i = 0; i < 10; i++) { int expected, codepage; string name; if (i < 9) { expected = 1250 + i; } else { expected = -1; } name = string.Format("windows-125{0}", i); codepage = CharsetUtils.GetCodePage(name); Assert.AreEqual(expected, codepage, "Invalid codepage for: {0}", name); name = string.Format("windows-cp125{0}", i); codepage = CharsetUtils.GetCodePage(name); Assert.AreEqual(expected, codepage, "Invalid codepage for: {0}", name); name = string.Format("cp125{0}", i); codepage = CharsetUtils.GetCodePage(name); Assert.AreEqual(expected, codepage, "Invalid codepage for: {0}", name); } foreach (var ibm in new int[] { 850, 852, 855, 857, 860, 861, 862, 863 }) { int codepage; string name; name = string.Format("ibm-{0}", ibm); codepage = CharsetUtils.GetCodePage(name); Assert.AreEqual(ibm, codepage, "Invalid codepage for: {0}", name); } }
internal static bool TryParse(ParserOptions options, byte[] text, ref int index, int endIndex, bool throwOnError, out ParameterList paramList) { var rfc2184 = new Dictionary <string, List <NameValuePair> > (icase); var @params = new List <NameValuePair> (); List <NameValuePair> parts; paramList = null; do { if (!ParseUtils.SkipCommentsAndWhiteSpace(text, ref index, endIndex, throwOnError)) { return(false); } if (index >= endIndex) { break; } // handle empty parameter name/value pairs if (text[index] == (byte)';') { index++; continue; } NameValuePair pair; if (!TryParseNameValuePair(text, ref index, endIndex, throwOnError, out pair)) { return(false); } if (!ParseUtils.SkipCommentsAndWhiteSpace(text, ref index, endIndex, throwOnError)) { return(false); } if (pair.Id.HasValue) { if (rfc2184.TryGetValue(pair.Name, out parts)) { parts.Add(pair); } else { parts = new List <NameValuePair> (); rfc2184.Add(pair.Name, parts); @params.Add(pair); parts.Add(pair); } } else { @params.Add(pair); } if (index >= endIndex) { break; } if (text[index] != (byte)';') { if (throwOnError) { throw new ParseException(string.Format("Invalid parameter list token at offset {0}", index), index, index); } return(false); } index++; } while (true); paramList = new ParameterList(); var hex = new HexDecoder(); foreach (var param in @params) { int startIndex = param.ValueStart; int length = param.ValueLength; Decoder decoder = null; string value; if (param.Id.HasValue) { parts = rfc2184[param.Name]; parts.Sort(); value = string.Empty; for (int i = 0; i < parts.Count; i++) { startIndex = parts[i].ValueStart; length = parts[i].ValueLength; if (parts[i].Encoded) { bool flush = i + 1 >= parts.Count || !parts[i + 1].Encoded; value += DecodeRfc2184(ref decoder, hex, text, startIndex, length, flush); } else if (length >= 2 && text[startIndex] == (byte)'"') { // FIXME: use Rfc2047.Unquote()?? value += CharsetUtils.ConvertToUnicode(options, text, startIndex + 1, length - 2); hex.Reset(); } else if (length > 0) { value += CharsetUtils.ConvertToUnicode(options, text, startIndex, length); hex.Reset(); } } hex.Reset(); } else if (param.Encoded) { value = DecodeRfc2184(ref decoder, hex, text, startIndex, length, true); hex.Reset(); } else if (length >= 2 && text[startIndex] == (byte)'"') { // FIXME: use Rfc2047.Unquote()?? value = Rfc2047.DecodeText(options, text, startIndex + 1, length - 2); } else if (length > 0) { value = Rfc2047.DecodeText(options, text, startIndex, length); } else { value = string.Empty; } paramList.Add(new Parameter(param.Name, value)); } return(true); }