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