Example #1
0
        MimeEntity CreateAttachment(ContentType contentType, string fileName, Stream stream)
        {
            MimeEntity attachment;

            if (contentType.IsMimeType("message", "rfc822"))
            {
                var message = MimeMessage.Load(stream);
                var rfc822  = new MessagePart {
                    Message = message
                };

                rfc822.ContentDisposition          = new ContentDisposition(linked ? ContentDisposition.Inline : ContentDisposition.Attachment);
                rfc822.ContentDisposition.FileName = Path.GetFileName(fileName);
                rfc822.ContentType.Name            = Path.GetFileName(fileName);

                attachment = rfc822;
            }
            else
            {
                MimePart part;

                if (contentType.IsMimeType("text", "*"))
                {
                    part = new TextPart(contentType.MediaSubtype);
                    foreach (var param in contentType.Parameters)
                    {
                        part.ContentType.Parameters.Add(param);
                    }

                    // TODO: should we try to auto-detect charsets if no charset parameter is specified?
                }
                else
                {
                    part = new MimePart(contentType);
                }

                part.FileName     = Path.GetFileName(fileName);
                part.IsAttachment = !linked;

                LoadContent(part, stream);
                attachment = part;
            }

            if (linked)
            {
                attachment.ContentLocation = new Uri(Path.GetFileName(fileName), UriKind.Relative);
            }

            return(attachment);
        }
Example #2
0
        /// <summary>
        /// Computes the MD5 checksum of the content.
        /// </summary>
        /// <remarks>
        /// Computes the MD5 checksum of the MIME content in its canonical
        /// format and then base64-encodes the result.
        /// </remarks>
        /// <returns>The md5sum of the content.</returns>
        /// <exception cref="System.InvalidOperationException">
        /// The <see cref="ContentObject"/> is <c>null</c>.
        /// </exception>
        public string ComputeContentMd5()
        {
            if (ContentObject == null)
            {
                throw new InvalidOperationException("Cannot compute Md5 checksum without a ContentObject.");
            }

            using (var stream = ContentObject.Open()) {
                byte[] checksum;

                using (var filtered = new FilteredStream(stream)) {
                    if (ContentType.IsMimeType("text", "*"))
                    {
                        filtered.Add(new Unix2DosFilter());
                    }

                    using (var md5 = MD5.Create())
                        checksum = md5.ComputeHash(filtered);
                }

                var base64 = new Base64Encoder(true);
                var digest = new byte[base64.EstimateOutputLength(checksum.Length)];
                int n      = base64.Flush(checksum, 0, checksum.Length, digest);

                return(Encoding.ASCII.GetString(digest, 0, n));
            }
        }
Example #3
0
        MimePart CreateAttachment(ContentType contentType, string fileName, Stream stream)
        {
            MimePart attachment;

            if (contentType.IsMimeType("text", "*"))
            {
                attachment = new TextPart(contentType.MediaSubtype);
                foreach (var param in contentType.Parameters)
                {
                    attachment.ContentType.Parameters.Add(param);
                }

                // TODO: should we try to auto-detect charsets if no charset parameter is specified?
            }
            else
            {
                attachment = new MimePart(contentType);
            }

            attachment.FileName     = Path.GetFileName(fileName);
            attachment.IsAttachment = !linked;

            if (linked)
            {
                attachment.ContentLocation = new Uri(Path.GetFileName(fileName), UriKind.Relative);
            }

            LoadContent(attachment, stream);

            return(attachment);
        }
Example #4
0
        async Task <MimeEntity> CreateAttachmentAsync(ContentType contentType, string path, Stream stream, CancellationToken cancellationToken)
        {
            var        fileName = GetFileName(path);
            MimeEntity attachment;

            if (contentType.IsMimeType("message", "rfc822"))
            {
                var message = await MimeMessage.LoadAsync(stream, cancellationToken).ConfigureAwait(false);

                attachment = new MessagePart {
                    Message = message
                };
            }
            else
            {
                MimePart part;

                if (contentType.IsMimeType("text", "*"))
                {
                    // TODO: should we try to auto-detect charsets if no charset parameter is specified?
                    part = new TextPart(contentType);
                }
                else
                {
                    part = new MimePart(contentType);
                }

                await LoadContentAsync(part, stream, cancellationToken).ConfigureAwait(false);

                attachment = part;
            }

            attachment.ContentDisposition          = new ContentDisposition(linked ? ContentDisposition.Inline : ContentDisposition.Attachment);
            attachment.ContentDisposition.FileName = fileName;
            attachment.ContentType.Name            = fileName;

            if (linked)
            {
                attachment.ContentLocation = new Uri(fileName, UriKind.Relative);
            }

            return(attachment);
        }
