public void TestBase64Encode () { using (var original = new MemoryStream ()) { using (var file = File.OpenRead ("../../TestData/encoders/photo.b64")) file.CopyTo (original, 4096); using (var encoded = new MemoryStream ()) { using (var filtered = new FilteredStream (encoded)) { filtered.Add (EncoderFilter.Create (ContentEncoding.Base64)); using (var file = File.OpenRead ("../../TestData/encoders/photo.jpg")) file.CopyTo (filtered, 4096); filtered.Flush (); } var buf0 = original.GetBuffer (); var buf1 = encoded.GetBuffer (); int n = (int) original.Length; Assert.AreEqual (original.Length, encoded.Length, "Encoded 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); } } }
public ImapReplayCommand (string command, string resource) { Command = command; using (var stream = GetType ().Assembly.GetManifestResourceStream ("UnitTests.Net.Imap.Resources." + resource)) { var memory = new MemoryBlockStream (); using (var filtered = new FilteredStream (memory)) { filtered.Add (new Unix2DosFilter ()); stream.CopyTo (filtered, 4096); } Response = memory.ToArray (); } }
public void TestDecodeSimpleYEncMessage () { using (var file = File.OpenRead ("../../TestData/yenc/simple.msg")) { var message = MimeMessage.Load (file); using (var decoded = new MemoryStream ()) { var ydec = new YDecoder (); using (var filtered = new FilteredStream (decoded)) { filtered.Add (new DecoderFilter (ydec)); ((MimePart) message.Body).ContentObject.WriteTo (filtered); } Assert.AreEqual (584, decoded.Length, "The decoded size does not match."); Assert.AreEqual (0xded29f4f, ydec.Checksum ^ 0xffffffff, "The checksum does not match."); } } }
public void TestBase64Decode () { 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 (file)) { filtered.Add (DecoderFilter.Create (ContentEncoding.Base64)); filtered.CopyTo (decoded, 4096); } } 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); } } var decoder = new Base64Decoder (); var output = new byte[4096]; Assert.AreEqual (ContentEncoding.Base64, decoder.Encoding); for (int i = 0; i < base64EncodedPatterns.Length; i++) { decoder.Reset (); var buf = Encoding.ASCII.GetBytes (base64EncodedPatterns[i]); int n = decoder.Decode (buf, 0, buf.Length, output); var actual = Encoding.ASCII.GetString (output, 0, n); Assert.AreEqual (base64DecodedPatterns[i], actual, "Failed to decode base64EncodedPatterns[{0}]", i); } for (int i = 0; i < base64EncodedLongPatterns.Length; i++) { decoder.Reset (); var buf = Encoding.ASCII.GetBytes (base64EncodedLongPatterns[i]); int n = decoder.Decode (buf, 0, buf.Length, output); for (int j = 0; j < n; j++) Assert.AreEqual (output[j], (byte) (j + i), "Failed to decode base64EncodedLongPatterns[{0}]", i); } }
void Data(MimeMessage message, CancellationToken cancellationToken) { var response = SendCommand ("DATA", cancellationToken); if (response.StatusCode != SmtpStatusCode.StartMailInput) throw new SmtpCommandException (SmtpErrorCode.UnexpectedStatusCode, response.StatusCode, response.Response); var options = FormatOptions.Default.Clone (); options.NewLineFormat = NewLineFormat.Dos; options.HiddenHeaders.Add (HeaderId.ContentLength); options.HiddenHeaders.Add (HeaderId.ResentBcc); options.HiddenHeaders.Add (HeaderId.Bcc); using (var filtered = new FilteredStream (stream)) { filtered.Add (new SmtpDataFilter ()); message.WriteTo (options, filtered, cancellationToken); filtered.Flush (); } stream.Write (EndData, 0, EndData.Length); response = ReadResponse (cancellationToken); switch (response.StatusCode) { default: throw new SmtpCommandException (SmtpErrorCode.MessageNotAccepted, response.StatusCode, response.Response); case SmtpStatusCode.AuthenticationRequired: throw new UnauthorizedAccessException (response.Response); case SmtpStatusCode.Ok: break; } }
/// <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); } }
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> /// Creates a new <see cref="MultipartSigned"/>. /// </summary> /// <remarks> /// Cryptographically signs the entity using the supplied signer in order /// to generate a detached signature and then adds the entity along with /// the detached signature data to a new multipart/signed part. /// </remarks> /// <returns>A new <see cref="MultipartSigned"/> instance.</returns> /// <param name="ctx">The S/MIME context to use for signing.</param> /// <param name="signer">The signer.</param> /// <param name="entity">The entity to sign.</param> /// <exception cref="System.ArgumentNullException"> /// <para><paramref name="ctx"/> is <c>null</c>.</para> /// <para>-or-</para> /// <para><paramref name="signer"/> is <c>null</c>.</para> /// <para>-or-</para> /// <para><paramref name="entity"/> is <c>null</c>.</para> /// </exception> /// <exception cref="Org.BouncyCastle.Cms.CmsException"> /// An error occurred in the cryptographic message syntax subsystem. /// </exception> public static MultipartSigned Create (SecureMimeContext ctx, CmsSigner signer, MimeEntity entity) { if (ctx == null) throw new ArgumentNullException ("ctx"); if (signer == null) throw new ArgumentNullException ("signer"); if (entity == null) throw new ArgumentNullException ("entity"); PrepareEntityForSigning (entity); using (var memory = new MemoryBlockStream ()) { using (var filtered = new FilteredStream (memory)) { // Note: see rfc3156, section 3 - second note filtered.Add (new ArmoredFromFilter ()); // Note: see rfc3156, section 5.4 (this is the main difference between rfc2015 and rfc3156) filtered.Add (new TrailingWhitespaceFilter ()); // Note: see rfc2015 or rfc3156, section 5.1 filtered.Add (new Unix2DosFilter ()); entity.WriteTo (filtered); filtered.Flush (); } memory.Position = 0; // Note: we need to parse the modified entity structure to preserve any modifications var parser = new MimeParser (memory, MimeFormat.Entity); var parsed = parser.ParseEntity (); memory.Position = 0; // sign the cleartext content var micalg = ctx.GetDigestAlgorithmName (signer.DigestAlgorithm); var signature = ctx.Sign (signer, memory); var signed = new MultipartSigned (); // set the protocol and micalg Content-Type parameters signed.ContentType.Parameters["protocol"] = ctx.SignatureProtocol; signed.ContentType.Parameters["micalg"] = micalg; // add the modified/parsed entity as our first part signed.Add (parsed); // add the detached signature as the second part signed.Add (signature); return signed; } }
/// <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> /// 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> public string GetText (Encoding charset) { if (charset == null) throw new ArgumentNullException ("charset"); using (var memory = new MemoryStream ()) { using (var filtered = new FilteredStream (memory)) { filtered.Add (new CharsetFilter (charset, Encoding.UTF8)); ContentObject.DecodeTo (filtered); filtered.Flush (); #if PORTABLE var buffer = memory.ToArray (); int length = buffer.Length; #else var buffer = memory.GetBuffer (); int length = (int) memory.Length; #endif return Encoding.UTF8.GetString (buffer, 0, length); } } }
Stream GetResponseStream (ImapReplayCommand command) { MemoryStream memory; if (testUnixFormat) { memory = new MemoryStream (); using (var filtered = new FilteredStream (memory)) { filtered.Add (new Dos2UnixFilter ()); filtered.Write (command.Response, 0, command.Response.Length); filtered.Flush (); } memory.Position = 0; } else { memory = new MemoryStream (command.Response, false); } return memory; }
/// <summary> /// Create a multipart/encrypted MIME part by encrypting the specified entity. /// </summary> /// <remarks> /// Encrypts the entity to the specified recipients, encapsulating the result in a /// new multipart/encrypted part. /// </remarks> /// <returns>A new <see cref="MultipartEncrypted"/> instance containing /// the encrypted version of the specified entity.</returns> /// <param name="ctx">The OpenPGP cryptography context to use for encrypting.</param> /// <param name="algorithm">The encryption algorithm.</param> /// <param name="recipients">The recipients for the encrypted entity.</param> /// <param name="entity">The entity to sign and encrypt.</param> /// <exception cref="System.ArgumentNullException"> /// <para><paramref name="ctx"/> is <c>null</c>.</para> /// <para>-or-</para> /// <para><paramref name="recipients"/> is <c>null</c>.</para> /// <para>-or-</para> /// <para><paramref name="entity"/> is <c>null</c>.</para> /// </exception> /// <exception cref="System.ArgumentException"> /// One or more of the recipient keys cannot be used for encrypting. /// </exception> /// <exception cref="System.NotSupportedException"> /// THe specified encryption algorithm is not supported. /// </exception> public static MultipartEncrypted Encrypt (OpenPgpContext ctx, EncryptionAlgorithm algorithm, IEnumerable<MailboxAddress> recipients, MimeEntity entity) { if (ctx == null) throw new ArgumentNullException (nameof (ctx)); if (recipients == null) throw new ArgumentNullException (nameof (recipients)); if (entity == null) throw new ArgumentNullException (nameof (entity)); using (var memory = new MemoryBlockStream ()) { using (var filtered = new FilteredStream (memory)) { filtered.Add (new Unix2DosFilter ()); entity.WriteTo (filtered); filtered.Flush (); } memory.Position = 0; var encrypted = new MultipartEncrypted (); encrypted.ContentType.Parameters["protocol"] = ctx.EncryptionProtocol; // add the protocol version part encrypted.Add (new ApplicationPgpEncrypted ()); // add the encrypted entity as the second part encrypted.Add (ctx.Encrypt (algorithm, recipients, memory)); return encrypted; } }
/// <summary> /// Gets the decoded text content using the provided charset to override /// the charset specified in the Content-Type parameters. /// </summary> /// <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> public string GetText(Encoding charset) { if (charset == null) throw new ArgumentNullException ("charset"); using (var memory = new MemoryStream ()) { using (var filtered = new FilteredStream (memory)) { filtered.Add (new CharsetFilter (charset, Encoding.UTF8)); ContentObject.DecodeTo (filtered); filtered.Flush (); return Encoding.UTF8.GetString (memory.GetBuffer (), 0, (int) memory.Length); } } }
/// <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); } }
static void ExtractAttachments (TnefReader reader, BodyBuilder builder) { var attachMethod = TnefAttachMethod.ByValue; var filter = new BestEncodingFilter (); var prop = reader.TnefPropertyReader; MimePart attachment = null; int outIndex, outLength; TnefAttachFlags flags; string[] mimeType; byte[] attachData; string text; do { if (reader.AttributeLevel != TnefAttributeLevel.Attachment) break; switch (reader.AttributeTag) { case TnefAttributeTag.AttachRenderData: attachMethod = TnefAttachMethod.ByValue; attachment = new MimePart (); break; case TnefAttributeTag.Attachment: if (attachment == null) break; while (prop.ReadNextProperty ()) { switch (prop.PropertyTag.Id) { case TnefPropertyId.AttachLongFilename: attachment.FileName = prop.ReadValueAsString (); break; case TnefPropertyId.AttachFilename: if (attachment.FileName == null) attachment.FileName = prop.ReadValueAsString (); break; case TnefPropertyId.AttachContentLocation: text = prop.ReadValueAsString (); if (Uri.IsWellFormedUriString (text, UriKind.Absolute)) attachment.ContentLocation = new Uri (text, UriKind.Absolute); else if (Uri.IsWellFormedUriString (text, UriKind.Relative)) attachment.ContentLocation = new Uri (text, UriKind.Relative); break; case TnefPropertyId.AttachContentBase: text = prop.ReadValueAsString (); attachment.ContentBase = new Uri (text, UriKind.Absolute); break; case TnefPropertyId.AttachContentId: attachment.ContentId = prop.ReadValueAsString (); break; case TnefPropertyId.AttachDisposition: text = prop.ReadValueAsString (); if (attachment.ContentDisposition == null) attachment.ContentDisposition = new ContentDisposition (text); else attachment.ContentDisposition.Disposition = text; break; case TnefPropertyId.AttachData: var stream = prop.GetRawValueReadStream (); var content = new MemoryStream (); var guid = new byte[16]; if (attachMethod == TnefAttachMethod.EmbeddedMessage) { var tnef = new TnefPart (); foreach (var param in attachment.ContentType.Parameters) tnef.ContentType.Parameters[param.Name] = param.Value; if (attachment.ContentDisposition != null) tnef.ContentDisposition = attachment.ContentDisposition; attachment = tnef; } // read the GUID stream.Read (guid, 0, 16); // the rest is content using (var filtered = new FilteredStream (content)) { filtered.Add (filter); stream.CopyTo (filtered, 4096); filtered.Flush (); } content.Position = 0; attachment.ContentTransferEncoding = filter.GetBestEncoding (EncodingConstraint.SevenBit); attachment.ContentObject = new ContentObject (content); filter.Reset (); builder.Attachments.Add (attachment); break; case TnefPropertyId.AttachMethod: attachMethod = (TnefAttachMethod) prop.ReadValueAsInt32 (); break; case TnefPropertyId.AttachMimeTag: mimeType = prop.ReadValueAsString ().Split ('/'); if (mimeType.Length == 2) { attachment.ContentType.MediaType = mimeType[0].Trim (); attachment.ContentType.MediaSubtype = mimeType[1].Trim (); } break; case TnefPropertyId.AttachFlags: flags = (TnefAttachFlags) prop.ReadValueAsInt32 (); if ((flags & TnefAttachFlags.RenderedInBody) != 0) { if (attachment.ContentDisposition == null) attachment.ContentDisposition = new ContentDisposition (ContentDisposition.Inline); else attachment.ContentDisposition.Disposition = ContentDisposition.Inline; } break; case TnefPropertyId.AttachSize: if (attachment.ContentDisposition == null) attachment.ContentDisposition = new ContentDisposition (); attachment.ContentDisposition.Size = prop.ReadValueAsInt64 (); break; case TnefPropertyId.DisplayName: attachment.ContentType.Name = prop.ReadValueAsString (); break; } } break; case TnefAttributeTag.AttachCreateDate: if (attachment != null) { if (attachment.ContentDisposition == null) attachment.ContentDisposition = new ContentDisposition (); attachment.ContentDisposition.CreationDate = prop.ReadValueAsDateTime (); } break; case TnefAttributeTag.AttachModifyDate: if (attachment != null) { if (attachment.ContentDisposition == null) attachment.ContentDisposition = new ContentDisposition (); attachment.ContentDisposition.ModificationDate = prop.ReadValueAsDateTime (); } break; case TnefAttributeTag.AttachTitle: if (attachment != null && string.IsNullOrEmpty (attachment.FileName)) attachment.FileName = prop.ReadValueAsString (); break; case TnefAttributeTag.AttachMetaFile: if (attachment == null) break; // TODO: what to do with the meta data? break; case TnefAttributeTag.AttachData: if (attachment == null || attachMethod != TnefAttachMethod.ByValue) break; attachData = prop.ReadValueAsBytes (); filter.Flush (attachData, 0, attachData.Length, out outIndex, out outLength); attachment.ContentTransferEncoding = filter.GetBestEncoding (EncodingConstraint.EightBit); attachment.ContentObject = new ContentObject (new MemoryStream (attachData, false)); filter.Reset (); builder.Attachments.Add (attachment); break; } } while (reader.ReadNextAttribute ()); }
/// <summary> /// Calculates the most efficient content encoding given the specified constraint. /// </summary> /// <remarks> /// If no <see cref="ContentObject"/> 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">A 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 (ContentObject == null) return ContentEncoding.SevenBit; using (var measure = new MeasuringStream ()) { using (var filtered = new FilteredStream (measure)) { var filter = new BestEncodingFilter (); filtered.Add (filter); ContentObject.DecodeTo (filtered, cancellationToken); filtered.Flush (); return filter.GetBestEncoding (constraint, maxLineLength); } } }
/// <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.Matches ("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); } }
/// <summary> /// Opens the decoded content stream. /// </summary> /// <remarks> /// Provides a means of reading the decoded content without having to first write it to another /// stream using <see cref="DecodeTo(System.IO.Stream,System.Threading.CancellationToken)"/>. /// </remarks> /// <returns>The decoded content stream.</returns> public Stream Open () { content.Seek (0, SeekOrigin.Begin); var filtered = new FilteredStream (content); filtered.Add (DecoderFilter.Create (Encoding)); return filtered; }
/// <summary> /// Decodes the content stream into another stream. /// </summary> /// <remarks> /// Uses the <see cref="Encoding"/> to decode the content stream to the output stream. /// </remarks> /// <param name="stream">The output stream.</param> /// <param name="cancellationToken">A 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> public void DecodeTo(Stream stream, CancellationToken cancellationToken) { if (stream == null) throw new ArgumentNullException ("stream"); using (var filtered = new FilteredStream (stream)) { filtered.Add (DecoderFilter.Create (Encoding)); WriteTo (filtered, cancellationToken); filtered.Flush (); } }
static void ExtractMapiProperties(TnefReader reader, MimeMessage message, BodyBuilder builder) { var prop = reader.TnefPropertyReader; while (prop.ReadNextProperty ()) { switch (prop.PropertyTag.Id) { case TnefPropertyId.InternetMessageId: if (prop.PropertyTag.ValueTnefType == TnefPropertyType.String8 || prop.PropertyTag.ValueTnefType == TnefPropertyType.Unicode) { message.MessageId = prop.ReadValueAsString (); Console.WriteLine ("Message Property: {0} = {1}", prop.PropertyTag.Id, message.MessageId); } else { Assert.Fail ("Unknown property type for Message-Id: {0}", prop.PropertyTag.ValueTnefType); } break; case TnefPropertyId.Subject: if (prop.PropertyTag.ValueTnefType == TnefPropertyType.String8 || prop.PropertyTag.ValueTnefType == TnefPropertyType.Unicode) { message.Subject = prop.ReadValueAsString (); Console.WriteLine ("Message Property: {0} = {1}", prop.PropertyTag.Id, message.Subject); } else { Assert.Fail ("Unknown property type for Subject: {0}", prop.PropertyTag.ValueTnefType); } break; case TnefPropertyId.RtfCompressed: if (prop.PropertyTag.ValueTnefType == TnefPropertyType.String8 || prop.PropertyTag.ValueTnefType == TnefPropertyType.Unicode || prop.PropertyTag.ValueTnefType == TnefPropertyType.Binary) { var rtf = new TextPart ("rtf"); rtf.ContentType.Name = "body.rtf"; var converter = new RtfCompressedToRtf (); var content = new MemoryStream (); using (var filtered = new FilteredStream (content)) { filtered.Add (converter); using (var compressed = prop.GetRawValueReadStream ()) { compressed.CopyTo (filtered, 4096); filtered.Flush (); } } rtf.ContentObject = new ContentObject (content); content.Position = 0; builder.Attachments.Add (rtf); Console.WriteLine ("Message Property: {0} = <compressed rtf data>", prop.PropertyTag.Id); } else { Assert.Fail ("Unknown property type for {0}: {1}", prop.PropertyTag.Id, prop.PropertyTag.ValueTnefType); } break; case TnefPropertyId.BodyHtml: if (prop.PropertyTag.ValueTnefType == TnefPropertyType.String8 || prop.PropertyTag.ValueTnefType == TnefPropertyType.Unicode || prop.PropertyTag.ValueTnefType == TnefPropertyType.Binary) { var html = new TextPart ("html"); html.ContentType.Name = "body.html"; html.Text = prop.ReadValueAsString (); builder.Attachments.Add (html); Console.WriteLine ("Message Property: {0} = {1}", prop.PropertyTag.Id, html.Text); } else { Assert.Fail ("Unknown property type for {0}: {1}", prop.PropertyTag.Id, prop.PropertyTag.ValueTnefType); } break; case TnefPropertyId.Body: if (prop.PropertyTag.ValueTnefType == TnefPropertyType.String8 || prop.PropertyTag.ValueTnefType == TnefPropertyType.Unicode || prop.PropertyTag.ValueTnefType == TnefPropertyType.Binary) { var plain = new TextPart ("plain"); plain.ContentType.Name = "body.txt"; plain.Text = prop.ReadValueAsString (); builder.Attachments.Add (plain); Console.WriteLine ("Message Property: {0} = {1}", prop.PropertyTag.Id, plain.Text); } else { Assert.Fail ("Unknown property type for {0}: {1}", prop.PropertyTag.Id, prop.PropertyTag.ValueTnefType); } break; default: Console.WriteLine ("Message Property (unhandled): {0} = {1}", prop.PropertyTag.Id, prop.ReadValue ()); break; } } }
void Data (FormatOptions options, MimeMessage message, CancellationToken cancellationToken, ITransferProgress progress) { var response = SendCommand ("DATA", cancellationToken); if (response.StatusCode != SmtpStatusCode.StartMailInput) throw new SmtpCommandException (SmtpErrorCode.UnexpectedStatusCode, response.StatusCode, response.Response); if (progress != null) { var ctx = new SendContext (progress, null); using (var stream = new ProgressStream (Stream, ctx.Update)) { using (var filtered = new FilteredStream (stream)) { filtered.Add (new SmtpDataFilter ()); message.WriteTo (options, filtered, cancellationToken); filtered.Flush (); } } } else { using (var filtered = new FilteredStream (Stream)) { filtered.Add (new SmtpDataFilter ()); message.WriteTo (options, filtered, cancellationToken); filtered.Flush (); } } Stream.Write (EndData, 0, EndData.Length, cancellationToken); Stream.Flush (cancellationToken); response = Stream.ReadResponse (cancellationToken); switch (response.StatusCode) { default: throw new SmtpCommandException (SmtpErrorCode.MessageNotAccepted, response.StatusCode, response.Response); case SmtpStatusCode.AuthenticationRequired: throw new ServiceNotAuthenticatedException (response.Response); case SmtpStatusCode.Ok: OnMessageSent (new MessageSentEventArgs (message, response.Response)); break; } }
static void ExtractMapiProperties (TnefReader reader, MimeMessage message, BodyBuilder builder) { var prop = reader.TnefPropertyReader; while (prop.ReadNextProperty ()) { switch (prop.PropertyTag.Id) { case TnefPropertyId.InternetMessageId: if (prop.PropertyTag.ValueTnefType == TnefPropertyType.String8 || prop.PropertyTag.ValueTnefType == TnefPropertyType.Unicode) { message.MessageId = prop.ReadValueAsString (); } break; case TnefPropertyId.Subject: if (prop.PropertyTag.ValueTnefType == TnefPropertyType.String8 || prop.PropertyTag.ValueTnefType == TnefPropertyType.Unicode) { message.Subject = prop.ReadValueAsString (); } break; case TnefPropertyId.RtfCompressed: if (prop.PropertyTag.ValueTnefType == TnefPropertyType.String8 || prop.PropertyTag.ValueTnefType == TnefPropertyType.Unicode || prop.PropertyTag.ValueTnefType == TnefPropertyType.Binary) { var rtf = new TextPart ("rtf"); rtf.ContentType.Name = "body.rtf"; var converter = new RtfCompressedToRtf (); var content = new MemoryStream (); using (var filtered = new FilteredStream (content)) { filtered.Add (converter); using (var compressed = prop.GetRawValueReadStream ()) { compressed.CopyTo (filtered, 4096); filtered.Flush (); } } rtf.ContentObject = new ContentObject (content); content.Position = 0; builder.Attachments.Add (rtf); } break; case TnefPropertyId.BodyHtml: if (prop.PropertyTag.ValueTnefType == TnefPropertyType.String8 || prop.PropertyTag.ValueTnefType == TnefPropertyType.Unicode || prop.PropertyTag.ValueTnefType == TnefPropertyType.Binary) { var html = new TextPart ("html"); html.ContentType.Name = "body.html"; html.Text = prop.ReadValueAsString (); builder.Attachments.Add (html); } break; case TnefPropertyId.Body: if (prop.PropertyTag.ValueTnefType == TnefPropertyType.String8 || prop.PropertyTag.ValueTnefType == TnefPropertyType.Unicode || prop.PropertyTag.ValueTnefType == TnefPropertyType.Binary) { var plain = new TextPart ("plain"); plain.ContentType.Name = "body.txt"; plain.Text = prop.ReadValueAsString (); builder.Attachments.Add (plain); } break; } } }
Stream GetResourceStream (string name) { using (var response = GetType ().Assembly.GetManifestResourceStream ("UnitTests.Net.Pop3.Resources." + name)) { var memory = new MemoryBlockStream (); using (var filtered = new FilteredStream (memory)) { if (testUnixFormat) filtered.Add (new Dos2UnixFilter ()); else filtered.Add (new Unix2DosFilter ()); response.CopyTo (filtered, 4096); } memory.Position = 0; return memory; } }
/// <summary> /// Creates a new <see cref="MultipartEncrypted"/>. /// </summary> /// <remarks> /// Encrypts the entity to the specified recipients, encapsulating the result in a /// new multipart/encrypted part. /// </remarks> /// <returns>A new <see cref="MimeKit.Cryptography.MultipartEncrypted"/> instance containing /// the encrypted version of the specified entity.</returns> /// <param name="ctx">The OpenPGP cryptography context to use for encrypting.</param> /// <param name="recipients">The recipients for the encrypted entity.</param> /// <param name="entity">The entity to sign and encrypt.</param> /// <exception cref="System.ArgumentNullException"> /// <para><paramref name="ctx"/> is <c>null</c>.</para> /// <para>-or-</para> /// <para><paramref name="recipients"/> is <c>null</c>.</para> /// <para>-or-</para> /// <para><paramref name="entity"/> is <c>null</c>.</para> /// </exception> /// <exception cref="System.ArgumentException"> /// One or more of the recipient keys cannot be used for encrypting. /// </exception> public static MultipartEncrypted Create (OpenPgpContext ctx, IEnumerable<PgpPublicKey> recipients, MimeEntity entity) { if (ctx == null) throw new ArgumentNullException ("ctx"); if (recipients == null) throw new ArgumentNullException ("recipients"); if (entity == null) throw new ArgumentNullException ("entity"); using (var memory = new MemoryStream ()) { using (var filtered = new FilteredStream (memory)) { filtered.Add (new Unix2DosFilter ()); PrepareEntityForEncrypting (entity); entity.WriteTo (filtered); filtered.Flush (); } memory.Position = 0; var encrypted = new MultipartEncrypted (); encrypted.ContentType.Parameters["protocol"] = ctx.EncryptionProtocol; // add the protocol version part encrypted.Add (new ApplicationPgpEncrypted ()); // add the encrypted entity as the second part encrypted.Add (ctx.Encrypt (recipients, memory)); return encrypted; } }
/// <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); } }
public void TestSimpleYEncMessage () { using (var file = File.OpenRead ("../../TestData/yenc/simple.msg")) { var message = MimeMessage.Load (file); using (var decoded = new MemoryStream ()) { var ydec = new YDecoder (); using (var filtered = new FilteredStream (decoded)) { filtered.Add (new DecoderFilter (ydec)); ((MimePart) message.Body).ContentObject.WriteTo (filtered); filtered.Flush (); } decoded.Position = 0; Assert.AreEqual (584, decoded.Length, "The decoded size does not match."); Assert.AreEqual (0xded29f4f, ydec.Checksum ^ 0xffffffff, "The decoded checksum does not match."); // now re-encode it using (var encoded = new MemoryStream ()) { var ybegin = Encoding.ASCII.GetBytes ("-- \n=ybegin line=128 size=584 name=testfile.txt \n"); var yend = Encoding.ASCII.GetBytes ("=yend size=584 crc32=ded29f4f \n"); var yenc = new YEncoder (); encoded.Write (ybegin, 0, ybegin.Length); using (var filtered = new FilteredStream (encoded)) { filtered.Add (new EncoderFilter (yenc)); decoded.CopyTo (filtered, 4096); filtered.Flush (); } encoded.Write (yend, 0, yend.Length); Assert.AreEqual (0xded29f4f, yenc.Checksum ^ 0xffffffff, "The encoded checksum does not match."); using (var original = new MemoryStream ()) { using (var filtered = new FilteredStream (original)) { filtered.Add (new Dos2UnixFilter ()); ((MimePart) message.Body).ContentObject.WriteTo (filtered); filtered.Flush (); } var latin1 = Encoding.GetEncoding ("iso-8859-1"); var buf = original.GetBuffer (); var expected = latin1.GetString (buf, 0, (int) original.Length); buf = encoded.GetBuffer (); var actual = latin1.GetString (buf, 0, (int) encoded.Length); Assert.AreEqual (expected, actual, "Encoded value does not match original."); } } } } }
/// <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); } }
public void TestUUEncode () { using (var original = new MemoryStream ()) { using (var file = File.OpenRead ("../../TestData/encoders/photo.uu")) file.CopyTo (original, 4096); using (var encoded = new MemoryStream ()) { var begin = Encoding.ASCII.GetBytes ("begin 644 photo.jpg"); var eol = FormatOptions.Default.NewLineBytes; var end = Encoding.ASCII.GetBytes ("end"); encoded.Write (begin, 0, begin.Length); encoded.Write (eol, 0, eol.Length); using (var filtered = new FilteredStream (encoded)) { filtered.Add (EncoderFilter.Create (ContentEncoding.UUEncode)); using (var file = File.OpenRead ("../../TestData/encoders/photo.jpg")) file.CopyTo (filtered, 4096); filtered.Flush (); } encoded.Write (end, 0, end.Length); encoded.Write (eol, 0, eol.Length); var buf0 = original.GetBuffer (); var buf1 = encoded.GetBuffer (); int n = (int) original.Length; Assert.AreEqual (original.Length, encoded.Length, "Encoded 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> /// Join the specified message/partial parts into the complete message. /// </summary> /// <param name="options">The parser options to use.</param> /// <param name="partials">The list of partial message parts.</param> /// <exception cref="System.ArgumentNullException"> /// <para><paramref name="options"/> is <c>null</c>.</para> /// <para>-or-</para> /// <para><paramref name="partials"/>is <c>null</c>.</para> /// </exception> public static MimeMessage Join(ParserOptions options, IEnumerable<MessagePartial> partials) { if (options == null) throw new ArgumentNullException ("options"); if (partials == null) throw new ArgumentNullException ("partials"); var parts = partials.ToList (); if (parts.Count == 0) return null; parts.Sort (PartialCompare); if (!parts[parts.Count - 1].Total.HasValue) throw new ArgumentException ("partials"); int total = parts[parts.Count - 1].Total.Value; if (parts.Count != total) throw new ArgumentException ("partials"); string id = parts[0].Id; using (var chained = new ChainedStream ()) { // chain all of the partial content streams... for (int i = 0; i < parts.Count; i++) { int number = parts[i].Number.Value; if (number != i + 1) throw new ArgumentException ("partials"); var content = parts[i].ContentObject; content.Stream.Seek (0, SeekOrigin.Begin); var filtered = new FilteredStream (content.Stream); filtered.Add (DecoderFilter.Create (content.Encoding)); chained.Add (filtered); } var parser = new MimeParser (options, chained); return parser.ParseMessage (); } }
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 (); } }