Exemple #1
0
        public static MimeMessage ParseMessage(Stream inputStream, HTTPHeaders headers, CancellationToken cancellationToken)
        {
            if (inputStream == null || inputStream == Stream.Null)
            {
                return(null);
            }

            var headerArray = headers.ToArray();

            if (IsMimeMessage(headerArray) == false)
            {
                return(null);
            }

            string headerString = String.Join("\r\n", headerArray.Select(h => $"{h.Name}: {h.Value}"));

            try
            {
                using (ChainedStream streamWithHeaders = new ChainedStream())
                {
                    streamWithHeaders.Add(new MemoryStream(Encoding.UTF8.GetBytes(headerString)), false);
                    streamWithHeaders.Add(inputStream, false);

                    var parser = new MimeKit.MimeParser(streamWithHeaders);

                    return(parser.ParseMessage(cancellationToken));
                }
            }
            catch (FormatException)
            {
                return(null);
            }
        }
		static MimeMessage Load (string path)
		{
			using (var file = File.OpenRead (path)) {
				var parser = new MimeParser (file);
				return parser.ParseMessage ();
			}
		}
Exemple #3
0
        public void TestJwzMbox()
        {
            var summary = File.ReadAllText ("../../TestData/mbox/jwz-summary.txt");
            var builder = new StringBuilder ();

            using (var stream = File.OpenRead ("../../TestData/mbox/jwz.mbox.txt")) {
                var parser = new MimeParser (stream, MimeFormat.Mbox);

                while (!parser.IsEndOfStream) {
                    var message = parser.ParseMessage ();

                    builder.AppendFormat ("{0}\n", parser.MboxMarker);
                    if (message.From.Count > 0)
                        builder.AppendFormat ("From: {0}\n", message.From);
                    if (message.To.Count > 0)
                        builder.AppendFormat ("To: {0}\n", message.To);
                    builder.AppendFormat ("Subject: {0}\n", message.Subject);
                    builder.AppendFormat ("Date: {0}\n", DateUtils.FormatDate (message.Date));
                    DumpMimeTree (builder, message.Body, 0);
                    builder.Append ("\n");
                }
            }

            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");
        }
Exemple #4
0
        /// <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());
            }
        }
Exemple #5
0
        /// <summary>
        /// Joins the specified message/partial parts into the complete message.
        /// </summary>
        /// <remarks>
        /// Combines all of the message/partial fragments into its original,
        /// complete, message.
        /// </remarks>
        /// <returns>The re-combined message.</returns>
        /// <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>
        /// <exception cref="System.ArgumentException">
        /// <para>The last partial does not have a Total.</para>
        /// <para>-or-</para>
        /// <para>The number of partials provided does not match the expected count.</para>
        /// <para>-or-</para>
        /// <para>One or more partials is missing.</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("The last partial does not have a Total.", "partials");
            }

            int total = parts[parts.Count - 1].Total.Value;

            if (parts.Count != total)
            {
                throw new ArgumentException("The number of partials provided does not match the expected count.", "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("One or more partials is missing.", "partials");
                    }

                    var content = parts[i].ContentObject;

                    chained.Add(content.Open());
                }

                var parser = new MimeParser(options, chained);

                return(parser.ParseMessage());
            }
        }
Exemple #6
0
    static void Main( )
    {
        // Use the system console object
            FileStream stream = new FileStream("/home/bhj/Maildir/SentMails/cur/1419597900.M517484P16503Q0.bhj-home:2,S", FileMode.Open);
            var parser = new MimeParser (stream, MimeFormat.Entity);
            var message = parser.ParseMessage ();

            FileStream output = new FileStream("/home/bhj/tmp/out.txt", FileMode.Create);
            foreach (var part in message.BodyParts) {
                    part.ContentObject.DecodeTo (output);
                    // do something
            }
    }
Exemple #7
0
        /// <summary>
        /// Load a <see cref="MimeEntity"/> from the specified stream.
        /// </summary>
        /// <remarks>
        /// <para>Loads a <see cref="MimeEntity"/> from the given stream, using the
        /// specified <see cref="ParserOptions"/>.</para>
        /// <para>If <paramref name="persistent"/> is <c>true</c> and <paramref name="stream"/> is seekable, then
        /// the <see cref="MimeParser"/> will not copy the content of <see cref="MimePart"/>s into memory. Instead,
        /// it will use a <see cref="MimeKit.IO.BoundStream"/> to reference a substream of <paramref name="stream"/>.
        /// This has the potential to not only save mmeory usage, but also improve <see cref="MimeParser"/>
        /// performance.</para>
        /// </remarks>
        /// <returns>The parsed MIME entity.</returns>
        /// <param name="options">The parser options.</param>
        /// <param name="stream">The stream.</param>
        /// <param name="persistent"><c>true</c> if the stream is persistent; otherwise <c>false</c>.</param>
        /// <param name="cancellationToken">A cancellation token.</param>
        /// <exception cref="System.ArgumentNullException">
        /// <para><paramref name="options"/> is <c>null</c>.</para>
        /// <para>-or-</para>
        /// <para><paramref name="stream"/> is <c>null</c>.</para>
        /// </exception>
        /// <exception cref="System.OperationCanceledException">
        /// The operation was canceled via the cancellation token.
        /// </exception>
        /// <exception cref="System.FormatException">
        /// There was an error parsing the entity.
        /// </exception>
        /// <exception cref="System.IO.IOException">
        /// An I/O error occurred.
        /// </exception>
        public static MimeEntity Load(ParserOptions options, Stream stream, bool persistent, CancellationToken cancellationToken = default(CancellationToken))
        {
            if (options == null)
            {
                throw new ArgumentNullException("options");
            }

            if (stream == null)
            {
                throw new ArgumentNullException("stream");
            }

            var parser = new MimeParser(options, stream, MimeFormat.Entity, persistent);

            return(parser.ParseEntity(cancellationToken));
        }
