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);
		}
Beispiel #3
0
		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);
		}