/// <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); } }
/// <summary> /// Writes the <see cref="MimeKit.MimePart"/> to the specified output stream. /// </summary> /// <param name="options">The formatting options.</param> /// <param name="stream">The output stream.</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, CancellationToken cancellationToken) { base.WriteTo(options, stream, cancellationToken); if (ContentObject == null) { return; } if (ContentObject.Encoding != encoding) { if (encoding == ContentEncoding.UUEncode) { cancellationToken.ThrowIfCancellationRequested(); var begin = string.Format("begin 0644 {0}", FileName ?? "unknown"); var buffer = Encoding.UTF8.GetBytes(begin); stream.Write(buffer, 0, buffer.Length); stream.Write(options.NewLineBytes, 0, options.NewLineBytes.Length); } // 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()); } ContentObject.DecodeTo(filtered, cancellationToken); filtered.Flush(); } if (encoding == ContentEncoding.UUEncode) { cancellationToken.ThrowIfCancellationRequested(); var buffer = Encoding.ASCII.GetBytes("end"); stream.Write(buffer, 0, buffer.Length); stream.Write(options.NewLineBytes, 0, options.NewLineBytes.Length); } } else if (encoding != ContentEncoding.Binary) { using (var filtered = new FilteredStream(stream)) { filtered.Add(options.CreateNewLineFilter()); ContentObject.WriteTo(filtered, cancellationToken); filtered.Flush(); } } else { ContentObject.WriteTo(stream, cancellationToken); } }
/// <summary> /// Writes the <see cref="MimeKit.HeaderList"/> to the specified output stream. /// </summary> /// <remarks> /// Writes all of the headers to the output stream. /// </remarks> /// <param name="options">The formatting options.</param> /// <param name="stream">The output stream.</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 void WriteTo(FormatOptions options, Stream stream, CancellationToken cancellationToken = default(CancellationToken)) { if (options == null) { throw new ArgumentNullException("options"); } if (stream == null) { throw new ArgumentNullException("stream"); } cancellationToken.ThrowIfCancellationRequested(); using (var filtered = new FilteredStream(stream)) { filtered.Add(options.CreateNewLineFilter()); foreach (var header in headers) { cancellationToken.ThrowIfCancellationRequested(); var name = Encoding.ASCII.GetBytes(header.Field); filtered.Write(name, 0, name.Length); filtered.WriteByte((byte)':'); filtered.Write(header.RawValue, 0, header.RawValue.Length); } filtered.Flush(); } }
static Task WriteBytesAsync(FormatOptions options, Stream stream, byte[] bytes, bool ensureNewLine, CancellationToken cancellationToken) { var filter = options.CreateNewLineFilter(ensureNewLine); int index, length; var output = filter.Flush(bytes, 0, bytes.Length, out index, out length); return(stream.WriteAsync(output, index, length, cancellationToken)); }
static void WriteBytes(FormatOptions options, Stream stream, byte[] bytes) { var filter = options.CreateNewLineFilter(); int index, length; var output = filter.Flush(bytes, 0, bytes.Length, out index, out length); stream.Write(output, index, length); }
/// <summary> /// Writes the message to the specified stream. /// </summary> /// <param name="options">The formatting options.</param> /// <param name="stream">The stream.</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> public void WriteTo(FormatOptions options, Stream stream) { if (options == null) { throw new ArgumentNullException("options"); } if (stream == null) { throw new ArgumentNullException("stream"); } if (!Headers.Contains("Date")) { Date = DateTimeOffset.Now; } if (messageId == null) { MessageId = MimeUtils.GenerateMessageId(); } if (version == null && Body != null && Body.Headers.Count > 0) { MimeVersion = new Version(1, 0); } if (Body == null) { Headers.WriteTo(stream); stream.Write(options.NewLineBytes, 0, options.NewLineBytes.Length); } else { using (var filtered = new FilteredStream(stream)) { filtered.Add(options.CreateNewLineFilter()); foreach (var header in MergeHeaders()) { var name = Encoding.ASCII.GetBytes(header.Field); filtered.Write(name, 0, name.Length); filtered.WriteByte((byte)':'); filtered.Write(header.RawValue, 0, header.RawValue.Length); } filtered.Flush(); } options.WriteHeaders = false; Body.WriteTo(options, stream); } }
static void WriteBytes(FormatOptions options, Stream stream, byte[] bytes, bool ensureNewLine, CancellationToken cancellationToken) { var cancellable = stream as ICancellableStream; var filter = options.CreateNewLineFilter(ensureNewLine); int index, length; var output = filter.Flush(bytes, 0, bytes.Length, out index, out length); if (cancellable != null) { cancellable.Write(output, index, length, cancellationToken); } else { cancellationToken.ThrowIfCancellationRequested(); stream.Write(output, index, length); } }
/// <summary> /// Writes the <see cref="MimeKit.HeaderList"/> to the specified output stream. /// </summary> /// <remarks> /// Writes all of the headers to the output stream. /// </remarks> /// <param name="options">The formatting options.</param> /// <param name="stream">The output stream.</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 void WriteTo(FormatOptions options, Stream stream, CancellationToken cancellationToken = default(CancellationToken)) { if (options == null) { throw new ArgumentNullException("options"); } if (stream == null) { throw new ArgumentNullException("stream"); } using (var filtered = new FilteredStream(stream)) { if (options.NewLineFormat != FormatOptions.Default.NewLineFormat) { filtered.Add(options.CreateNewLineFilter()); } foreach (var header in headers) { var name = Encoding.ASCII.GetBytes(header.Field); byte[] rawValue; filtered.Write(name, 0, name.Length, cancellationToken); filtered.Write(new [] { (byte)':' }, 0, 1, cancellationToken); if (options.International) { rawValue = header.GetRawValue(options, Encoding.UTF8); } else { rawValue = header.RawValue; } filtered.Write(rawValue, 0, rawValue.Length, cancellationToken); } filtered.Flush(cancellationToken); } }
/// <summary> /// 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">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)) { base.WriteTo(options, stream, contentOnly, cancellationToken); if (ContentObject == null) { return; } var cancellable = stream as ICancellableStream; if (ContentObject.Encoding != encoding) { if (encoding == ContentEncoding.UUEncode) { var begin = string.Format("begin 0644 {0}", FileName ?? "unknown"); var buffer = Encoding.UTF8.GetBytes(begin); if (cancellable != null) { cancellable.Write(buffer, 0, buffer.Length, cancellationToken); cancellable.Write(options.NewLineBytes, 0, options.NewLineBytes.Length, cancellationToken); } else { cancellationToken.ThrowIfCancellationRequested(); stream.Write(buffer, 0, buffer.Length); stream.Write(options.NewLineBytes, 0, options.NewLineBytes.Length); } } // 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(true)); } ContentObject.DecodeTo(filtered, cancellationToken); filtered.Flush(cancellationToken); } if (encoding == ContentEncoding.UUEncode) { var buffer = Encoding.ASCII.GetBytes("end"); if (cancellable != null) { cancellable.Write(buffer, 0, buffer.Length, cancellationToken); cancellable.Write(options.NewLineBytes, 0, options.NewLineBytes.Length, cancellationToken); } else { cancellationToken.ThrowIfCancellationRequested(); stream.Write(buffer, 0, buffer.Length); stream.Write(options.NewLineBytes, 0, options.NewLineBytes.Length); } } } 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)); ContentObject.WriteTo(filtered, cancellationToken); filtered.Flush(cancellationToken); } } else { ContentObject.WriteTo(stream, cancellationToken); } }
/// <summary> /// Writes the message to the specified output stream. /// </summary> /// <remarks> /// Writes the message to the output stream using the provided formatting options. /// </remarks> /// <param name="options">The formatting options.</param> /// <param name="stream">The output stream.</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 void WriteTo (FormatOptions options, Stream stream, CancellationToken cancellationToken = default (CancellationToken)) { if (options == null) throw new ArgumentNullException ("options"); if (stream == null) throw new ArgumentNullException ("stream"); if (version == null && Body != null && Body.Headers.Count > 0) MimeVersion = new Version (1, 0); if (Body != null) { using (var filtered = new FilteredStream (stream)) { filtered.Add (options.CreateNewLineFilter ()); foreach (var header in MergeHeaders ()) { if (options.HiddenHeaders.Contains (header.Id)) continue; filtered.Write (header.RawField, 0, header.RawField.Length, cancellationToken); filtered.Write (new [] { (byte) ':' }, 0, 1, cancellationToken); filtered.Write (header.RawValue, 0, header.RawValue.Length, cancellationToken); } filtered.Flush (cancellationToken); } var cancellable = stream as ICancellableStream; if (cancellable != null) { cancellable.Write (options.NewLineBytes, 0, options.NewLineBytes.Length, cancellationToken); } else { cancellationToken.ThrowIfCancellationRequested (); stream.Write (options.NewLineBytes, 0, options.NewLineBytes.Length); } try { Body.Headers.Suppress = true; Body.WriteTo (options, stream, cancellationToken); } finally { Body.Headers.Suppress = false; } } else { Headers.WriteTo (options, stream, cancellationToken); } }
/// <summary> /// Verify the specified DKIM-Signature header. /// </summary> /// <remarks> /// Verifies the specified DKIM-Signature header. /// </remarks> /// <param name="options">The formatting options.</param> /// <param name="dkimSignature">The DKIM-Signature header.</param> /// <param name="publicKeyLocator">The public key locator service.</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="dkimSignature"/> is <c>null</c>.</para> /// <para>-or-</para> /// <para><paramref name="publicKeyLocator"/> is <c>null</c>.</para> /// </exception> /// <exception cref="System.ArgumentException"> /// <paramref name="dkimSignature"/> is not a DKIM-Signature header. /// </exception> /// <exception cref="System.FormatException"> /// The DKIM-Signature header value is malformed. /// </exception> /// <exception cref="System.OperationCanceledException"> /// The operation was canceled via the cancellation token. /// </exception> bool Verify (FormatOptions options, Header dkimSignature, IDkimPublicKeyLocator publicKeyLocator, CancellationToken cancellationToken = default (CancellationToken)) { if (options == null) throw new ArgumentNullException ("options"); if (dkimSignature == null) throw new ArgumentNullException ("dkimSignature"); if (dkimSignature.Id != HeaderId.DkimSignature) throw new ArgumentException ("The dkimSignature parameter MUST be a DKIM-Signature header.", "dkimSignature"); if (publicKeyLocator == null) throw new ArgumentNullException ("publicKeyLocator"); var parameters = ParseDkimSignature (dkimSignature.Value); DkimCanonicalizationAlgorithm headerAlgorithm, bodyAlgorithm; DkimSignatureAlgorithm signatureAlgorithm; AsymmetricKeyParameter key; string d, s, q, h, bh, b; int maxLength; ValidateDkimSignatureParameters (parameters, out signatureAlgorithm, out headerAlgorithm, out bodyAlgorithm, out d, out s, out q, out h, out bh, out b, out maxLength); key = publicKeyLocator.LocatePublicKey (q, d, s, cancellationToken); options = options.Clone (); options.NewLineFormat = NewLineFormat.Dos; // first check the body hash (if that's invalid, then the entire signature is invalid) var hash = Convert.ToBase64String (DkimHashBody (options, signatureAlgorithm, bodyAlgorithm, maxLength)); if (hash != bh) return false; using (var stream = new DkimSignatureStream (DkimGetDigestSigner (signatureAlgorithm, key))) { using (var filtered = new FilteredStream (stream)) { filtered.Add (options.CreateNewLineFilter ()); DkimWriteHeaders (options, h.Split (':'), headerAlgorithm, filtered); // now include the DKIM-Signature header that we are verifying, // but only after removing the "b=" signature value. var header = GetSignedDkimSignatureHeader (dkimSignature); switch (headerAlgorithm) { case DkimCanonicalizationAlgorithm.Relaxed: DkimWriteHeaderRelaxed (options, filtered, header); break; default: DkimWriteHeaderSimple (options, filtered, header); break; } filtered.Flush (); } return stream.VerifySignature (b); } }
/// <summary> /// Digitally sign the message using a DomainKeys Identified Mail (DKIM) signature. /// </summary> /// <remarks> /// Digitally signs the message using a DomainKeys Identified Mail (DKIM) signature. /// </remarks> /// <param name="options">The formatting options.</param> /// <param name="signer">The DKIM signer.</param> /// <param name="headers">The list of header fields to sign.</param> /// <param name="headerCanonicalizationAlgorithm">The header canonicalization algorithm.</param> /// <param name="bodyCanonicalizationAlgorithm">The body canonicalization algorithm.</param> /// <exception cref="System.ArgumentNullException"> /// <para><paramref name="options"/> is <c>null</c>.</para> /// <para>-or-</para> /// <para><paramref name="signer"/> is <c>null</c>.</para> /// <para>-or-</para> /// <para><paramref name="headers"/> is <c>null</c>.</para> /// </exception> /// <exception cref="System.ArgumentException"> /// <para><paramref name="headers"/> does not contain the 'From' header.</para> /// <para>-or-</para> /// <para><paramref name="headers"/> contains one or more of the following headers: Return-Path, /// Received, Comments, Keywords, Bcc, Resent-Bcc, or DKIM-Signature.</para> /// </exception> void Sign (FormatOptions options, DkimSigner signer, IList<HeaderId> headers, DkimCanonicalizationAlgorithm headerCanonicalizationAlgorithm = DkimCanonicalizationAlgorithm.Simple, DkimCanonicalizationAlgorithm bodyCanonicalizationAlgorithm = DkimCanonicalizationAlgorithm.Simple) { if (options == null) throw new ArgumentNullException ("options"); if (signer == null) throw new ArgumentNullException ("signer"); if (headers == null) throw new ArgumentNullException ("headers"); if (!headers.Contains (HeaderId.From)) throw new ArgumentException ("The list of headers to sign MUST include the 'From' header."); var fields = new string[headers.Count]; for (int i = 0; i < headers.Count; i++) { if (DkimShouldNotInclude.Contains (headers[i])) throw new ArgumentException (string.Format ("The list of headers to sign SHOULD NOT include the '{0}' header.", headers[i].ToHeaderName ())); fields[i] = headers[i].ToHeaderName ().ToLowerInvariant (); } if (version == null && Body != null && Body.Headers.Count > 0) MimeVersion = new Version (1, 0); Prepare (EncodingConstraint.SevenBit, 78); var t = DateTime.Now - DateUtils.UnixEpoch; var value = new StringBuilder ("v=1"); byte[] signature, hash; Header dkim; options = options.Clone (); options.NewLineFormat = NewLineFormat.Dos; switch (signer.SignatureAlgorithm) { case DkimSignatureAlgorithm.RsaSha256: value.Append ("; a=rsa-sha256"); break; default: value.Append ("; a=rsa-sha1"); break; } value.AppendFormat ("; d={0}; s={1}", signer.Domain, signer.Selector); value.AppendFormat ("; c={0}/{1}", headerCanonicalizationAlgorithm.ToString ().ToLowerInvariant (), bodyCanonicalizationAlgorithm.ToString ().ToLowerInvariant ()); if (!string.IsNullOrEmpty (signer.QueryMethod)) value.AppendFormat ("; q={0}", signer.QueryMethod); if (!string.IsNullOrEmpty (signer.AgentOrUserIdentifier)) value.AppendFormat ("; i={0}", signer.AgentOrUserIdentifier); value.AppendFormat ("; t={0}", (long) t.TotalSeconds); using (var stream = new DkimSignatureStream (DkimGetDigestSigner (signer.SignatureAlgorithm, signer.PrivateKey))) { using (var filtered = new FilteredStream (stream)) { filtered.Add (options.CreateNewLineFilter ()); // write the specified message headers DkimWriteHeaders (options, fields, headerCanonicalizationAlgorithm, filtered); value.AppendFormat ("; h={0}", string.Join (":", fields.ToArray ())); hash = DkimHashBody (options, signer.SignatureAlgorithm, bodyCanonicalizationAlgorithm, -1); value.AppendFormat ("; bh={0}", Convert.ToBase64String (hash)); value.Append ("; b="); dkim = new Header (HeaderId.DkimSignature, value.ToString ()); Headers.Insert (0, dkim); switch (headerCanonicalizationAlgorithm) { case DkimCanonicalizationAlgorithm.Relaxed: DkimWriteHeaderRelaxed (options, filtered, dkim); break; default: DkimWriteHeaderSimple (options, filtered, dkim); break; } filtered.Flush (); } signature = stream.GenerateSignature (); dkim.Value += Convert.ToBase64String (signature); } }
byte[] DkimHashBody (FormatOptions options, DkimSignatureAlgorithm signatureAlgorithm, DkimCanonicalizationAlgorithm bodyCanonicalizationAlgorithm, int maxLength) { using (var stream = new DkimHashStream (signatureAlgorithm, maxLength)) { using (var filtered = new FilteredStream (stream)) { filtered.Add (options.CreateNewLineFilter ()); if (bodyCanonicalizationAlgorithm == DkimCanonicalizationAlgorithm.Relaxed) filtered.Add (new DkimRelaxedBodyFilter ()); else filtered.Add (new DkimSimpleBodyFilter ()); if (Body != null) { try { Body.Headers.Suppress = true; Body.WriteTo (options, stream, CancellationToken.None); } finally { Body.Headers.Suppress = false; } } filtered.Flush (); } return stream.GenerateHash (); } }
/// <summary> /// Writes the message to the specified stream. /// </summary> /// <param name="options">The formatting options.</param> /// <param name="stream">The stream.</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> public void WriteTo(FormatOptions options, Stream stream) { if (options == null) throw new ArgumentNullException ("options"); if (stream == null) throw new ArgumentNullException ("stream"); if (!Headers.Contains ("Date")) Date = DateTimeOffset.Now; if (messageId == null) MessageId = MimeUtils.GenerateMessageId (); if (version == null && Body != null && Body.Headers.Count > 0) MimeVersion = new Version (1, 0); if (Body == null) { Headers.WriteTo (stream); stream.Write (options.NewLineBytes, 0, options.NewLineBytes.Length); } else { using (var filtered = new FilteredStream (stream)) { filtered.Add (options.CreateNewLineFilter ()); foreach (var header in MergeHeaders ()) { var name = Encoding.ASCII.GetBytes (header.Field); filtered.Write (name, 0, name.Length); filtered.WriteByte ((byte) ':'); filtered.Write (header.RawValue, 0, header.RawValue.Length); } filtered.Flush (); } options.WriteHeaders = false; Body.WriteTo (options, stream); } }
/// <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); } } }
byte[] DkimHashBody (FormatOptions options, DkimSignatureAlgorithm signatureAlgorithm, DkimCanonicalizationAlgorithm bodyCanonicalizationAlgorithm, int maxLength) { using (var stream = new DkimHashStream (signatureAlgorithm, maxLength)) { using (var filtered = new FilteredStream (stream)) { DkimBodyFilter dkim; if (bodyCanonicalizationAlgorithm == DkimCanonicalizationAlgorithm.Relaxed) dkim = new DkimRelaxedBodyFilter (); else dkim = new DkimSimpleBodyFilter (); filtered.Add (options.CreateNewLineFilter ()); filtered.Add (dkim); if (Body != null) { try { Body.EnsureNewLine = compliance == RfcComplianceMode.Strict; Body.Headers.Suppress = true; Body.WriteTo (options, filtered, CancellationToken.None); } finally { Body.Headers.Suppress = false; Body.EnsureNewLine = false; } } filtered.Flush (); if (!dkim.LastWasNewLine) stream.Write (options.NewLineBytes, 0, options.NewLineBytes.Length); } return stream.GenerateHash (); } }
/// <summary> /// Writes the <see cref="MimeKit.MimePart"/> to the specified stream. /// </summary> /// <param name="options">The formatting options.</param> /// <param name="stream">The stream.</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> public override void WriteTo(FormatOptions options, Stream stream) { base.WriteTo (options, stream); if (ContentObject == null) return; if (ContentObject.Encoding != encoding) { if (encoding == ContentEncoding.UUEncode) { var begin = string.Format ("begin 0644 {0}", FileName ?? "unknown"); var buffer = Encoding.UTF8.GetBytes (begin); stream.Write (buffer, 0, buffer.Length); stream.Write (options.NewLineBytes, 0, options.NewLineBytes.Length); } // transcode the content into the desired Content-Transfer-Encoding using (var filtered = new FilteredStream (stream)) { filtered.Add (EncoderFilter.Create (encoding)); filtered.Add (options.CreateNewLineFilter ()); ContentObject.DecodeTo (filtered); filtered.Flush (); } if (encoding == ContentEncoding.UUEncode) { var buffer = Encoding.ASCII.GetBytes ("end"); stream.Write (buffer, 0, buffer.Length); stream.Write (options.NewLineBytes, 0, options.NewLineBytes.Length); } } else { using (var filtered = new FilteredStream (stream)) { filtered.Add (options.CreateNewLineFilter ()); ContentObject.WriteTo (filtered); filtered.Flush (); } } }
/// <summary> /// 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="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, CancellationToken cancellationToken = default (CancellationToken)) { base.WriteTo (options, stream, cancellationToken); if (ContentObject == null) return; var cancellable = stream as ICancellableStream; if (ContentObject.Encoding != encoding) { if (encoding == ContentEncoding.UUEncode) { var begin = string.Format ("begin 0644 {0}", FileName ?? "unknown"); var buffer = Encoding.UTF8.GetBytes (begin); if (cancellable != null) { cancellable.Write (buffer, 0, buffer.Length, cancellationToken); cancellable.Write (options.NewLineBytes, 0, options.NewLineBytes.Length, cancellationToken); } else { cancellationToken.ThrowIfCancellationRequested (); stream.Write (buffer, 0, buffer.Length); stream.Write (options.NewLineBytes, 0, options.NewLineBytes.Length); } } // 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 (true)); ContentObject.DecodeTo (filtered, cancellationToken); filtered.Flush (cancellationToken); } if (encoding == ContentEncoding.UUEncode) { var buffer = Encoding.ASCII.GetBytes ("end"); if (cancellable != null) { cancellable.Write (buffer, 0, buffer.Length, cancellationToken); cancellable.Write (options.NewLineBytes, 0, options.NewLineBytes.Length, cancellationToken); } else { cancellationToken.ThrowIfCancellationRequested (); stream.Write (buffer, 0, buffer.Length); stream.Write (options.NewLineBytes, 0, options.NewLineBytes.Length); } } } 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 (Headers.Suppress)); ContentObject.WriteTo (filtered, cancellationToken); filtered.Flush (cancellationToken); } } else { ContentObject.WriteTo (stream, cancellationToken); } }
/// <summary> /// Writes the message to the specified output stream. /// </summary> /// <remarks> /// Writes the message to the output stream using the provided formatting options. /// </remarks> /// <param name="options">The formatting options.</param> /// <param name="stream">The output stream.</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 void WriteTo (FormatOptions options, Stream stream, CancellationToken cancellationToken = default (CancellationToken)) { if (options == null) throw new ArgumentNullException ("options"); if (stream == null) throw new ArgumentNullException ("stream"); if (version == null && Body != null && Body.Headers.Count > 0) MimeVersion = new Version (1, 0); if (Body == null) { Headers.WriteTo (options, stream, cancellationToken); cancellationToken.ThrowIfCancellationRequested (); stream.Write (options.NewLineBytes, 0, options.NewLineBytes.Length); } else { cancellationToken.ThrowIfCancellationRequested (); using (var filtered = new FilteredStream (stream)) { filtered.Add (options.CreateNewLineFilter ()); foreach (var header in MergeHeaders ()) { if (options.HiddenHeaders.Contains (header.Id)) continue; cancellationToken.ThrowIfCancellationRequested (); var name = Encoding.ASCII.GetBytes (header.Field); filtered.Write (name, 0, name.Length); filtered.WriteByte ((byte) ':'); filtered.Write (header.RawValue, 0, header.RawValue.Length); } filtered.Flush (); } options.WriteHeaders = false; Body.WriteTo (options, stream, cancellationToken); } }