Esempio n. 1
0
        /// <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;
        }
Esempio n. 2
0
		/// <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));
		}
Esempio n. 3
0
        /// <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);
        }
Esempio n. 4
0
        static Encoding GetEncoding(string paramName, string encodingName)
        {
            if (encodingName == null)
            {
                throw new ArgumentNullException(paramName);
            }

            return(CharsetUtils.GetEncoding(encodingName));
        }
Esempio n. 5
0
        /// <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)));
        }
Esempio n. 6
0
		/// <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);
		}
Esempio n. 7
0
        /// <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));
        }
Esempio n. 8
0
        /// <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);
        }
Esempio n. 9
0
        /// <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);
        }
Esempio n. 10
0
        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);
            }
        }
Esempio n. 11
0
        /// <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);
        }
Esempio n. 12
0
        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));
        }
Esempio n. 13
0
        /// <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));
        }
Esempio n. 14
0
        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);
        }