001/* 002 * (C) Copyright 2006-2019 Nuxeo (http://nuxeo.com/) and others. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 * 016 * Contributors: 017 * bstefanescu 018 */ 019package org.nuxeo.ecm.automation.core.mail; 020 021import java.io.IOException; 022import java.io.InputStream; 023import java.util.Properties; 024 025import javax.mail.Address; 026import javax.mail.Authenticator; 027import javax.mail.MessagingException; 028import javax.mail.PasswordAuthentication; 029import javax.mail.Session; 030import javax.mail.Transport; 031import javax.mail.internet.InternetAddress; 032import javax.mail.internet.MimeMessage; 033 034import org.nuxeo.mail.MailSessionBuilder; 035 036import static org.nuxeo.mail.MailConstants.CONFIGURATION_MAIL_DEBUG; 037import static org.nuxeo.mail.MailConstants.CONFIGURATION_MAIL_SMTP_AUTH; 038import static org.nuxeo.mail.MailConstants.CONFIGURATION_MAIL_SMTP_HOST; 039import static org.nuxeo.mail.MailConstants.CONFIGURATION_MAIL_SMTP_PASSWORD; 040import static org.nuxeo.mail.MailConstants.CONFIGURATION_MAIL_SMTP_PORT; 041import static org.nuxeo.mail.MailConstants.CONFIGURATION_MAIL_SMTP_USER; 042 043/** 044 * @author <a href="mailto:bs@nuxeo.com">Bogdan Stefanescu</a> 045 */ 046public class Mailer { 047 048 protected Properties config; 049 050 protected volatile Session session; 051 052 /** 053 * @deprecated since 11.1, not used anymore 054 */ 055 @Deprecated(since = "11.1") 056 protected Authenticator auth; 057 058 /** 059 * The JNDI session name. If not null JNDI will be used to lookup the default session, otherwise local configuration 060 * (through {@link #config}) will be used to create a session. 061 */ 062 protected final String sessionName; 063 064 /** 065 * Create a mailer that can be configured using the API. 066 * 067 * @see #setAuthenticator(Authenticator) 068 * @see #setCredentials(String, String) 069 * @see #setServer(String) 070 * @deprecated since 11.1, use other constructors instead 071 */ 072 @Deprecated(since = "11.1") 073 public Mailer() { 074 this(null, new Properties()); 075 } 076 077 /** 078 * Create a mailer that use the given properties to configure the session. 079 */ 080 public Mailer(Properties config) { 081 this(null, config); 082 } 083 084 /** 085 * Create a mailer using a session that lookup for the session in JNDI under the given session name. 086 */ 087 public Mailer(String sessionName) { 088 this(sessionName, new Properties()); 089 } 090 091 /** 092 * Create a mailer using a session that lookup for the session in JNDI under the given session name. If the JNDI 093 * binding doesn't exists use the given properties to cinfiugure the session. 094 */ 095 public Mailer(String sessionName, Properties config) { 096 this.config = config; 097 this.sessionName = sessionName; 098 final String user = config.getProperty(CONFIGURATION_MAIL_SMTP_USER); 099 final String pass = config.getProperty(CONFIGURATION_MAIL_SMTP_PASSWORD); 100 if (user != null && pass != null) { 101 this.auth = new Authenticator() { 102 @Override 103 protected PasswordAuthentication getPasswordAuthentication() { 104 return new PasswordAuthentication(user, pass); 105 } 106 }; 107 } 108 } 109 110 public void setServer(String host) { 111 setServer(host, "25", false); 112 } 113 114 public void setServer(String host, boolean ssl) { 115 setServer(host, ssl ? "465" : "25", ssl); 116 } 117 118 /** 119 * Set the SMTP server address to use 120 */ 121 public void setServer(String host, String port) { 122 setServer(host, port, false); 123 } 124 125 public void setServer(String host, String port, boolean ssl) { 126 if (ssl) { 127 if (port == null) { 128 port = "465"; 129 } 130 config.put("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory"); 131 config.put("mail.smtp.ssl.checkserveridentity", "true"); 132 config.put("mail.smtp.socketFactory.fallback", "false"); 133 config.put("mail.smtp.socketFactory.port", port); 134 135 } else if (port == null) { 136 port = "25"; 137 } 138 config.setProperty(CONFIGURATION_MAIL_SMTP_HOST, host); 139 config.setProperty(CONFIGURATION_MAIL_SMTP_PORT, port); 140 session = null; 141 } 142 143 /** 144 * Set SMTP credentials. 145 * 146 * @deprecated since 11.1, use {@code mail.${protocol}.user} and {@code mail.${protocol}.password} instead 147 */ 148 @Deprecated(since = "11.1") 149 public void setCredentials(final String user, final String pass) { 150 config.setProperty(CONFIGURATION_MAIL_SMTP_AUTH, "true"); 151 config.setProperty(CONFIGURATION_MAIL_SMTP_USER, user); 152 config.setProperty(CONFIGURATION_MAIL_SMTP_PASSWORD, user); 153 auth = new Authenticator() { 154 @Override 155 protected PasswordAuthentication getPasswordAuthentication() { 156 return new PasswordAuthentication(user, pass); 157 } 158 }; 159 session = null; 160 } 161 162 /** 163 * @deprecated since 11.1, this method does nothing, use {@code mail.${protocol}.user} and 164 * {@code mail.${protocol}.password} instead 165 */ 166 @Deprecated(since = "11.1") 167 public void setAuthenticator(Authenticator auth) { 168 config.setProperty(CONFIGURATION_MAIL_SMTP_AUTH, "true"); 169 this.auth = auth; 170 session = null; 171 } 172 173 public void setDebug(boolean debug) { 174 config.setProperty(CONFIGURATION_MAIL_DEBUG, Boolean.toString(debug)); 175 } 176 177 public Session getSession() { 178 if (session == null) { 179 synchronized (this) { 180 if (session == null) { 181 if (sessionName != null) { 182 session = MailSessionBuilder.fromJndi(sessionName).fallbackOn(config).build(); 183 } else { 184 session = MailSessionBuilder.fromProperties(config).build(); 185 } 186 } 187 } 188 } 189 return session; 190 } 191 192 public Properties getConfiguration() { 193 return config; 194 } 195 196 public void setConfiguration(Properties config) { 197 this.config = config; 198 } 199 200 public void loadConfiguration(InputStream in) throws IOException { 201 config.load(in); 202 } 203 204 public void send(MimeMessage message) throws MessagingException { 205 Transport.send(message); 206 } 207 208 public Message newMessage() { 209 return new Message(getSession()); 210 } 211 212 /** 213 * Send a single email. 214 */ 215 public void sendEmail(String from, String to, String subject, String body) throws MessagingException { 216 // Here, no Authenticator argument is used (it is null). 217 // Authenticators are used to prompt the user for user 218 // name and password. 219 MimeMessage message = new MimeMessage(getSession()); 220 // the "from" address may be set in code, or set in the 221 // config file under "mail.from" ; here, the latter style is used 222 message.setFrom(new InternetAddress(from)); 223 message.addRecipient(javax.mail.Message.RecipientType.TO, new InternetAddress(to)); 224 message.setSubject(subject); 225 message.setText(body); 226 Transport.send(message); 227 } 228 229 public static class Message extends MimeMessage { 230 231 public enum AS { 232 FROM, TO, CC, BCC, REPLYTO 233 } 234 235 public Message(Session session) { 236 super(session); 237 } 238 239 public Message(Session session, InputStream in) throws MessagingException { 240 super(session, in); 241 } 242 243 public Message addTo(String to) throws MessagingException { 244 addRecipient(javax.mail.Message.RecipientType.TO, new InternetAddress(to)); 245 return this; 246 } 247 248 public Message addCc(String cc) throws MessagingException { 249 addRecipient(javax.mail.Message.RecipientType.CC, new InternetAddress(cc)); 250 return this; 251 } 252 253 public Message addBcc(String bcc) throws MessagingException { 254 addRecipient(javax.mail.Message.RecipientType.BCC, new InternetAddress(bcc)); 255 return this; 256 } 257 258 public Message addFrom(String from) throws MessagingException { 259 addFrom(new InternetAddress[] { new InternetAddress(from) }); 260 return this; 261 } 262 263 public void addInfoInMessageHeader(String address, AS as) throws MessagingException { 264 switch (as) { 265 case FROM: 266 addFrom(address); 267 break; 268 case TO: 269 addTo(address); 270 break; 271 case CC: 272 addCc(address); 273 break; 274 case BCC: 275 addBcc(address); 276 break; 277 case REPLYTO: 278 Address[] oldValue = getReplyTo(); 279 Address[] replyToValue; 280 if (getReplyTo() == null) { 281 replyToValue = new Address[1]; 282 } else { 283 replyToValue = new Address[oldValue.length + 1]; 284 } 285 for (int i = 0; i < oldValue.length; i++) { 286 replyToValue[i] = oldValue[i]; 287 } 288 replyToValue[oldValue.length] = new InternetAddress(address); 289 setReplyTo(replyToValue); 290 break; 291 default: 292 throw new MessagingException("Unknown header info " + as.toString()); 293 } 294 } 295 296 public Message setFrom(String from) throws MessagingException { 297 setFrom(new InternetAddress(from)); 298 return this; 299 } 300 301 public void send() throws MessagingException { 302 Transport.send(this); 303 } 304 305 } 306 307}