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); } } }
/// <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 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> /// 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); } }
/// <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> /// 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 (); } }
/// <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); } } }
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); } } }
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 ()); }
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 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); } }
public void TestImapClientGMail () { var commands = new List<ImapReplayCommand> (); commands.Add (new ImapReplayCommand ("", "gmail.greeting.txt")); commands.Add (new ImapReplayCommand ("A00000000 CAPABILITY\r\n", "gmail.capability.txt")); commands.Add (new ImapReplayCommand ("A00000001 AUTHENTICATE PLAIN AHVzZXJuYW1lAHBhc3N3b3Jk\r\n", "gmail.authenticate.txt")); commands.Add (new ImapReplayCommand ("A00000002 NAMESPACE\r\n", "gmail.namespace.txt")); commands.Add (new ImapReplayCommand ("A00000003 LIST \"\" \"INBOX\"\r\n", "gmail.list-inbox.txt")); commands.Add (new ImapReplayCommand ("A00000004 XLIST \"\" \"*\"\r\n", "gmail.xlist.txt")); commands.Add (new ImapReplayCommand ("A00000005 LIST \"\" \"%\"\r\n", "gmail.list-personal.txt")); commands.Add (new ImapReplayCommand ("A00000006 CREATE UnitTests\r\n", "gmail.create-unittests.txt")); commands.Add (new ImapReplayCommand ("A00000007 LIST \"\" UnitTests\r\n", "gmail.list-unittests.txt")); commands.Add (new ImapReplayCommand ("A00000008 SELECT UnitTests (CONDSTORE)\r\n", "gmail.select-unittests.txt")); for (int i = 0; i < 50; i++) { using (var stream = new MemoryStream ()) { using (var resource = GetResourceStream (string.Format ("common.message.{0}.msg", i))) { using (var filtered = new FilteredStream (stream)) { filtered.Add (new Unix2DosFilter ()); resource.CopyTo (filtered, 4096); filtered.Flush (); } stream.Position = 0; } var message = MimeMessage.Load (stream); long length = stream.Length; string latin1; stream.Position = 0; using (var reader = new StreamReader (stream, Latin1)) latin1 = reader.ReadToEnd (); var command = string.Format ("A{0:D8} APPEND UnitTests (\\Seen) ", i + 9); command += "{" + length + "}\r\n"; commands.Add (new ImapReplayCommand (command, "gmail.go-ahead.txt")); commands.Add (new ImapReplayCommand (latin1 + "\r\n", string.Format ("gmail.append.{0}.txt", i + 1))); } } commands.Add (new ImapReplayCommand ("A00000059 UID SEARCH RETURN () CHARSET US-ASCII OR TO nsb CC nsb\r\n", "gmail.search.txt")); commands.Add (new ImapReplayCommand ("A00000060 UID FETCH 1:3,5,7:9,11:14,26:29,31,34,41:43,50 (UID FLAGS INTERNALDATE RFC822.SIZE ENVELOPE BODY)\r\n", "gmail.search-summary.txt")); commands.Add (new ImapReplayCommand ("A00000061 UID FETCH 1 (BODY.PEEK[])\r\n", "gmail.fetch.1.txt")); commands.Add (new ImapReplayCommand ("A00000062 UID FETCH 2 (BODY.PEEK[])\r\n", "gmail.fetch.2.txt")); commands.Add (new ImapReplayCommand ("A00000063 UID FETCH 3 (BODY.PEEK[])\r\n", "gmail.fetch.3.txt")); commands.Add (new ImapReplayCommand ("A00000064 UID FETCH 5 (BODY.PEEK[])\r\n", "gmail.fetch.5.txt")); commands.Add (new ImapReplayCommand ("A00000065 UID FETCH 7 (BODY.PEEK[])\r\n", "gmail.fetch.7.txt")); commands.Add (new ImapReplayCommand ("A00000066 UID FETCH 8 (BODY.PEEK[])\r\n", "gmail.fetch.8.txt")); commands.Add (new ImapReplayCommand ("A00000067 UID FETCH 9 (BODY.PEEK[])\r\n", "gmail.fetch.9.txt")); commands.Add (new ImapReplayCommand ("A00000068 UID FETCH 11 (BODY.PEEK[])\r\n", "gmail.fetch.11.txt")); commands.Add (new ImapReplayCommand ("A00000069 UID FETCH 12 (BODY.PEEK[])\r\n", "gmail.fetch.12.txt")); commands.Add (new ImapReplayCommand ("A00000070 UID FETCH 13 (BODY.PEEK[])\r\n", "gmail.fetch.13.txt")); commands.Add (new ImapReplayCommand ("A00000071 UID FETCH 14 (BODY.PEEK[])\r\n", "gmail.fetch.14.txt")); commands.Add (new ImapReplayCommand ("A00000072 UID FETCH 26 (BODY.PEEK[])\r\n", "gmail.fetch.26.txt")); commands.Add (new ImapReplayCommand ("A00000073 UID FETCH 27 (BODY.PEEK[])\r\n", "gmail.fetch.27.txt")); commands.Add (new ImapReplayCommand ("A00000074 UID FETCH 28 (BODY.PEEK[])\r\n", "gmail.fetch.28.txt")); commands.Add (new ImapReplayCommand ("A00000075 UID FETCH 29 (BODY.PEEK[])\r\n", "gmail.fetch.29.txt")); commands.Add (new ImapReplayCommand ("A00000076 UID FETCH 31 (BODY.PEEK[])\r\n", "gmail.fetch.31.txt")); commands.Add (new ImapReplayCommand ("A00000077 UID FETCH 34 (BODY.PEEK[])\r\n", "gmail.fetch.34.txt")); commands.Add (new ImapReplayCommand ("A00000078 UID FETCH 41 (BODY.PEEK[])\r\n", "gmail.fetch.41.txt")); commands.Add (new ImapReplayCommand ("A00000079 UID FETCH 42 (BODY.PEEK[])\r\n", "gmail.fetch.42.txt")); commands.Add (new ImapReplayCommand ("A00000080 UID FETCH 43 (BODY.PEEK[])\r\n", "gmail.fetch.43.txt")); commands.Add (new ImapReplayCommand ("A00000081 UID FETCH 50 (BODY.PEEK[])\r\n", "gmail.fetch.50.txt")); commands.Add (new ImapReplayCommand ("A00000082 UID STORE 1:3,5,7:9,11:14,26:29,31,34,41:43,50 FLAGS (\\Answered \\Seen)\r\n", "gmail.set-flags.txt")); commands.Add (new ImapReplayCommand ("A00000083 UID STORE 1:3,5,7:9,11:14,26:29,31,34,41:43,50 -FLAGS.SILENT (\\Answered)\r\n", "gmail.remove-flags.txt")); commands.Add (new ImapReplayCommand ("A00000084 UID STORE 1:3,5,7:9,11:14,26:29,31,34,41:43,50 +FLAGS.SILENT (\\Deleted)\r\n", "gmail.add-flags.txt")); commands.Add (new ImapReplayCommand ("A00000085 UNSELECT\r\n", "gmail.unselect-unittests.txt")); commands.Add (new ImapReplayCommand ("A00000086 DELETE UnitTests\r\n", "gmail.delete-unittests.txt")); commands.Add (new ImapReplayCommand ("A00000087 LOGOUT\r\n", "gmail.logout.txt")); using (var client = new ImapClient ()) { try { client.ReplayConnect ("localhost", new ImapReplayStream (commands, false), CancellationToken.None); } catch (Exception ex) { Assert.Fail ("Did not expect an exception in Connect: {0}", ex); } Assert.IsTrue (client.IsConnected, "Client failed to connect."); Assert.AreEqual (GMailInitialCapabilities, client.Capabilities); Assert.AreEqual (4, client.AuthenticationMechanisms.Count); Assert.IsTrue (client.AuthenticationMechanisms.Contains ("XOAUTH"), "Expected SASL XOAUTH auth mechanism"); Assert.IsTrue (client.AuthenticationMechanisms.Contains ("XOAUTH2"), "Expected SASL XOAUTH2 auth mechanism"); Assert.IsTrue (client.AuthenticationMechanisms.Contains ("PLAIN"), "Expected SASL PLAIN auth mechanism"); Assert.IsTrue (client.AuthenticationMechanisms.Contains ("PLAIN-CLIENTTOKEN"), "Expected SASL PLAIN-CLIENTTOKEN auth mechanism"); try { var credentials = new NetworkCredential ("username", "password"); // Note: Do not try XOAUTH2 client.AuthenticationMechanisms.Remove ("XOAUTH2"); client.Authenticate (credentials, CancellationToken.None); } catch (Exception ex) { Assert.Fail ("Did not expect an exception in Authenticate: {0}", ex); } Assert.AreEqual (GMailAuthenticatedCapabilities, client.Capabilities); var inbox = client.Inbox; Assert.IsNotNull (inbox, "Expected non-null Inbox folder."); Assert.AreEqual (FolderAttributes.Inbox | FolderAttributes.HasNoChildren, inbox.Attributes, "Expected Inbox attributes to be \\HasNoChildren."); foreach (var special in Enum.GetValues (typeof (SpecialFolder)).OfType<SpecialFolder> ()) { var folder = client.GetFolder (special); if (special != SpecialFolder.Archive) { var expected = GetSpecialFolderAttribute (special) | FolderAttributes.HasNoChildren; Assert.IsNotNull (folder, "Expected non-null {0} folder.", special); Assert.AreEqual (expected, folder.Attributes, "Expected {0} attributes to be \\HasNoChildren.", special); } else { Assert.IsNull (folder, "Expected null {0} folder.", special); } } var personal = client.GetFolder (client.PersonalNamespaces[0]); var folders = personal.GetSubfolders (false, CancellationToken.None).ToList (); Assert.AreEqual (client.Inbox, folders[0], "Expected the first folder to be the Inbox."); Assert.AreEqual ("[Gmail]", folders[1].FullName, "Expected the second folder to be [Gmail]."); Assert.AreEqual (FolderAttributes.NoSelect | FolderAttributes.HasChildren, folders[1].Attributes, "Expected [Gmail] folder to be \\Noselect \\HasChildren."); var created = personal.Create ("UnitTests", true, CancellationToken.None); Assert.IsNotNull (created.ParentFolder, "The ParentFolder property should not be null."); const MessageFlags ExpectedPermanentFlags = MessageFlags.Answered | MessageFlags.Flagged | MessageFlags.Draft | MessageFlags.Deleted | MessageFlags.Seen | MessageFlags.UserDefined; const MessageFlags ExpectedAcceptedFlags = MessageFlags.Answered | MessageFlags.Flagged | MessageFlags.Draft | MessageFlags.Deleted | MessageFlags.Seen; var access = created.Open (FolderAccess.ReadWrite, CancellationToken.None); Assert.AreEqual (FolderAccess.ReadWrite, access, "The UnitTests folder was not opened with the expected access mode."); Assert.AreEqual (ExpectedPermanentFlags, created.PermanentFlags, "The PermanentFlags do not match the expected value."); Assert.AreEqual (ExpectedAcceptedFlags, created.AcceptedFlags, "The AcceptedFlags do not match the expected value."); for (int i = 0; i < 50; i++) { using (var stream = GetResourceStream (string.Format ("common.message.{0}.msg", i))) { var message = MimeMessage.Load (stream); var uid = created.Append (message, MessageFlags.Seen, CancellationToken.None); Assert.IsTrue (uid.HasValue, "Expected a UID to be returned from folder.Append()."); Assert.AreEqual ((uint) (i + 1), uid.Value.Id, "The UID returned from the APPEND command does not match the expected UID."); } } var query = SearchQuery.ToContains ("nsb").Or (SearchQuery.CcContains ("nsb")); var matches = created.Search (query, CancellationToken.None); const MessageSummaryItems items = MessageSummaryItems.Full | MessageSummaryItems.UniqueId; var summaries = created.Fetch (matches, items, CancellationToken.None); foreach (var summary in summaries) { if (summary.UniqueId.HasValue) created.GetMessage (summary.UniqueId.Value, CancellationToken.None); else created.GetMessage (summary.Index, CancellationToken.None); } created.SetFlags (matches, MessageFlags.Seen | MessageFlags.Answered, false, CancellationToken.None); created.RemoveFlags (matches, MessageFlags.Answered, true, CancellationToken.None); created.AddFlags (matches, MessageFlags.Deleted, true, CancellationToken.None); created.Close (false, CancellationToken.None); created.Delete (CancellationToken.None); client.Disconnect (true, CancellationToken.None); } }
/// <summary> /// Converts the contents of <paramref name="source"/> from the <see cref="InputFormat"/> to the /// <see cref="OutputFormat"/> and writes the resulting text to <paramref name="destination"/>. /// </summary> /// <remarks> /// Converts the contents of <paramref name="source"/> from the <see cref="InputFormat"/> to the /// <see cref="OutputFormat"/> and writes the resulting text to <paramref name="destination"/>. /// </remarks> /// <param name="source">The source stream.</param> /// <param name="destination">The destination stream.</param> /// <exception cref="System.ArgumentNullException"> /// <para><paramref name="source"/> is <c>null</c>.</para> /// <para>-or-</para> /// <para><paramref name="destination"/> is <c>null</c>.</para> /// </exception> public void Convert (Stream source, Stream destination) { if (source == null) throw new ArgumentNullException ("source"); if (destination == null) throw new ArgumentNullException ("destination"); using (var filtered = new FilteredStream (destination)) { filtered.Add (this); source.CopyTo (filtered, 4096); filtered.Flush (); } }
private void ExtractMapiProperties(TnefReader reader, MimeMessage message, BodyBuilder builder) { var tnefPropertyReader = reader.TnefPropertyReader; while (tnefPropertyReader.ReadNextProperty()) { switch (tnefPropertyReader.PropertyTag.Id) { case TnefPropertyId.RtfCompressed: var memoryStream = new MemoryStream(); if (tnefPropertyReader.PropertyTag.ValueTnefType == TnefPropertyType.String8 || tnefPropertyReader.PropertyTag.ValueTnefType == TnefPropertyType.Unicode || tnefPropertyReader.PropertyTag.ValueTnefType == TnefPropertyType.Binary) { var textPart = new TextPart("rtf"); textPart.ContentType.Name = "body.rtf"; RtfCompressedToRtf rtfCompressedToRtf = new RtfCompressedToRtf(); //var memoryStream = new MemoryStream(); using (var filteredStream = new FilteredStream(memoryStream)) { filteredStream.Add(rtfCompressedToRtf); using (Stream rawValueReadStream = tnefPropertyReader.GetRawValueReadStream()) { rawValueReadStream.CopyTo(filteredStream, 4096); filteredStream.Flush(); } } textPart.ContentObject = new ContentObject(memoryStream, ContentEncoding.Default); memoryStream.Position = 0L; builder.Attachments.Add(textPart); continue; } memoryStream.Dispose(); continue; case TnefPropertyId.BodyHtml: if (tnefPropertyReader.PropertyTag.ValueTnefType == TnefPropertyType.String8 || tnefPropertyReader.PropertyTag.ValueTnefType == TnefPropertyType.Unicode || tnefPropertyReader.PropertyTag.ValueTnefType == TnefPropertyType.Binary) { var textPart = new TextPart("html"); textPart.ContentType.Name = "body.html"; textPart.Text = tnefPropertyReader.ReadValueAsString(); builder.Attachments.Add(textPart); continue; } continue; case TnefPropertyId.InternetMessageId: if (tnefPropertyReader.PropertyTag.ValueTnefType == TnefPropertyType.String8 || tnefPropertyReader.PropertyTag.ValueTnefType == TnefPropertyType.Unicode) { message.MessageId = tnefPropertyReader.ReadValueAsString(); continue; } continue; case TnefPropertyId.Subject: if (tnefPropertyReader.PropertyTag.ValueTnefType == TnefPropertyType.String8 || tnefPropertyReader.PropertyTag.ValueTnefType == TnefPropertyType.Unicode) { message.Subject = tnefPropertyReader.ReadValueAsString(); continue; } continue; case TnefPropertyId.Body: if (tnefPropertyReader.PropertyTag.ValueTnefType == TnefPropertyType.String8 || tnefPropertyReader.PropertyTag.ValueTnefType == TnefPropertyType.Unicode || tnefPropertyReader.PropertyTag.ValueTnefType == TnefPropertyType.Binary) { var textPart = new TextPart("plain"); textPart.ContentType.Name = "body.txt"; textPart.Text = tnefPropertyReader.ReadValueAsString(); builder.Attachments.Add(textPart); continue; } continue; case TnefPropertyId.ConversationTopic: if (tnefPropertyReader.PropertyTag.ValueTnefType == TnefPropertyType.String8 || tnefPropertyReader.PropertyTag.ValueTnefType == TnefPropertyType.Unicode) { message.Subject = tnefPropertyReader.ReadValueAsString(); continue; } continue; case TnefPropertyId.SenderName: if (tnefPropertyReader.PropertyTag.ValueTnefType == TnefPropertyType.String8 || tnefPropertyReader.PropertyTag.ValueTnefType == TnefPropertyType.Unicode) { var sender = new MailboxAddress(string.Empty, tnefPropertyReader.ReadValueAsString()); message.Sender = sender; } continue; case (TnefPropertyId)Mapi.ID.PR_PRIMARY_SEND_ACCOUNT: if (tnefPropertyReader.PropertyTag.ValueTnefType == TnefPropertyType.String8 || tnefPropertyReader.PropertyTag.ValueTnefType == TnefPropertyType.Unicode) { var senderEmail = new MailboxAddress(string.Empty, tnefPropertyReader.ReadValueAsString()); message.Sender = senderEmail; } continue; default: try { tnefPropertyReader.ReadValue(); continue; } catch { continue; } } } }
/// <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; } }
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) { 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); } }
/// <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> /// 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> /// 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); } }
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; }
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; } }
/// <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); } } }
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 (); } }
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; } } }
/// <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); } }
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; } } }
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; } }
public void TestJwzMbox () { var summary = File.ReadAllText (Path.Combine (MboxDataDir, "jwz-summary.txt")).Replace ("\r\n", "\n"); var options = FormatOptions.Default.Clone (); var original = new MemoryBlockStream (); var output = new MemoryBlockStream (); var builder = new StringBuilder (); var expected = new byte[4096]; var buffer = new byte[4096]; int nx, n; options.NewLineFormat = NewLineFormat.Unix; using (var stream = File.OpenRead (Path.Combine (MboxDataDir, "jwz.mbox.txt"))) { var parser = new MimeParser (stream, MimeFormat.Mbox); int count = 0; while (!parser.IsEndOfStream) { var message = parser.ParseMessage (); builder.AppendFormat ("{0}", parser.MboxMarker).Append ('\n'); if (message.From.Count > 0) builder.AppendFormat ("From: {0}", message.From).Append ('\n'); if (message.To.Count > 0) builder.AppendFormat ("To: {0}", message.To).Append ('\n'); builder.AppendFormat ("Subject: {0}", message.Subject).Append ('\n'); builder.AppendFormat ("Date: {0}", DateUtils.FormatDate (message.Date)).Append ('\n'); DumpMimeTree (builder, message); builder.Append ('\n'); var marker = Encoding.UTF8.GetBytes ((count > 0 ? "\n" : string.Empty) + parser.MboxMarker + "\n"); output.Write (marker, 0, marker.Length); message.WriteTo (options, output); count++; } } string actual = builder.ToString (); // WORKAROUND: Mono's iso-2022-jp decoder breaks on this input in versions <= 3.2.3 but is fixed in 3.2.4+ string iso2022jp = Encoding.GetEncoding ("iso-2022-jp").GetString (Convert.FromBase64String ("GyRAOjRGI0stGyhK")); if (iso2022jp != "佐藤豊") actual = actual.Replace (iso2022jp, "佐藤豊"); Assert.AreEqual (summary, actual, "Summaries do not match for jwz.mbox"); using (var stream = File.OpenRead (Path.Combine (MboxDataDir, "jwz.mbox.txt"))) { using (var filtered = new FilteredStream (original)) { filtered.Add (new Dos2UnixFilter ()); stream.CopyTo (filtered); filtered.Flush (); } } original.Position = 0; output.Position = 0; Assert.AreEqual (original.Length, output.Length, "The length of the mbox did not match."); do { var position = original.Position; nx = original.Read (expected, 0, expected.Length); n = output.Read (buffer, 0, buffer.Length); if (nx == 0) break; for (int i = 0; i < nx; i++) { if (buffer[i] == expected[i]) continue; var strExpected = CharsetUtils.Latin1.GetString (expected, 0, nx); var strActual = CharsetUtils.Latin1.GetString (buffer, 0, n); Assert.AreEqual (strExpected, strActual, "The mbox differs at position {0}", position + i); } } while (true); }