Exemple #8
0
        /// <summary>
        /// Load a <see cref="HeaderList"/> from the specified stream.
        /// </summary>
        /// <remarks>
        /// Loads a <see cref="HeaderList"/> from the given stream, using the
        /// specified <see cref="ParserOptions"/>.
        /// </remarks>
        /// <returns>The parsed list of headers.</returns>
        /// <param name="options">The parser options.</param>
        /// <param name="stream">The 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.FormatException">
        /// There was an error parsing the headers.
        /// </exception>
        /// <exception cref="System.IO.IOException">
        /// An I/O error occurred.
        /// </exception>
        public static HeaderList Load(ParserOptions options, Stream stream, CancellationToken cancellationToken = default(CancellationToken))
        {
            if (options == null)
            {
                throw new ArgumentNullException("options");
            }

            if (stream == null)
            {
                throw new ArgumentNullException("stream");
            }

            var parser = new MimeParser(options, stream, MimeFormat.Entity);

            return(parser.ParseHeaders(cancellationToken));
        }
Exemple #9
0
        public void TestEmptyMultipartAlternative()
        {
            string expected = @"Content-Type: multipart/mixed
               Content-Type: multipart/alternative
               Content-Type: text/plain
            ";

            using (var stream = File.OpenRead ("../../TestData/messages/empty-multipart.txt")) {
                var parser = new MimeParser (stream, MimeFormat.Entity);
                var message = parser.ParseMessage ();
                var builder = new StringBuilder ();

                DumpMimeTree (builder, message.Body, 0);

                Assert.AreEqual (expected, builder.ToString (), "Unexpected MIME tree structure.");
            }
        }
Exemple #10
0
		public void TestMimeVisitor ()
		{
			var dataDir = Path.Combine ("..", "..", "TestData", "mbox");
			var visitor = new HtmlPreviewVisitor ();
			int index = 0;

			using (var stream = File.OpenRead (Path.Combine (dataDir, "jwz.mbox.txt"))) {
				var parser = new MimeParser (stream, MimeFormat.Mbox);

				while (!parser.IsEndOfStream) {
					var filename = string.Format ("jwz.body.{0}.html", index);
					var path = Path.Combine (dataDir, filename);
					var message = parser.ParseMessage ();
					string expected, actual;

					visitor.Visit (message);

					actual = visitor.HtmlBody;

					if (!string.IsNullOrEmpty (actual))
						actual = actual.Replace ("\r\n", "\n");

					if (!File.Exists (path) && actual != null)
						File.WriteAllText (path, actual);

					if (File.Exists (path))
						expected = File.ReadAllText (path, Encoding.UTF8).Replace ("\r\n", "\n");
					else
						expected = null;

					if (index != 6 && index != 13 && index != 31) {
						// message 6, 13 and 31 contain some japanese text that is broken in Mono
						Assert.AreEqual (expected, actual, "The bodies do not match for message {0}", index);
					}

					visitor.Reset ();
					index++;
				}
			}
		}
        public void TestSecureMimeDecryptThunderbird()
        {
            var p12 = Path.Combine ("..", "..", "TestData", "smime", "gnome.p12");
            MimeMessage message;

            if (!File.Exists (p12))
                return;

            using (var file = File.OpenRead (Path.Combine ("..", "..", "TestData", "smime", "thunderbird-encrypted.txt"))) {
                var parser = new MimeParser (file, MimeFormat.Default);
                message = parser.ParseMessage ();
            }

            using (var ctx = CreateContext ()) {
                var encrypted = (ApplicationPkcs7Mime) message.Body;
                MimeEntity decrypted = null;

                using (var file = File.OpenRead (p12)) {
                    ctx.Import (file, "no.secret");
                }

                var type = encrypted.ContentType.Parameters["smime-type"];
                Assert.AreEqual ("enveloped-data", type, "Unexpected smime-type parameter.");

                try {
                    decrypted = encrypted.Decrypt (ctx);
                } catch (Exception ex) {
                    Console.WriteLine (ex);
                    Assert.Fail ("Failed to decrypt thunderbird message: {0}", ex);
                }

                // The decrypted part should be a multipart/mixed with a text/plain part and an image attachment,
                // very much like the thunderbird-signed.txt message.
                Assert.IsInstanceOfType (typeof (Multipart), decrypted, "Expected the decrypted part to be a Multipart.");
                var multipart = (Multipart) decrypted;

                Assert.IsInstanceOfType (typeof (TextPart), multipart[0], "Expected the first part of the decrypted multipart to be a TextPart.");
                Assert.IsInstanceOfType (typeof (MimePart), multipart[1], "Expected the second part of the decrypted multipart to be a MimePart.");
            }
        }
