/*
* See the NOTICE file distributed with this work for additional
* information regarding copyright ownership.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.xwiki.mail.internal.configuration;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton;
import org.apache.commons.lang.exception.ExceptionUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.xwiki.component.annotation.Component;
import org.xwiki.configuration.ConfigurationSource;
import org.xwiki.mail.MailSenderConfiguration;
/**
* Gets the Mail Sending configuration. The configuration is checked in the following order:
* <ul>
* <li>Look in Mail.MailConfig in the current wiki</li>
* <li>[Backward compatibility] Look in (current space).XWikiPreferences in the current wiki</li>
* <li>[Backward compatibility] Look in XWiki.XWikiPreferences in the current wiki</li>
* <li>Look in the xwiki properties file</li>
* </ul>
*
* @version $Id: 5cf91c6bd1457bc57f610563f947408a43d40da8 $
* @since 6.1M2
*/
@Component
@Singleton
public class DefaultMailSenderConfiguration implements MailSenderConfiguration
{
/**
* Java Mail SMTP property for the protocol.
*/
public static final String JAVAMAIL_TRANSPORT_PROTOCOL = "mail.transport.protocol";
/**
* Java Mail SMTP property for the host.
*/
public static final String JAVAMAIL_SMTP_HOST = "mail.smtp.host";
/**
* Java Mail SMTP property for the server port.
*/
public static final String JAVAMAIL_SMTP_PORT = "mail.smtp.port";
/**
* Java Mail SMTP property for the username.
*/
public static final String JAVAMAIL_SMTP_USERNAME = "mail.smtp.user";
/**
* Java Mail SMTP property for the from email address.
*/
public static final String JAVAMAIL_SMTP_FROM = "mail.smtp.from";
/**
* Java Mail SMTP property for specifying that we are authenticating.
*/
public static final String JAVAMAIL_SMTP_AUTH = "mail.smtp.auth";
/**
* Prefix for configuration keys for the Mail Sending module.
*/
private static final String PREFIX = "mail.sender.";
private static final int DEFAULT_PORT = 25;
/**
* By default we wait 8 seconds between each mail in order to throttle the mail sending and not be considered as
* a spammer by mail servers.
*/
private static final long DEFAULT_SEND_WAIT_TIME = 8 * 1000L;
private static final String FROM_PROPERTY = "from";
private static final String BCC_PROPERTY = "bcc";
private static final String HOST_PROPERTY = "host";
private static final String PORT_PROPERTY = "port";
private static final String USERNAME_PROPERTY = "username";
private static final String PASSWORD_PROPERTY = "password";
private static final String PROPERTIES_PROPERTY = "properties";
private static final String SEND_WAIT_TIME = "sendWaitTime";
@Inject
private Logger logger;
@Inject
@Named("mailsend")
private ConfigurationSource mailConfigSource;
@Inject
@Named("documents")
private ConfigurationSource documentsSource;
@Inject
@Named("xwikiproperties")
private ConfigurationSource xwikiPropertiesSource;
@Override
public String getHost()
{
String host;
// First, look in the document sources
host = this.mailConfigSource.getProperty(HOST_PROPERTY,
this.documentsSource.getProperty("smtp_server", String.class));
// If not found, look in the xwiki properties source
if (host == null) {
host = this.xwikiPropertiesSource.getProperty(PREFIX + HOST_PROPERTY, "localhost");
}
return host;
}
@Override
public int getPort()
{
Integer port = null;
// First, look in the document sources
String portAsString = this.documentsSource.getProperty("smtp_port");
if (!StringUtils.isEmpty(portAsString)) {
try {
port = this.mailConfigSource.getProperty(PORT_PROPERTY, Integer.parseInt(portAsString));
} catch (NumberFormatException e) {
port = DEFAULT_PORT;
}
} else {
port = this.mailConfigSource.getProperty(PORT_PROPERTY, Integer.class);
}
// If not found, look in the xwiki properties source
if (port == null) {
port = this.xwikiPropertiesSource.getProperty(PREFIX + PORT_PROPERTY, DEFAULT_PORT);
}
return port;
}
@Override
public String getUsername()
{
String username;
// First, look in the document sources
username = this.mailConfigSource.getProperty(USERNAME_PROPERTY,
this.documentsSource.getProperty("smtp_server_username", String.class));
// If not found, look in the xwiki properties source
if (username == null) {
username = this.xwikiPropertiesSource.getProperty(PREFIX + USERNAME_PROPERTY, String.class);
}
return username;
}
@Override
public String getPassword()
{
String password;
// First, look in the document sources
password = this.mailConfigSource.getProperty(PASSWORD_PROPERTY,
this.documentsSource.getProperty("smtp_server_password", String.class));
// If not found, look in the xwiki properties source
if (password == null) {
password = this.xwikiPropertiesSource.getProperty(PREFIX + PASSWORD_PROPERTY, String.class);
}
return password;
}
@Override
public List<String> getBCCAddresses()
{
List<String> bccAddresses = new ArrayList<>();
// First, look in the document source
String bccAsString = this.mailConfigSource.getProperty(BCC_PROPERTY, String.class);
// If not found, look in the xwiki properties source
if (bccAsString == null) {
bccAsString = this.xwikiPropertiesSource.getProperty(PREFIX + BCC_PROPERTY, String.class);
}
// Convert into a list (if property is found and not null)
if (bccAsString != null) {
for (String address : StringUtils.split(bccAsString, ',')) {
bccAddresses.add(StringUtils.trim(address));
}
}
return bccAddresses;
}
@Override
public String getFromAddress()
{
String from;
// First, look in the document sources
from = this.mailConfigSource.getProperty(FROM_PROPERTY,
this.documentsSource.getProperty("admin_email", String.class));
// If not found, look in the xwiki properties source
if (from == null) {
from = this.xwikiPropertiesSource.getProperty(PREFIX + FROM_PROPERTY, String.class);
}
return from;
}
@Override
public Properties getAdditionalProperties()
{
Properties properties;
// First, look in the document sources
String extraPropertiesAsString = this.mailConfigSource.getProperty(PROPERTIES_PROPERTY,
this.documentsSource.getProperty("javamail_extra_props", String.class));
// If not found, look in the xwiki properties source
if (extraPropertiesAsString == null) {
properties = this.xwikiPropertiesSource.getProperty(PREFIX + PROPERTIES_PROPERTY, Properties.class);
} else {
// The "javamail_extra_props" property is stored in a text area and thus we need to convert it to a Map.
InputStream is = new ByteArrayInputStream(extraPropertiesAsString.getBytes());
properties = new Properties();
try {
properties.load(is);
} catch (Exception e) {
// Will happen if the user has not used the right format, in which case we log a warning but discard
// the user values.
this.logger.warn("Error while parsing mail properties [{}]. Root cause [{}]. Ignoring configuration...",
extraPropertiesAsString, ExceptionUtils.getRootCauseMessage(e));
}
}
return properties;
}
@Override
public Properties getAllProperties()
{
Properties properties = new Properties();
addProperty(properties, JAVAMAIL_TRANSPORT_PROTOCOL, "smtp");
addProperty(properties, JAVAMAIL_SMTP_HOST, getHost());
addProperty(properties, JAVAMAIL_SMTP_USERNAME, getUsername());
addProperty(properties, JAVAMAIL_SMTP_FROM, getFromAddress());
addProperty(properties, JAVAMAIL_SMTP_PORT, Integer.toString(getPort()));
// If a username and a password have been provided consider we're authenticating against the SMTP server
if (usesAuthentication()) {
properties.put(JAVAMAIL_SMTP_AUTH, "true");
}
// Add user-specified mail properties.
// Note: We're only supporting SMTP (and not SMTPS) at the moment, which means that for sending emails to a
// SMTP server requiring TLS the user will need to pass the "mail.smtp.starttls.enable=true" property and use
// the proper port for TLS (587 for Gmail for example, while port 465 is used for SMTPS/SSL).
properties.putAll(getAdditionalProperties());
return properties;
}
private void addProperty(Properties properties, String key, String value)
{
if (value != null) {
properties.setProperty(key, value);
}
}
@Override
public boolean usesAuthentication()
{
return !StringUtils.isEmpty(getUsername()) && !StringUtils.isEmpty(getPassword());
}
@Override
public String getScriptServicePermissionCheckerHint()
{
return this.xwikiPropertiesSource.getProperty(PREFIX + "scriptServiceCheckerHint", "programmingrights");
}
@Override
public long getSendWaitTime()
{
Long waitTime = this.mailConfigSource.getProperty(SEND_WAIT_TIME);
if (waitTime == null) {
waitTime = this.xwikiPropertiesSource.getProperty(PREFIX + SEND_WAIT_TIME, DEFAULT_SEND_WAIT_TIME);
}
return waitTime;
}
}