Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Leading double period from .NET Framework's SmtpClient not decoded #1607

Closed
mrmonday opened this issue Jul 10, 2023 · 5 comments
Closed

Leading double period from .NET Framework's SmtpClient not decoded #1607

mrmonday opened this issue Jul 10, 2023 · 5 comments
Labels
enhancement New feature or request question A question about how to do something

Comments

@mrmonday
Copy link

When loading an email generated by .NET Framework's SmtpClient, if a line starts with a leading ., it is replaced with ...

When using .NET 7 instead of .NET Framework it produces a .eml without the additional leading ..

Program.cs:

using MimeKit;
using System.Net.Mail;

// Set up a temporary pick up directory
string pickupDirectory = Path.GetFullPath("pickup");

try
{
    Directory.Delete(pickupDirectory, recursive: true);
}
catch
{
}

Directory.CreateDirectory(pickupDirectory);

// Configure SMTP client to use it
var client = new SmtpClient()
{
    DeliveryMethod = SmtpDeliveryMethod.SpecifiedPickupDirectory,
    PickupDirectoryLocation = pickupDirectory,
};

const string goodBody = "<p>aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.bbbbbb</p>";
const string badBody = "<p>aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.bbbbbb</p>";

// `goodBody` moves the `.` away from the start of the line, avoiding the encoding issue
string body = badBody;

var message = new MailMessage("[email protected]", "[email protected]", "subject", body)
{
    IsBodyHtml = true,
};
client.Send(message);

// Load the generated email and compare the bodies
string file = Directory.EnumerateFiles(pickupDirectory, "*.eml").FirstOrDefault();

var mimeMessage = MimeMessage.Load(file);
if (mimeMessage.HtmlBody.TrimEnd() != body)
{
    throw new Exception("body does not match");
}

Project file:

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <LangVersion>11</LangVersion>
    <OutputType>Exe</OutputType>
    <TargetFramework>net48</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="MimeKit" Version="4.1.0" />
  </ItemGroup>

</Project>

Sample generated email when using badBody.

X-Sender: [email protected]
X-Receiver: [email protected]
MIME-Version: 1.0
From: [email protected]
To: [email protected]
Date: 10 Jul 2023 12:49:26 +0100
Subject: subject
Content-Type: text/html; charset=us-ascii
Content-Transfer-Encoding: quoted-printable

<p>aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa=
..bbbbbb</p>

I could not see an option to change the decode behaviour when loading - sorry if I've overlooked something.

@jstedfast
Copy link
Owner

The files saved by System.Net.Mail are not in MIME format (they have been pre-escaped for SMTP transport) and so MimeKit's parser does not know to unescape them.

If you want them to be unescaped, you'll need to unescape the stream before you parse it.

Unfortunately, neither MimeKit nor MailKit have a filter for this so you'll have to write your own logic, but it should be fairly simple to do.

Is there a reason you are saving the message using System.Net.Mail and then parsing it using MimeKit?

Can you use MimeMessage.CreateFromMailMessage(mailMessage) instead? Or even better, just generate the message using MimeKit?

@jstedfast jstedfast added the question A question about how to do something label Jul 10, 2023
@mrmonday
Copy link
Author

We have a suite of (legacy) applications which use System.Net.Mail to write mail into a pick up directory.

We had previously used the IIS 6 SMTP Server that ships with Windows to pick these up and forward them to a real mail server, but that was an endless source of weird bugs.

We now have a tool based on MailKit which reads these in and forwards them, which is how we encountered this.

Could you point me in the right direction to build such a filter? If you believe it's something that would be more generally useful I'd be happy to open a PR once it's written.

@jstedfast
Copy link
Owner

The 2 closest filters would probably be https://github.com/jstedfast/MailKit/blob/master/MailKit/Net/Smtp/SmtpDataFilter.cs and maybe https://github.com/jstedfast/MimeKit/blob/master/MimeKit/IO/Filters/Dos2UnixFilter.cs

The SmtpDataFilter is a filter that adds the extra dot (which is the opposite of what you want to do).

Once you've got your filter written, you'll be able to do something like this:

string file = Directory.EnumerateFiles(pickupDirectory, "*.eml").FirstOrDefault();
using var stream = File.OpenRead (file);
using var filtered = new FIlteredStream (stream);
filtered.Add (new CustomSmtpDataUnescapeFilter ());

var message = MimeMessage.Load (filtered);

@jstedfast jstedfast transferred this issue from jstedfast/MimeKit Jul 13, 2023
@jstedfast
Copy link
Owner

I opted to add it as a mode of the existing SmtpDataFilter instead and optimized the logic a bit.

@jstedfast jstedfast added the enhancement New feature or request label Jul 13, 2023
@mrmonday
Copy link
Author

That looks great, thank you!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request question A question about how to do something
Projects
None yet
Development

No branches or pull requests

2 participants