// Copyright (c) 2003-present, Jodd Team (http://jodd.org) // All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are met: // // 1. Redistributions of source code must retain the above copyright notice, // this list of conditions and the following disclaimer. // // 2. Redistributions in binary form must reproduce the above copyright // notice, this list of conditions and the following disclaimer in the // documentation and/or other materials provided with the distribution. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // POSSIBILITY OF SUCH DAMAGE. package jodd.mail; import jodd.util.StringPool; import javax.activation.DataHandler; import javax.mail.Session; import javax.mail.Transport; import javax.mail.MessagingException; import javax.mail.Message; import javax.mail.Multipart; import javax.mail.internet.MimeMessage; import javax.mail.internet.InternetAddress; import javax.mail.internet.MimeBodyPart; import javax.mail.internet.MimeMultipart; import java.util.ArrayList; import java.util.Date; import java.util.Iterator; import java.util.List; import java.util.Map; /** * Encapsulates email sending session. Prepares and sends message(s). */ public class SendMailSession implements AutoCloseable { private static final String ALTERNATIVE = "alternative"; private static final String RELATED = "related"; private static final String CHARSET = ";charset="; private static final String INLINE = "inline"; protected final Session mailSession; protected final Transport mailTransport; static { JoddMail.mailSystem.defineJavaMailSystemProperties(); } /** * Creates new mail session. */ public SendMailSession(Session session, Transport transport) { this.mailSession = session; this.mailTransport = transport; } /** * Opens mail session. */ public void open() { try { mailTransport.connect(); } catch (MessagingException msex) { throw new MailException("Failed to connect", msex); } } /** * Returns {@code true} if mail session is still connected. */ public boolean isConnected() { return mailTransport.isConnected(); } /** * Prepares message and sends it. * Returns Message ID of sent email. */ public String sendMail(Email mail) { MimeMessage msg; try { msg = createMessage(mail, mailSession); } catch (MessagingException mex) { throw new MailException("Failed to prepare email: " + mail, mex); } try { mailTransport.sendMessage(msg, msg.getAllRecipients()); return msg.getMessageID(); } catch (MessagingException mex) { throw new MailException("Failed to send email: " + mail, mex); } } /** * Closes session. */ @Override public void close() { try { mailTransport.close(); } catch (MessagingException mex) { throw new MailException("Failed to close session", mex); } } // ---------------------------------------------------------------- adapter /** * Creates new JavaX message from {@link Email email}. */ protected MimeMessage createMessage(Email email, Session session) throws MessagingException { MimeMessage msg = new MimeMessage(session); msg.setFrom(email.getFrom().toInternetAddress()); // to int totalTo = email.getTo().length; InternetAddress[] address = new InternetAddress[totalTo]; for (int i = 0; i < totalTo; i++) { address[i] = email.getTo()[i].toInternetAddress(); } msg.setRecipients(Message.RecipientType.TO, address); // replyTo if (email.getReplyTo() != null) { int totalReplyTo = email.getReplyTo().length; address = new InternetAddress[totalReplyTo]; for (int i = 0; i < totalReplyTo; i++) { address[i] = email.getReplyTo()[i].toInternetAddress(); } msg.setReplyTo(address); } // cc if (email.getCc() != null) { int totalCc = email.getCc().length; address = new InternetAddress[totalCc]; for (int i = 0; i < totalCc; i++) { address[i] = email.getCc()[i].toInternetAddress(); } msg.setRecipients(Message.RecipientType.CC, address); } // bcc if (email.getBcc() != null) { int totalBcc = email.getBcc().length; address = new InternetAddress[totalBcc]; for (int i = 0; i < totalBcc; i++) { address[i] = email.getBcc()[i].toInternetAddress(); } msg.setRecipients(Message.RecipientType.BCC, address); } // subject & date if (email.getSubjectEncoding() != null) { msg.setSubject(email.getSubject(), email.getSubjectEncoding()); } else { msg.setSubject(email.getSubject()); } Date date = email.getSentDate(); if (date == null) { date = new Date(); } msg.setSentDate(date); // headers Map<String, String> headers = email.getAllHeaders(); if (headers != null) { for (Map.Entry<String, String> stringStringEntry : headers.entrySet()) { String value = stringStringEntry.getValue(); msg.setHeader(stringStringEntry.getKey(), value); } } // message data and attachments final List<EmailMessage> messages = email.getAllMessages(); final List<EmailAttachment> attachments = email.getAttachments() == null ? null : new ArrayList<>(email.getAttachments()); final int totalMessages = messages.size(); if ((attachments == null) && (totalMessages == 1)) { // special case: no attachments and just one content EmailMessage emailMessage = messages.get(0); msg.setContent(emailMessage.getContent(), emailMessage.getMimeType() + CHARSET + emailMessage.getEncoding()); } else { Multipart multipart = new MimeMultipart(); Multipart msgMultipart = multipart; if (totalMessages > 1) { MimeBodyPart bodyPart = new MimeBodyPart(); msgMultipart = new MimeMultipart(ALTERNATIVE); bodyPart.setContent(msgMultipart); multipart.addBodyPart(bodyPart); } for (EmailMessage emailMessage : messages) { // detect embedded attachments List<EmailAttachment> embeddedAttachments = filterEmbeddedAttachments(attachments, emailMessage); MimeBodyPart bodyPart = new MimeBodyPart(); if (embeddedAttachments == null) { // no embedded attachments, just add message bodyPart.setContent(emailMessage.getContent(), emailMessage.getMimeType() + CHARSET + emailMessage.getEncoding()); } else { // embedded attachments detected, join them as related MimeMultipart relatedMultipart = new MimeMultipart(RELATED); MimeBodyPart messageData = new MimeBodyPart(); messageData.setContent(emailMessage.getContent(), emailMessage.getMimeType() + CHARSET + emailMessage.getEncoding()); relatedMultipart.addBodyPart(messageData); for (EmailAttachment att : embeddedAttachments) { MimeBodyPart attBodyPart = createAttachmentBodyPart(att); relatedMultipart.addBodyPart(attBodyPart); } bodyPart.setContent(relatedMultipart); } msgMultipart.addBodyPart(bodyPart); } if (attachments != null) { // attach remaining attachments for (EmailAttachment att : attachments) { MimeBodyPart attBodyPart = createAttachmentBodyPart(att); multipart.addBodyPart(attBodyPart); } } msg.setContent(multipart); } return msg; } /** * Creates attachment body part. Handles regular and inline attachments. */ protected MimeBodyPart createAttachmentBodyPart(EmailAttachment attachment) throws MessagingException { MimeBodyPart attBodyPart = new MimeBodyPart(); String attachmentName = attachment.getEncodedName(); if (attachmentName != null) { attBodyPart.setFileName(attachmentName); } attBodyPart.setDataHandler(new DataHandler(attachment.getDataSource())); if (attachment.getContentId() != null) { attBodyPart.setContentID(StringPool.LEFT_CHEV + attachment.getContentId() + StringPool.RIGHT_CHEV); } if (attachment.isInline()) { attBodyPart.setDisposition(INLINE); } return attBodyPart; } /** * Filters out the list of embedded attachments for given message. If none found, returns <code>null</code>. */ protected List<EmailAttachment> filterEmbeddedAttachments(List<EmailAttachment> attachments, EmailMessage emailMessage) { if (attachments == null) { return null; } List<EmailAttachment> embeddedAttachments = null; Iterator<EmailAttachment> iterator = attachments.iterator(); while (iterator.hasNext()) { EmailAttachment emailAttachment = iterator.next(); if (emailAttachment.isEmbeddedInto(emailMessage)) { if (embeddedAttachments == null) { embeddedAttachments = new ArrayList<>(); } embeddedAttachments.add(emailAttachment); iterator.remove(); } } return embeddedAttachments; } }