Exemple #12
0
		public void TestSimpleMbox ()
		{
			using (var stream = File.OpenRead ("../../TestData/mbox/simple.mbox.txt")) {
				var parser = new MimeParser (stream, MimeFormat.Mbox);

				while (!parser.IsEndOfStream) {
					var message = parser.ParseMessage ();
					Multipart multipart;
					MimeEntity entity;

					Assert.IsInstanceOfType (typeof (Multipart), message.Body);
					multipart = (Multipart) message.Body;
					Assert.AreEqual (1, multipart.Count);
					entity = multipart[0];

					Assert.IsInstanceOfType (typeof (Multipart), entity);
					multipart = (Multipart) entity;
					Assert.AreEqual (1, multipart.Count);
					entity = multipart[0];

					Assert.IsInstanceOfType (typeof (Multipart), entity);
					multipart = (Multipart) entity;
					Assert.AreEqual (1, multipart.Count);
					entity = multipart[0];

					Assert.IsInstanceOfType (typeof (TextPart), entity);

					using (var memory = new MemoryStream ()) {
						entity.WriteTo (UnixFormatOptions, memory);

						var text = Encoding.ASCII.GetString (memory.ToArray ());
						Assert.IsTrue (text.StartsWith ("Content-Type: text/plain\n\n", StringComparison.Ordinal), "Headers are not properly terminated.");
					}
				}
			}
		}
Exemple #13
0
		/// <summary>
		/// Joins the specified message/partial parts into the complete message.
		/// </summary>
		/// <remarks>
		/// Combines all of the message/partial fragments into its original,
		/// complete, message.
		/// </remarks>
		/// <returns>The re-combined message.</returns>
		/// <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>
		/// <exception cref="System.ArgumentException">
		/// <para>The last partial does not have a Total.</para>
		/// <para>-or-</para>
		/// <para>The number of partials provided does not match the expected count.</para>
		/// <para>-or-</para>
		/// <para>One or more partials is missing.</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 ("The last partial does not have a Total.", "partials");

			int total = parts[parts.Count - 1].Total.Value;
			if (parts.Count != total)
				throw new ArgumentException ("The number of partials provided does not match the expected count.", "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 ("One or more partials is missing.", "partials");

					var content = parts[i].ContentObject;

					chained.Add (content.Open ());
				}

				var parser = new MimeParser (options, chained);

				return parser.ParseMessage ();
			}
		}
Exemple #14
0
        MimeMessage ParseMessage(CancellationToken cancellationToken)
        {
            if (parser == null)
                parser = new MimeParser (ParserOptions.Default, engine.Stream);
            else
                parser.SetStream (ParserOptions.Default, engine.Stream);

            return parser.ParseMessage (cancellationToken);
        }
Exemple #15
0
        /// <summary>
        /// Load a <see cref="MimeEntity"/> from the specified stream.
        /// </summary>
        /// <remarks>
        /// Loads a <see cref="MimeEntity"/> from the given stream, using the
        /// specified <see cref="ParserOptions"/>.
        /// </remarks>
        /// <returns>The parsed MIME entity.</returns>
        /// <param name="options">The parser options.</param>
        /// <param name="stream">The 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.FormatException">
        /// There was an error parsing the entity.
        /// </exception>
        /// <exception cref="System.IO.IOException">
        /// An I/O error occurred.
        /// </exception>
        public static MimeEntity Load(ParserOptions options, Stream stream, CancellationToken cancellationToken)
        {
            if (options == null)
                throw new ArgumentNullException ("options");

            if (stream == null)
                throw new ArgumentNullException ("stream");

            var parser = new MimeParser (options, stream, MimeFormat.Entity);

            return parser.ParseEntity (cancellationToken);
        }
Exemple #16
0
        /// <summary>
        /// Load a <see cref="MimeEntity"/> from the specified stream.
        /// </summary>
        /// <returns>The parsed MIME entity.</returns>
        /// <param name="stream">The stream.</param>
        /// <exception cref="System.ArgumentNullException">
        /// <paramref name="stream"/> is <c>null</c>.
        /// </exception>
        public static MimeEntity Load(Stream stream)
        {
            if (stream == null)
                throw new ArgumentNullException ("stream");

            var parser = new MimeParser (stream, MimeFormat.Entity);

            return parser.ParseEntity ();
        }
Exemple #17
0
		//[Ignore]
		public void TestDkimSignVerifyJwzMbox ()
		{
			using (var stream = File.OpenRead ("../../TestData/mbox/jwz.mbox.txt")) {
				var parser = new MimeParser (stream, MimeFormat.Mbox);

				while (!parser.IsEndOfStream) {
					var message = parser.ParseMessage ();

					TestDkimSignVerify (message, DkimSignatureAlgorithm.RsaSha1,
						DkimCanonicalizationAlgorithm.Relaxed,
						DkimCanonicalizationAlgorithm.Relaxed);

					TestDkimSignVerify (message, DkimSignatureAlgorithm.RsaSha256,
						DkimCanonicalizationAlgorithm.Relaxed,
						DkimCanonicalizationAlgorithm.Simple);

					TestDkimSignVerify (message, DkimSignatureAlgorithm.RsaSha1,
						DkimCanonicalizationAlgorithm.Simple,
						DkimCanonicalizationAlgorithm.Relaxed);

					TestDkimSignVerify (message, DkimSignatureAlgorithm.RsaSha256,
						DkimCanonicalizationAlgorithm.Simple,
						DkimCanonicalizationAlgorithm.Simple);
				}
			}
		}
