/** * Copyright (c) 2000-present Liferay, Inc. All rights reserved. * * This library is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free * Software Foundation; either version 2.1 of the License, or (at your option) * any later version. * * This library is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. */ package com.liferay.util.mail; import com.liferay.mail.kernel.model.Account; import com.liferay.mail.kernel.model.FileAttachment; import com.liferay.mail.kernel.model.MailMessage; import com.liferay.mail.kernel.model.SMTPAccount; import com.liferay.mail.kernel.service.MailServiceUtil; import com.liferay.portal.kernel.exception.SystemException; import com.liferay.portal.kernel.io.unsync.UnsyncByteArrayInputStream; import com.liferay.portal.kernel.log.Log; import com.liferay.portal.kernel.log.LogFactoryUtil; import com.liferay.portal.kernel.log.LogUtil; import com.liferay.portal.kernel.util.ArrayUtil; import com.liferay.portal.kernel.util.CharPool; import com.liferay.portal.kernel.util.GetterUtil; import com.liferay.portal.kernel.util.InfrastructureUtil; import com.liferay.portal.kernel.util.ListUtil; import com.liferay.portal.kernel.util.PropsKeys; import com.liferay.portal.kernel.util.PropsUtil; import com.liferay.portal.kernel.util.StringUtil; import com.liferay.portal.kernel.util.Validator; import java.io.File; import java.net.SocketException; import java.util.Arrays; import java.util.Date; import java.util.Enumeration; import java.util.List; import java.util.Properties; import javax.activation.DataHandler; import javax.activation.DataSource; import javax.activation.FileDataSource; import javax.mail.Address; import javax.mail.Header; import javax.mail.Message; import javax.mail.MessagingException; import javax.mail.Part; import javax.mail.SendFailedException; import javax.mail.Session; import javax.mail.Transport; import javax.mail.internet.AddressException; import javax.mail.internet.InternetAddress; import javax.mail.internet.InternetHeaders; import javax.mail.internet.MimeBodyPart; import javax.mail.internet.MimeMessage; import javax.mail.internet.MimeMultipart; /** * @author Brian Wing Shun Chan * @author Brian Myunghun Kim * @author Jorge Ferrer * @author Neil Griffin * @author Thiago Moreira * @author Brett Swaim * @see com.liferay.petra.mail.MailEngine */ public class MailEngine { public static Session getSession() { return getSession(false); } public static Session getSession(Account account) { Properties properties = _getProperties(account); Session session = Session.getInstance(properties); if (_log.isDebugEnabled()) { session.setDebug(true); session.getProperties().list(System.out); } return session; } public static Session getSession(boolean cache) { Session session = null; try { session = MailServiceUtil.getSession(); } catch (SystemException se) { if (_log.isWarnEnabled()) { _log.warn(se, se); } session = InfrastructureUtil.getMailSession(); } if (_log.isDebugEnabled()) { session.setDebug(true); session.getProperties().list(System.out); } return session; } public static void send(byte[] bytes) throws MailEngineException { try { Session session = getSession(); Message message = new MimeMessage( session, new UnsyncByteArrayInputStream(bytes)); _send(session, message, null, _BATCH_SIZE); } catch (Exception e) { throw new MailEngineException(e); } } public static void send( InternetAddress from, InternetAddress to, String subject, String body) throws MailEngineException { send( from, new InternetAddress[] {to}, null, null, subject, body, false, null, null, null); } public static void send( InternetAddress from, InternetAddress to, String subject, String body, boolean htmlFormat) throws MailEngineException { send( from, new InternetAddress[] {to}, null, null, subject, body, htmlFormat, null, null, null); } public static void send( InternetAddress from, InternetAddress[] to, InternetAddress[] cc, InternetAddress[] bcc, InternetAddress[] bulkAddresses, String subject, String body, boolean htmlFormat, InternetAddress[] replyTo, String messageId, String inReplyTo) throws MailEngineException { send( from, to, cc, bcc, bulkAddresses, subject, body, htmlFormat, replyTo, messageId, inReplyTo, null); } public static void send( InternetAddress from, InternetAddress[] to, InternetAddress[] cc, InternetAddress[] bcc, InternetAddress[] bulkAddresses, String subject, String body, boolean htmlFormat, InternetAddress[] replyTo, String messageId, String inReplyTo, List<FileAttachment> fileAttachments) throws MailEngineException { send( from, to, cc, bcc, bulkAddresses, subject, body, htmlFormat, replyTo, messageId, inReplyTo, fileAttachments, null); } public static void send( InternetAddress from, InternetAddress[] to, InternetAddress[] cc, InternetAddress[] bcc, InternetAddress[] bulkAddresses, String subject, String body, boolean htmlFormat, InternetAddress[] replyTo, String messageId, String inReplyTo, List<FileAttachment> fileAttachments, SMTPAccount smtpAccount) throws MailEngineException { send( from, to, cc, bcc, bulkAddresses, subject, body, htmlFormat, replyTo, messageId, inReplyTo, fileAttachments, smtpAccount, null); } public static void send( InternetAddress from, InternetAddress[] to, InternetAddress[] cc, InternetAddress[] bcc, InternetAddress[] bulkAddresses, String subject, String body, boolean htmlFormat, InternetAddress[] replyTo, String messageId, String inReplyTo, List<FileAttachment> fileAttachments, SMTPAccount smtpAccount, InternetHeaders internetHeaders) throws MailEngineException { long startTime = System.currentTimeMillis(); if (_log.isDebugEnabled()) { _log.debug("From: " + from); _log.debug("To: " + Arrays.toString(to)); _log.debug("CC: " + Arrays.toString(cc)); _log.debug("BCC: " + Arrays.toString(bcc)); _log.debug("List Addresses: " + Arrays.toString(bulkAddresses)); _log.debug("Subject: " + subject); _log.debug("Body: " + body); _log.debug("HTML Format: " + htmlFormat); _log.debug("Reply to: " + Arrays.toString(replyTo)); _log.debug("Message ID: " + messageId); _log.debug("In Reply To: " + inReplyTo); if ((fileAttachments != null) && _log.isDebugEnabled()) { for (int i = 0; i < fileAttachments.size(); i++) { FileAttachment fileAttachment = fileAttachments.get(i); File file = fileAttachment.getFile(); if (file == null) { continue; } _log.debug( "Attachment " + i + " file " + file.getAbsolutePath() + " and file name " + fileAttachment.getFileName()); } } } try { InternetAddressUtil.validateAddress(from); if (ArrayUtil.isNotEmpty(to)) { InternetAddressUtil.validateAddresses(to); } if (ArrayUtil.isNotEmpty(cc)) { InternetAddressUtil.validateAddresses(cc); } if (ArrayUtil.isNotEmpty(bcc)) { InternetAddressUtil.validateAddresses(bcc); } if (ArrayUtil.isNotEmpty(replyTo)) { InternetAddressUtil.validateAddresses(replyTo); } if (ArrayUtil.isNotEmpty(bulkAddresses)) { InternetAddressUtil.validateAddresses(bulkAddresses); } Session session = null; if (smtpAccount == null) { session = getSession(); } else { session = getSession(smtpAccount); } Message message = new LiferayMimeMessage(session); message.addHeader( "X-Auto-Response-Suppress", "AutoReply, DR, NDR, NRN, OOF, RN"); message.setFrom(from); if (ArrayUtil.isNotEmpty(to)) { message.setRecipients(Message.RecipientType.TO, to); } if (ArrayUtil.isNotEmpty(cc)) { message.setRecipients(Message.RecipientType.CC, cc); } if (ArrayUtil.isNotEmpty(bcc)) { message.setRecipients(Message.RecipientType.BCC, bcc); } subject = GetterUtil.getString(subject); message.setSubject(_sanitizeCRLF(subject)); if (ListUtil.isNotEmpty(fileAttachments)) { MimeMultipart rootMultipart = new MimeMultipart( _MULTIPART_TYPE_MIXED); MimeMultipart messageMultipart = new MimeMultipart( _MULTIPART_TYPE_ALTERNATIVE); MimeBodyPart messageBodyPart = new MimeBodyPart(); messageBodyPart.setContent(messageMultipart); rootMultipart.addBodyPart(messageBodyPart); if (htmlFormat) { MimeBodyPart bodyPart = new MimeBodyPart(); bodyPart.setContent(body, _TEXT_HTML); messageMultipart.addBodyPart(bodyPart); } else { MimeBodyPart bodyPart = new MimeBodyPart(); bodyPart.setText(body); messageMultipart.addBodyPart(bodyPart); } for (int i = 0; i < fileAttachments.size(); i++) { FileAttachment fileAttachment = fileAttachments.get(i); File file = fileAttachment.getFile(); if (file == null) { continue; } MimeBodyPart mimeBodyPart = new MimeBodyPart(); DataSource dataSource = new FileDataSource(file); mimeBodyPart.setDataHandler(new DataHandler(dataSource)); mimeBodyPart.setDisposition(Part.ATTACHMENT); if (fileAttachment.getFileName() != null) { mimeBodyPart.setFileName(fileAttachment.getFileName()); } else { mimeBodyPart.setFileName(file.getName()); } rootMultipart.addBodyPart(mimeBodyPart); } message.setContent(rootMultipart); message.saveChanges(); } else { if (htmlFormat) { message.setContent(body, _TEXT_HTML); } else { message.setContent(body, _TEXT_PLAIN); } } message.setSentDate(new Date()); if (ArrayUtil.isNotEmpty(replyTo)) { message.setReplyTo(replyTo); } if (messageId != null) { message.setHeader("Message-ID", _sanitizeCRLF(messageId)); } if (inReplyTo != null) { message.setHeader("In-Reply-To", _sanitizeCRLF(inReplyTo)); message.setHeader("References", _sanitizeCRLF(inReplyTo)); } if (internetHeaders != null) { Enumeration enumeration = internetHeaders.getAllHeaders(); while (enumeration.hasMoreElements()) { Header header = (Header)enumeration.nextElement(); message.setHeader(header.getName(), header.getValue()); } } int batchSize = GetterUtil.getInteger( PropsUtil.get(PropsKeys.MAIL_BATCH_SIZE), _BATCH_SIZE); _send(session, message, bulkAddresses, batchSize); } catch (SendFailedException sfe) { _log.error(sfe); if (_isThrowsExceptionOnFailure()) { throw new MailEngineException(sfe); } } catch (Exception e) { throw new MailEngineException(e); } if (_log.isDebugEnabled()) { _log.debug( "Sending mail takes " + (System.currentTimeMillis() - startTime) + " ms"); } } public static void send( InternetAddress from, InternetAddress[] to, InternetAddress[] cc, InternetAddress[] bcc, String subject, String body) throws MailEngineException { send(from, to, cc, bcc, subject, body, false, null, null, null); } public static void send( InternetAddress from, InternetAddress[] to, InternetAddress[] cc, InternetAddress[] bcc, String subject, String body, boolean htmlFormat, InternetAddress[] replyTo, String messageId, String inReplyTo) throws MailEngineException { send( from, to, cc, bcc, null, subject, body, htmlFormat, replyTo, messageId, inReplyTo, null); } public static void send( InternetAddress from, InternetAddress[] to, InternetAddress[] cc, String subject, String body) throws MailEngineException { send(from, to, cc, null, subject, body, false, null, null, null); } public static void send( InternetAddress from, InternetAddress[] to, InternetAddress[] cc, String subject, String body, boolean htmlFormat) throws MailEngineException { send(from, to, cc, null, subject, body, htmlFormat, null, null, null); } public static void send( InternetAddress from, InternetAddress[] to, String subject, String body) throws MailEngineException { send(from, to, null, null, subject, body, false, null, null, null); } public static void send( InternetAddress from, InternetAddress[] to, String subject, String body, boolean htmlFormat) throws MailEngineException { send(from, to, null, null, subject, body, htmlFormat, null, null, null); } public static void send(MailMessage mailMessage) throws MailEngineException { send( mailMessage.getFrom(), mailMessage.getTo(), mailMessage.getCC(), mailMessage.getBCC(), mailMessage.getBulkAddresses(), mailMessage.getSubject(), mailMessage.getBody(), mailMessage.isHTMLFormat(), mailMessage.getReplyTo(), mailMessage.getMessageId(), mailMessage.getInReplyTo(), mailMessage.getFileAttachments(), mailMessage.getSMTPAccount(), mailMessage.getInternetHeaders()); } public static void send(String from, String to, String subject, String body) throws MailEngineException { try { send( new InternetAddress(from), new InternetAddress(to), subject, body); } catch (AddressException ae) { throw new MailEngineException(ae); } } private static Address[] _getBatchAddresses( Address[] addresses, int index, int batchSize) { if ((batchSize == _BATCH_SIZE) && (index == 0)) { return addresses; } else if (batchSize == _BATCH_SIZE) { return null; } int start = index * batchSize; if (start > addresses.length) { return null; } int end = (index + 1) * batchSize; if (end > addresses.length) { end = addresses.length; } return ArrayUtil.subset(addresses, start, end); } private static Properties _getProperties(Account account) { Properties properties = new Properties(); String protocol = account.getProtocol(); properties.setProperty("mail.transport.protocol", protocol); properties.setProperty("mail." + protocol + ".host", account.getHost()); properties.setProperty( "mail." + protocol + ".port", String.valueOf(account.getPort())); if (account.isRequiresAuthentication()) { properties.setProperty("mail." + protocol + ".auth", "true"); properties.setProperty( "mail." + protocol + ".user", account.getUser()); properties.setProperty( "mail." + protocol + ".password", account.getPassword()); } if (account.isSecure()) { properties.setProperty( "mail." + protocol + ".socketFactory.class", "javax.net.ssl.SSLSocketFactory"); properties.setProperty( "mail." + protocol + ".socketFactory.fallback", "false"); properties.setProperty( "mail." + protocol + ".socketFactory.port", String.valueOf(account.getPort())); } return properties; } private static String _getSMTPProperty(Session session, String suffix) { String protocol = GetterUtil.getString( session.getProperty("mail.transport.protocol")); if (protocol.equals(Account.PROTOCOL_SMTPS)) { return session.getProperty("mail.smtps." + suffix); } else { return session.getProperty("mail.smtp." + suffix); } } private static boolean _isThrowsExceptionOnFailure() { return GetterUtil.getBoolean( PropsUtil.get(PropsKeys.MAIL_THROWS_EXCEPTION_ON_FAILURE)); } private static String _sanitizeCRLF(String text) { return StringUtil.replace( text, new char[] {CharPool.NEW_LINE, CharPool.RETURN}, new char[] {CharPool.SPACE, CharPool.SPACE}); } private static void _send( Session session, Message message, InternetAddress[] bulkAddresses, int batchSize) throws MailEngineException { try { boolean smtpAuth = GetterUtil.getBoolean( _getSMTPProperty(session, "auth")); String smtpHost = _getSMTPProperty(session, "host"); int smtpPort = GetterUtil.getInteger( _getSMTPProperty(session, "port"), Account.PORT_SMTP); String user = _getSMTPProperty(session, "user"); String password = _getSMTPProperty(session, "password"); if (smtpAuth && Validator.isNotNull(user) && Validator.isNotNull(password)) { String protocol = GetterUtil.getString( session.getProperty("mail.transport.protocol"), Account.PROTOCOL_SMTP); Transport transport = session.getTransport(protocol); transport.connect(smtpHost, smtpPort, user, password); Address[] addresses = null; if (ArrayUtil.isNotEmpty(bulkAddresses)) { addresses = bulkAddresses; } else { addresses = message.getAllRecipients(); } for (int i = 0;; i++) { Address[] batchAddresses = _getBatchAddresses( addresses, i, batchSize); if (ArrayUtil.isEmpty(batchAddresses)) { break; } transport.sendMessage(message, batchAddresses); } transport.close(); } else { if (ArrayUtil.isNotEmpty(bulkAddresses)) { int curBatch = 0; Address[] portion = _getBatchAddresses( bulkAddresses, curBatch, batchSize); while (ArrayUtil.isNotEmpty(portion)) { Transport.send(message, portion); curBatch++; portion = _getBatchAddresses( bulkAddresses, curBatch, batchSize); } } else { Transport.send(message); } } } catch (MessagingException me) { if (me.getNextException() instanceof SocketException) { if (_log.isWarnEnabled()) { _log.warn( "Unable to connect to a valid mail server. Please " + "make sure one is properly configured: " + me.getMessage()); } } else { LogUtil.log( _log, me, "Unable to send message: " + me.getMessage()); } if (_isThrowsExceptionOnFailure()) { throw new MailEngineException(me); } } } private static final int _BATCH_SIZE = 0; private static final String _MULTIPART_TYPE_ALTERNATIVE = "alternative"; private static final String _MULTIPART_TYPE_MIXED = "mixed"; private static final String _TEXT_HTML = "text/html;charset=\"UTF-8\""; private static final String _TEXT_PLAIN = "text/plain;charset=\"UTF-8\""; private static final Log _log = LogFactoryUtil.getLog(MailEngine.class); }