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