Exemple #18
0
		public void TestReserialization ()
		{
			string rawMessageText = @"X-Andrew-Authenticated-As: 4099;greenbush.galaxy;Nathaniel Borenstein
Received: from Messages.8.5.N.CUILIB.3.45.SNAP.NOT.LINKED.greenbush.galaxy.sun4.41
          via MS.5.6.greenbush.galaxy.sun4_41;
          Fri, 12 Jun 1992 13:29:05 -0400 (EDT)
Message-ID : <*****@*****.**>
Date: Fri, 12 Jun 1992 13:29:05 -0400 (EDT)
From: Nathaniel Borenstein <nsb>
X-Andrew-Message-Size: 152+1
MIME-Version: 1.0
Content-Type: multipart/alternative; 
	boundary=""Interpart.Boundary.IeCBvV20M2YtEoUA0A""
To: Ned Freed <*****@*****.**>,
    [email protected] (Yutaka Sato =?ISO-2022-JP?B?GyRAOjRGI0stGyhK?= )
Subject: MIME & int'l mail

> THIS IS A MESSAGE IN 'MIME' FORMAT.  Your mail reader does not support MIME.
> Please read the first section, which is plain text, and ignore the rest.

--Interpart.Boundary.IeCBvV20M2YtEoUA0A
Content-type: text/plain; charset=US-ASCII

In honor of the Communications Week error about MIME's ability to handle
international character sets. a screen dump:

[An Andrew ToolKit view (mailobjv) was included here, but could not be
displayed.]
Just for fun....  -- Nathaniel

--Interpart.Boundary.IeCBvV20M2YtEoUA0A
Content-Type: multipart/mixed; 
	boundary=""Alternative.Boundary.IeCBvV20M2Yt4oU=wd""

--Alternative.Boundary.IeCBvV20M2Yt4oU=wd
Content-type: text/richtext; charset=US-ASCII
Content-Transfer-Encoding: quoted-printable

In honor of the <italic>Communications Week</italic> error about MIME's abilit=
y to handle international character sets. a screen dump:<nl>
<nl>

--Alternative.Boundary.IeCBvV20M2Yt4oU=wd
Content-type: image/gif
Content-Description: Some international characters
Content-Transfer-Encoding: base64

R0lGODdhEgLiAKEAAAAAAP///wAA////4CwAAAAAEgLiAAAC/oSPqcvtD6OctNqLs968
...
R+mUIAiVUTmCU0mVJmiVV5mCfaiVQtaUXVlKXwmWZiSWY3lDZWmWIISWaalUWcmW+bWW
b9lAcSmXCUSXdWlKbomX7HWXe4llXOmXQAmYgTmUg0mYRmmYh5mUscGYjemYjwmZkSmZ
k0mZlWmZl4mZqVEAADs=

--Alternative.Boundary.IeCBvV20M2Yt4oU=wd
Content-type: text/richtext; charset=US-ASCII
Content-Transfer-Encoding: quoted-printable

<nl>
<nl>
Just for fun....  -- Nathaniel<nl>

--Interpart.Boundary.IeCBvV20M2YtEoUA0A--
".Replace ("\r\n", "\n");
			string result;

			using (var source = new MemoryStream (Encoding.UTF8.GetBytes (rawMessageText))) {
				var parser = new MimeParser (source, MimeFormat.Default);
				var message = parser.ParseMessage ();

				using (var serialized = new MemoryStream ()) {
					var options = FormatOptions.Default.Clone ();
					options.NewLineFormat = NewLineFormat.Unix;

					message.WriteTo (options, serialized);

					result = Encoding.UTF8.GetString (serialized.ToArray ());
				}
			}

			Assert.AreEqual (rawMessageText, result, "Reserialized message is not identical to the original.");
		}
