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); }