static bool GetNextValue (string charset, Encoder encoder, HexEncoder hex, char[] chars, ref int index, ref byte[] bytes, ref byte[] encoded, int maxLength, out string value) { int length = chars.Length - index; if (length < maxLength) { switch (GetEncodeMethod (chars, index, length)) { case EncodeMethod.Quote: value = MimeUtils.Quote (new string (chars, index, length)); index += length; return false; case EncodeMethod.None: value = new string (chars, index, length); index += length; return false; } } length = Math.Min (maxLength, length); int ratio, count, n; do { count = encoder.GetByteCount (chars, index, length, true); if (count > maxLength && length > 1) { ratio = (int) Math.Round ((double) count / (double) length); length -= Math.Max ((count - maxLength) / ratio, 1); continue; } if (bytes.Length < count) Array.Resize<byte> (ref bytes, count); count = encoder.GetBytes (chars, index, length, bytes, 0, true); // Note: the first chunk needs to be encoded in order to declare the charset if (index > 0 || charset == "us-ascii") { var method = GetEncodeMethod (bytes, count); if (method == EncodeMethod.Quote) { value = MimeUtils.Quote (Encoding.ASCII.GetString (bytes, 0, count)); index += length; return false; } if (method == EncodeMethod.None) { value = Encoding.ASCII.GetString (bytes, 0, count); index += length; return false; } } n = hex.EstimateOutputLength (count); if (encoded.Length < n) Array.Resize<byte> (ref encoded, n); // only the first value gets a charset declaration int charsetLength = index == 0 ? charset.Length + 2 : 0; n = hex.Encode (bytes, 0, count, encoded); if (n > 3 && (charsetLength + n) > maxLength) { int x = 0; for (int i = n - 1; i >= 0 && charsetLength + i >= maxLength; i--) { if (encoded[i] == (byte) '%') x--; else x++; } ratio = (int) Math.Round ((double) count / (double) length); length -= Math.Max (x / ratio, 1); continue; } if (index == 0) value = charset + "''" + Encoding.ASCII.GetString (encoded, 0, n); else value = Encoding.ASCII.GetString (encoded, 0, n); index += length; return true; } while (true); }
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); }