Exemple #19
0
		/// <summary>
		/// Load a <see cref="MimeMessage"/> from the specified stream.
		/// </summary>
		/// <remarks>
		/// <para>Loads a <see cref="MimeMessage"/> from the given stream, using the
		/// specified <see cref="ParserOptions"/>.</para>
		/// <para>If <paramref name="persistent"/> is <c>true</c> and <paramref name="stream"/> is seekable, then
		/// the <see cref="MimeParser"/> will not copy the content of <see cref="MimePart"/>s into memory. Instead,
		/// it will use a <see cref="MimeKit.IO.BoundStream"/> to reference a substream of <paramref name="stream"/>.
		/// This has the potential to not only save mmeory usage, but also improve <see cref="MimeParser"/>
		/// performance.</para>
		/// </remarks>
		/// <returns>The parsed message.</returns>
		/// <param name="options">The parser options.</param>
		/// <param name="stream">The stream.</param>
		/// <param name="persistent"><c>true</c> if the stream is persistent; otherwise <c>false</c>.</param>
		/// <param name="cancellationToken">A cancellation token.</param>
		/// <exception cref="System.ArgumentNullException">
		/// <para><paramref name="options"/> is <c>null</c>.</para>
		/// <para>-or-</para>
		/// <para><paramref name="stream"/> is <c>null</c>.</para>
		/// </exception>
		/// <exception cref="System.OperationCanceledException">
		/// The operation was canceled via the cancellation token.
		/// </exception>
		/// <exception cref="System.FormatException">
		/// There was an error parsing the entity.
		/// </exception>
		/// <exception cref="System.IO.IOException">
		/// An I/O error occurred.
		/// </exception>
		public static MimeMessage Load (ParserOptions options, Stream stream, bool persistent, CancellationToken cancellationToken = default (CancellationToken))
		{
			if (options == null)
				throw new ArgumentNullException ("options");

			if (stream == null)
				throw new ArgumentNullException ("stream");

			var parser = new MimeParser (options, stream, MimeFormat.Entity, persistent);

			return parser.ParseMessage (cancellationToken);
		}
		void HandlePgpMime(Outlook.MailItem mailItem, Outlook.Attachment encryptedMime,
			Outlook.Attachment sigMime, string sigHash = "sha1")
		{
			logger.Trace("> HandlePgpMime");
			CryptoContext Context = null;

			byte[] cyphertext = null;
			byte[] clearbytes = null;
			var cleartext = mailItem.Body;

			// 1. Decrypt attachement

			if (encryptedMime != null)
			{
				logger.Trace("Decrypting cypher text.");

				var tempfile = Path.GetTempFileName();
				encryptedMime.SaveAsFile(tempfile);
				cyphertext = File.ReadAllBytes(tempfile);
				File.Delete(tempfile);

				clearbytes = DecryptAndVerify(mailItem.To, cyphertext, out Context);
				if (clearbytes == null)
					return;

				cleartext = this._encoding.GetString(clearbytes);
			}

			// 2. Verify signature

			if (sigMime != null)
			{
				Context = new CryptoContext(PasswordCallback, _settings.Cipher, _settings.Digest);
				var Crypto = new PgpCrypto(Context);
				var mailType = mailItem.BodyFormat;

				try
				{
					logger.Trace("Verify detached signature");

					var tempfile = Path.GetTempFileName();
					sigMime.SaveAsFile(tempfile);
					var detachedsig = File.ReadAllText(tempfile);
					File.Delete(tempfile);

					// Build up a clearsignature format for validation
					// the rules for are the same with the addition of two heaer fields.
					// Ultimately we need to get these fields out of email itself.

					// NOTE: encoding could be uppercase or lowercase. Try both.
					//       this is definetly hacky :/

					var encoding = GetEncodingFromMail(mailItem);
					var body = string.Empty;

					// Try two different methods to get the mime body
					try
					{
						body = encoding.GetString(
							(byte[])mailItem.PropertyAccessor.GetProperty(
								"http://schemas.microsoft.com/mapi/string/{4E3A7680-B77A-11D0-9DA5-00C04FD65685}/Internet Charset Body/0x00000102"));
					}
					catch (Exception)
					{
						body = (string)mailItem.PropertyAccessor.GetProperty(
								"http://schemas.microsoft.com/mapi/proptag/0x1000001F"); // PR_BODY
					}

					var clearsigUpper = new StringBuilder();

					clearsigUpper.Append(string.Format("-----BEGIN PGP SIGNED MESSAGE-----\r\nHash: {0}\r\nCharset: {1}\r\n\r\n", sigHash, encoding.BodyName.ToUpper()));
					clearsigUpper.Append("Content-Type: text/plain; charset=");
					clearsigUpper.Append(encoding.BodyName.ToUpper());
					clearsigUpper.Append("\r\nContent-Transfer-Encoding: quoted-printable\r\n\r\n");

					clearsigUpper.Append(PgpClearDashEscapeAndQuoteEncode(body));

					clearsigUpper.Append("\r\n");
					clearsigUpper.Append(detachedsig);

					var clearsigLower = new StringBuilder(clearsigUpper.Length);

					clearsigLower.Append(string.Format("-----BEGIN PGP SIGNED MESSAGE-----\r\nHash: {0}\r\nCharset: {1}\r\n\r\n", sigHash, encoding.BodyName.ToUpper()));
					clearsigLower.Append("Content-Type: text/plain; charset=");
					clearsigLower.Append(encoding.BodyName.ToLower());
					clearsigLower.Append("\r\nContent-Transfer-Encoding: quoted-printable\r\n\r\n");

					clearsigLower.Append(PgpClearDashEscapeAndQuoteEncode(body));

					clearsigLower.Append("\r\n");
					clearsigLower.Append(detachedsig);

					logger.Trace(clearsigUpper.ToString());

					if (Crypto.VerifyClear(_encoding.GetBytes(clearsigUpper.ToString())) || Crypto.VerifyClear(_encoding.GetBytes(clearsigLower.ToString())))
					{
						Context = Crypto.Context;

						var message = "** " + string.Format(Localized.MsgValidSig,
							Context.SignedByUserId, Context.SignedByKeyId) + "\n\n";

						if (mailType == Outlook.OlBodyFormat.olFormatPlain)
							mailItem.Body = message + mailItem.Body;
						else
							mailItem.HTMLBody = AddMessageToHtmlBody(mailItem.HTMLBody, message);
					}
					else
					{
						Context = Crypto.Context;

						var message = "** " + string.Format(Localized.MsgInvalidSig,
							Context.SignedByUserId, Context.SignedByKeyId) + "\n\n";

						if (mailType == Outlook.OlBodyFormat.olFormatPlain)
							mailItem.Body = message + mailItem.Body;
						else
							mailItem.HTMLBody = AddMessageToHtmlBody(mailItem.HTMLBody, message);
					}
				}
				catch (PublicKeyNotFoundException ex)
				{
					logger.Debug(ex.ToString());

					Context = Crypto.Context;

					var message = "** " + Localized.MsgSigMissingPubKey + "\n\n";

					if (mailType == Outlook.OlBodyFormat.olFormatPlain)
						mailItem.Body = message + mailItem.Body;
					else
						mailItem.HTMLBody = AddMessageToHtmlBody(mailItem.HTMLBody, message);
				}
				catch (Exception ex)
				{
					logger.Debug(ex.ToString());

					WriteErrorData("VerifyEmail", ex);
					MessageBox.Show(
						ex.Message,
						Localized.ErrorDialogTitle,
						MessageBoxButtons.OK,
						MessageBoxIcon.Error);
				}

				return;

			}

			if (Context == null)
				return;

			// Extract files from MIME data

			MimeMessage msg = null;
			TextPart textPart = null;
			MimeEntity htmlPart = null;
			var isHtml = false;

			using(var sin = new MemoryStream(clearbytes))
			{
				var parser = new MimeParser(sin);
				msg = parser.ParseMessage();
				var iter = new MimeIterator(msg);

				while(iter.MoveNext())
				{
					var part = iter.Current as TextPart;
					if (part == null)
						continue;

					if (part.IsAttachment)
						continue;

					// message could include both text and html
					// if we find html use that over text.
					if (part.IsHtml)
					{
						htmlPart = part;
						isHtml = true;
					}
					else
					{
						textPart = part;
					}
				}
			}

			var DecryptAndVerifyHeaderMessage = "** ";

			if (Context.IsEncrypted)
				DecryptAndVerifyHeaderMessage += Localized.MsgDecrypt + " ";

			if (Context.FailedIntegrityCheck)
				DecryptAndVerifyHeaderMessage += Localized.MsgFailedIntegrityCheck + " ";

			if (Context.IsSigned && Context.SignatureValidated)
			{
				DecryptAndVerifyHeaderMessage += string.Format(Localized.MsgValidSig,
					Context.SignedByUserId, Context.SignedByKeyId);
			}
			else if (Context.IsSigned)
			{
				DecryptAndVerifyHeaderMessage +=  string.Format(Localized.MsgInvalidSig,
					Context.SignedByUserId, Context.SignedByKeyId);
			}
			else
				DecryptAndVerifyHeaderMessage += Localized.MsgUnsigned;

			DecryptAndVerifyHeaderMessage += "\n\n";

			if(isHtml)
			{
				var htmlBody = msg.HtmlBody;
				var related = msg.Body as MultipartRelated;
				var doc = new HtmlAgilityPack.HtmlDocument();
				var savedImages = new List<MimePart>();

				doc.LoadHtml(htmlBody);

				// Find any embedded images
				foreach (var img in doc.DocumentNode.SelectNodes("//img[@src]"))
				{
					var src = img.Attributes["src"];
					Uri uri;

					if (src == null || src.Value == null)
						continue;

					// parse the <img src=...> attribute value into a Uri
					if (Uri.IsWellFormedUriString(src.Value, UriKind.Absolute))
						uri = new Uri(src.Value, UriKind.Absolute);
					else
						uri = new Uri(src.Value, UriKind.Relative);

					// locate the index of the attachment within the multipart/related (if it exists)
					string imageCid = src.Value.Substring(4);

					var iter = new MimeIterator(msg);
					MimePart attachment = null;
					while (iter.MoveNext())
					{
						if (iter.Current.ContentId == imageCid)
						{
							attachment = iter.Current as MimePart;
							break;
						}
					}

					if (attachment == null)
						continue;

					string fileName;

					// save the attachment (if we haven't already saved it)
					if (!savedImages.Contains(attachment))
					{
						fileName = attachment.FileName;

						if (string.IsNullOrEmpty(fileName))
							fileName = Guid.NewGuid().ToString();

						using (var stream = File.Create(fileName))
							attachment.ContentObject.DecodeTo(stream);

						try
						{
							var att = mailItem.Attachments.Add(fileName, Outlook.OlAttachmentType.olEmbeddeditem, null, "");
							att.PropertyAccessor.SetProperty("http://schemas.microsoft.com/mapi/proptag/0x3712001E", imageCid);
							savedImages.Add(attachment);
						}
						finally
						{
							// try not to leak temp files :)
							File.Delete(fileName);
						}
					}
				}

				mailItem.BodyFormat = Outlook.OlBodyFormat.olFormatHTML;
				mailItem.HTMLBody = AddMessageToHtmlBody(htmlBody, DecryptAndVerifyHeaderMessage);
			}
			else
			{
				// NOTE: For some reason we cannot change the BodyFormat once it's set.
				//       So if we are set to HTML we need to wrap the plain text so it's
				//       displayed okay. Also of course prevent XSS.

				if (mailItem.BodyFormat == Outlook.OlBodyFormat.olFormatPlain)
				{
					mailItem.Body = DecryptAndVerifyHeaderMessage + msg.TextBody;
				}
				else
				{
					var sb = new StringBuilder(msg.TextBody.Length + 100);
					sb.Append("<html><body><pre>");
					sb.Append(WebUtility.HtmlEncode(DecryptAndVerifyHeaderMessage));
					sb.Append(WebUtility.HtmlEncode(msg.TextBody));
					sb.Append("</pre></body></html>");

					mailItem.HTMLBody = sb.ToString();
				}
			}

			// NOTE: Removing existing attachments is perminant, even if the message
			//       is not saved.

			foreach (var mimeAttachment in msg.Attachments)
			{
				var fileName = mimeAttachment.FileName;
				var tempFile = Path.Combine(Path.GetTempPath(), fileName);

				using (var fout = File.OpenWrite(tempFile))
				{
					mimeAttachment.ContentObject.DecodeTo(fout);
				}

				mailItem.Attachments.Add(tempFile, Outlook.OlAttachmentType.olByValue, 1, fileName);

				File.Delete(tempFile);
			}
		}
