public void TestEncodedWordInvalidEncoding() { const string text = "blurdy bloop =?iso-8859-1?x?invalid_encoding?= beep boop"; var buffer = Encoding.UTF8.GetBytes(text); string result; result = Rfc2047.DecodePhrase(buffer); Assert.AreEqual(text, result); result = Rfc2047.DecodeText(buffer); Assert.AreEqual(text, result); }
public void EncodeTextWithAllowMixedHeaderCharsetsFormatOptions() { var text = "50304_2201_2021_Contract_Main_2021-11-04.pdf"; var encodingOptionsWithAllowMixedHeaderCharsets = new FormatOptions { International = false, MaxLineLength = 988, AllowMixedHeaderCharsets = true }; var encodedString = Rfc2047.EncodeText(encodingOptionsWithAllowMixedHeaderCharsets, Encoding.UTF8, text); var decodedString = Rfc2047.DecodeText(encodedString); Assert.AreEqual(text, decodedString); }
public void TestEncodedWordIncompleteCharset() { const string text = "blurdy bloop =?iso-8859-1"; var buffer = Encoding.UTF8.GetBytes(text); string result; result = Rfc2047.DecodePhrase(buffer); Assert.AreEqual(text, result); result = Rfc2047.DecodeText(buffer); Assert.AreEqual(text, result); }
internal override void Encode(FormatOptions options, StringBuilder builder, bool firstToken, ref int lineLength) { if (!string.IsNullOrEmpty(Name)) { string name; if (!options.International) { var encoded = Rfc2047.EncodePhrase(options, Encoding, Name); name = Encoding.ASCII.GetString(encoded, 0, encoded.Length); } else { name = EncodeInternationalizedPhrase(Name); } if (lineLength + name.Length > options.MaxLineLength) { if (name.Length > options.MaxLineLength) { // we need to break up the name... builder.AppendFolded(options, firstToken, name, ref lineLength); } else { // the name itself is short enough to fit on a single line, // but only if we write it on a line by itself if (!firstToken && lineLength > 1) { builder.LineWrap(options); lineLength = 1; } lineLength += name.Length; builder.Append(name); } } else { // we can safely fit the name on this line... lineLength += name.Length; builder.Append(name); } } builder.Append(": "); lineLength += 2; Members.Encode(options, builder, false, ref lineLength); builder.Append(';'); lineLength++; }
public void TestFoldPreFoldedHeaderValue() { const string expected = " This is a pre\r\n folded header value.\r\n"; const string text = "This is a pre\r\n folded header value."; var options = FormatOptions.Default.Clone(); options.NewLineFormat = NewLineFormat.Dos; var result = Encoding.ASCII.GetString(Rfc2047.FoldUnstructuredHeader(options, "Subject", Encoding.ASCII.GetBytes(text))); Assert.AreEqual(expected, result); }
public void TestFoldReallyLongWordToken() { const string expected = " This header value has a\r\n really-really-really-really-long-rfc0822-word-token-that-exceeds-the-max-allo\r\n wable-line-length-and-must-be-folded lets see what MimeKit does...\r\n"; const string text = "This header value has a really-really-really-really-long-rfc0822-word-token-that-exceeds-the-max-allowable-line-length-and-must-be-folded lets see what MimeKit does..."; var options = FormatOptions.Default.Clone(); options.NewLineFormat = NewLineFormat.Dos; var result = Encoding.ASCII.GetString(Rfc2047.FoldUnstructuredHeader(options, "Subject", Encoding.ASCII.GetBytes(text))); Assert.AreEqual(expected, result); }
public void TestEncodeSurrogatePair() { const string expected = "I'm so happy! =?utf-8?b?8J+YgA==?= I love MIME so much =?utf-8?b?4p2k77iP4oCN8J+UpSE=?= Isn't it great?"; const string text = "I'm so happy! 😀 I love MIME so much ❤️🔥! Isn't it great?"; string result; result = Encoding.ASCII.GetString(Rfc2047.EncodePhrase(Encoding.UTF8, text)); Assert.AreEqual(expected, result, "EncodePhrase"); result = Encoding.ASCII.GetString(Rfc2047.EncodeText(Encoding.UTF8, text)); Assert.AreEqual(expected, result, "EncodeText"); }
public void TestEncodeControls() { const string expected = "I'm so happy! =?utf-8?q?=07?= I love MIME so much =?utf-8?q?=07=07!?= Isn't it great?"; const string text = "I'm so happy! \a I love MIME so much \a\a! Isn't it great?"; string result; result = Encoding.ASCII.GetString(Rfc2047.EncodePhrase(Encoding.UTF8, text)); Assert.AreEqual(expected, result, "EncodePhrase"); result = Encoding.ASCII.GetString(Rfc2047.EncodeText(Encoding.UTF8, text)); Assert.AreEqual(expected, result, "EncodeText"); }
public void TestEncodedWordEmptyCharsetWithLang() { const string text = "blurdy bloop =?*en?q?no_charset?= beep boop"; var buffer = Encoding.UTF8.GetBytes(text); string result; result = Rfc2047.DecodePhrase(buffer); Assert.AreEqual(text, result); result = Rfc2047.DecodeText(buffer); Assert.AreEqual(text, result); }
public void TestEncodeWrongCharset() { const string expected = "I'm so happy! =?utf-8?b?5ZCN44GM44OJ44Oh44Kk44Oz?= I love MIME so much =?utf-8?b?4p2k77iP4oCN8J+UpSE=?= Isn't it great?"; const string text = "I'm so happy! 名がドメイン I love MIME so much ❤️🔥! Isn't it great?"; var latin1 = Encoding.GetEncoding("iso-8859-1", EncoderFallback.ExceptionFallback, DecoderFallback.ExceptionFallback); string result; result = Encoding.ASCII.GetString(Rfc2047.EncodePhrase(latin1, text)); Assert.AreEqual(expected, result, "EncodePhrase"); result = Encoding.ASCII.GetString(Rfc2047.EncodeText(latin1, text)); Assert.AreEqual(expected, result, "EncodeText"); }
public void TestEncodedWordWithLang() { const string text = "blurdy bloop =?iso-8859-1*en?q?this_is_english?= beep boop"; const string expected = "blurdy bloop this is english beep boop"; var buffer = Encoding.UTF8.GetBytes(text); string result; result = Rfc2047.DecodePhrase(buffer); Assert.AreEqual(expected, result); result = Rfc2047.DecodeText(buffer); Assert.AreEqual(expected, result); }
static byte[] EncodeUnstructuredHeader(ParserOptions options, FormatOptions format, Encoding charset, string field, string value) { if (format.International) { var folded = Fold(format, field, value); return(Encoding.UTF8.GetBytes(folded)); } var encoded = Rfc2047.EncodeText(format, charset, value); return(Rfc2047.FoldUnstructuredHeader(format, field, encoded)); }
/// <summary> /// Gets the header value using the specified charset. /// </summary> /// <remarks> /// <para>If the raw header value does not properly encode non-ASCII text, the decoder /// will fall back to a default charset encoding. Sometimes, however, this /// default charset fallback is wrong and the mail client may wish to override /// that default charset on a per-header basis.</para> /// <para>By using this method, the client is able to override the fallback charset /// on a per-header basis.</para> /// </remarks> /// <returns>The value.</returns> /// <param name="charset">Charset.</param> public string GetValue(Encoding charset) { if (charset == null) { throw new ArgumentNullException("charset"); } var options = Options.Clone(); options.CharsetEncoding = charset; return(Unfold(Rfc2047.DecodeText(options, RawValue))); }
static void AddEnvelopeAddress(InternetAddressList list, ImapEngine engine, CancellationToken cancellationToken) { var values = new string[4]; ImapToken token; int index = 0; do { token = engine.ReadToken(cancellationToken); switch (token.Type) { case ImapTokenType.Literal: values[index] = engine.ReadLiteral(cancellationToken); break; case ImapTokenType.QString: case ImapTokenType.Atom: values[index] = (string)token.Value; break; case ImapTokenType.Nil: break; default: throw ImapEngine.UnexpectedToken(token, false); } index++; } while (index < 4); token = engine.ReadToken(cancellationToken); if (token.Type != ImapTokenType.CloseParen) { throw ImapEngine.UnexpectedToken(token, false); } string name = null; if (values[0] != null) { // Note: since the ImapEngine.ReadLiteral() uses iso-8859-1 // to convert bytes to unicode, we can undo that here: name = Rfc2047.DecodePhrase(Latin1.GetBytes(values[0])); } string address = values[3] != null ? values[2] + "@" + values[3] : values[2]; list.Add(new MailboxAddress(name, address)); }
public void TestRfc2047DecodeInvalidPayloadBreak() { const string japanese = "狂ったこの世で狂うなら気は確かだ。"; var utf8 = Encoding.UTF8.GetBytes(japanese); var base64 = Convert.ToBase64String(utf8); var builder = new StringBuilder(); builder.Append("=?utf-8?b?").Append(base64.Substring(0, base64.Length - 6)).Append("?= "); builder.Append("=?utf-8?b?").Append(base64.Substring(base64.Length - 6)).Append("?="); var decoded = Rfc2047.DecodeText(Encoding.ASCII.GetBytes(builder.ToString())); Assert.AreEqual(japanese, decoded, "Decoded text did not match the original."); }
public void TestSimpleRfc2047QEncodedPhrase() { var options = ParserOptions.Default.Clone(); var input = "=?iso-8859-1?q?hola?="; string actual; options.EnableRfc2047Workarounds = false; actual = Rfc2047.DecodePhrase(options, Encoding.ASCII.GetBytes(input)); Assert.AreEqual("hola", actual); options.EnableRfc2047Workarounds = true; actual = Rfc2047.DecodePhrase(options, Encoding.ASCII.GetBytes(input)); Assert.AreEqual("hola", actual, "Unexpected result when workarounds enabled."); }
public void TestArgumentExceptions() { var text = Encoding.UTF8.GetBytes("this is some text"); // DecodePhrase Assert.Throws <ArgumentNullException> (() => Rfc2047.DecodePhrase(null, text, 0, text.Length)); Assert.Throws <ArgumentNullException> (() => Rfc2047.DecodePhrase(ParserOptions.Default, null, 0, text.Length)); Assert.Throws <ArgumentOutOfRangeException> (() => Rfc2047.DecodePhrase(ParserOptions.Default, text, -1, text.Length)); Assert.Throws <ArgumentOutOfRangeException> (() => Rfc2047.DecodePhrase(ParserOptions.Default, text, 0, -1)); Assert.Throws <ArgumentNullException> (() => Rfc2047.DecodePhrase(null, 0, text.Length)); Assert.Throws <ArgumentOutOfRangeException> (() => Rfc2047.DecodePhrase(text, -1, text.Length)); Assert.Throws <ArgumentOutOfRangeException> (() => Rfc2047.DecodePhrase(text, 0, -1)); Assert.Throws <ArgumentNullException> (() => Rfc2047.DecodePhrase(null, text)); Assert.Throws <ArgumentNullException> (() => Rfc2047.DecodePhrase(ParserOptions.Default, null)); Assert.Throws <ArgumentNullException> (() => Rfc2047.DecodePhrase(null)); // DecodeText Assert.Throws <ArgumentNullException> (() => Rfc2047.DecodeText(null, text, 0, text.Length)); Assert.Throws <ArgumentNullException> (() => Rfc2047.DecodeText(ParserOptions.Default, null, 0, text.Length)); Assert.Throws <ArgumentOutOfRangeException> (() => Rfc2047.DecodeText(ParserOptions.Default, text, -1, text.Length)); Assert.Throws <ArgumentOutOfRangeException> (() => Rfc2047.DecodeText(ParserOptions.Default, text, 0, -1)); Assert.Throws <ArgumentNullException> (() => Rfc2047.DecodeText(null, 0, text.Length)); Assert.Throws <ArgumentOutOfRangeException> (() => Rfc2047.DecodeText(text, -1, text.Length)); Assert.Throws <ArgumentOutOfRangeException> (() => Rfc2047.DecodeText(text, 0, -1)); Assert.Throws <ArgumentNullException> (() => Rfc2047.DecodeText(null, text)); Assert.Throws <ArgumentNullException> (() => Rfc2047.DecodeText(ParserOptions.Default, null)); Assert.Throws <ArgumentNullException> (() => Rfc2047.DecodeText(null)); // EncodePhrase Assert.Throws <ArgumentNullException> (() => Rfc2047.EncodePhrase(null, Encoding.UTF8, "phrase")); Assert.Throws <ArgumentNullException> (() => Rfc2047.EncodePhrase(FormatOptions.Default, null, "phrase")); Assert.Throws <ArgumentNullException> (() => Rfc2047.EncodePhrase(FormatOptions.Default, Encoding.UTF8, null)); Assert.Throws <ArgumentNullException> (() => Rfc2047.EncodePhrase(null, "phrase")); Assert.Throws <ArgumentNullException> (() => Rfc2047.EncodePhrase(Encoding.UTF8, null)); // EncodeText Assert.Throws <ArgumentNullException> (() => Rfc2047.EncodeText(null, Encoding.UTF8, "text")); Assert.Throws <ArgumentNullException> (() => Rfc2047.EncodeText(FormatOptions.Default, null, "text")); Assert.Throws <ArgumentNullException> (() => Rfc2047.EncodeText(FormatOptions.Default, Encoding.UTF8, null)); Assert.Throws <ArgumentNullException> (() => Rfc2047.EncodeText(null, "text")); Assert.Throws <ArgumentNullException> (() => Rfc2047.EncodeText(Encoding.UTF8, null)); }
public void TestDecodeInvalidCharset() { const string xunknown = "=?x-unknown?B?aG9sYQ==?="; const string cp1260 = "=?cp1260?B?aG9sYQ==?="; string actual; // Note: we won't be able to get a codepage for x-unknown. actual = Rfc2047.DecodeText(Encoding.ASCII.GetBytes(xunknown)); Assert.AreEqual("hola", actual, "Unexpected decoding of x-unknown."); // Note: cp-1260 doesn't exist, but will make CharsetUtils parse the codepage as 1260. actual = Rfc2047.DecodeText(Encoding.ASCII.GetBytes(cp1260)); Assert.AreEqual("hola", actual, "Unexpected decoding of cp1260."); }
public void TestSimpleRfc2047QEncodedText() { var options = ParserOptions.Default.Clone(); var input = "=?iso-8859-1?q?hola?="; string actual; options.Rfc2047ComplianceMode = RfcComplianceMode.Strict; actual = Rfc2047.DecodeText(options, Encoding.ASCII.GetBytes(input)); Assert.AreEqual("hola", actual); options.Rfc2047ComplianceMode = RfcComplianceMode.Loose; actual = Rfc2047.DecodeText(options, Encoding.ASCII.GetBytes(input)); Assert.AreEqual("hola", actual, "Unexpected result when workarounds enabled."); }
public void TestDecodeEmptyString() { var empty = new byte[0]; Assert.AreEqual(string.Empty, Rfc2047.DecodePhrase(empty)); Assert.AreEqual(string.Empty, Rfc2047.DecodePhrase(empty, 0, 0)); Assert.AreEqual(string.Empty, Rfc2047.DecodePhrase(ParserOptions.Default, empty)); Assert.AreEqual(string.Empty, Rfc2047.DecodePhrase(ParserOptions.Default, empty, 0, 0)); Assert.AreEqual(string.Empty, Rfc2047.DecodePhrase(ParserOptions.Default, empty, 0, 0, out _)); Assert.AreEqual(string.Empty, Rfc2047.DecodeText(empty)); Assert.AreEqual(string.Empty, Rfc2047.DecodeText(empty, 0, 0)); Assert.AreEqual(string.Empty, Rfc2047.DecodeText(ParserOptions.Default, empty)); Assert.AreEqual(string.Empty, Rfc2047.DecodeText(ParserOptions.Default, empty, 0, 0)); Assert.AreEqual(string.Empty, Rfc2047.DecodeText(ParserOptions.Default, empty, 0, 0, out _)); }
public void TestRfc2047DecodeInvalidMultibyteBreak() { const string japanese = "狂ったこの世で狂うなら気は確かだ。"; var utf8 = Encoding.UTF8.GetBytes(japanese); var builder = new StringBuilder(); for (int wordBreak = (utf8.Length / 2) - 5; wordBreak < (utf8.Length / 2) + 5; wordBreak++) { builder.Append("=?utf-8?b?").Append(Convert.ToBase64String(utf8, 0, wordBreak)).Append("?= "); builder.Append("=?utf-8?b?").Append(Convert.ToBase64String(utf8, wordBreak, utf8.Length - wordBreak)).Append("?="); var decoded = Rfc2047.DecodeText(Encoding.ASCII.GetBytes(builder.ToString())); Assert.AreEqual(japanese, decoded, "Decoded text did not match the original."); builder.Clear(); } }
/// <summary> /// Sets the header value using the specified charset. /// </summary> /// <param name="charset">A charset encoding.</param> /// <param name="value">The header value.</param> /// <exception cref="System.ArgumentNullException"> /// <para><paramref name="charset"/> is <c>null</c>.</para> /// <para>-or-</para> /// <para><paramref name="value"/> is <c>null</c>.</para> /// </exception> public void SetValue(Encoding charset, string value) { if (charset == null) { throw new ArgumentNullException("charset"); } if (value == null) { throw new ArgumentNullException("value"); } textValue = Unfold(value.Trim()); var encoded = Rfc2047.EncodeText(FormatOptions.Default, charset, textValue); RawValue = Rfc2047.FoldUnstructuredHeader(FormatOptions.Default, Field, encoded); OnChanged(); }
public void TestEncodingLongNameMixedQuotingAndEncoding() { const string name = "Dr. xxxxxxxxxx xxxxx | xxxxxx.xxxxxxx für xxxxxxxxxxxxx xxxx"; const string encodedName = "\"Dr. xxxxxxxxxx xxxxx | xxxxxx.xxxxxxx\" =?iso-8859-1?b?Zvxy?= xxxxxxxxxxxxx xxxx"; const string encodedMailbox = "\"Dr. xxxxxxxxxx xxxxx | xxxxxx.xxxxxxx\" =?iso-8859-1?b?Zvxy?= xxxxxxxxxxxxx\n xxxx <*****@*****.**>"; const string address = "*****@*****.**"; var buffer = Rfc2047.EncodePhrase(Encoding.UTF8, name); var result = Encoding.UTF8.GetString(buffer); Assert.AreEqual(encodedName, result); var mailbox = new MailboxAddress(name, address); var list = new InternetAddressList(); list.Add(mailbox); result = list.ToString(true); Assert.AreEqual(encodedMailbox, result); }
static string ReadNStringToken(ImapEngine engine, bool rfc2047, CancellationToken cancellationToken) { var token = engine.ReadToken (cancellationToken); string value; switch (token.Type) { case ImapTokenType.Literal: value = engine.ReadLiteral (cancellationToken); break; case ImapTokenType.QString: case ImapTokenType.Atom: value = (string) token.Value; break; case ImapTokenType.Nil: return null; default: throw ImapEngine.UnexpectedToken (token, false); } return rfc2047 ? Rfc2047.DecodeText (Latin1.GetBytes (value)) : value; }
internal static bool TryParse(ParserOptions options, byte[] text, ref int index, int endIndex, bool throwOnError, out InternetAddress address) { address = null; if (!ParseUtils.SkipCommentsAndWhiteSpace(text, ref index, endIndex, throwOnError)) { return(false); } // keep track of the start & length of the phrase int startIndex = index; int length = 0; while (index < endIndex && ParseUtils.Skip8bitWord(text, ref index, endIndex, throwOnError)) { length = index - startIndex; do { if (!ParseUtils.SkipCommentsAndWhiteSpace(text, ref index, endIndex, throwOnError)) { return(false); } // Note: some clients don't quote dots in the name if (index >= endIndex || text[index] != (byte)'.') { break; } index++; } while (true); } if (!ParseUtils.SkipCommentsAndWhiteSpace(text, ref index, endIndex, throwOnError)) { return(false); } // specials = "(" / ")" / "<" / ">" / "@" ; Must be in quoted- // / "," / ";" / ":" / "\" / <"> ; string, to use // / "." / "[" / "]" ; within a word. if (index >= endIndex || text[index] == (byte)',' || text[index] == ';') { // we've completely gobbled up an addr-spec w/o a domain byte sentinel = index < endIndex ? text[index] : (byte)','; string name, addrspec; // rewind back to the beginning of the local-part index = startIndex; if (!TryParseAddrspec(text, ref index, endIndex, sentinel, throwOnError, out addrspec)) { return(false); } ParseUtils.SkipWhiteSpace(text, ref index, endIndex); if (index < endIndex && text[index] == '(') { int comment = index; if (!ParseUtils.SkipComment(text, ref index, endIndex)) { if (throwOnError) { throw new ParseException(string.Format("Incomplete comment token at offset {0}", comment), comment, index); } return(false); } comment++; name = Rfc2047.DecodePhrase(options, text, comment, (index - 1) - comment).Trim(); } else { name = string.Empty; } address = new MailboxAddress(name, addrspec); return(true); } if (text[index] == (byte)':') { // rfc2822 group address int codepage; string name; if (length > 0) { name = Rfc2047.DecodePhrase(options, text, startIndex, length, out codepage); } else { name = string.Empty; codepage = 65001; } return(TryParseGroup(options, text, startIndex, ref index, endIndex, MimeUtils.Unquote(name), codepage, throwOnError, out address)); } if (text[index] == (byte)'<') { // rfc2822 angle-addr token int codepage; string name; if (length > 0) { name = Rfc2047.DecodePhrase(options, text, startIndex, length, out codepage); } else { name = string.Empty; codepage = 65001; } return(TryParseMailbox(text, startIndex, ref index, endIndex, MimeUtils.Unquote(name), codepage, throwOnError, out address)); } if (text[index] == (byte)'@') { // we're either in the middle of an addr-spec token or we completely gobbled up an addr-spec w/o a domain string name, addrspec; // rewind back to the beginning of the local-part index = startIndex; if (!TryParseAddrspec(text, ref index, endIndex, (byte)',', throwOnError, out addrspec)) { return(false); } ParseUtils.SkipWhiteSpace(text, ref index, endIndex); if (index < endIndex && text[index] == '(') { int comment = index; if (!ParseUtils.SkipComment(text, ref index, endIndex)) { if (throwOnError) { throw new ParseException(string.Format("Incomplete comment token at offset {0}", comment), comment, index); } return(false); } comment++; name = Rfc2047.DecodePhrase(options, text, comment, (index - 1) - comment).Trim(); } else { name = string.Empty; } address = new MailboxAddress(name, addrspec); return(true); } if (throwOnError) { throw new ParseException(string.Format("Invalid address token at offset {0}", startIndex), startIndex, index); } return(false); }
internal override void Encode(FormatOptions options, StringBuilder builder, ref int lineLength) { if (builder == null) { throw new ArgumentNullException("builder"); } if (lineLength < 0) { throw new ArgumentOutOfRangeException("lineLength"); } if (!string.IsNullOrEmpty(Name)) { string name; if (!options.International) { var encoded = Rfc2047.EncodePhrase(options, Encoding, Name); name = Encoding.ASCII.GetString(encoded, 0, encoded.Length); } else { name = EncodeInternationalizedPhrase(Name); } if (lineLength + name.Length > options.MaxLineLength) { if (name.Length > options.MaxLineLength) { // we need to break up the name... builder.AppendFolded(options, name, ref lineLength); } else { // the name itself is short enough to fit on a single line, // but only if we write it on a line by itself if (lineLength > 1) { builder.LineWrap(options); lineLength = 1; } lineLength += name.Length; builder.Append(name); } } else { // we can safely fit the name on this line... lineLength += name.Length; builder.Append(name); } } builder.Append(": "); lineLength += 2; foreach (var member in Members) { member.Encode(options, builder, ref lineLength); } }
static int Rfc2047EncodeNextChunk(StringBuilder str, string text, ref int index, Encoding encoding, string charset, Encoder encoder, int maxLength) { int byteCount = 0, charCount = 0, encodeCount = 0; var buffer = new char[2]; int startIndex = index; int nchars, n; char c; while (index < text.Length) { c = text[index++]; if (c < 127) { if (IsCtrl(c) || c == '"' || c == '\\') { encodeCount++; } byteCount++; charCount++; nchars = 1; n = 1; } else if (c < 256) { // iso-8859-1 encodeCount++; byteCount++; charCount++; nchars = 1; n = 1; } else { if (char.IsSurrogatePair(text, index - 1)) { buffer[1] = text[index++]; nchars = 2; } else { nchars = 1; } buffer[0] = c; try { n = encoder.GetByteCount(buffer, 0, nchars, true); } catch { n = 3; } charCount += nchars; encodeCount += n; byteCount += n; } if (ExceedsMaxWordLength(charset, byteCount, encodeCount, maxLength)) { // restore our previous state charCount -= nchars; index -= nchars; byteCount -= n; break; } } return(Rfc2047.AppendEncodedWord(str, encoding, text, startIndex, charCount, QEncodeMode.Text)); }
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 override void Encode(FormatOptions options, StringBuilder builder, ref int lineLength) { if (builder == null) { throw new ArgumentNullException("builder"); } if (lineLength < 0) { throw new ArgumentOutOfRangeException("lineLength"); } string route = Route.ToString(); if (!string.IsNullOrEmpty(route)) { route += ":"; } if (!string.IsNullOrEmpty(Name)) { string name; if (!options.International) { var encoded = Rfc2047.EncodePhrase(options, Encoding, Name); name = Encoding.ASCII.GetString(encoded, 0, encoded.Length); } else { name = EncodeInternationalizedPhrase(Name); } if (lineLength + name.Length > options.MaxLineLength) { if (name.Length > options.MaxLineLength) { // we need to break up the name... builder.AppendFolded(options, name, ref lineLength); } else { // the name itself is short enough to fit on a single line, // but only if we write it on a line by itself if (lineLength > 1) { builder.LineWrap(options); lineLength = 1; } lineLength += name.Length; builder.Append(name); } } else { // we can safely fit the name on this line... lineLength += name.Length; builder.Append(name); } if ((lineLength + route.Length + Address.Length + 3) > options.MaxLineLength) { builder.Append(options.NewLine); builder.Append("\t<"); lineLength = 2; } else { builder.Append(" <"); lineLength += 2; } lineLength += route.Length; builder.Append(route); lineLength += Address.Length + 1; builder.Append(Address); builder.Append('>'); } else if (!string.IsNullOrEmpty(route)) { if ((lineLength + route.Length + Address.Length + 2) > options.MaxLineLength) { builder.Append(options.NewLine); builder.Append("\t<"); lineLength = 2; } else { builder.Append('<'); lineLength++; } lineLength += route.Length; builder.Append(route); lineLength += Address.Length + 1; builder.Append(Address); builder.Append('>'); } else { if ((lineLength + Address.Length) > options.MaxLineLength) { builder.LineWrap(options); lineLength = 1; } lineLength += Address.Length; builder.Append(Address); } }
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); }