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