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 Session session = null;
218
219 if (smtpAccount == null) {
220 session = getSession();
221 }
222 else {
223 session = getSession(smtpAccount);
224 }
225
226 Message message = new LiferayMimeMessage(session);
227
228 message.setFrom(from);
229 message.setRecipients(Message.RecipientType.TO, to);
230
231 if (cc != null) {
232 message.setRecipients(Message.RecipientType.CC, cc);
233 }
234
235 if (bcc != null) {
236 message.setRecipients(Message.RecipientType.BCC, bcc);
237 }
238
239 subject = GetterUtil.getString(subject);
240
241 message.setSubject(subject);
242
243 if ((fileAttachments != null) && (fileAttachments.size() > 0)) {
244 MimeMultipart rootMultipart = new MimeMultipart(
245 _MULTIPART_TYPE_MIXED);
246
247 MimeMultipart messageMultipart = new MimeMultipart(
248 _MULTIPART_TYPE_ALTERNATIVE);
249
250 MimeBodyPart messageBodyPart = new MimeBodyPart();
251
252 messageBodyPart.setContent(messageMultipart);
253
254 rootMultipart.addBodyPart(messageBodyPart);
255
256 if (htmlFormat) {
257 MimeBodyPart bodyPart = new MimeBodyPart();
258
259 bodyPart.setContent(body, _TEXT_HTML);
260
261 messageMultipart.addBodyPart(bodyPart);
262 }
263 else {
264 MimeBodyPart bodyPart = new MimeBodyPart();
265
266 bodyPart.setText(body);
267
268 messageMultipart.addBodyPart(bodyPart);
269 }
270
271 for (int i = 0; i < fileAttachments.size(); i++) {
272 FileAttachment fileAttachment = fileAttachments.get(i);
273
274 File file = fileAttachment.getFile();
275
276 if (file == null) {
277 continue;
278 }
279
280 MimeBodyPart mimeBodyPart = new MimeBodyPart();
281
282 DataSource dataSource = new FileDataSource(file);
283
284 mimeBodyPart.setDataHandler(new DataHandler(dataSource));
285 mimeBodyPart.setDisposition(Part.ATTACHMENT);
286
287 if (fileAttachment.getFileName() != null) {
288 mimeBodyPart.setFileName(fileAttachment.getFileName());
289 }
290 else {
291 mimeBodyPart.setFileName(file.getName());
292 }
293
294 rootMultipart.addBodyPart(mimeBodyPart);
295 }
296
297 message.setContent(rootMultipart);
298
299 message.saveChanges();
300 }
301 else {
302 if (htmlFormat) {
303 message.setContent(body, _TEXT_HTML);
304 }
305 else {
306 message.setContent(body, _TEXT_PLAIN);
307 }
308 }
309
310 message.setSentDate(new Date());
311
312 if (replyTo != null) {
313 message.setReplyTo(replyTo);
314 }
315
316 if (messageId != null) {
317 message.setHeader("Message-ID", messageId);
318 }
319
320 if (inReplyTo != null) {
321 message.setHeader("In-Reply-To", inReplyTo);
322 message.setHeader("References", inReplyTo);
323 }
324
325 int batchSize = GetterUtil.getInteger(
326 PropsUtil.get(PropsKeys.MAIL_BATCH_SIZE), _BATCH_SIZE);
327
328 _send(session, message, bulkAddresses, batchSize);
329 }
330 catch (SendFailedException sfe) {
331 _log.error(sfe);
332 }
333 catch (Exception e) {
334 throw new MailEngineException(e);
335 }
336
337 if (_log.isDebugEnabled()) {
338 _log.debug("Sending mail takes " + stopWatch.getTime() + " ms");
339 }
340 }
341
342 public static void send(
343 InternetAddress from, InternetAddress[] to, InternetAddress[] cc,
344 InternetAddress[] bcc, String subject, String body)
345 throws MailEngineException {
346
347 send(from, to, cc, bcc, subject, body, false, null, null, null);
348 }
349
350 public static void send(
351 InternetAddress from, InternetAddress[] to, InternetAddress[] cc,
352 InternetAddress[] bcc, String subject, String body,
353 boolean htmlFormat, InternetAddress[] replyTo, String messageId,
354 String inReplyTo)
355 throws MailEngineException {
356
357 send(
358 from, to, cc, bcc, null, subject, body, htmlFormat, replyTo,
359 messageId, inReplyTo, null);
360 }
361
362 public static void send(
363 InternetAddress from, InternetAddress[] to, InternetAddress[] cc,
364 String subject, String body)
365 throws MailEngineException {
366
367 send(from, to, cc, null, subject, body, false, null, null, null);
368 }
369
370 public static void send(
371 InternetAddress from, InternetAddress[] to, InternetAddress[] cc,
372 String subject, String body, boolean htmlFormat)
373 throws MailEngineException {
374
375 send(from, to, cc, null, subject, body, htmlFormat, null, null, null);
376 }
377
378 public static void send(
379 InternetAddress from, InternetAddress[] to, String subject,
380 String body)
381 throws MailEngineException {
382
383 send(from, to, null, null, subject, body, false, null, null, null);
384 }
385
386 public static void send(
387 InternetAddress from, InternetAddress[] to, String subject,
388 String body, boolean htmlFormat)
389 throws MailEngineException {
390
391 send(from, to, null, null, subject, body, htmlFormat, null, null, null);
392 }
393
394 public static void send(MailMessage mailMessage)
395 throws MailEngineException {
396
397 send(
398 mailMessage.getFrom(), mailMessage.getTo(), mailMessage.getCC(),
399 mailMessage.getBCC(), mailMessage.getBulkAddresses(),
400 mailMessage.getSubject(), mailMessage.getBody(),
401 mailMessage.isHTMLFormat(), mailMessage.getReplyTo(),
402 mailMessage.getMessageId(), mailMessage.getInReplyTo(),
403 mailMessage.getFileAttachments(), mailMessage.getSMTPAccount());
404 }
405
406 public static void send(String from, String to, String subject, String body)
407 throws MailEngineException {
408
409 try {
410 send(
411 new InternetAddress(from), new InternetAddress(to), subject,
412 body);
413 }
414 catch (AddressException ae) {
415 throw new MailEngineException(ae);
416 }
417 }
418
419 private static Address[] _getBatchAddresses(
420 Address[] addresses, int index, int batchSize) {
421
422 if ((batchSize == _BATCH_SIZE) && (index == 0)) {
423 return addresses;
424 }
425 else if (batchSize == _BATCH_SIZE) {
426 return null;
427 }
428
429 int start = index * batchSize;
430
431 if (start > addresses.length) {
432 return null;
433 }
434
435 int end = ((index + 1) * batchSize);
436
437 if (end > addresses.length) {
438 end = addresses.length;
439 }
440
441 return ArrayUtil.subset(addresses, start, end);
442 }
443
444 private static Properties _getProperties(Account account) {
445 Properties properties = new Properties();
446
447 String protocol = account.getProtocol();
448
449 properties.setProperty("mail.transport.protocol", protocol);
450 properties.setProperty("mail." + protocol + ".host", account.getHost());
451 properties.setProperty(
452 "mail." + protocol + ".port", String.valueOf(account.getPort()));
453
454 if (account.isRequiresAuthentication()) {
455 properties.setProperty("mail." + protocol + ".auth", "true");
456 properties.setProperty(
457 "mail." + protocol + ".user", account.getUser());
458 properties.setProperty(
459 "mail." + protocol + ".password", account.getPassword());
460 }
461
462 if (account.isSecure()) {
463 properties.setProperty(
464 "mail." + protocol + ".socketFactory.class",
465 "javax.net.ssl.SSLSocketFactory");
466 properties.setProperty(
467 "mail." + protocol + ".socketFactory.fallback", "false");
468 properties.setProperty(
469 "mail." + protocol + ".socketFactory.port",
470 String.valueOf(account.getPort()));
471 }
472
473 return properties;
474 }
475
476 private static String _getSMTPProperty(Session session, String suffix) {
477 String protocol = GetterUtil.getString(
478 session.getProperty("mail.transport.protocol"));
479
480 if (protocol.equals(Account.PROTOCOL_SMTPS)) {
481 return session.getProperty("mail.smtps." + suffix);
482 }
483 else {
484 return session.getProperty("mail.smtp." + suffix);
485 }
486 }
487
488 private static void _send(
489 Session session, Message message, InternetAddress[] bulkAddresses,
490 int batchSize) {
491
492 try {
493 boolean smtpAuth = GetterUtil.getBoolean(
494 _getSMTPProperty(session, "auth"), false);
495 String smtpHost = _getSMTPProperty(session, "host");
496 int smtpPort = GetterUtil.getInteger(
497 _getSMTPProperty(session, "port"), Account.PORT_SMTP);
498 String user = _getSMTPProperty(session, "user");
499 String password = _getSMTPProperty(session, "password");
500
501 if (smtpAuth && Validator.isNotNull(user) &&
502 Validator.isNotNull(password)) {
503
504 String protocol = GetterUtil.getString(
505 session.getProperty("mail.transport.protocol"),
506 Account.PROTOCOL_SMTP);
507
508 Transport transport = session.getTransport(protocol);
509
510 transport.connect(smtpHost, smtpPort, user, password);
511
512 Address[] addresses = null;
513
514 if (Validator.isNotNull(bulkAddresses)) {
515 addresses = bulkAddresses;
516 }
517 else {
518 addresses = message.getAllRecipients();
519 }
520
521 for (int i = 0;; i++) {
522 Address[] batchAddresses = _getBatchAddresses(
523 addresses, i, batchSize);
524
525 if ((batchAddresses == null) ||
526 (batchAddresses.length == 0)) {
527
528 break;
529 }
530
531 transport.sendMessage(message, batchAddresses);
532 }
533
534 transport.close();
535 }
536 else {
537 if (Validator.isNotNull(bulkAddresses)) {
538 int curBatch = 0;
539
540 Address[] portion = _getBatchAddresses(
541 bulkAddresses, curBatch, batchSize);
542
543 while (Validator.isNotNull(portion)) {
544 Transport.send(message, portion);
545
546 curBatch++;
547
548 portion = _getBatchAddresses(
549 bulkAddresses, curBatch, batchSize);
550 }
551 }
552 else {
553 Transport.send(message);
554 }
555 }
556 }
557 catch (MessagingException me) {
558 if (me.getNextException() instanceof SocketException) {
559 if (_log.isWarnEnabled()) {
560 _log.warn(
561 "Failed to connect to a valid mail server. Please " +
562 "make sure one is properly configured. " +
563 me.getMessage());
564 }
565 }
566 else {
567 _log.error(me.getMessage());
568
569 LogUtil.log(_log, me);
570 }
571 }
572 }
573
574 private static final int _BATCH_SIZE = 0;
575
576 private static final String _MULTIPART_TYPE_ALTERNATIVE = "alternative";
577
578 private static final String _MULTIPART_TYPE_MIXED = "mixed";
579
580 private static final String _TEXT_HTML = "text/html;charset=\"UTF-8\"";
581
582 private static final String _TEXT_PLAIN = "text/plain;charset=\"UTF-8\"";
583
584 private static Log _log = LogFactoryUtil.getLog(MailEngine.class);
585
586 }