Example #5
0
        /// <summary>
        /// Calculates the most efficient content encoding given the specified constraint.
        /// </summary>
        /// <remarks>
        /// If no <see cref="Content"/> is set, <see cref="ContentEncoding.SevenBit"/> will be returned.
        /// </remarks>
        /// <returns>The most efficient content encoding.</returns>
        /// <param name="constraint">The encoding constraint.</param>
        /// <param name="maxLineLength">The maximum allowable length for a line (not counting the CRLF). Must be between <c>72</c> and <c>998</c> (inclusive).</param>
        /// <param name="cancellationToken">The cancellation token.</param>
        /// <exception cref="System.ArgumentOutOfRangeException">
        /// <para><paramref name="maxLineLength"/> is not between <c>72</c> and <c>998</c> (inclusive).</para>
        /// <para>-or-</para>
        /// <para><paramref name="constraint"/> is not a valid value.</para>
        /// </exception>
        /// <exception cref="System.OperationCanceledException">
        /// The operation was canceled via the cancellation token.
        /// </exception>
        /// <exception cref="System.IO.IOException">
        /// An I/O error occurred.
        /// </exception>
        public ContentEncoding GetBestEncoding(EncodingConstraint constraint, int maxLineLength, CancellationToken cancellationToken = default(CancellationToken))
        {
            if (ContentType.IsMimeType("text", "*") || ContentType.IsMimeType("message", "*"))
            {
                if (Content == null)
                {
                    return(ContentEncoding.SevenBit);
                }

                using (var measure = new MeasuringStream()) {
                    using (var filtered = new FilteredStream(measure)) {
                        var filter = new BestEncodingFilter();

                        filtered.Add(filter);
                        Content.DecodeTo(filtered, cancellationToken);
                        filtered.Flush();

                        return(filter.GetBestEncoding(constraint, maxLineLength));
                    }
                }
            }

            return(constraint == EncodingConstraint.None ? ContentEncoding.Binary : ContentEncoding.Base64);
        }
Example #6
0
        /// <summary>
        /// Asynchronously write the <see cref="MimePart"/> to the specified output stream.
        /// </summary>
        /// <remarks>
        /// Asynchronously writes the MIME part to the output stream.
        /// </remarks>
        /// <returns>An awaitable task.</returns>
        /// <param name="options">The formatting options.</param>
        /// <param name="stream">The output stream.</param>
        /// <param name="contentOnly"><c>true</c> if only the content should be written; otherwise, <c>false</c>.</param>
        /// <param name="cancellationToken">The cancellation token.</param>
        /// <exception cref="System.ArgumentNullException">
        /// <para><paramref name="options"/> is <c>null</c>.</para>
        /// <para>-or-</para>
        /// <para><paramref name="stream"/> is <c>null</c>.</para>
        /// </exception>
        /// <exception cref="System.OperationCanceledException">
        /// The operation was canceled via the cancellation token.
        /// </exception>
        /// <exception cref="System.IO.IOException">
        /// An I/O error occurred.
        /// </exception>
        public override async Task WriteToAsync(FormatOptions options, Stream stream, bool contentOnly, CancellationToken cancellationToken = default(CancellationToken))
        {
            await base.WriteToAsync(options, stream, contentOnly, cancellationToken).ConfigureAwait(false);

            if (Content == null)
            {
                return;
            }

            var isText = ContentType.IsMimeType("text", "*") || ContentType.IsMimeType("message", "*");

            if (Content.Encoding != encoding)
            {
                if (encoding == ContentEncoding.UUEncode)
                {
                    var begin  = string.Format("begin 0644 {0}", FileName ?? "unknown");
                    var buffer = Encoding.UTF8.GetBytes(begin);

                    await stream.WriteAsync(buffer, 0, buffer.Length, cancellationToken).ConfigureAwait(false);

                    await stream.WriteAsync(options.NewLineBytes, 0, options.NewLineBytes.Length, cancellationToken).ConfigureAwait(false);
                }

                // transcode the content into the desired Content-Transfer-Encoding
                using (var filtered = new FilteredStream(stream)) {
                    filtered.Add(EncoderFilter.Create(encoding));

                    if (encoding != ContentEncoding.Binary)
                    {
                        filtered.Add(options.CreateNewLineFilter(EnsureNewLine));
                    }

                    await Content.DecodeToAsync(filtered, cancellationToken).ConfigureAwait(false);

                    await filtered.FlushAsync(cancellationToken).ConfigureAwait(false);
                }

                if (encoding == ContentEncoding.UUEncode)
                {
                    var buffer = Encoding.ASCII.GetBytes("end");

                    await stream.WriteAsync(buffer, 0, buffer.Length, cancellationToken).ConfigureAwait(false);

                    await stream.WriteAsync(options.NewLineBytes, 0, options.NewLineBytes.Length, cancellationToken).ConfigureAwait(false);
                }
            }
            else if (encoding == ContentEncoding.Binary)
            {
                // Do not alter binary content.
                await Content.WriteToAsync(stream, cancellationToken).ConfigureAwait(false);
            }
            else if (options.VerifyingSignature && Content.NewLineFormat.HasValue && Content.NewLineFormat.Value == NewLineFormat.Mixed)
            {
                // Allow pass-through of the original parsed content without canonicalization when verifying signatures
                // if the content contains a mix of line-endings.
                //
                // See https://github.com/jstedfast/MimeKit/issues/569 for details.
                await Content.WriteToAsync(stream, cancellationToken).ConfigureAwait(false);
            }
            else
            {
                using (var filtered = new FilteredStream(stream)) {
                    // Note: if we are writing the top-level MimePart, make sure it ends with a new-line so that
                    // MimeMessage.WriteTo() *always* ends with a new-line.
                    filtered.Add(options.CreateNewLineFilter(EnsureNewLine));
                    await Content.WriteToAsync(filtered, cancellationToken).ConfigureAwait(false);

                    await filtered.FlushAsync(cancellationToken).ConfigureAwait(false);
                }
            }
        }
