Seems like JavaMail's MimeBodyPart.setFileName inserts a line break in the email message and causes the filename to show up as invalid

Viewed 2675

We have code that goes out to a person's mailbox and copies emails with attachments to a filesystem.

The code for copying the message and attachment works fine for most files but there's an issue with long filenames.

if (attachment instanceof FileAttachment || attachment.getIsInline()) {
    System.out.println(attachment.getName());
    String FILE_NAME = "C:path\\" + attachment.getName();
    attachment.load(FILE_NAME);

    MimeBodyPart attachmentMime = new MimeBodyPart();
    attachmentMime.setContent(new MimeMultipart(attachment.getContentType()));
    javax.activation.DataSource source = new FileDataSource(FILE_NAME);
    attachmentMime.setDataHandler(new DataHandler(source));
    attachmentMime.setFileName(attachment.getName());
    multipart.addBodyPart(attachmentMime);
} 

For instance, the filename: "Copy of SKI17042 surgery CPT choices for CRLM population.xlsx" shows up in the email attachment with the name "Untitled attachment 00006.dat". When I look at the .eml file that is created, it looks like JavaMail inserts a line break in the middle of the filename which may be causing the issue.

When I open the .eml in a text editor, I see the headers with a line break (notice the line breaks in lines 3/4 and 7/8 that span the filename:

------=_Part_3_840180718.1542390637623
Content-Type: application/octet-stream; 
    name*0="Copy of SKI17042 surgery CPT choices for CRLM
 population.xls"; name*1=x
Content-Transfer-Encoding: base64
Content-Disposition: attachment; 
    filename*0="Copy of SKI17042 surgery CPT choices for CRLM
 population.xls"; filename*1=x

The file content is fine, if you take the file and slap on an .xlsx it opens in Excel with the content as expected.

Does anyone have any info or ideas with how to solve this issue on file attachment name in JavaMail?

Thanks!

EDIT - Solution

    Properties props = System.getProperties();

    props.put("mail.mime.splitlongparameters", false);

    Session session = Session.getInstance(props, null);

    createProjectFolder(folder);

    Message message = new MimeMessage(session);
    message.setFrom(new InternetAddress(objectJSON.getString("from"), objectJSON.getString("fromName")));
    message.setRecipients(Message.RecipientType.TO, InternetAddress.parse(objectJSON.getString("to")));
    message.setRecipients(Message.RecipientType.CC, InternetAddress.parse(objectJSON.getString("cc")));
    message.setSubject(objectJSON.getString("subject"));
    message.setSentDate(new SimpleDateFormat("yyyy-MM-dd hh:mm:ss a").parse(objectJSON.getString("date")));

    // create the message part 
    Multipart multipart = new MimeMultipart("mixed");
    MimeBodyPart content = new MimeBodyPart();

    // fill message
    if (objectJSON.getString("body").toLowerCase().contains("html")) {
        content.setContent( objectJSON.getString("body"), "text/html; charset=utf-8" );
    }
    else {
        content.setText(objectJSON.getString("body"), "utf-8");
    }

    multipart.addBodyPart(content);

        if (objectJSON.getInt("hasAttachment") == 1) {

            ExchangeService service = new ExchangeService(ExchangeVersion.Exchange2010_SP2);

            service.setUrl(new URI("https://mail/ews/Exchange.asmx"));

            ExchangeCredentials credentials = new WebCredentials(developerEmail, password);

            service.setCredentials(credentials);

            try {

                EmailMessage messageWithAttachment = EmailMessage.bind(service, new ItemId(emailId));

                    AttachmentCollection attachmentsCol = messageWithAttachment.getAttachments(); 
                    System.out.println("attachments: " + attachmentsCol.getCount());
                    for (int i = 0; i < attachmentsCol.getCount(); i++) { 
                        FileAttachment attachment = (FileAttachment)attachmentsCol.getPropertyAtIndex(i); 

                        if (attachment instanceof FileAttachment || attachment.getIsInline()) {
                            System.out.println(attachment.getName());
                            String FILE_NAME = "C:\\R2D4\\eclipse-workspace\\DataLine\\WebContent\\WEB-INF\\email_attachments\\" + attachment.getName();
                            attachment.load(FILE_NAME);

                            MimeBodyPart attachmentMime = new MimeBodyPart();
                            attachmentMime.setContent(new MimeMultipart(attachment.getContentType()));
                            javax.activation.DataSource source = new FileDataSource(FILE_NAME);
                            attachmentMime.setDataHandler(new DataHandler(source));
                            attachmentMime.setFileName(attachment.getName());
                            multipart.addBodyPart(attachmentMime);

                        } 

                    }

            }
            catch(Exception e) {
                e.printStackTrace();
            }

            service.close();

        }


    // integration
    message.setContent(multipart);
    message.saveChanges();
2 Answers

If the filename is greater than 60 characters long, it will be split across multiple parameters as described in RFC 2231.

It looks like your code that processes messages doesn't understand how to handle RFC 2231 encoded parameters. Here are possible solutions:

  • you can disable all use of RFC 2231 encoding by setting the property mail.mime.encodeparameters to false.

  • you can disable just the splitting of long parameters by setting the (unfortunately undocumented) property mail.mime.splitlongparameters to false.

Complete solution allows to make an HTML mail with an attachment with a long name :

import java.io.*;
import java.util.*;
import javax.mail.*;
import javax.mail.internet.*;
import javax.activation.*;

File file = new File("C:/fichiers/2- CENTRE EST à VENTES EXTRA GROUPE HORS EXPORT_VIANDE 14-09-2022.pdf");

    String from = "adresse@gmail.com";
    String[] to =  {"adresse1@gmail.com","adresse2@gmail.com"};
    
    String subject = "test de mail";
    String body    = "<html><body><H1>hello word !</H1></body></html>";

    Properties properties = System.getProperties();
    properties.put("mail.smtp.host", "server.smtp.fr");
    properties.put("mail.smtp.port", "25");
//  properties.put("mail.smtp.auth", auth);
//  properties.put("mail.sender.username", sender_username);
//  properties.put("mail.sender.password", sender_password);
    properties.put("mail.mime.splitlongparameters",false);
    
    Session session = Session.getInstance(properties, null);
    try {
            
        MimeMessage message = new MimeMessage(session);
        message.addHeader("MIME-Version", "1.0");
        message.addHeader("Content-Transfer-Encoding", "BASE64");
        message.addHeader("Content-Type", "multipart/mixed"); 
        message.setFrom(new InternetAddress(from));
        message.setSentDate(new Date());

        InternetAddress[] toAddress = new InternetAddress[to.length];
        for (int i = 0; i < to.length; i++)
            toAddress[i] = new InternetAddress(to[i]);
        message.setRecipients(Message.RecipientType.TO, toAddress);    
        
        message.setSubject(subject);

        MimeMultipart multipart = new MimeMultipart();

        BodyPart messageBodyPart = new MimeBodyPart();
        messageBodyPart.setContent(body+"<br>","text/html");
        multipart.addBodyPart(messageBodyPart);

        MimeBodyPart messageBodyPart2 = new MimeBodyPart();
        FileDataSource fileDataSource = new FileDataSource(file) {
            @Override
            public String getContentType() {
                return "application/octet-stream";
            }
        };
        messageBodyPart2.setDataHandler(new DataHandler(fileDataSource));
        messageBodyPart2.setFileName(MimeUtility.encodeText(file.getName()));
        multipart.addBodyPart(messageBodyPart2);

        message.setContent(multipart);

        session.setDebug(true);
        Transport.send(message);
        System.out.println("sending message");
        
    } catch (AddressException e) {
        e.printStackTrace();
    } catch (MessagingException e) {
        e.printStackTrace();
    }

The debug mode allows to apprehend the whole dialogue with the smtp server in the stdout output stream...

Related