Example #1
0
        /// <summary>
        /// Asynchronously writes the <see cref="MimeKit.MimePart"/> to the specified output stream.
        /// </summary>
        /// <remarks>
        /// Writes the MIME part 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 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;
            }

            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)
            {
                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);
                }
            }
            else
            {
                await Content.WriteToAsync(stream, cancellationToken).ConfigureAwait(false);
            }
        }
        public async Task TestWriteAsync()
        {
            using (var original = new MemoryStream()) {
                using (var file = File.OpenRead("../../TestData/encoders/photo.jpg"))
                    file.CopyTo(original, 4096);

                using (var decoded = new MemoryStream()) {
                    using (var file = File.OpenRead("../../TestData/encoders/photo.b64")) {
                        using (var filtered = new FilteredStream(decoded)) {
                            filtered.Add(DecoderFilter.Create(ContentEncoding.Base64));
                            await file.CopyToAsync(filtered, 4096);

                            await filtered.FlushAsync();
                        }
                    }

                    var buf0 = original.GetBuffer();
                    var buf1 = decoded.GetBuffer();
                    int n    = (int)original.Length;

                    Assert.AreEqual(original.Length, decoded.Length, "Decoded length is incorrect.");

                    for (int i = 0; i < n; i++)
                    {
                        Assert.AreEqual(buf0[i], buf1[i], "The byte at offset {0} does not match.", i);
                    }
                }
            }
        }
Example #3
0
        /// <summary>
        /// Asynchronously decodes the content stream into another stream.
        /// </summary>
        /// <remarks>
        /// If the content stream is encoded, this method will decode it into the output stream
        /// using a suitable decoder based on the <see cref="Encoding"/> property, otherwise the
        /// stream will be copied into the output stream as-is.
        /// </remarks>
        /// <param name="stream">The output stream.</param>
        /// <param name="cancellationToken">The cancellation token.</param>
        /// <exception cref="System.ArgumentNullException">
        /// <paramref name="stream"/> is <c>null</c>.
        /// </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>
        /// <example>
        /// <code language="c#" source="Examples\AttachmentExamples.cs" region="SaveAttachments" />
        /// </example>
        public async Task DecodeToAsync(Stream stream, CancellationToken cancellationToken = default(CancellationToken))
        {
            if (stream == null)
            {
                throw new ArgumentNullException(nameof(stream));
            }

            using (var filtered = new FilteredStream(stream)) {
                filtered.Add(DecoderFilter.Create(Encoding));
                await WriteToAsync(filtered, cancellationToken).ConfigureAwait(false);

                await filtered.FlushAsync(cancellationToken).ConfigureAwait(false);
            }
        }
Example #4
0
        internal async Task SaveToPickupDirectoryAsync(MimeMessage message)
        {
            var    path = Path.Combine("C:/Emails", Guid.NewGuid().ToString() + ".eml");
            Stream stream;

            stream = File.Open(path, FileMode.CreateNew);
            using (stream)
            {
                using (var filtered = new FilteredStream(stream))
                {
                    filtered.Add(new SmtpDataFilter());

                    var options = FormatOptions.Default.Clone();
                    options.NewLineFormat = NewLineFormat.Dos;

                    await message.WriteToAsync(options, filtered);

                    await filtered.FlushAsync();

                    return;
                }
            }
        }
Example #5
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 #6
0
        public virtual async Task SaveAsync(string pickupDirectory, MailMessage message)
        {
            Guard.NotEmpty(pickupDirectory, nameof(pickupDirectory));
            Guard.NotNull(message, nameof(message));

            var mimeMessage = BuildMimeMessage(message);

            do
            {
                // Generate a random file name to save the message to.
                var    path = Path.Combine(pickupDirectory, Guid.NewGuid().ToString() + ".eml");
                Stream stream;

                try
                {
                    // Attempt to create the new file.
                    stream = File.Open(path, FileMode.CreateNew);
                }
                catch (IOException)
                {
                    // If the file already exists, try again with a new Guid.
                    if (File.Exists(path))
                    {
                        continue;
                    }

                    // Otherwise, fail immediately since it probably means that there is
                    // no graceful way to recover from this error.
                    throw;
                }

                try
                {
                    using (stream)
                    {
                        // IIS pickup directories expect the message to be "byte-stuffed"
                        // which means that lines beginning with "." need to be escaped
                        // by adding an extra "." to the beginning of the line.
                        //
                        // Use an SmtpDataFilter "byte-stuff" the message as it is written
                        // to the file stream. This is the same process that an SmtpClient
                        // would use when sending the message in a `DATA` command.
                        using (var filtered = new FilteredStream(stream))
                        {
                            filtered.Add(new SmtpDataFilter());

                            // Make sure to write the message in DOS (<CR><LF>) format.
                            var options = FormatOptions.Default.Clone();
                            options.NewLineFormat = NewLineFormat.Dos;

                            await mimeMessage.WriteToAsync(options, filtered);

                            await filtered.FlushAsync();

                            return;
                        }
                    }
                }
                catch
                {
                    // An exception here probably means that the disk is full.
                    //
                    // Delete the file that was created above so that incomplete files are not
                    // left behind for IIS to send accidentally.
                    File.Delete(path);
                    throw;
                }
            } while (true);
        }