001    /**
002     * Copyright (c) 2000-2013 Liferay, Inc. All rights reserved.
003     *
004     * This library is free software; you can redistribute it and/or modify it under
005     * the terms of the GNU Lesser General Public License as published by the Free
006     * Software Foundation; either version 2.1 of the License, or (at your option)
007     * any later version.
008     *
009     * This library is distributed in the hope that it will be useful, but WITHOUT
010     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
011     * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
012     * details.
013     */
014    
015    package com.liferay.util.mail;
016    
017    import com.liferay.mail.model.FileAttachment;
018    import com.liferay.mail.service.MailServiceUtil;
019    import com.liferay.portal.kernel.exception.SystemException;
020    import com.liferay.portal.kernel.io.unsync.UnsyncByteArrayInputStream;
021    import com.liferay.portal.kernel.log.Log;
022    import com.liferay.portal.kernel.log.LogFactoryUtil;
023    import com.liferay.portal.kernel.log.LogUtil;
024    import com.liferay.portal.kernel.mail.Account;
025    import com.liferay.portal.kernel.mail.MailMessage;
026    import com.liferay.portal.kernel.mail.SMTPAccount;
027    import com.liferay.portal.kernel.util.ArrayUtil;
028    import com.liferay.portal.kernel.util.GetterUtil;
029    import com.liferay.portal.kernel.util.InfrastructureUtil;
030    import com.liferay.portal.kernel.util.PropsKeys;
031    import com.liferay.portal.kernel.util.PropsUtil;
032    import com.liferay.portal.kernel.util.Validator;
033    
034    import java.io.File;
035    
036    import java.net.SocketException;
037    
038    import java.util.Arrays;
039    import java.util.Date;
040    import java.util.List;
041    import java.util.Properties;
042    
043    import javax.activation.DataHandler;
044    import javax.activation.DataSource;
045    import javax.activation.FileDataSource;
046    
047    import javax.mail.Address;
048    import javax.mail.Message;
049    import javax.mail.MessagingException;
050    import javax.mail.Part;
051    import javax.mail.SendFailedException;
052    import javax.mail.Session;
053    import javax.mail.Transport;
054    import javax.mail.internet.AddressException;
055    import javax.mail.internet.InternetAddress;
056    import javax.mail.internet.MimeBodyPart;
057    import javax.mail.internet.MimeMessage;
058    import javax.mail.internet.MimeMultipart;
059    
060    import org.apache.commons.lang.time.StopWatch;
061    
062    /**
063     * @author Brian Wing Shun Chan
064     * @author Brian Myunghun Kim
065     * @author Jorge Ferrer
066     * @author Neil Griffin
067     * @author Thiago Moreira
068     * @author Brett Swaim
069     */
070    public class MailEngine {
071    
072            public static Session getSession() {
073                    return getSession(false);
074            }
075    
076            public static Session getSession(Account account) {
077                    Properties properties = _getProperties(account);
078    
079                    Session session = Session.getInstance(properties);
080    
081                    if (_log.isDebugEnabled()) {
082                            session.setDebug(true);
083    
084                            session.getProperties().list(System.out);
085                    }
086    
087                    return session;
088            }
089    
090            public static Session getSession(boolean cache) {
091                    Session session = null;
092    
093                    try {
094                            session = MailServiceUtil.getSession();
095                    }
096                    catch (SystemException se) {
097                            if (_log.isWarnEnabled()) {
098                                    _log.warn(se, se);
099                            }
100    
101                            session = InfrastructureUtil.getMailSession();
102                    }
103    
104                    if (_log.isDebugEnabled()) {
105                            session.setDebug(true);
106    
107                            session.getProperties().list(System.out);
108                    }
109    
110                    return session;
111            }
112    
113            public static void send(byte[] bytes) throws MailEngineException {
114                    try {
115                            Session session = getSession();
116    
117                            Message message = new MimeMessage(
118                                    session, new UnsyncByteArrayInputStream(bytes));
119    
120                            _send(session, message, null, _BATCH_SIZE);
121                    }
122                    catch (Exception e) {
123                            throw new MailEngineException(e);
124                    }
125            }
126    
127            public static void send(
128                            InternetAddress from, InternetAddress to, String subject,
129                            String body)
130                    throws MailEngineException {
131    
132                    send(
133                            from, new InternetAddress[] {to}, null, null, subject, body, false,
134                            null, null, null);
135            }
136    
137            public static void send(
138                            InternetAddress from, InternetAddress to, String subject,
139                            String body, boolean htmlFormat)
140                    throws MailEngineException {
141    
142                    send(
143                            from, new InternetAddress[] {to}, null, null, subject, body,
144                            htmlFormat, null, null, null);
145            }
146    
147            public static void send(
148                            InternetAddress from, InternetAddress[] to, InternetAddress[] cc,
149                            InternetAddress[] bcc, InternetAddress[] bulkAddresses,
150                            String subject, String body, boolean htmlFormat,
151                            InternetAddress[] replyTo, String messageId, String inReplyTo)
152                    throws MailEngineException {
153    
154                    send(
155                            from, to, cc, bcc, bulkAddresses, subject, body, htmlFormat,
156                            replyTo, messageId, inReplyTo, null);
157            }
158    
159            public static void send(
160                            InternetAddress from, InternetAddress[] to, InternetAddress[] cc,
161                            InternetAddress[] bcc, InternetAddress[] bulkAddresses,
162                            String subject, String body, boolean htmlFormat,
163                            InternetAddress[] replyTo, String messageId, String inReplyTo,
164                            List<FileAttachment> fileAttachments)
165                    throws MailEngineException {
166    
167                    send(
168                            from, to, cc, bcc, bulkAddresses, subject, body, htmlFormat,
169                            replyTo, messageId, inReplyTo, fileAttachments, null);
170            }
171    
172            public static void send(
173                            InternetAddress from, InternetAddress[] to, InternetAddress[] cc,
174                            InternetAddress[] bcc, InternetAddress[] bulkAddresses,
175                            String subject, String body, boolean htmlFormat,
176                            InternetAddress[] replyTo, String messageId, String inReplyTo,
177                            List<FileAttachment> fileAttachments, SMTPAccount smtpAccount)
178                    throws MailEngineException {
179    
180                    StopWatch stopWatch = null;
181    
182                    if (_log.isDebugEnabled()) {
183                            stopWatch = new StopWatch();
184    
185                            stopWatch.start();
186    
187                            _log.debug("From: " + from);
188                            _log.debug("To: " + Arrays.toString(to));
189                            _log.debug("CC: " + Arrays.toString(cc));
190                            _log.debug("BCC: " + Arrays.toString(bcc));
191                            _log.debug("List Addresses: " + Arrays.toString(bulkAddresses));
192                            _log.debug("Subject: " + subject);
193                            _log.debug("Body: " + body);
194                            _log.debug("HTML Format: " + htmlFormat);
195                            _log.debug("Reply to: " + Arrays.toString(replyTo));
196                            _log.debug("Message ID: " + messageId);
197                            _log.debug("In Reply To: " + inReplyTo);
198    
199                            if ((fileAttachments != null) && _log.isDebugEnabled()) {
200                                    for (int i = 0; i < fileAttachments.size(); i++) {
201                                            FileAttachment fileAttachment = fileAttachments.get(i);
202    
203                                            File file = fileAttachment.getFile();
204    
205                                            if (file == null) {
206                                                    continue;
207                                            }
208    
209                                            _log.debug(
210                                                    "Attachment " + i + " file " + file.getAbsolutePath() +
211                                                            " and file name " + fileAttachment.getFileName());
212                                    }
213                            }
214                    }
215    
216                    try {
217                            Session session = null;
218    
219                            if (smtpAccount == null) {
220                                    session = getSession();
221                            }
222                            else {
223                                    session = getSession(smtpAccount);
224                            }
225    
226                            Message message = new LiferayMimeMessage(session);
227    
228                            message.addHeader(
229                                    "X-Auto-Response-Suppress", "AutoReply, DR, NDR, NRN, OOF, RN");
230    
231                            message.setFrom(from);
232                            message.setRecipients(Message.RecipientType.TO, to);
233    
234                            if (cc != null) {
235                                    message.setRecipients(Message.RecipientType.CC, cc);
236                            }
237    
238                            if (bcc != null) {
239                                    message.setRecipients(Message.RecipientType.BCC, bcc);
240                            }
241    
242                            subject = GetterUtil.getString(subject);
243    
244                            message.setSubject(subject);
245    
246                            if ((fileAttachments != null) && (fileAttachments.size() > 0)) {
247                                    MimeMultipart rootMultipart = new MimeMultipart(
248                                            _MULTIPART_TYPE_MIXED);
249    
250                                    MimeMultipart messageMultipart = new MimeMultipart(
251                                            _MULTIPART_TYPE_ALTERNATIVE);
252    
253                                    MimeBodyPart messageBodyPart = new MimeBodyPart();
254    
255                                    messageBodyPart.setContent(messageMultipart);
256    
257                                    rootMultipart.addBodyPart(messageBodyPart);
258    
259                                    if (htmlFormat) {
260                                            MimeBodyPart bodyPart = new MimeBodyPart();
261    
262                                            bodyPart.setContent(body, _TEXT_HTML);
263    
264                                            messageMultipart.addBodyPart(bodyPart);
265                                    }
266                                    else {
267                                            MimeBodyPart bodyPart = new MimeBodyPart();
268    
269                                            bodyPart.setText(body);
270    
271                                            messageMultipart.addBodyPart(bodyPart);
272                                    }
273    
274                                    for (int i = 0; i < fileAttachments.size(); i++) {
275                                            FileAttachment fileAttachment = fileAttachments.get(i);
276    
277                                            File file = fileAttachment.getFile();
278    
279                                            if (file == null) {
280                                                    continue;
281                                            }
282    
283                                            MimeBodyPart mimeBodyPart = new MimeBodyPart();
284    
285                                            DataSource dataSource = new FileDataSource(file);
286    
287                                            mimeBodyPart.setDataHandler(new DataHandler(dataSource));
288                                            mimeBodyPart.setDisposition(Part.ATTACHMENT);
289    
290                                            if (fileAttachment.getFileName() != null) {
291                                                    mimeBodyPart.setFileName(fileAttachment.getFileName());
292                                            }
293                                            else {
294                                                    mimeBodyPart.setFileName(file.getName());
295                                            }
296    
297                                            rootMultipart.addBodyPart(mimeBodyPart);
298                                    }
299    
300                                    message.setContent(rootMultipart);
301    
302                                    message.saveChanges();
303                            }
304                            else {
305                                    if (htmlFormat) {
306                                            message.setContent(body, _TEXT_HTML);
307                                    }
308                                    else {
309                                            message.setContent(body, _TEXT_PLAIN);
310                                    }
311                            }
312    
313                            message.setSentDate(new Date());
314    
315                            if (replyTo != null) {
316                                    message.setReplyTo(replyTo);
317                            }
318    
319                            if (messageId != null) {
320                                    message.setHeader("Message-ID", messageId);
321                            }
322    
323                            if (inReplyTo != null) {
324                                    message.setHeader("In-Reply-To", inReplyTo);
325                                    message.setHeader("References", inReplyTo);
326                            }
327    
328                            int batchSize = GetterUtil.getInteger(
329                                    PropsUtil.get(PropsKeys.MAIL_BATCH_SIZE), _BATCH_SIZE);
330    
331                            _send(session, message, bulkAddresses, batchSize);
332                    }
333                    catch (SendFailedException sfe) {
334                            _log.error(sfe);
335                    }
336                    catch (Exception e) {
337                            throw new MailEngineException(e);
338                    }
339    
340                    if (_log.isDebugEnabled()) {
341                            _log.debug("Sending mail takes " + stopWatch.getTime() + " ms");
342                    }
343            }
344    
345            public static void send(
346                            InternetAddress from, InternetAddress[] to, InternetAddress[] cc,
347                            InternetAddress[] bcc, String subject, String body)
348                    throws MailEngineException {
349    
350                    send(from, to, cc, bcc, subject, body, false, null, null, null);
351            }
352    
353            public static void send(
354                            InternetAddress from, InternetAddress[] to, InternetAddress[] cc,
355                            InternetAddress[] bcc, String subject, String body,
356                            boolean htmlFormat, InternetAddress[] replyTo, String messageId,
357                            String inReplyTo)
358                    throws MailEngineException {
359    
360                    send(
361                            from, to, cc, bcc, null, subject, body, htmlFormat, replyTo,
362                            messageId, inReplyTo, null);
363            }
364    
365            public static void send(
366                            InternetAddress from, InternetAddress[] to, InternetAddress[] cc,
367                            String subject, String body)
368                    throws MailEngineException {
369    
370                    send(from, to, cc, null, subject, body, false, null, null, null);
371            }
372    
373            public static void send(
374                            InternetAddress from, InternetAddress[] to, InternetAddress[] cc,
375                            String subject, String body, boolean htmlFormat)
376                    throws MailEngineException {
377    
378                    send(from, to, cc, null, subject, body, htmlFormat, null, null, null);
379            }
380    
381            public static void send(
382                            InternetAddress from, InternetAddress[] to, String subject,
383                            String body)
384                    throws MailEngineException {
385    
386                    send(from, to, null, null, subject, body, false, null, null, null);
387            }
388    
389            public static void send(
390                            InternetAddress from, InternetAddress[] to, String subject,
391                            String body, boolean htmlFormat)
392                    throws MailEngineException {
393    
394                    send(from, to, null, null, subject, body, htmlFormat, null, null, null);
395            }
396    
397            public static void send(MailMessage mailMessage)
398                    throws MailEngineException {
399    
400                    send(
401                            mailMessage.getFrom(), mailMessage.getTo(), mailMessage.getCC(),
402                            mailMessage.getBCC(), mailMessage.getBulkAddresses(),
403                            mailMessage.getSubject(), mailMessage.getBody(),
404                            mailMessage.isHTMLFormat(), mailMessage.getReplyTo(),
405                            mailMessage.getMessageId(), mailMessage.getInReplyTo(),
406                            mailMessage.getFileAttachments(), mailMessage.getSMTPAccount());
407            }
408    
409            public static void send(String from, String to, String subject, String body)
410                    throws MailEngineException {
411    
412                    try {
413                            send(
414                                    new InternetAddress(from), new InternetAddress(to), subject,
415                                    body);
416                    }
417                    catch (AddressException ae) {
418                            throw new MailEngineException(ae);
419                    }
420            }
421    
422            private static Address[] _getBatchAddresses(
423                    Address[] addresses, int index, int batchSize) {
424    
425                    if ((batchSize == _BATCH_SIZE) && (index == 0)) {
426                            return addresses;
427                    }
428                    else if (batchSize == _BATCH_SIZE) {
429                            return null;
430                    }
431    
432                    int start = index * batchSize;
433    
434                    if (start > addresses.length) {
435                            return null;
436                    }
437    
438                    int end = ((index + 1) * batchSize);
439    
440                    if (end > addresses.length) {
441                            end = addresses.length;
442                    }
443    
444                    return ArrayUtil.subset(addresses, start, end);
445            }
446    
447            private static Properties _getProperties(Account account) {
448                    Properties properties = new Properties();
449    
450                    String protocol = account.getProtocol();
451    
452                    properties.setProperty("mail.transport.protocol", protocol);
453                    properties.setProperty("mail." + protocol + ".host", account.getHost());
454                    properties.setProperty(
455                            "mail." + protocol + ".port", String.valueOf(account.getPort()));
456    
457                    if (account.isRequiresAuthentication()) {
458                            properties.setProperty("mail." + protocol + ".auth", "true");
459                            properties.setProperty(
460                                    "mail." + protocol + ".user", account.getUser());
461                            properties.setProperty(
462                                    "mail." + protocol + ".password", account.getPassword());
463                    }
464    
465                    if (account.isSecure()) {
466                            properties.setProperty(
467                                    "mail." + protocol + ".socketFactory.class",
468                                    "javax.net.ssl.SSLSocketFactory");
469                            properties.setProperty(
470                                    "mail." + protocol + ".socketFactory.fallback", "false");
471                            properties.setProperty(
472                                    "mail." + protocol + ".socketFactory.port",
473                                    String.valueOf(account.getPort()));
474                    }
475    
476                    return properties;
477            }
478    
479            private static String _getSMTPProperty(Session session, String suffix) {
480                    String protocol = GetterUtil.getString(
481                            session.getProperty("mail.transport.protocol"));
482    
483                    if (protocol.equals(Account.PROTOCOL_SMTPS)) {
484                            return session.getProperty("mail.smtps." + suffix);
485                    }
486                    else {
487                            return session.getProperty("mail.smtp." + suffix);
488                    }
489            }
490    
491            private static void _send(
492                    Session session, Message message, InternetAddress[] bulkAddresses,
493                    int batchSize) {
494    
495                    try {
496                            boolean smtpAuth = GetterUtil.getBoolean(
497                                    _getSMTPProperty(session, "auth"), false);
498                            String smtpHost = _getSMTPProperty(session, "host");
499                            int smtpPort = GetterUtil.getInteger(
500                                    _getSMTPProperty(session, "port"), Account.PORT_SMTP);
501                            String user = _getSMTPProperty(session, "user");
502                            String password = _getSMTPProperty(session, "password");
503    
504                            if (smtpAuth && Validator.isNotNull(user) &&
505                                    Validator.isNotNull(password)) {
506    
507                                    String protocol = GetterUtil.getString(
508                                            session.getProperty("mail.transport.protocol"),
509                                            Account.PROTOCOL_SMTP);
510    
511                                    Transport transport = session.getTransport(protocol);
512    
513                                    transport.connect(smtpHost, smtpPort, user, password);
514    
515                                    Address[] addresses = null;
516    
517                                    if (Validator.isNotNull(bulkAddresses)) {
518                                            addresses = bulkAddresses;
519                                    }
520                                    else {
521                                            addresses = message.getAllRecipients();
522                                    }
523    
524                                    for (int i = 0;; i++) {
525                                            Address[] batchAddresses = _getBatchAddresses(
526                                                    addresses, i, batchSize);
527    
528                                            if ((batchAddresses == null) ||
529                                                    (batchAddresses.length == 0)) {
530    
531                                                    break;
532                                            }
533    
534                                            transport.sendMessage(message, batchAddresses);
535                                    }
536    
537                                    transport.close();
538                            }
539                            else {
540                                    if (Validator.isNotNull(bulkAddresses)) {
541                                            int curBatch = 0;
542    
543                                            Address[] portion = _getBatchAddresses(
544                                                    bulkAddresses, curBatch, batchSize);
545    
546                                            while (Validator.isNotNull(portion)) {
547                                                    Transport.send(message, portion);
548    
549                                                    curBatch++;
550    
551                                                    portion = _getBatchAddresses(
552                                                            bulkAddresses, curBatch, batchSize);
553                                            }
554                                    }
555                                    else {
556                                            Transport.send(message);
557                                    }
558                            }
559                    }
560                    catch (MessagingException me) {
561                            if (me.getNextException() instanceof SocketException) {
562                                    if (_log.isWarnEnabled()) {
563                                            _log.warn(
564                                                    "Failed to connect to a valid mail server. Please " +
565                                                            "make sure one is properly configured. " +
566                                                                    me.getMessage());
567                                    }
568                            }
569                            else {
570                                    _log.error(me.getMessage());
571    
572                                    LogUtil.log(_log, me);
573                            }
574                    }
575            }
576    
577            private static final int _BATCH_SIZE = 0;
578    
579            private static final String _MULTIPART_TYPE_ALTERNATIVE = "alternative";
580    
581            private static final String _MULTIPART_TYPE_MIXED = "mixed";
582    
583            private static final String _TEXT_HTML = "text/html;charset=\"UTF-8\"";
584    
585            private static final String _TEXT_PLAIN = "text/plain;charset=\"UTF-8\"";
586    
587            private static Log _log = LogFactoryUtil.getLog(MailEngine.class);
588    
589    }