edu.uiuc.ncsa.security.util.mail.MailUtil Maven / Gradle / Ivy
package edu.uiuc.ncsa.security.util.mail;
import edu.uiuc.ncsa.security.core.Logable;
import edu.uiuc.ncsa.security.core.exceptions.GeneralException;
import edu.uiuc.ncsa.security.core.util.AbstractEnvironment;
import edu.uiuc.ncsa.security.core.util.MyLoggingFacade;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.AddressException;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import javax.naming.NamingException;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.Arrays;
import java.util.Map;
import java.util.Properties;
/**
* A utility for sending messages via SMTP or SMTPS using Java mail.
* Created by Jeff Gaynor
* on 10/5/11 at 1:18 PM
*/
public class MailUtil implements Logable {
public Session getSession(Properties props) throws NamingException {
return Session.getDefaultInstance(props);
}
public MailUtil(MyLoggingFacade myLogger) {
this.myLogger = myLogger;
}
/**
* The left-hand delimiter for replacements in templates. Must make a valid regex when combined with the
* key and right-hand delimiter.
*/
public static String LEFT_DELIMITER = "${";
public static String REGEX_LEFT_DELIMITER = "\\$\\{";
/**
* The right-hand delimiter for replacements in templates. Must make a valid regex when combined with the
* left-hand delimiter and key.
*/
public static String RIGHT_DELIMITER = "}";
public static String REGEX_RIGHT_DELIMITER = "\\}";
/**
* The separator between email addresses. The default is a semi-colon.
*/
public static String ADDRESS_SEPARATOR = ";";
public MailUtil() {
try {
mailEnvironment = new MailEnvironment(false, null, -1, null, null, null, null, null, false, false);
} catch (Throwable x) {
warn("Error: could not create mail environment:" + x);
}
}
public MailUtil(MailEnvironment me) {
mailEnvironment = me;
}
public boolean isEnabled() {
return getMailEnvironment().mailEnabled;
}
public static class MailEnvironment extends AbstractEnvironment {
public MailEnvironment(boolean mailEnabled) {
this.mailEnabled = mailEnabled;
}
boolean starttls = false;
public MailEnvironment(
boolean mailEnabled,
String server,
int port,
String password,
String from,
String recipients,
String messageTemplate,
String subjectTemplate,
boolean useSSL,
boolean starttls) throws Exception {
if (from != null) {
this.from = new InternetAddress(from);
}
this.mailEnabled = mailEnabled;
this.messageTemplate = messageTemplate;
this.password = password;
this.port = port;
this.recipients = parseRecipients(recipients);
this.server = server;
this.subjectTemplate = subjectTemplate;
this.useSSL = useSSL;
this.starttls = starttls;
}
protected void init(boolean mailEnabled,
String server,
int port,
String password,
String from,
String recipients,
String messageTemplate,
String subjectTemplate,
boolean useSSL,
boolean starttls) throws AddressException {
this.mailEnabled = mailEnabled;
info("Mail notifications " + (mailEnabled ? "en" : "dis") + "abled");
this.useSSL = useSSL;
this.starttls = starttls;
if (from != null) {
this.from = new InternetAddress(from);
}
if (subjectTemplate == null) {
warn("Email: No subject template found, using default");
subjectTemplate = "Something happened on the server.";
} else {
this.subjectTemplate = subjectTemplate;
}
this.server = server;
if (messageTemplate == null) {
warn("Email: No message template found, using default");
messageTemplate = "Something happened on the server.";
} else {
this.messageTemplate = messageTemplate;
}
this.port = port;
this.password = password;
this.recipients = parseRecipients(recipients);
debug("Email uses ssl: " + (useSSL ? "y" : "n") + ", server=" + server + ", sender=" + from);
}
boolean mailEnabled;
boolean useSSL;
InternetAddress from = null;
String password;
String server;
int port = -1;
String messageTemplate;
String subjectTemplate;
public InternetAddress[] recipients;
protected InternetAddress[] parseRecipients(String x) throws AddressException {
InternetAddress[] z = null;
if (x != null) {
String[] addresses = x.split(ADDRESS_SEPARATOR);
z = new InternetAddress[addresses.length];
int i = 0;
for (String y : addresses) {
z[i++] = new InternetAddress(y);
}
}
if (z == null) {
warn("Warning: No recipients found for email notification");
z = new InternetAddress[0];
}
return z;
}
}
public MailEnvironment getMailEnvironment() {
return mailEnvironment;
}
MailEnvironment mailEnvironment;
/**
* This takes a map of replacements for the templates and sends the message.
*
This will return a true if the message succeeded and a false otherwise.
* It will not cause a failure outright, since a failed notification should not
* bring down your server.
*
* @param replacements
* @return
*/
public boolean sendMessage(Map replacements) {
Transport tr = null;
try {
info("Preparing to send mail notification");
Properties props = new Properties();
if (getMailEnvironment().isDebugOn()) {
props.put("mail.debug", "true");
}
String protocol;
int defaultPort = getMailEnvironment().port;
if (getMailEnvironment().useSSL) {
protocol = "smtps";
if (getMailEnvironment().starttls) {
props.put("mail.smtp.starttls.enable", "true"); // fix for gmail, mostly. .
}
defaultPort = defaultPort == -1 ? 465 : defaultPort;
} else {
protocol = "smtp";
defaultPort = defaultPort == -1 ? 25 : defaultPort;
}
debug("preparing message with protocol=" + protocol + " on server=" + getMailEnvironment().server);
props.put("mail.transport.protocol", protocol);
props.put("mail." + protocol + ".host", getMailEnvironment().server);
if (protocol.equals("smtp") && getMailEnvironment().from == null) {
props.put("mail." + protocol + ".auth", "false");
} else {
debug("Using authorization for user " + getMailEnvironment().from);
props.put("mail." + protocol + ".auth", "true");
}
Session session = getSession(props);
tr = session.getTransport(protocol);
tr.connect(getMailEnvironment().server,
defaultPort,
getMailEnvironment().from == null ? null : getMailEnvironment().from.toString(),
getMailEnvironment().password);
Message message = new MimeMessage(session);
message.setFrom(getMailEnvironment().from);
message.setRecipients(Message.RecipientType.TO, getMailEnvironment().recipients);
message.setSubject(replaceAll(getSubjectTemplate(), replacements));
message.setContent(replaceAll(getMessageTemplate(), replacements), "text/plain");
tr.sendMessage(message, getMailEnvironment().recipients);
info("Mail notification sent to " + Arrays.toString(getMailEnvironment().recipients));
return true;
} catch (Throwable throwable) {
info("got exception sending message:");
for (Object key : replacements.keySet()) {
info("(" + key + "," + replacements.get(key.toString()) + ")");
}
throwable.printStackTrace();
error("Sending mail failed. Continuing & message reads \"" + throwable.getMessage() + "\"");
return false;
//throw new GeneralException("Error", throwable);
} finally {
if (tr != null) {
try {
tr.close();
} catch (MessagingException e) {
throw new GeneralException("Error: could not close java mail transport", e);
}
}
}
}
public String getMessageTemplate() throws IOException {
if (messageTemplate == null) {
messageTemplate = readTemplate(getMailEnvironment().messageTemplate);
}
return messageTemplate;
}
public void setMessageTemplate(String messageTemplate) {
this.messageTemplate = messageTemplate;
}
public void setSubjectTemplate(String subjectTemplate) {
this.subjectTemplate = subjectTemplate;
}
String messageTemplate;
public String getSubjectTemplate() throws IOException {
if (subjectTemplate == null) {
subjectTemplate = readTemplate(getMailEnvironment().subjectTemplate);
}
return subjectTemplate;
}
String subjectTemplate;
/**
* Simple-minded template replacement. This works well for small, simple arguments.
*
* @param template
* @param replacements
* @return
*/
protected String replaceAll(String template, Map replacements) {
String out = template;
if (out == null || out.length() == 0) {
warn("Empty email template found, no message body or subject.");
}
int count = 0;
for (Object key : replacements.keySet()) {
// Have to properly escape the regex here.
count++;
String newKey = REGEX_LEFT_DELIMITER + key.toString() + REGEX_RIGHT_DELIMITER; // makes a reg ex.
if (replacements.containsKey(key) && (replacements.get(key) != null)) {
out = out.replaceAll(newKey, replacements.get(key).toString());
}
}
debug("made " + count + " replacements in the template");
return out;
}
protected String readTemplate(String fileName) throws IOException {
debug("reading in template file \"" + fileName + "\"");
BufferedReader br = new BufferedReader(new FileReader(fileName));
String out = null;
String inLine = br.readLine();
boolean isFirst = true;
while (inLine != null) {
if (!inLine.startsWith("#")) {
if (isFirst) {
isFirst = false;
out = inLine;
} else {
out = out + "\n" + inLine;
}
}
inLine = br.readLine();
}
br.close();
return out;
}
public MyLoggingFacade getMyLogger() {
if (myLogger == null) {
myLogger = new MyLoggingFacade(getClass().getName());
}
return myLogger;
}
public void setMyLogger(MyLoggingFacade myLogger) {
this.myLogger = myLogger;
}
MyLoggingFacade myLogger;
public void debug(String x) {
getMyLogger().debug(x);
}
public void setDebugOn(boolean debugOn) {
getMyLogger().setDebugOn(debugOn);
}
public boolean isDebugOn() {
return getMyLogger().isDebugOn();
}
public void info(String x) {
getMyLogger().info(x);
}
public void warn(String x) {
getMyLogger().warn(x);
}
public void error(String x) {
getMyLogger().error(x);
}
}