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