Exemple #21
0
        /// <summary>
        /// Load a <see cref="MimeMessage"/> from the specified stream.
        /// </summary>
        /// <returns>The parsed message.</returns>
        /// <param name="options">The parser 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 static MimeMessage Load(ParserOptions options, Stream stream)
        {
            if (options == null)
                throw new ArgumentNullException ("options");

            if (stream == null)
                throw new ArgumentNullException ("stream");

            var parser = new MimeParser (options, stream, MimeFormat.Entity);

            return parser.ParseMessage ();
        }
Exemple #22
0
        public MimeMessage ParseMessage(Stream stream, bool persistent, CancellationToken cancellationToken)
        {
            if (parser == null)
                parser = new MimeParser (ParserOptions.Default, stream, persistent);
            else
                parser.SetStream (ParserOptions.Default, stream, persistent);

            return parser.ParseMessage (cancellationToken);
        }
Exemple #23
0
		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);
		}
		public void TestUnmungedFromLines ()
		{
			int count = 0;

			using (var stream = File.OpenRead ("../../TestData/mbox/unmunged.mbox.txt")) {
				var parser = new MimeParser (stream, MimeFormat.Mbox);

				while (!parser.IsEndOfStream) {
					parser.ParseMessage ();

					var marker = parser.MboxMarker;

					if ((count % 2) == 0) {
						Assert.AreEqual ("From -", marker.TrimEnd (), "Message #{0}", count);
					} else {
						Assert.AreEqual ("From Russia with love", marker.TrimEnd (), "Message #{0}", count);
					}

					count++;
				}
			}

			Assert.AreEqual (4, count, "Expected to find 4 messages.");
		}
