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.StringPool;
034 import com.liferay.portal.kernel.util.StringUtil;
035 import com.liferay.portal.kernel.util.Validator;
036
037 import java.io.File;
038
039 import java.net.SocketException;
040
041 import java.util.Arrays;
042 import java.util.Date;
043 import java.util.List;
044 import java.util.Properties;
045
046 import javax.activation.DataHandler;
047 import javax.activation.DataSource;
048 import javax.activation.FileDataSource;
049
050 import javax.mail.Address;
051 import javax.mail.Message;
052 import javax.mail.MessagingException;
053 import javax.mail.Part;
054 import javax.mail.SendFailedException;
055 import javax.mail.Session;
056 import javax.mail.Transport;
057 import javax.mail.internet.AddressException;
058 import javax.mail.internet.InternetAddress;
059 import javax.mail.internet.MimeBodyPart;
060 import javax.mail.internet.MimeMessage;
061 import javax.mail.internet.MimeMultipart;
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 long startTime = System.currentTimeMillis();
182
183 if (_log.isDebugEnabled()) {
184 _log.debug("From: " + from);
185 _log.debug("To: " + Arrays.toString(to));
186 _log.debug("CC: " + Arrays.toString(cc));
187 _log.debug("BCC: " + Arrays.toString(bcc));
188 _log.debug("List Addresses: " + Arrays.toString(bulkAddresses));
189 _log.debug("Subject: " + subject);
190 _log.debug("Body: " + body);
191 _log.debug("HTML Format: " + htmlFormat);
192 _log.debug("Reply to: " + Arrays.toString(replyTo));
193 _log.debug("Message ID: " + messageId);
194 _log.debug("In Reply To: " + inReplyTo);
195
196 if ((fileAttachments != null) && _log.isDebugEnabled()) {
197 for (int i = 0; i < fileAttachments.size(); i++) {
198 FileAttachment fileAttachment = fileAttachments.get(i);
199
200 File file = fileAttachment.getFile();
201
202 if (file == null) {
203 continue;
204 }
205
206 _log.debug(
207 "Attachment " + i + " file " + file.getAbsolutePath() +
208 " and file name " + fileAttachment.getFileName());
209 }
210 }
211 }
212
213 try {
214 InternetAddressUtil.validateAddress(from);
215
216 if (ArrayUtil.isNotEmpty(to)) {
217 InternetAddressUtil.validateAddresses(to);
218 }
219
220 if (ArrayUtil.isNotEmpty(cc)) {
221 InternetAddressUtil.validateAddresses(cc);
222 }
223
224 if (ArrayUtil.isNotEmpty(bcc)) {
225 InternetAddressUtil.validateAddresses(bcc);
226 }
227
228 if (ArrayUtil.isNotEmpty(replyTo)) {
229 InternetAddressUtil.validateAddresses(replyTo);
230 }
231
232 if (ArrayUtil.isNotEmpty(bulkAddresses)) {
233 InternetAddressUtil.validateAddresses(bulkAddresses);
234 }
235
236 Session session = null;
237
238 if (smtpAccount == null) {
239 session = getSession();
240 }
241 else {
242 session = getSession(smtpAccount);
243 }
244
245 Message message = new LiferayMimeMessage(session);
246
247 message.addHeader(
248 "X-Auto-Response-Suppress", "AutoReply, DR, NDR, NRN, OOF, RN");
249
250 message.setFrom(from);
251
252 if (ArrayUtil.isNotEmpty(to)) {
253 message.setRecipients(Message.RecipientType.TO, to);
254 }
255
256 if (ArrayUtil.isNotEmpty(cc)) {
257 message.setRecipients(Message.RecipientType.CC, cc);
258 }
259
260 if (ArrayUtil.isNotEmpty(bcc)) {
261 message.setRecipients(Message.RecipientType.BCC, bcc);
262 }
263
264 subject = GetterUtil.getString(subject);
265
266 message.setSubject(_sanitizeCRLF(subject));
267
268 if (ListUtil.isNotEmpty(fileAttachments)) {
269 MimeMultipart rootMultipart = new MimeMultipart(
270 _MULTIPART_TYPE_MIXED);
271
272 MimeMultipart messageMultipart = new MimeMultipart(
273 _MULTIPART_TYPE_ALTERNATIVE);
274
275 MimeBodyPart messageBodyPart = new MimeBodyPart();
276
277 messageBodyPart.setContent(messageMultipart);
278
279 rootMultipart.addBodyPart(messageBodyPart);
280
281 if (htmlFormat) {
282 MimeBodyPart bodyPart = new MimeBodyPart();
283
284 bodyPart.setContent(body, _TEXT_HTML);
285
286 messageMultipart.addBodyPart(bodyPart);
287 }
288 else {
289 MimeBodyPart bodyPart = new MimeBodyPart();
290
291 bodyPart.setText(body);
292
293 messageMultipart.addBodyPart(bodyPart);
294 }
295
296 for (int i = 0; i < fileAttachments.size(); i++) {
297 FileAttachment fileAttachment = fileAttachments.get(i);
298
299 File file = fileAttachment.getFile();
300
301 if (file == null) {
302 continue;
303 }
304
305 MimeBodyPart mimeBodyPart = new MimeBodyPart();
306
307 DataSource dataSource = new FileDataSource(file);
308
309 mimeBodyPart.setDataHandler(new DataHandler(dataSource));
310 mimeBodyPart.setDisposition(Part.ATTACHMENT);
311
312 if (fileAttachment.getFileName() != null) {
313 mimeBodyPart.setFileName(fileAttachment.getFileName());
314 }
315 else {
316 mimeBodyPart.setFileName(file.getName());
317 }
318
319 rootMultipart.addBodyPart(mimeBodyPart);
320 }
321
322 message.setContent(rootMultipart);
323
324 message.saveChanges();
325 }
326 else {
327 if (htmlFormat) {
328 message.setContent(body, _TEXT_HTML);
329 }
330 else {
331 message.setContent(body, _TEXT_PLAIN);
332 }
333 }
334
335 message.setSentDate(new Date());
336
337 if (ArrayUtil.isNotEmpty(replyTo)) {
338 message.setReplyTo(replyTo);
339 }
340
341 if (messageId != null) {
342 message.setHeader("Message-ID", _sanitizeCRLF(messageId));
343 }
344
345 if (inReplyTo != null) {
346 message.setHeader("In-Reply-To", _sanitizeCRLF(inReplyTo));
347 message.setHeader("References", _sanitizeCRLF(inReplyTo));
348 }
349
350 int batchSize = GetterUtil.getInteger(
351 PropsUtil.get(PropsKeys.MAIL_BATCH_SIZE), _BATCH_SIZE);
352
353 _send(session, message, bulkAddresses, batchSize);
354 }
355 catch (SendFailedException sfe) {
356 _log.error(sfe);
357
358 if (_isThrowsExceptionOnFailure()) {
359 throw new MailEngineException(sfe);
360 }
361 }
362 catch (Exception e) {
363 throw new MailEngineException(e);
364 }
365
366 if (_log.isDebugEnabled()) {
367 _log.debug(
368 "Sending mail takes " +
369 (System.currentTimeMillis() - startTime) + " 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 String _sanitizeCRLF(String text) {
525 return StringUtil.replace(
526 text, new String[] {StringPool.NEW_LINE, StringPool.RETURN},
527 new String[] {StringPool.SPACE, StringPool.SPACE});
528 }
529
530 private static void _send(
531 Session session, Message message, InternetAddress[] bulkAddresses,
532 int batchSize)
533 throws MailEngineException {
534
535 try {
536 boolean smtpAuth = GetterUtil.getBoolean(
537 _getSMTPProperty(session, "auth"), false);
538 String smtpHost = _getSMTPProperty(session, "host");
539 int smtpPort = GetterUtil.getInteger(
540 _getSMTPProperty(session, "port"), Account.PORT_SMTP);
541 String user = _getSMTPProperty(session, "user");
542 String password = _getSMTPProperty(session, "password");
543
544 if (smtpAuth && Validator.isNotNull(user) &&
545 Validator.isNotNull(password)) {
546
547 String protocol = GetterUtil.getString(
548 session.getProperty("mail.transport.protocol"),
549 Account.PROTOCOL_SMTP);
550
551 Transport transport = session.getTransport(protocol);
552
553 transport.connect(smtpHost, smtpPort, user, password);
554
555 Address[] addresses = null;
556
557 if (ArrayUtil.isNotEmpty(bulkAddresses)) {
558 addresses = bulkAddresses;
559 }
560 else {
561 addresses = message.getAllRecipients();
562 }
563
564 for (int i = 0;; i++) {
565 Address[] batchAddresses = _getBatchAddresses(
566 addresses, i, batchSize);
567
568 if (ArrayUtil.isEmpty(batchAddresses)) {
569 break;
570 }
571
572 transport.sendMessage(message, batchAddresses);
573 }
574
575 transport.close();
576 }
577 else {
578 if (ArrayUtil.isNotEmpty(bulkAddresses)) {
579 int curBatch = 0;
580
581 Address[] portion = _getBatchAddresses(
582 bulkAddresses, curBatch, batchSize);
583
584 while (ArrayUtil.isNotEmpty(portion)) {
585 Transport.send(message, portion);
586
587 curBatch++;
588
589 portion = _getBatchAddresses(
590 bulkAddresses, curBatch, batchSize);
591 }
592 }
593 else {
594 Transport.send(message);
595 }
596 }
597 }
598 catch (MessagingException me) {
599 if (me.getNextException() instanceof SocketException) {
600 if (_log.isWarnEnabled()) {
601 _log.warn(
602 "Failed to connect to a valid mail server. Please " +
603 "make sure one is properly configured. " +
604 me.getMessage());
605 }
606 }
607 else {
608 LogUtil.log(_log, me);
609 }
610
611 if (_isThrowsExceptionOnFailure()) {
612 throw new MailEngineException(me);
613 }
614 }
615 }
616
617 private static final int _BATCH_SIZE = 0;
618
619 private static final String _MULTIPART_TYPE_ALTERNATIVE = "alternative";
620
621 private static final String _MULTIPART_TYPE_MIXED = "mixed";
622
623 private static final String _TEXT_HTML = "text/html;charset=\"UTF-8\"";
624
625 private static final String _TEXT_PLAIN = "text/plain;charset=\"UTF-8\"";
626
627 private static final Log _log = LogFactoryUtil.getLog(MailEngine.class);
628
629 }