0

I have an issue where I'm sending a PDF file as an attachment, but when the email arrives with the client, it's arriving as a file with a name very similar to the MIME type, but with the correct content. I specify an attachment file name of "Statement.pdf", but it arrives as "application_pdf.txt" which then confuses the receiver as it doesn't open as a PDF. The mail sending is handled by a Windows Service written in Visual Basic.

Here is the main bit of code that actually sends it:

Dim SmtpServer As New SmtpClient()
  Dim mail As New MailMessage()
  SmtpServer.Credentials = New Net.NetworkCredential(mailAddress, mailPassword)
  SmtpServer.Port = mailPort
  SmtpServer.Host = mailServer
  SmtpServer.EnableSsl = mailSSL 
  SmtpServer.DeliveryMethod = SmtpDeliveryMethod.Network
  mail = New MailMessage()

  If fromName <> "" Then mailName = fromName

  mail.From = New MailAddress(fromAddr, mailName) 
  mail.IsBodyHtml = True
  mail.To.Add(toAddr)
  If replyTo <> "" Then
    mail.ReplyToList.Add(replyTo)
    End If
  If cc <> "" Then
    mail.CC.Add(cc)
    End If
  mail.Subject = subject
  mail.Body = body
  If attached <> "" And attachMIME <> "" And attachname <> "" Then
    mail.Attachments.Add(Attachment.CreateAttachmentFromString(attached, attachMIME))
    mail.Attachments.Last().ContentDisposition.FileName = attachname
    End If
  SmtpServer.Send(mail)

The various variables in here are passed in from the calling code and I'm confident that they're correct, in that if the password, port etc were incorrect the email wouldn't send. In particular the attachMIME is "application/pdf" which appears to be correct according to pdfa.org.

The PDF is generated on another machine, and passed to this one using FTP in binary mode. This service then reads the PDF into a string and passes it into the mail sending function:

Dim attachBytes() As Byte = Nothing

attachBytes = My.Computer.FileSystem.ReadAllBytes(FileDir & "\" & msg.attachFile)
mailAttachFile = ""
For Each attbyt As Byte In attachBytes
  mailAttachFile &= Chr(attbyt)
  Next

I thought I'd cracked it because I was originally using ReadAllText which strips out any non-printable characters, and I imagined that the mail transport somewhere was checking the PDF for validity, seeing the unprintable characters had changed and presuming it was some sort of mal-formed file. Unfortunately changing to ReadAllBytes didn't seem to help, and the PDF file is still arriving as a file named "attachment_pdf.txt", still with the correct content. If I save the file from my email client and then rename it, it displays perfectly as a PDF in Acrobat Reader. My customer is receiving emails from other companies with PDF attachments, so it's not just that his email provider doesn't trust PDFs.

I don't simply use AddAttachment(filename) to load the attachment because the code works in two ways - one where the service downloads the email files from a remote FTP server, and the other (this one) where the files are sent to the machine running the service and the service opens them locally. In order to keep the actual mail sending the same, loading the files is done further "up" the code and the resulting email message and attachment passed in here via a function call. So by the time I get to the first code block I posted above, the code doesn't know (or care) where the message and attachment came from.

Is there anything glaringly obvious in what I have posted that might be causing this issue? I should add that I have the same code running on another machine, using gmail as the mail transport (which this one does not) and the other method I mentioned above and it sends a PDF attachment every day without issue.

(Edited to clarify that the attachment file contents are not changing, just the attachment name is changing, which confuses the receiver.)

droopsnoot
  • 931
  • 1
  • 7
  • 11
  • *"This service then reads the PDF into a string"*. And you're really wondering why you end up with a text file? What do you think a string is? It's text. Don't use text unless you want text. – John Jun 16 '22 at 08:41
  • That may be poor wording on my part - the string contains exactly what I expect it to, in that the non-printable characters have the correct values. In fact that's exactly why I use a loop to concatenate each byte into the string rather than using `.ToString` or equivalent. But, would that explain why the mail transport is ignoring my MIME type and changing the attachment name? And why it works perfectly in one situation and not in this one? – droopsnoot Jun 16 '22 at 08:59
  • I've edited the question to make it clearer that the attachment file content is correct, it's just that the attachment name changes to be the MIME type, which tells the Windows PC that the email is received on that it is a text file when, in fact, it is a PDF. – droopsnoot Jun 16 '22 at 10:54
  • Sorry, but this whole situation seems to be very over complicated to me. Why do you even need to do all these steps to simply attach a file? – Hursey Jun 16 '22 at 21:05
  • Can you show the resulting MIME headers from the generated MIME part? They should look something `Content-type: application/pdf; name="Statement.pdf"`, `Content-Disposition: attachment; filename="Statement.pdf"` (Perhaps see also https://stackoverflow.com/a/51719748/874188) – tripleee Jun 17 '22 at 08:04
  • @Hursey because of the two different ways that the file arrives at the machine that runs the service. One way has the service connecting to an FTP server to retrieve the file, the other way (which is the one I'm having trouble with) has the service retrieve it from a local directory. I was trying to not duplicate code, and this function just receives a string that contains the file contents, but I'm now thinking that it might be easiest to just do that. – droopsnoot Jun 17 '22 at 11:16
  • @triplee maybe. `Content-Type: application/pdf; name="mytest.pdf"`, `Content-Disposition: attachment; filename="statement.pdf"`, `Content-Transfer-Encoding: base64`. – droopsnoot Jun 17 '22 at 11:18
  • In testing I have discovered that if I send the email to a mailbox that my Outlook client sees, it arrives correctly, as it does if I send it to a gmail address and view it in their webmail client. But when I send it to my customers address and he views it in his webmail client, it appears as a text attachment. – droopsnoot Jun 17 '22 at 11:19
  • The discrepancy between the file names is a concern, though by the RFC the `Content-Disposition:` one should win. Can you fix your code so you get the same filename in both? – tripleee Jun 17 '22 at 11:57
  • I think that might be a mistake in my post above, it is normally the same. My attachments arrive at the machine called "accountcode.pdf" where accountcode is the client account code, so we'll have something like "cash.pdf", "00039.pdf" and so on, and the code sets the attachment name so that the attachment is always called "statement.pdf" when it's received by the client. I edited the above comment erroneously. And of course those are headers from my test email, hence not being "statement.pdf" - in the test email both of those strings are "mytest.pdf". – droopsnoot Jun 17 '22 at 12:00

0 Answers0