Exemple #25
0
		public void TestSecureMimeVerifyThunderbird ()
		{
			MimeMessage message;

			using (var file = File.OpenRead (Path.Combine ("..", "..", "TestData", "smime", "thunderbird-signed.txt"))) {
				var parser = new MimeParser (file, MimeFormat.Default);
				message = parser.ParseMessage ();
			}

			using (var ctx = CreateContext ()) {
				var multipart = (MultipartSigned) message.Body;

				var protocol = multipart.ContentType.Parameters["protocol"];
				Assert.IsTrue (ctx.Supports (protocol), "The multipart/signed protocol is not supported.");

				Assert.IsInstanceOfType (typeof (ApplicationPkcs7Signature), multipart[1], "The second child is not a detached signature.");

				var signatures = multipart.Verify (ctx);
				Assert.AreEqual (1, signatures.Count, "Verify returned an unexpected number of signatures.");
				foreach (var signature in signatures) {
					try {
						bool valid = signature.Verify ();

						Assert.IsTrue (valid, "Bad signature from {0}", signature.SignerCertificate.Email);
					} catch (DigitalSignatureVerifyException ex) {
						Assert.Fail ("Failed to verify signature: {0}", ex);
					}

					var algorithms = ((SecureMimeDigitalSignature) signature).EncryptionAlgorithms;
					Assert.AreEqual (EncryptionAlgorithm.Aes256, algorithms[0], "Expected AES-256 capability");
					Assert.AreEqual (EncryptionAlgorithm.Aes128, algorithms[1], "Expected AES-128 capability");
					Assert.AreEqual (EncryptionAlgorithm.TripleDes, algorithms[2], "Expected Triple-DES capability");
					Assert.AreEqual (EncryptionAlgorithm.RC2128, algorithms[3], "Expected RC2-128 capability");
					Assert.AreEqual (EncryptionAlgorithm.RC264, algorithms[4], "Expected RC2-64 capability");
					Assert.AreEqual (EncryptionAlgorithm.Des, algorithms[5], "Expected DES capability");
					Assert.AreEqual (EncryptionAlgorithm.RC240, algorithms[6], "Expected RC2-40 capability");
				}
			}
		}
Exemple #26
0
		public void TestEmptyMultipartAlternative ()
		{
			string expected = @"Content-Type: multipart/mixed
   Content-Type: multipart/alternative
   Content-Type: text/plain
".Replace ("\r\n", "\n");

			using (var stream = File.OpenRead (Path.Combine (MessagesDataDir, "empty-multipart.txt"))) {
				var parser = new MimeParser (stream, MimeFormat.Entity);
				var message = parser.ParseMessage ();
				var builder = new StringBuilder ();

				DumpMimeTree (builder, message);

				Assert.AreEqual (expected, builder.ToString (), "Unexpected MIME tree structure.");
			}
		}
