Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Force System.Net.Mail.SmtpClient to encode the Subject header using Base64

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: "[email protected]",
    to: "[email protected]",
    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:

  • When the client connects to a MTA that supports SMTPUTF8, the Subject header is encoded using UTF8: Subject: Caractères accentués.
  • When the client connects to a MTA that does not support SMTPUTF8, the Subject header is encoded in Base64 then in US-ASCII: Subject: =?utf-8?B?Q2FyYWN0w6hyZXMgYWNjZW50dcOpcw==?=.
  • If we switch from 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.

like image 235
Maxime Rossini Avatar asked Oct 16 '25 06:10

Maxime Rossini


1 Answers

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 [email protected], 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é[email protected], 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;
    }
}
like image 196
Heinzi Avatar answered Oct 17 '25 20:10

Heinzi



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!