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.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
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 }