Exemple #27
0
		public void TestReserializationEmptyParts ()
		{
			string rawMessageText = @"Date: Fri, 22 Jan 2016 8:44:05 -0500 (EST)
From: MimeKit Unit Tests <*****@*****.**>
To: MimeKit Unit Tests <*****@*****.**>
MIME-Version: 1.0
Content-Type: multipart/mixed; 
	boundary=""Interpart.Boundary.IeCBvV20M2YtEoUA0A""
Subject: Reserialization test of empty mime parts

THIS IS A MESSAGE IN 'MIME' FORMAT.  Your mail reader does not support MIME.
Please read the first section, which is plain text, and ignore the rest.

--Interpart.Boundary.IeCBvV20M2YtEoUA0A
Content-type: text/plain; charset=US-ASCII

This is the body.

--Interpart.Boundary.IeCBvV20M2YtEoUA0A
Content-type: text/plain; charset=US-ASCII; name=empty.txt
Content-Description: this part contains no content

--Interpart.Boundary.IeCBvV20M2YtEoUA0A
Content-type: text/plain; charset=US-ASCII; name=blank-line.txt
Content-Description: this part contains a single blank line


--Interpart.Boundary.IeCBvV20M2YtEoUA0A--
".Replace ("\r\n", "\n");
			string result;

			using (var source = new MemoryStream (Encoding.UTF8.GetBytes (rawMessageText))) {
				var parser = new MimeParser (source, MimeFormat.Default);
				var message = parser.ParseMessage ();

				using (var serialized = new MemoryStream ()) {
					var options = FormatOptions.Default.Clone ();
					options.NewLineFormat = NewLineFormat.Unix;

					message.WriteTo (options, serialized);

					result = Encoding.UTF8.GetString (serialized.ToArray ());
				}
			}

			Assert.AreEqual (rawMessageText, result, "Reserialized message is not identical to the original.");
		}
Exemple #28
0
		public void TestSecureMimeDecryptVerifyThunderbird ()
		{
			var p12 = Path.Combine ("..", "..", "TestData", "smime", "gnome.p12");
			MimeMessage message;

			if (!File.Exists (p12))
				return;

			using (var file = File.OpenRead (Path.Combine ("..", "..", "TestData", "smime", "thunderbird-signed-encrypted.txt"))) {
				var parser = new MimeParser (file, MimeFormat.Default);
				message = parser.ParseMessage ();
			}

			using (var ctx = CreateContext ()) {
				var encrypted = (ApplicationPkcs7Mime) message.Body;
				MimeEntity decrypted = null;

				using (var file = File.OpenRead (p12)) {
					ctx.Import (file, "no.secret");
				}

				var type = encrypted.ContentType.Parameters["smime-type"];
				Assert.AreEqual ("enveloped-data", type, "Unexpected smime-type parameter.");

				try {
					decrypted = encrypted.Decrypt (ctx);
				} catch (Exception ex) {
					Console.WriteLine (ex);
					Assert.Fail ("Failed to decrypt thunderbird message: {0}", ex);
				}

				// The decrypted part should be a multipart/signed
				Assert.IsInstanceOfType (typeof (MultipartSigned), decrypted, "Expected the decrypted part to be a multipart/signed.");
				var signed = (MultipartSigned) decrypted;

				// The first part of the multipart/signed should be a multipart/mixed with a text/plain part and 2 image attachments,
				// very much like the thunderbird-signed.txt message.
				Assert.IsInstanceOfType (typeof (Multipart), signed[0], "Expected the first part of the multipart/signed to be a multipart.");
				Assert.IsInstanceOfType (typeof (ApplicationPkcs7Signature), signed[1], "Expected second part of the multipart/signed to be a pkcs7-signature.");

				var multipart = (Multipart) signed[0];

				Assert.IsInstanceOfType (typeof (TextPart), multipart[0], "Expected the first part of the decrypted multipart to be a TextPart.");
				Assert.IsInstanceOfType (typeof (MimePart), multipart[1], "Expected the second part of the decrypted multipart to be a MimePart.");
				Assert.IsInstanceOfType (typeof (MimePart), multipart[2], "Expected the third part of the decrypted multipart to be a MimePart.");

				var signatures = signed.Verify (ctx);

				Assert.AreEqual (1, signatures.Count, "Verify returned an unexpected number of signatures.");
				foreach (var signature in signatures) {
					try {
						bool valid = signature.Verify ();

						Assert.IsTrue (valid, "Bad signature from {0}", signature.SignerCertificate.Email);
					} catch (DigitalSignatureVerifyException ex) {
						Assert.Fail ("Failed to verify signature: {0}", ex);
					}

					var algorithms = ((SecureMimeDigitalSignature) signature).EncryptionAlgorithms;
					Assert.AreEqual (EncryptionAlgorithm.Aes256, algorithms[0], "Expected AES-256 capability");
					Assert.AreEqual (EncryptionAlgorithm.Aes128, algorithms[1], "Expected AES-128 capability");
					Assert.AreEqual (EncryptionAlgorithm.TripleDes, algorithms[2], "Expected Triple-DES capability");
					Assert.AreEqual (EncryptionAlgorithm.RC2128, algorithms[3], "Expected RC2-128 capability");
					Assert.AreEqual (EncryptionAlgorithm.RC264, algorithms[4], "Expected RC2-64 capability");
					Assert.AreEqual (EncryptionAlgorithm.Des, algorithms[5], "Expected DES capability");
					Assert.AreEqual (EncryptionAlgorithm.RC240, algorithms[6], "Expected RC2-40 capability");
				}
			}
		}
Exemple #29
0
        /// <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 ();
            }
        }
		public void TestJwzPersistentMbox ()
		{
			var summary = File.ReadAllText ("../../TestData/mbox/jwz-summary.txt").Replace ("\r\n", "\n");
			var builder = new StringBuilder ();

			using (var stream = File.OpenRead ("../../TestData/mbox/jwz.mbox.txt")) {
				var parser = new MimeParser (stream, MimeFormat.Mbox);

				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');

					// Force the various MimePart objects to write their content streams.
					// The idea is that by forcing the MimeParts to seek in their content,
					// we will test to make sure that parser correctly deals with it.
					message.WriteTo (Stream.Null);
				}
			}

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