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