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