Example #7
0
        /// <summary>
        /// Asynchronously write the <see cref="Multipart"/> to the specified output stream.
        /// </summary>
        /// <remarks>
        /// Asynchronously writes the multipart MIME entity and its subparts to the output stream.
        /// </remarks>
        /// <returns>An awaitable task.</returns>
        /// <param name="options">The formatting options.</param>
        /// <param name="stream">The output stream.</param>
        /// <param name="contentOnly"><c>true</c> if only the content should be written; otherwise, <c>false</c>.</param>
        /// <param name="cancellationToken">The cancellation token.</param>
        /// <exception cref="System.ArgumentNullException">
        /// <para><paramref name="options"/> is <c>null</c>.</para>
        /// <para>-or-</para>
        /// <para><paramref name="stream"/> is <c>null</c>.</para>
        /// </exception>
        /// <exception cref="System.OperationCanceledException">
        /// The operation was canceled via the cancellation token.
        /// </exception>
        /// <exception cref="System.IO.IOException">
        /// An I/O error occurred.
        /// </exception>
        public override async Task WriteToAsync(FormatOptions options, Stream stream, bool contentOnly, CancellationToken cancellationToken = default(CancellationToken))
        {
            await base.WriteToAsync(options, stream, contentOnly, cancellationToken).ConfigureAwait(false);

            if (ContentType.IsMimeType("multipart", "signed"))
            {
                // don't hide or reformat the headers of any children of a multipart/signed
                if (options.International || options.HiddenHeaders.Count > 0)
                {
                    options = options.Clone();
                    options.HiddenHeaders.Clear();
                    options.International = false;
                }
            }

            if (RawPreamble != null && RawPreamble.Length > 0)
            {
                await WriteBytesAsync(options, stream, RawPreamble, children.Count > 0 || EnsureNewLine, cancellationToken).ConfigureAwait(false);
            }

            var boundary = Encoding.ASCII.GetBytes("--" + Boundary + "--");

            for (int i = 0; i < children.Count; i++)
            {
                var msg   = children[i] as MessagePart;
                var multi = children[i] as Multipart;
                var part  = children[i] as MimePart;

                await stream.WriteAsync(boundary, 0, boundary.Length - 2, cancellationToken).ConfigureAwait(false);

                await stream.WriteAsync(options.NewLineBytes, 0, options.NewLineBytes.Length, cancellationToken).ConfigureAwait(false);

                await children[i].WriteToAsync(options, stream, false, cancellationToken).ConfigureAwait(false);

                if (msg != null && msg.Message != null && msg.Message.Body != null)
                {
                    multi = msg.Message.Body as Multipart;
                    part  = msg.Message.Body as MimePart;
                }

                if ((part != null && part.Content == null) ||
                    (multi != null && !multi.WriteEndBoundary))
                {
                    continue;
                }

                await stream.WriteAsync(options.NewLineBytes, 0, options.NewLineBytes.Length, cancellationToken).ConfigureAwait(false);
            }

            if (!WriteEndBoundary)
            {
                return;
            }

            await stream.WriteAsync(boundary, 0, boundary.Length, cancellationToken).ConfigureAwait(false);

            if (RawEpilogue == null)
            {
                await stream.WriteAsync(options.NewLineBytes, 0, options.NewLineBytes.Length, cancellationToken).ConfigureAwait(false);
            }

            if (RawEpilogue != null && RawEpilogue.Length > 0)
            {
                await WriteBytesAsync(options, stream, RawEpilogue, EnsureNewLine, cancellationToken).ConfigureAwait(false);
            }
        }
