package org.ovirt.engine.core.notifier.utils.sender.mail; import java.util.Date; import java.util.Map; import java.util.Properties; import javax.mail.Address; import javax.mail.Authenticator; import javax.mail.Message; import javax.mail.MessagingException; import javax.mail.PasswordAuthentication; import javax.mail.Session; import javax.mail.Transport; import javax.mail.internet.AddressException; import javax.mail.internet.InternetAddress; import javax.mail.internet.MimeMessage; import org.apache.commons.lang.StringUtils; import org.ovirt.engine.core.compat.LogCompat; import org.ovirt.engine.core.compat.LogFactoryCompat; import org.ovirt.engine.core.notifier.utils.NotificationProperties; /** * Support email sending by SMTP or SMTP over SSL methods. */ public class JavaMailSender { private static final LogCompat log = LogFactoryCompat.getLog(JavaMailSender.class); private Session session = null; private InternetAddress from = null; private InternetAddress replyTo = null; private boolean isBodyHtml = false; private boolean isSSL = false; private EmailAuthenticator auth; /** * Creates an instance of {@code javax.mail.Session} which could be used for multiple messages dispatch. * @param aMailProps * properties required for creating a mail session */ public JavaMailSender(Map<String, String> aMailProps) { Properties mailSessionProps = setCommonProperties(aMailProps); // enable SSL if (Boolean.valueOf(aMailProps.get(NotificationProperties.MAIL_ENABLE_SSL))) { mailSessionProps.put("mail.transport.protocol", "smtps"); mailSessionProps.put("mail.smtps.auth", "true"); mailSessionProps.put("mail.smtps.host", aMailProps.get(NotificationProperties.MAIL_SERVER)); String portString = aMailProps.get(NotificationProperties.MAIL_PORT); if (StringUtils.isNotEmpty(portString)) { mailSessionProps.put("mail.smtps.socketFactory.port", Integer.valueOf(portString)); } mailSessionProps.put("mail.smtps.socketFactory.class", "javax.net.ssl.SSLSocketFactory"); mailSessionProps.put("mail.smtps.socketFactory.fallback", false); this.isSSL = true; } else { mailSessionProps.put("mail.transport.protocol", "smtp"); mailSessionProps.put("mail.smtp.host", aMailProps.get(NotificationProperties.MAIL_SERVER)); } String password = aMailProps.get(NotificationProperties.MAIL_PASSWORD); boolean isAuthenticated = StringUtils.isNotEmpty(password); if (isAuthenticated) { auth = new EmailAuthenticator(aMailProps.get(NotificationProperties.MAIL_USER), aMailProps.get(NotificationProperties.MAIL_PASSWORD)); this.session = Session.getDefaultInstance(mailSessionProps, auth); } else { this.session = Session.getInstance(mailSessionProps); } } /** * Set common properties for both secured and non-secured mail session * @param aMailProps * mail configuration properties * @return a session common properties */ private Properties setCommonProperties(Map<String, String> aMailProps) { Properties mailSessionProps = new Properties(); if (log.isTraceEnabled()) { mailSessionProps.put("mail.debug", "true"); } String mailHost = aMailProps.get(NotificationProperties.MAIL_SERVER); if (StringUtils.isEmpty(mailHost)) { throw new IllegalArgumentException(NotificationProperties.MAIL_SERVER + " must not be null or empty"); } String emailUser = aMailProps.get(NotificationProperties.MAIL_USER); if (StringUtils.isEmpty(emailUser)) { if (Boolean.valueOf(aMailProps.get(NotificationProperties.MAIL_ENABLE_SSL)) || StringUtils.isNotEmpty(aMailProps.get(NotificationProperties.MAIL_PASSWORD))) { throw new IllegalArgumentException(NotificationProperties.MAIL_USER + " must be set when SSL is enabled or when password is set"); } else { log.warn(NotificationProperties.MAIL_USER + " property is empty in notification service configuration file"); } } if (StringUtils.isNotEmpty(aMailProps.get(NotificationProperties.MAIL_FROM))) { try { from = new InternetAddress(aMailProps.get(NotificationProperties.MAIL_FROM)); } catch (AddressException e) { log.errorFormat("Failed to parse 'from' user {0} provided by property {1}", aMailProps.get(NotificationProperties.MAIL_FROM), NotificationProperties.MAIL_FROM, e); } } else { try { if (StringUtils.isNotEmpty(emailUser)) { from = new InternetAddress(emailUser); } } catch (AddressException e) { log.errorFormat("Failed to parse 'email user' {0} provided by property {1}", emailUser, NotificationProperties.MAIL_USER, e); } } if (StringUtils.isNotEmpty(aMailProps.get(NotificationProperties.MAIL_REPLY_TO))) { try { replyTo = new InternetAddress(aMailProps.get(NotificationProperties.MAIL_REPLY_TO)); } catch (AddressException e) { log.errorFormat("Failed to parse 'replyTo' email {0} provided by property {1}", aMailProps.get(NotificationProperties.MAIL_REPLY_TO), NotificationProperties.MAIL_REPLY_TO, e); } } String isBodyHtmlStr = aMailProps.get(NotificationProperties.HTML_MESSAGE_FORMAT); if (StringUtils.isNotEmpty(isBodyHtmlStr)) { isBodyHtml = Boolean.valueOf(isBodyHtmlStr); } return mailSessionProps; } /** * Sends a message to a recipient using pre-configured mail session, either as a plan text message or as a html * message body * @param recipient * a recipient mail address * @param messageSubject * the subject of the message * @param messageBody * the body of the message * @throws MessagingException */ public void send(String recipient, String messageSubject, String messageBody) throws MessagingException { try { Message msg = new MimeMessage(session); msg.setFrom(from); InternetAddress address = new InternetAddress(recipient); msg.setRecipient(Message.RecipientType.TO, address); if (replyTo != null) { msg.setReplyTo(new Address[] { replyTo }); } msg.setSubject(messageSubject); if (isBodyHtml){ msg.setContent(String.format("<html><head><title>%s</title></head><body><p>%s</body></html>", messageSubject, messageBody), "text/html"); } else { msg.setText(messageBody); } msg.setSentDate(new Date()); if (isSSL) { Transport transport = session.getTransport("smtps"); transport.connect(); transport.sendMessage(msg, new Address[] { address }); } else { Transport.send(msg); } } catch (MessagingException mex) { log.error(String.format("Failed to send message from [%s] to [%s] with subject [%s] due to error: [%s]", from.toString(), recipient, messageSubject, mex.getMessage()), mex); throw mex; } } /** * An implementation of the {@link Authenticator}, holds the authentication credentials for a network connection. */ private class EmailAuthenticator extends Authenticator { private String userName; private String password; public EmailAuthenticator(String userName, String password) { this.userName = userName; this.password = password; } protected PasswordAuthentication getPasswordAuthentication() { return new PasswordAuthentication(userName, password); } } }