/// <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"> /// <paramref name="name"/> contains illegal characters. /// </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(nameof(charset)); } if (name == null) { throw new ArgumentNullException(nameof(name)); } if (name.Length == 0) { throw new ArgumentException("Parameter names are not allowed to be empty.", nameof(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.", nameof(name)); } } if (value == null) { throw new ArgumentNullException(nameof(value)); } Encoding = CharsetUtils.GetEncoding(charset); Value = value; Name = name; }
/// <summary> /// Gets the decoded text content using the provided charset to override /// the charset specified in the Content-Type parameters. /// </summary> /// <remarks> /// Uses the provided charset encoding to convert the raw text content /// into a unicode string, overriding any charset specified in the /// Content-Type header. /// </remarks> /// <returns>The decoded text.</returns> /// <param name="charset">The charset encoding to use.</param> /// <exception cref="System.ArgumentNullException"> /// <paramref name="charset"/> is <c>null</c>. /// </exception> /// <exception cref="System.NotSupportedException"> /// The <paramref name="charset"/> is not supported. /// </exception> public string GetText (string charset) { if (charset == null) throw new ArgumentNullException (nameof (charset)); return GetText (CharsetUtils.GetEncoding (charset)); }
/// <summary> /// Initializes a new instance of the <see cref="MimeKit.Header"/> class. /// </summary> /// <remarks> /// Creates a new message or entity header for the specified field and /// value pair. The encoding is used to determine which charset to use /// when encoding the value according to the rules of rfc2047. /// </remarks> /// <param name="charset">The charset that should be used to encode the /// header value.</param> /// <param name="id">The header identifier.</param> /// <param name="value">The value of the header.</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> /// <exception cref="System.ArgumentOutOfRangeException"> /// <paramref name="id"/> is not a valid <see cref="HeaderId"/>. /// </exception> /// <exception cref="System.ArgumentException"> /// <paramref name="charset"/> cannot be empty. /// </exception> /// <exception cref="System.NotSupportedException"> /// <paramref name="charset"/> is not supported. /// </exception> public Header(string charset, HeaderId id, string value) { if (charset == null) { throw new ArgumentNullException("charset"); } if (charset.Length == 0) { throw new ArgumentException("The charset name cannot be empty.", "charset"); } if (id == HeaderId.Unknown) { throw new ArgumentOutOfRangeException("id"); } if (value == null) { throw new ArgumentNullException("value"); } var encoding = CharsetUtils.GetEncoding(charset); Options = ParserOptions.Default.Clone(); Field = id.ToHeaderName(); Id = id; SetValue(encoding, value); }
static Encoding GetEncoding(string paramName, string encodingName) { if (encodingName == null) { throw new ArgumentNullException(paramName); } return(CharsetUtils.GetEncoding(encodingName)); }
/// <summary> /// Gets the decoded text content using the provided charset to override /// the charset specified in the Content-Type parameters. /// </summary> /// <remarks> /// Uses the provided charset encoding to convert the raw text content /// into a unicode string, overriding any charset specified in the /// Content-Type header. /// </remarks> /// <returns>The decoded text.</returns> /// <param name="charset">The charset encoding to use.</param> /// <exception cref="System.ArgumentNullException"> /// <paramref name="charset"/> is <c>null</c>. /// </exception> /// <exception cref="System.NotSupportedException"> /// The <paramref name="charset"/> is not supported. /// </exception> public string GetText(string charset) { if (charset == null) { throw new ArgumentNullException("charset"); } return(GetText(CharsetUtils.GetEncoding(charset))); }
/// <summary> /// Sets the text content and the charset parameter in the Content-Type header. /// </summary> /// <remarks> /// This method is similar to setting the <see cref="TextPart.Text"/> property, /// but allows specifying a charset encoding to use. Also updates the /// <see cref="ContentType.Charset"/> property. /// </remarks> /// <param name="charset">The charset encoding.</param> /// <param name="text">The text content.</param> /// <exception cref="System.ArgumentNullException"> /// <para><paramref name="charset"/> is <c>null</c>.</para> /// <para>-or-</para> /// <para><paramref name="text"/> is <c>null</c>.</para> /// </exception> /// <exception cref="System.NotSupportedException"> /// The <paramref name="charset"/> is not supported. /// </exception> public void SetText (string charset, string text) { if (charset == null) throw new ArgumentNullException (nameof (charset)); if (text == null) throw new ArgumentNullException (nameof (text)); SetText (CharsetUtils.GetEncoding (charset), text); }
/// <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">The charset to use as a fallback.</param> public string GetValue(string charset) { if (charset == null) { throw new ArgumentNullException("charset"); } var encoding = CharsetUtils.GetEncoding(charset); return(GetValue(encoding)); }
/// <summary> /// Sets the header value using the specified charset. /// </summary> /// <remarks> /// When a particular charset is desired for encoding the header value /// according to the rules of rfc2047, this method should be used /// instead of the <see cref="Value"/> setter. /// </remarks> /// <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> /// <exception cref="System.NotSupportedException"> /// <paramref name="charset"/> is not supported. /// </exception> public void SetValue(string charset, string value) { if (charset == null) { throw new ArgumentNullException("charset"); } var encoding = CharsetUtils.GetEncoding(charset); SetValue(encoding, value); }
/// <summary> /// Sets the text content and the charset parameter in the Content-Type header. /// </summary> /// <remarks> /// This method is similar to setting the <see cref="Text"/> property, but allows /// specifying a charset encoding to use. Also updates the /// <see cref="ContentType.Charset"/> property. /// </remarks> /// <param name="charset">The charset encoding.</param> /// <param name="text">The text content.</param> /// <exception cref="System.ArgumentNullException"> /// <para><paramref name="charset"/> is <c>null</c>.</para> /// <para>-or-</para> /// <para><paramref name="text"/> is <c>null</c>.</para> /// </exception> /// <exception cref="System.NotSupportedException"> /// The <paramref name="charset"/> is not supported. /// </exception> public void SetText(string charset, string text) { if (charset == null) { throw new ArgumentNullException("charset"); } if (text == null) { throw new ArgumentNullException("text"); } SetText(CharsetUtils.GetEncoding(charset), text); }
static string DecodeRfc2231(out Encoding encoding, ref Decoder decoder, HexDecoder hex, byte[] text, int startIndex, int count, bool flush) { int endIndex = startIndex + count; int index = startIndex; string charset; // Note: decoder is only null if this is the first segment if (decoder == null) { if (TryGetCharset(text, ref index, endIndex, out charset)) { try { encoding = CharsetUtils.GetEncoding(charset, "?"); decoder = (Decoder)encoding.GetDecoder(); } catch (NotSupportedException) { encoding = Encoding.GetEncoding(28591); // iso-8859-1 decoder = (Decoder)encoding.GetDecoder(); } } else { // When no charset is specified, it should be safe to assume US-ASCII... // but we all know what assume means, right?? encoding = Encoding.GetEncoding(28591); // iso-8859-1 decoder = (Decoder)encoding.GetDecoder(); } } else { encoding = null; } int length = endIndex - index; var decoded = ArrayPool <byte> .Shared.Rent(hex.EstimateOutputLength(length)); try { // hex decode... length = hex.Decode(text, index, length, decoded); int outLength = decoder.GetCharCount(decoded, 0, length, flush); var output = new char[outLength]; outLength = decoder.GetChars(decoded, 0, length, output, 0, flush); return(new string (output, 0, outLength)); } finally { ArrayPool <byte> .Shared.Return(decoded); } }
/// <summary> /// Initializes a new instance of the <see cref="MimeKit.Header"/> class. /// </summary> /// <remarks> /// Creates a new message or entity header for the specified field and /// value pair. The encoding is used to determine which charset to use /// when encoding the value according to the rules of rfc2047. /// </remarks> /// <param name="charset">The charset that should be used to encode the /// header value.</param> /// <param name="field">The name of the header field.</param> /// <param name="value">The value of the header.</param> /// <exception cref="System.ArgumentNullException"> /// <para><paramref name="charset"/> is <c>null</c>.</para> /// <para>-or-</para> /// <para><paramref name="field"/> 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>The <paramref name="field"/> contains illegal characters.</para> /// </exception> /// <exception cref="System.NotSupportedException"> /// <paramref name="charset"/> is not supported. /// </exception> public Header(string charset, string field, string value) { if (charset == null) { throw new ArgumentNullException("charset"); } if (charset.Length == 0) { throw new ArgumentException("The charset name cannot be empty.", "charset"); } if (field == null) { throw new ArgumentNullException("field"); } if (field.Length == 0) { throw new ArgumentException("Header field names are not allowed to be empty.", "field"); } for (int i = 0; i < field.Length; i++) { if (field[i] >= 127 || !IsAsciiAtom((byte)field[i])) { throw new ArgumentException("Illegal characters in header field name.", "field"); } } if (value == null) { throw new ArgumentNullException("value"); } var encoding = CharsetUtils.GetEncoding(charset); Options = ParserOptions.Default.Clone(); Id = field.ToHeaderId(); Field = field; SetValue(encoding, value); }
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> /// 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)); }
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); }