/// <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); } } } }
/// <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); } }
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; } } }
/// <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); } } }
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); }