package act.mail;
/*-
* #%L
* ACT Framework
* %%
* Copyright (C) 2014 - 2017 ActFramework
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import act.app.App;
import act.app.AppHolderBase;
import org.osgl.exception.ConfigurationException;
import org.osgl.http.H;
import org.osgl.logging.LogManager;
import org.osgl.logging.Logger;
import org.osgl.util.C;
import org.osgl.util.E;
import org.osgl.util.S;
import javax.mail.Authenticator;
import javax.mail.PasswordAuthentication;
import javax.mail.Session;
import javax.mail.internet.AddressException;
import javax.mail.internet.InternetAddress;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Properties;
public class MailerConfig extends AppHolderBase {
public static final Logger logger = LogManager.get(MailerConfig.class);
public static final String FROM = "from";
public static final String CONTENT_TYPE = "content_type";
public static final String LOCALE = "locale";
public static final String SUBJECT = "subject";
public static final String TO = "to";
public static final String CC = "cc";
public static final String BCC = "bcc";
public static final String SMTP_HOST = "smtp.host";
public static final String SMTP_PORT = "smtp.port";
public static final String SMTP_TLS = "smtp.tls";
public static final String SMTP_SSL = "smtp.ssl";
public static final String SMTP_USERNAME = "smtp.username";
public static final String SMTP_PASSWORD = "smtp.password";
private String id;
private boolean isDefault;
private boolean mock;
private InternetAddress from;
private H.Format contentType;
private Locale locale;
private String subject;
private String host;
private String port;
private boolean useTls;
private boolean useSsl;
private String username;
private String password;
private List<InternetAddress> toList;
private List<InternetAddress> ccList;
private List<InternetAddress> bccList;
private volatile Session session;
public MailerConfig(String id, Map<String, String> properties, App app) {
super(app);
E.illegalArgumentIf(S.blank(id), "mailer config id expected");
this.id = id;
this.isDefault = "default".equals(id);
this.from = getFromConfig(properties);
this.contentType = getContentTypeConfig(properties);
this.locale = getLocaleConfig(properties);
this.subject = getProperty(SUBJECT, properties);
this.host = getProperty(SMTP_HOST, properties);
if ("gmail".equals(this.host)) {
this.host = "smtp.gmail.com";
}
this.username = getProperty(SMTP_USERNAME, properties);
if (null == host) {
if (S.notBlank(this.username) && this.username.endsWith("gmail.com")) {
this.host = "smtp.gmail.com";
} else {
logger.warn("smtp host configuration not found, will use mock smtp to send email");
mock = true;
}
}
if (!mock) {
this.useTls = getBooleanConfig(SMTP_TLS, properties) || S.eq("smtp.gmail.com", this.host);
this.useSsl = !this.useTls && getBooleanConfig(SMTP_SSL, properties);
this.port = getPortConfig(properties);
this.password = getProperty(SMTP_PASSWORD, properties);
if (null == username || null == password) {
logger.warn("Either smtp.username or smtp.password is not configured for mailer[%s]", id);
}
}
this.toList = getEmailListConfig(TO, properties);
this.ccList = getEmailListConfig(CC, properties);
this.bccList = getEmailListConfig(BCC, properties);
}
private String getProperty(String key, Map<String, String> properties) {
String key0 = key;
key = S.concat("mailer.", id, ".", key);
String val = properties.get(key);
if (null != val) {
return val;
}
String key2 = "act." + key;
val = properties.get(key2);
if (null != val) {
return val;
}
if (isDefault) {
key = S.concat("mailer.", key0);
val = properties.get(key);
if (null != val) {
return val;
}
return properties.get(S.concat("act.", key));
} else {
return null;
}
}
private List<InternetAddress> getEmailListConfig(String key, Map<String, String> properties) {
String s = getProperty(key, properties);
if (S.blank(s)) {
return C.list();
}
List<InternetAddress> l = C.newList();
return MailerContext.canonicalRecipients(l, s);
}
private String getPortConfig(Map<String, String> properties) {
String port = getProperty(SMTP_PORT, properties);
if (null == port) {
if (!useSsl && !useTls) {
port = "25";
} else if (useSsl) {
port = "465";
} else {
port = "587";
}
logger.warn("No smtp.port found for mailer[%s] configuration will use the default number: ", id, port);
} else {
try {
Integer.parseInt(port);
} catch (Exception e) {
throw E.invalidConfiguration("Invalid port configuration for mailer[%]: %s", id, port);
}
}
return port;
}
private boolean getBooleanConfig(String key, Map<String, String> properties) {
String s = getProperty(key, properties);
return null != s && Boolean.parseBoolean(s);
}
private Locale getLocaleConfig(Map<String, String> properties) {
String s = getProperty(LOCALE, properties);
if (null == s) {
return app().config().locale();
} else {
// the following code credit to
// http://www.java2s.com/Code/Java/Network-Protocol/GetLocaleFromString.htm
String localeString = s.trim();
if (localeString.toLowerCase().equals("default")) {
return app().config().locale();
}
// Extract language
int languageIndex = localeString.indexOf('_');
String language = null;
if (languageIndex == -1) {
// No further "_" so is "{language}" only
return new Locale(localeString, "");
} else {
language = localeString.substring(0, languageIndex);
}
// Extract country
int countryIndex = localeString.indexOf('_', languageIndex + 1);
String country;
if (countryIndex == -1) {
// No further "_" so is "{language}_{country}"
country = localeString.substring(languageIndex + 1);
return new Locale(language, country);
} else {
// Assume all remaining is the variant so is "{language}_{country}_{variant}"
country = localeString.substring(languageIndex + 1, countryIndex);
String variant = localeString.substring(countryIndex + 1);
return new Locale(language, country, variant);
}
}
}
private H.Format getContentTypeConfig(Map<String, String> properties) {
String s = getProperty(CONTENT_TYPE, properties);
if (null == s) {
return null;
}
try {
H.Format fmt = H.Format.valueOf(s);
if (H.Format.HTML == fmt || H.Format.TXT == fmt) {
return fmt;
}
throw E.invalidConfiguration("Content type not supported by mailer: %s", fmt);
} catch (ConfigurationException e) {
throw e;
} catch (Exception e) {
throw E.invalidConfiguration("Invalid mailer config content type: %s", s);
}
}
private InternetAddress getFromConfig(Map<String, String> properties) {
String s = getProperty(FROM, properties);
if (null == s) {
return null;
}
try {
InternetAddress[] ia = InternetAddress.parse(s);
if (null == ia || ia.length == 0) return null;
return ia[0];
} catch (AddressException e) {
throw E.invalidConfiguration(e, "invalid mailer from address: %s", s);
}
}
@Override
protected void releaseResources() {
if (null != session) {
session = null;
}
}
public String id() {
return id;
}
public String subject() {
return subject;
}
public H.Format contentType() {
return contentType;
}
public InternetAddress from() {
return from;
}
public String username() {
return username;
}
public String password() {
return password;
}
public Locale locale() {
return (null != locale) ? locale : app().config().locale();
}
public List<InternetAddress> to() {
return toList;
}
public List<InternetAddress> ccList() {
return ccList;
}
public List<InternetAddress> bccList() {
return bccList;
}
public boolean mock() {
return mock;
}
public Session session() {
if (null == session) {
synchronized (this) {
if (null == session) {
session = createSession();
}
}
}
return session;
}
private Session createSession() {
Properties p = new Properties();
if (mock()) {
p.setProperty("mail.smtp.host", "unknown");
p.setProperty("mail.smtp.port", "465");
} else {
p.setProperty("mail.smtp.host", host);
p.setProperty("mail.smtp.port", port);
}
if (null != username && null != password) {
if (useTls) {
p.put("mail.smtp.starttls.enable", "true");
} else if (useSsl) {
p.put("mail.smtp.socketFactory.port", port);
p.put("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory");
}
p.setProperty("mail.smtp.auth", "true");
Authenticator auth = new Authenticator() {
//override the getPasswordAuthentication method
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(username, password);
}
};
return Session.getInstance(p, auth);
} else {
return Session.getInstance(p);
}
}
}