Search code examples
javaspringemailjakarta-mail

JavaMail - How to set SMTP “RCPT TO:” that will not match “To:” Header in DATA


In my small project, I want to setup the "RCPT TO" and "To" look like this:

RCPT TO: <[email protected]>
DATA
354  Go ahead vw9sm7458976pbc.68 - gsmtp
Date: Thu, 02 Mar 2012 14:06:02 +0200 (ICT)
To: +10xxxxxxxxxx

And I tried with the following code

JavaMailSenderImpl mailSender = new JavaMailSenderImpl();
mailSender.setHost("HOST");
mailSender.setPort("PORT");
mailSender.setUsername("USERNAME");
mailSender.setPassword("PASSWORD");

MimeMessage mimeMessage = mailSender.createMimeMessage();

try {
    mimeMessage.setRecipient(MimeMessage.RecipientType.TO, 
        new InternetAddress("[email protected]"));

    MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true);
    helper.setTo("+10xxxxxxxxxx");

} catch(Exception e) {
     System.out.println(e.getMessage());
}

And the log I got

MAIL FROM:<[email protected]>
250 2.1.0 OK x5sm16625925pbw.26 - gsmtp
RCPT TO:<[email protected]>
250 2.1.5 OK x5sm16625925pbw.26 - gsmtp
RCPT TO:<+10xxxxxxxxxx>

As you see, the program created a new "RCPT TO" value with "+10xxxxxxxxxx".

How can I set "RCPT TO" value that will not match with "To" header in DATA?


