We are using .NET Core 3.1's default SMTP client to send an email, like this:
private async Task SendMail(string from, string to, string subject, string body)
{
var message = new MailMessage();
message.From = new MailAddress(from);
var toAddress = new MailAddress(to);
message.To.Add(toAddress);
message.Subject = subject;
message.SubjectEncoding = Encoding.UTF8;
message.Body = body;
message.BodyEncoding = Encoding.UTF8;
message.IsBodyHtml = false;
using var smtp = new SmtpClient();
smtp.Host = SMTP_HOST;
smtp.Port = SMTP_PORT;
smtp.EnableSsl = false;
smtp.DeliveryMethod = SmtpDeliveryMethod.Network;
smtp.DeliveryFormat = SmtpDeliveryFormat.International;
smtp.UseDefaultCredentials = false;
smtp.Credentials = new NetworkCredential(SMTP_USER, SMTP_PASSWORD);
await smtp.SendMailAsync(message);
}
Then we call the method like this:
await this.SendMail(
from: "noreply@ourdomain.com",
to: "recipient@example.com",
subject: "Caractères accentués",
body: "dummy"
);
When using a network analyzer to check how the Subject header is encoded, we are observing the following:
Subject: Caractères accentués
.Subject: =?utf-8?B?Q2FyYWN0w6hyZXMgYWNjZW50dcOpcw==?=
.SmtpDeliveryFormat.International
to SmtpDeliveryFormat.SevenBits
, the Subject header is always encoded in Base64 then in US-ASCII (or maybe quoted-url then US-ASCII).This is all standard and excpected behaviour.
We are using a third party email service as a SMTP relay, which supports SMTPUTF8 itself. But their service has a bug and fails to detect the lack of SMTPUTF8 support on recipient's side, resulting in email Subjects being improperly displayed in our clients mailboxes when they contain non-ASCII characters. They send the Subject with the same encoding we used with their MTA, which in our case is UTF8, because we need SmtpDeliveryFormat.International
(for compatibility with non-ASCII email addresses).
The issue disappears when we encode the Subject header using Base64, so we would like to do that as a workaround until our provider fixes the issue on their side. Using smtp.DeliveryFormat = SmtpDeliveryFormat.SevenBits
achieves this, but it also prevents us from using non-ASCII characters in email addresses, which is an even bigger problem than the Subject encoding issue. So we can't do that.
Is there a way to force the .NET client to use Base64 encoding for the Subject header while also using SmtpDeliveryFormat.International
, even when the SMTP relay supports SMTPUTF8? I tried to do this:
message.Subject = $"=?utf-8?B?{Convert.ToBase64String(Encoding.UTF8.GetBytes(subject))}?=";
But the subject header is not passed through, it is decoded by the SmtpClient
then UTF8-encoded as Caractères accentués
, so it doesn't change anything.
Not a solution, but a workaround:
Set DeliveryFormat to SmtpDeliveryFormat.International
if, and only if, the local part (i.e. the part before the "@") of the target mail address contains a non-ascii character. This has the following effect:
For recipient@example.com
, the subject will be properly encoded.
For recipient@éxample.com
, everything will work as well: The MailAddress class can encode the host-part of a mail address with punycode without requiring SMTPUTF8.
For récipient@example.com
, the subject won't be Base64-encoded. However, since local part contains a non-ASCII character, it is highly likely that the recipient mail server supports SMTPUTF8, and, thus, can interpret your subject line correctly.
Example code:
public static void EnableNonAsciiMailAddressesIfNecessary(
SmtpClient client, MailMessage message)
{
var allAddresses =
(message.From != null
? new[] { message.From }
: Array.Empty<MailAddress>())
.Concat(message.To)
.Concat(message.CC)
.Concat(message.Bcc)
.Concat(message.ReplyToList);
var smtpUtf8Required = allAddresses.Any(a => a.User.Any(c => c > 0x7f));
if (smtpUtf8Required)
{
client.DeliveryFormat = SmtpDeliveryFormat.International;
}
}