Example #8
0
        /// <summary>
        /// Write the <see cref="Multipart"/> to the specified output stream.
        /// </summary>
        /// <remarks>
        /// Writes the multipart MIME entity and its subparts to the output stream.
        /// </remarks>
        /// <param name="options">The formatting options.</param>
        /// <param name="stream">The output stream.</param>
        /// <param name="contentOnly"><c>true</c> if only the content should be written; otherwise, <c>false</c>.</param>
        /// <param name="cancellationToken">The cancellation token.</param>
        /// <exception cref="System.ArgumentNullException">
        /// <para><paramref name="options"/> is <c>null</c>.</para>
        /// <para>-or-</para>
        /// <para><paramref name="stream"/> is <c>null</c>.</para>
        /// </exception>
        /// <exception cref="System.OperationCanceledException">
        /// The operation was canceled via the cancellation token.
        /// </exception>
        /// <exception cref="System.IO.IOException">
        /// An I/O error occurred.
        /// </exception>
        public override void WriteTo(FormatOptions options, Stream stream, bool contentOnly, CancellationToken cancellationToken = default(CancellationToken))
        {
            base.WriteTo(options, stream, contentOnly, cancellationToken);

            if (ContentType.IsMimeType("multipart", "signed"))
            {
                // don't reformat the headers or content of any children of a multipart/signed
                if (options.International || options.HiddenHeaders.Count > 0)
                {
                    options = options.Clone();
                    options.HiddenHeaders.Clear();
                    options.International = false;
                }
            }

            var cancellable = stream as ICancellableStream;

            if (RawPreamble != null && RawPreamble.Length > 0)
            {
                WriteBytes(options, stream, RawPreamble, children.Count > 0 || EnsureNewLine, cancellationToken);
            }

            var boundary = Encoding.ASCII.GetBytes("--" + Boundary + "--");

            if (cancellable != null)
            {
                for (int i = 0; i < children.Count; i++)
                {
                    var msg   = children[i] as MessagePart;
                    var multi = children[i] as Multipart;
                    var part  = children[i] as MimePart;

                    cancellable.Write(boundary, 0, boundary.Length - 2, cancellationToken);
                    cancellable.Write(options.NewLineBytes, 0, options.NewLineBytes.Length, cancellationToken);
                    children[i].WriteTo(options, stream, false, cancellationToken);

                    if (msg != null && msg.Message != null && msg.Message.Body != null)
                    {
                        multi = msg.Message.Body as Multipart;
                        part  = msg.Message.Body as MimePart;
                    }

                    if ((part != null && part.Content == null) ||
                        (multi != null && !multi.WriteEndBoundary))
                    {
                        continue;
                    }

                    cancellable.Write(options.NewLineBytes, 0, options.NewLineBytes.Length, cancellationToken);
                }

                if (!WriteEndBoundary)
                {
                    return;
                }

                cancellable.Write(boundary, 0, boundary.Length, cancellationToken);

                if (RawEpilogue == null)
                {
                    cancellable.Write(options.NewLineBytes, 0, options.NewLineBytes.Length, cancellationToken);
                }
            }
            else
            {
                for (int i = 0; i < children.Count; i++)
                {
                    var rfc822 = children[i] as MessagePart;
                    var multi  = children[i] as Multipart;
                    var part   = children[i] as MimePart;

                    cancellationToken.ThrowIfCancellationRequested();
                    stream.Write(boundary, 0, boundary.Length - 2);
                    stream.Write(options.NewLineBytes, 0, options.NewLineBytes.Length);
                    children[i].WriteTo(options, stream, false, cancellationToken);

                    if (rfc822 != null && rfc822.Message != null && rfc822.Message.Body != null)
                    {
                        multi = rfc822.Message.Body as Multipart;
                        part  = rfc822.Message.Body as MimePart;
                    }

                    if ((part != null && part.Content == null) ||
                        (multi != null && !multi.WriteEndBoundary))
                    {
                        continue;
                    }

                    cancellationToken.ThrowIfCancellationRequested();
                    stream.Write(options.NewLineBytes, 0, options.NewLineBytes.Length);
                }

                if (!WriteEndBoundary)
                {
                    return;
                }

                cancellationToken.ThrowIfCancellationRequested();
                stream.Write(boundary, 0, boundary.Length);

                if (RawEpilogue == null)
                {
                    cancellationToken.ThrowIfCancellationRequested();
                    stream.Write(options.NewLineBytes, 0, options.NewLineBytes.Length);
                }
            }

            if (RawEpilogue != null && RawEpilogue.Length > 0)
            {
                WriteBytes(options, stream, RawEpilogue, EnsureNewLine, cancellationToken);
            }
        }
