Search code examples
c#email-attachmentssmtpclientmailkit

Changing name of automatic attached file in e-mail


I am using Mailkit library to send e-mails. This is the code to do so:

    public async Task SendAsync(IdentityMessage message)
    {
        if (message == null)
            return;

        LinkedResource inline = new LinkedResource(System.Web.Hosting.HostingEnvironment.MapPath($"~/Images/logo.png"))
        {
            ContentId = Guid.NewGuid().ToString(),
            ContentType = new ContentType("image/png")
            {
                Name = "logo.png"
            }
        };

        var htmlBody = message.Body.Replace("{CID:LOGO}", String.Format("cid:{0}", inline.ContentId));

        AlternateView avHtml = AlternateView.CreateAlternateViewFromString(htmlBody, null, MediaTypeNames.Text.Html);
        avHtml.LinkedResources.Add(inline);

        using (MailMessage objMailMsg = new MailMessage())
        {
            objMailMsg.AlternateViews.Add(avHtml);
            objMailMsg.Body = htmlBody;
            objMailMsg.Subject = message.Subject;
            objMailMsg.BodyEncoding = Encoding.UTF8;

            Properties.Settings.Default.Reload();

            string fromEmail = Properties.Settings.Default.FromMail;
            string fromName = Properties.Settings.Default.FromName;
            string smtpPassword = Properties.Settings.Default.PwdSmtp;

            objMailMsg.From = new MailAddress(fromEmail, fromName);
            objMailMsg.To.Add(message.Destination);
            objMailMsg.IsBodyHtml = true;

            MimeMessage mime = MimeMessage.CreateFromMailMessage(objMailMsg);
            HeaderId[] headersToSign = new HeaderId[] { HeaderId.From, HeaderId.Subject, HeaderId.Date };
            string domain = "example.cl";
            string selector = "default";
            DkimSigner signer = new DkimSigner(@"C:\inetpub\dkim.pem", domain, selector)
            {
                HeaderCanonicalizationAlgorithm = DkimCanonicalizationAlgorithm.Relaxed,
                BodyCanonicalizationAlgorithm = DkimCanonicalizationAlgorithm.Simple,
                AgentOrUserIdentifier = "@contact.example.cl",
                QueryMethod = "dns/txt",
            };
            mime.Prepare(EncodingConstraint.EightBit);
            signer.Sign(mime, headersToSign);

            using (SmtpClient smtpClient = new SmtpClient())
            {
                await Task.Run(() =>
                {
                    try
                    {
                        smtpClient.Connect(Properties.Settings.Default.ServidorSmtp, Properties.Settings.Default.PuertoSmtp, Properties.Settings.Default.SSLSmtp);
                        smtpClient.Authenticate(fromEmail, smtpPassword);
                        smtpClient.Send(mime);
                        smtpClient.Disconnect(true);
                    }
                    catch (Exception ex)
                    {
                        ErrorLog.Save(ex);
                        //InfoLog.Save(htmlBody);
                    }
                });
            }
        }
    }

Well.. when the e-mail arrives, e-mail client shows it correctly in HTML format. However, an HTML file is attached in the e-mail also.

That file is named, for example, Attached data without title 00391.html. If I open that file in browser, the e-mail body is shown.

I have not found a way to change the name of that attachment.

Does anyone know?


Solution

  • The problem is probably this:

    var htmlBody = message.Body.Replace("{CID:LOGO}", String.Format("cid:{0}", inline.ContentId));
    
    AlternateView avHtml = AlternateView.CreateAlternateViewFromString(htmlBody, null, MediaTypeNames.Text.Html);
    
    // ...
    
    objMailMsg.AlternateViews.Add(avHtml);
    objMailMsg.Body = htmlBody;
    

    You are setting the HTML body text as 2 different body parts. If you create the avHtml part, you shouldn't also need to set objMailMsg.Body = htmlBody.

    When MailKit later converts the System.Net.Mail.MailMessage into a MimeMessage, it probably ends up with 2 duplicate parts, one that gets used as the actual HTML body and one that gets added as an attachment w/o a name (the receiving client likely assigns this part a random name which is what you are seeing).

    Alternatively, you could remove the logic that creates a System.Net.Mail.MailMessage completely and just construct a MimeMessage and avoid potential issues in conversion.

    var bodyBuilder = new BodyBuilder ();
    var logo = bodyBuilder.LinkedResources.Add (System.Web.Hosting.HostingEnvironment.MapPath($"~/Images/logo.png"));
    var htmlBody = message.Body.Replace("{CID:LOGO}", String.Format("cid:{0}", logo.ContentId));
    bodyBuilder.HtmlBody = htmlBody;
    
    Properties.Settings.Default.Reload();
    string fromEmail = Properties.Settings.Default.FromMail;
    string fromName = Properties.Settings.Default.FromName;
    string smtpPassword = Properties.Settings.Default.PwdSmtp;
    
    var mime = new MimeMessage ();
    mime.From.Add (new MailboxAddress (fromName, fromEmail));
    mime.To.Add (MailboxAddress.Parse (message.Destination));
    mime.Subject = message.Subject;
    mime.Body = bodyBuilder.ToMessageBody ();
    
    HeaderId[] headersToSign = new HeaderId[] { HeaderId.From, HeaderId.Subject, HeaderId.Date };
    string domain = "example.cl";
    string selector = "default";
    DkimSigner signer = new DkimSigner(@"C:\inetpub\dkim.pem", domain, selector)
    {
        HeaderCanonicalizationAlgorithm = DkimCanonicalizationAlgorithm.Relaxed,
        BodyCanonicalizationAlgorithm = DkimCanonicalizationAlgorithm.Simple,
        AgentOrUserIdentifier = "@contact.example.cl",
        QueryMethod = "dns/txt",
    };
    mime.Prepare(EncodingConstraint.EightBit);
    signer.Sign(mime, headersToSign);