From 42ea1c75bad44d7cbe16a1b818558226ac217706 Mon Sep 17 00:00:00 2001 From: Zachary Kestenbaum Date: Thu, 12 Feb 2015 16:23:21 -0500 Subject: [PATCH 1/2] added check to see if body part has a name and disposition is unknown, before putting it in the body --- MessageBuilder.cs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/MessageBuilder.cs b/MessageBuilder.cs index 31e367f..61c499b 100644 --- a/MessageBuilder.cs +++ b/MessageBuilder.cs @@ -352,21 +352,23 @@ internal static void AddBodypart(this MailMessage message, Bodypart part, string // Attachments collection. bool hasName = part.Parameters.ContainsKey("name"); + // Check for alternative view. + string ContentType = ParseMIMEField(message.Headers["Content-Type"])["value"]; + bool preferAlternative = string.Compare(ContentType, "multipart/alternative", true) == 0; + // If the MailMessage's Body fields haven't been initialized yet, put it there. Some weird // (i.e. spam) mails like to omit content-types so we don't check for that here and just // assume it's text. if (String.IsNullOrEmpty(message.Body) && - part.Disposition.Type != ContentDispositionType.Attachment) { + !(part.Disposition.Type == ContentDispositionType.Attachment || + (part.Disposition.Type == ContentDispositionType.Unknown && + preferAlternative == false && hasName))) { message.Body = encoding.GetString(bytes); message.BodyEncoding = encoding; message.IsBodyHtml = part.Subtype.ToLower() == "html"; return; } - // Check for alternative view. - string ContentType = ParseMIMEField(message.Headers["Content-Type"])["value"]; - bool preferAlternative = string.Compare(ContentType, "multipart/alternative", true) == 0; - // Many attachments are missing the disposition-type. If it's not defined as alternative // and it has a name attribute, assume it is Attachment rather than an AlternateView. if (part.Disposition.Type == ContentDispositionType.Attachment || From 170450b16984fdba33d2e760280c42695eff3c9b Mon Sep 17 00:00:00 2001 From: Zachary Kestenbaum Date: Thu, 12 Feb 2015 19:23:00 -0500 Subject: [PATCH 2/2] Added a unit test for a message with an empty body and an attachment. --- MessageBuilder.cs | 2 + Tests/MessageBuilderTest.cs | 37 ++++++++++++++++++- Tests/Properties/Resources.Designer.cs | 27 ++++++++++++-- Tests/Properties/Resources.resx | 3 ++ .../MailWithoutDispositionEmptyBody.txt | 26 +++++++++++++ Tests/Tests.csproj | 4 ++ 6 files changed, 95 insertions(+), 4 deletions(-) create mode 100644 Tests/Resources/MailWithoutDispositionEmptyBody.txt diff --git a/MessageBuilder.cs b/MessageBuilder.cs index 61c499b..14395e6 100644 --- a/MessageBuilder.cs +++ b/MessageBuilder.cs @@ -363,6 +363,8 @@ internal static void AddBodypart(this MailMessage message, Bodypart part, string !(part.Disposition.Type == ContentDispositionType.Attachment || (part.Disposition.Type == ContentDispositionType.Unknown && preferAlternative == false && hasName))) { + //if (String.IsNullOrEmpty(message.Body) && + // !(part.Disposition.Type == ContentDispositionType.Attachment)) { message.Body = encoding.GetString(bytes); message.BodyEncoding = encoding; message.IsBodyHtml = part.Subtype.ToLower() == "html"; diff --git a/Tests/MessageBuilderTest.cs b/Tests/MessageBuilderTest.cs index de3b4fc..067ab27 100644 --- a/Tests/MessageBuilderTest.cs +++ b/Tests/MessageBuilderTest.cs @@ -1,4 +1,5 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; +using System; +using Microsoft.VisualStudio.TestTools.UnitTesting; using System.IO; using System.Linq; using System.Net.Mail; @@ -128,6 +129,40 @@ public void MessageWithMultipartMixedAttachment() { "Attachment name missmatch."); } + /// + /// Creates a MailMessage instance from an RFC822/MIME string containing an attachment without + /// a disposition and an empty body. + /// + [TestMethod] + [TestCategory("BuildMessageFromMIME822")] + public void MessageWithMultipartMixedAttachmentAndEmptyBody() + { + // The multiparts is manipulated base64 data and not the real files. + MailMessage m = MessageBuilder.FromMIME822(Properties.Resources.MailWithoutDispositionEmptyBody); + + // Check the From header. + Assert.AreEqual("", m.From.DisplayName, "Expected no From displayname."); + Assert.AreEqual("invxxxxx@xxxxx.com", m.From.Address, "Unexpected From address."); + // Check the To header. + Assert.AreEqual(1, m.To.Count, "Unexpected To address count."); + Assert.AreEqual("", m.To[0].DisplayName, "Expected no To displayname."); + Assert.AreEqual("faktxxx@xxxxxx.se", m.To[0].Address, "Unexpected To address."); + Assert.AreEqual("Fakt__xxxx___xxxx___07_02_2013", m.Subject, "Unexpected Subject."); + Assert.AreEqual( + "multipart/mixed;\tboundary=\"--boundary_0_6cb33448-390c-4f02-b75a-2738f1d6dd45\"", + m.Headers["Content-Type"], "Unexpected Content-Type"); + Assert.IsFalse(m.IsBodyHtml, "Expected non HTML body"); + // Ensure that the body encoding is null, since there is no body. + Assert.AreEqual(null, m.BodyEncoding, "Expected null Body Encoding."); + // Ensure that the body is empty. + Assert.AreEqual(String.Empty, m.Body); + Assert.AreEqual(0, m.AlternateViews.Count, "AlternateViews count missmatch."); + // Ensure that the attachment is treated correctly as an attachment. + Assert.AreEqual(1, m.Attachments.Count, "Attachment count missmatch."); + Assert.AreEqual("Fakt_fil_xxxx_20130207.pdf", m.Attachments[0].Name, + "Attachment name missmatch."); + } + /// /// Ensures a malformed, invalid or missing From header is handled properly. See issue #50. /// diff --git a/Tests/Properties/Resources.Designer.cs b/Tests/Properties/Resources.Designer.cs index 22fab27..2c9a820 100644 --- a/Tests/Properties/Resources.Designer.cs +++ b/Tests/Properties/Resources.Designer.cs @@ -1,7 +1,7 @@ //------------------------------------------------------------------------------ // // This code was generated by a tool. -// Runtime Version:4.0.30319.18408 +// Runtime Version:4.0.30319.34209 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. @@ -257,6 +257,27 @@ internal static string MailWithoutDisposition { } } + /// + /// Looks up a localized string similar to Received: from xxx.xxxxx.com (xx.xxxx.com [123.45.67.89]) + /// by xxxx.asxxx.se (Postfix) with ESMTP id E357F2CCC6A + /// for <faktxxx@xxxxxx.se>; Thu, 7 Feb 2013 17:12:05 +0100 (CET) + ///MIME-Version: 1.0 + ///From: <invxxxxx@xxxxx.com> + ///To: <faktxxx@xxxxxx.se> + ///Date: Thu, 7 Feb 2013 17:12:02 +0100 + ///Subject: Fakt__xxxx___xxxx___07_02_2013 + ///Content-Type: multipart/mixed; + /// boundary="--boundary_0_6cb33448-390c-4f02-b75a-2738f1d6dd45" + ///Message-ID: <44967f46-57cc-47c4-ab9f-1c450e1d87bb@XXXXI0123.xx.xxxx.com> + /// + ///----boundar [rest of string was truncated]";. + /// + internal static string MailWithoutDispositionEmptyBody { + get { + return ResourceManager.GetString("MailWithoutDispositionEmptyBody", resourceCulture); + } + } + /// /// Looks up a localized string similar to Return-Path: <xxxxxxx@canon.no> ///Received: from mail-gtw.canon.no (mail.canon.no [194.248.71.4]) @@ -289,7 +310,7 @@ internal static string MailWithQuotedPrintables { ///X-Mailer: tie-bomber ///Date: Sat, 04 Sep 1999 18:26:50 +0900 ///Message-ID: <19990904092850.5963.foo@example.org> - ///Content-Type: multipart/mixed; boun [rest of string was truncated]";. + ///Content-Type: multipart [rest of string was truncated]";. /// internal static string MailWithRfc2231Headers { get { @@ -310,7 +331,7 @@ internal static string MailWithRfc2231Headers { ///X-Mailer: tie-bomber ///Date: Sat, 04 Sep 1999 18:26:50 +0900 ///Message-ID: <19990904092850.5963.foo@example.org> - ///Content-Type: multipart/mixed; boun [rest of string was truncated]";. + ///Content-Type: multipart [rest of string was truncated]";. /// internal static string MailWithRfc2231Headers2 { get { diff --git a/Tests/Properties/Resources.resx b/Tests/Properties/Resources.resx index 1e07a20..55ee4b7 100644 --- a/Tests/Properties/Resources.resx +++ b/Tests/Properties/Resources.resx @@ -161,6 +161,9 @@ X-MimeOLE: Produced By Microsoft MimeOLE V6.0.6002.18463 ..\Resources\MailWithoutDisposition.txt;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;Windows-1252 + + ..\Resources\MailWithoutDispositionEmptyBody.txt;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;Windows-1252 + ..\Resources\MailWithQuotedPrintables.txt;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;Windows-1252 diff --git a/Tests/Resources/MailWithoutDispositionEmptyBody.txt b/Tests/Resources/MailWithoutDispositionEmptyBody.txt new file mode 100644 index 0000000..a35f00a --- /dev/null +++ b/Tests/Resources/MailWithoutDispositionEmptyBody.txt @@ -0,0 +1,26 @@ +Received: from xxx.xxxxx.com (xx.xxxx.com [123.45.67.89]) + by xxxx.asxxx.se (Postfix) with ESMTP id E357F2CCC6A + for ; Thu, 7 Feb 2013 17:12:05 +0100 (CET) +MIME-Version: 1.0 +From: +To: +Date: Thu, 7 Feb 2013 17:12:02 +0100 +Subject: Fakt__xxxx___xxxx___07_02_2013 +Content-Type: multipart/mixed; + boundary="--boundary_0_6cb33448-390c-4f02-b75a-2738f1d6dd45" +Message-ID: <44967f46-57cc-47c4-ab9f-1c450e1d87bb@XXXXI0123.xx.xxxx.com> + +----boundary_0_6cb33448-390c-4f02-b75a-2738f1d6dd45 +Content-Type: text/plain; charset="utf-8" +Content-Transfer-Encoding: base64 + +----boundary_0_6cb33448-390c-4f02-b75a-2738f1d6dd45 +Content-Type: application/octet-stream; name="Fakt_fil_xxxx_20130207.pdf" +Content-Transfer-Encoding: base64 + +JVBERi0xLjMNCiX15Pb8DQoNCjcgMCBvYmogPDwgL1R5cGUgL1hPYmplY3QNCi9TdWJ0eXBl +IC9JbWFnZQ0KL05hbWUgL0kxDQovV2lkdGggNDYxDQovSGVpZ2h0IDExMA0KL0JpdHNQZXJD +b21wb25lbnQgOA0KL0NvbG9yU3BhY2UgL0RldmljZUdyYXkNCi9MZW5ndGggOTUyOCAvRmls +dGVyIFsgL0FTQ0lJODVEZWNvZGUgL0ZsYXRlRGVjb2RlIF0gPj4NCnN0cmVhbQ0KR2IiLyxN +NDU0MFtKcD8/V14uckZCMXQuLUBjRVxBclFMVkgzJW1iWWR0bEpcRkVdO21faT== +----boundary_0_6cb33448-390c-4f02-b75a-2738f1d6dd45-- diff --git a/Tests/Tests.csproj b/Tests/Tests.csproj index fda401f..f4bd213 100644 --- a/Tests/Tests.csproj +++ b/Tests/Tests.csproj @@ -85,6 +85,7 @@ ResXFileCodeGenerator Resources.Designer.cs + Designer @@ -111,6 +112,9 @@ + + +