Solution

  • I just had a look at the source code of MimeMessageHelper you will see that the setTo() functions all call javax.mail.Message#setRecipient just like you did the command before using the Helper.

    I see two solutions using JavaMail:

    Extend MimeMessage and override getAllRecipients() as this method is used to determine recipients in JavaMailImpl

    // yes you need to use this package as SmartMimeMessage is package-private or you loose spring advantages
    package org.springframework.mail.javamail;
    
    import javax.activation.FileTypeMap;
    import javax.mail.Address;
    import javax.mail.MessagingException;
    import javax.mail.Session;
    import javax.mail.internet.MimeMessage;
    
    /**
     * Created for http://stackoverflow.com/q/22860793/1266906
     */
    public class RoutedSmartMimeMessage extends SmartMimeMessage {
        private Address[] rcptTo = null;
    
        /**
         * Create a new SmartMimeMessage.
         *
         * @param session
         *         the JavaMail Session to create the message for
         * @param defaultEncoding
         *         the default encoding, or {@code null} if none
         * @param defaultFileTypeMap
         *         the default FileTypeMap, or {@code null} if none
         */
        public RoutedSmartMimeMessage(final Session session,
                                      final String defaultEncoding,
                                      final FileTypeMap defaultFileTypeMap) {
            super(session, defaultEncoding, defaultFileTypeMap);
        }
    
        public Address[] getRcptTo() {
            return rcptTo;
        }
    
        public void setRcptTo(final Address... rcptTo) {
            this.rcptTo = rcptTo;
        }
    
        @Override
        public Address[] getAllRecipients() throws MessagingException {
            if(rcptTo != null) {
                return rcptTo;
            } else {
                return super.getAllRecipients();
            }
        }
    
        public static MimeMessage createMesage(final JavaMailSenderImpl javaMailSender) {
            return new SmartMimeMessage(javaMailSender.getSession(), javaMailSender.getDefaultEncoding(), javaMailSender.getDefaultFileTypeMap());
        }
    }
    

    Write your own send method and use Transport#sendMessage(javax.mail.Message, javax.mail.Address[]) directly

    Based upon the spring-wrapper around JavaMail:

    import org.springframework.mail.MailAuthenticationException;
    import org.springframework.mail.MailException;
    import org.springframework.mail.MailSendException;
    import org.springframework.mail.javamail.JavaMailSenderImpl;
    import org.springframework.mail.javamail.MimeMessageHelper;
    
    import javax.mail.*;
    import javax.mail.internet.InternetAddress;
    import javax.mail.internet.MimeMessage;
    import java.util.Date;
    import java.util.LinkedHashMap;
    import java.util.Map;
    
    /**
     * Created for http://stackoverflow.com/q/22860793/1266906
     */
    public class Mailing {
        public static void main(String[] args) {
            JavaMailSenderImpl mailSender = new JavaMailSenderImpl();
            mailSender.setHost("HOST");
            mailSender.setPort(25);
            mailSender.setUsername("USERNAME");
            mailSender.setPassword("PASSWORD");
    
            MimeMessage mimeMessage = mailSender.createMimeMessage();
    
            try {
                /*
                Not needed
                mimeMessage.setRecipient(MimeMessage.RecipientType.TO,
                                         new InternetAddress("[email protected]"));
                 */
    
                MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true);
                helper.setTo("+10xxxxxxxxxx");
    
                sendMail(mailSender, mimeMessage, new InternetAddress("[email protected]"));
    
            } catch (Exception e) {
                System.out.println(e.getMessage());
            }
        }
    
        /**
         * Adopted from {@link org.springframework.mail.javamail.JavaMailSenderImpl#doSend(javax.mail.internet.MimeMessage[], Object[])} to be able to call {@link javax.mail.Transport#send(javax.mail.Message, javax.mail.Address[])} with a different second parameter than {@link javax.mail.Message#getAllRecipients() mimeMessage.getAllRecipients()}
         *
         * @param javaMailSender
         *         JavaMailSender object holding configuration options
         * @param mimeMessage
         *         MimeMessage object to send
         * @param realRecipients
         *         RCPT TO: sddresses
         * @throws org.springframework.mail.MailAuthenticationException
         *         in case of authentication failure
         * @throws org.springframework.mail.MailSendException
         *         in case of failure when sending a message
         */
        private static void sendMail(final JavaMailSenderImpl javaMailSender,
                                     final MimeMessage mimeMessage,
                                     final Address... realRecipients) throws MailException {
            final Map<Object, Exception> failedMessages = new LinkedHashMap<Object, Exception>();
    
            final Transport transport;
            try {
                Session session = javaMailSender.getSession();
                String protocol = javaMailSender.getProtocol();
                if (protocol == null) {
                    protocol = session.getProperty("mail.transport.protocol");
                    if (protocol == null) {
                        protocol = JavaMailSenderImpl.DEFAULT_PROTOCOL;
                    }
                }
                transport = session.getTransport(protocol);
                transport.connect(javaMailSender.getHost(),
                                  javaMailSender.getPort(),
                                  javaMailSender.getUsername(),
                                  javaMailSender.getPassword());
            } catch (AuthenticationFailedException ex) {
                throw new MailAuthenticationException(ex);
            } catch (MessagingException ex) {
                failedMessages.put(mimeMessage, ex);
                throw new MailSendException("Mail server connection failed", ex, failedMessages);
            }
    
            try {
                try {
                    if (mimeMessage.getSentDate() == null) {
                        mimeMessage.setSentDate(new Date());
                    }
                    String messageId = mimeMessage.getMessageID();
                    mimeMessage.saveChanges();
                    if (messageId != null) {
                        // Preserve explicitly specified message id...
                        mimeMessage.setHeader("Message-ID", messageId);
                    }
                    transport.sendMessage(mimeMessage, realRecipients);
                } catch (MessagingException ex) {
                    failedMessages.put(mimeMessage, ex);
                }
            } finally {
                try {
                    transport.close();
                } catch (MessagingException ex) {
                    if (!failedMessages.isEmpty()) {
                        throw new MailSendException("Failed to close server connection after message failures", ex,
                                                    failedMessages);
                    } else {
                        throw new MailSendException("Failed to close server connection after message sending", ex);
                    }
                }
            }
    
            if (!failedMessages.isEmpty()) {
                throw new MailSendException(failedMessages);
            }
        }
    }