Example #9
0
        /// <summary>
        /// Writes the <see cref="MimeKit.Multipart"/> to the specified output stream.
        /// </summary>
        /// <remarks>
        /// Writes the multipart MIME entity and its subparts to the output stream.
        /// </remarks>
        /// <param name="options">The formatting options.</param>
        /// <param name="stream">The output stream.</param>
        /// <param name="contentOnly"><c>true</c> if only the content should be written; otherwise, <c>false</c>.</param>
        /// <param name="cancellationToken">A cancellation token.</param>
        /// <exception cref="System.ArgumentNullException">
        /// <para><paramref name="options"/> is <c>null</c>.</para>
        /// <para>-or-</para>
        /// <para><paramref name="stream"/> is <c>null</c>.</para>
        /// </exception>
        /// <exception cref="System.OperationCanceledException">
        /// The operation was canceled via the cancellation token.
        /// </exception>
        /// <exception cref="System.IO.IOException">
        /// An I/O error occurred.
        /// </exception>
        public override void WriteTo(FormatOptions options, Stream stream, bool contentOnly, CancellationToken cancellationToken = default(CancellationToken))
        {
            if (Boundary == null)
            {
                Boundary = GenerateBoundary();
            }

            base.WriteTo(options, stream, contentOnly, cancellationToken);

            if (ContentType.IsMimeType("multipart", "signed"))
            {
                // don't reformat the headers or content of any children of a multipart/signed
                if (options.International || options.HiddenHeaders.Count > 0)
                {
                    options = options.Clone();
                    options.HiddenHeaders.Clear();
                    options.International = false;
                }
            }

            var cancellable = stream as ICancellableStream;

            if (RawPreamble != null && RawPreamble.Length > 0)
            {
                WriteBytes(options, stream, RawPreamble, cancellationToken);
            }

            var boundary = Encoding.ASCII.GetBytes("--" + Boundary + "--");

            if (cancellable != null)
            {
                for (int i = 0; i < children.Count; i++)
                {
                    var part = children[i] as MimePart;

                    cancellable.Write(boundary, 0, boundary.Length - 2, cancellationToken);
                    cancellable.Write(options.NewLineBytes, 0, options.NewLineBytes.Length, cancellationToken);
                    children[i].WriteTo(options, stream, false, cancellationToken);

                    if (part == null || (part.ContentObject != null && part.ContentObject.Stream.Length != 0))
                    {
                        cancellable.Write(options.NewLineBytes, 0, options.NewLineBytes.Length, cancellationToken);
                    }
                }

                cancellable.Write(boundary, 0, boundary.Length, cancellationToken);

                if (RawEpilogue == null)
                {
                    cancellable.Write(options.NewLineBytes, 0, options.NewLineBytes.Length, cancellationToken);
                }
            }
            else
            {
                for (int i = 0; i < children.Count; i++)
                {
                    var part = children[i] as MimePart;

                    cancellationToken.ThrowIfCancellationRequested();
                    stream.Write(boundary, 0, boundary.Length - 2);
                    stream.Write(options.NewLineBytes, 0, options.NewLineBytes.Length);
                    children[i].WriteTo(options, stream, false, cancellationToken);

                    if (part == null || (part.ContentObject != null && part.ContentObject.Stream.Length != 0))
                    {
                        stream.Write(options.NewLineBytes, 0, options.NewLineBytes.Length);
                    }
                }

                cancellationToken.ThrowIfCancellationRequested();
                stream.Write(boundary, 0, boundary.Length);

                if (RawEpilogue == null)
                {
                    stream.Write(options.NewLineBytes, 0, options.NewLineBytes.Length);
                }
            }

            if (RawEpilogue != null && RawEpilogue.Length > 0)
            {
                WriteBytes(options, stream, RawEpilogue, cancellationToken);
            }
        }