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