001/* 002 * (C) Copyright 2006-2011 Nuxeo SA (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; 033import javax.naming.InitialContext; 034import javax.naming.NamingException; 035 036import org.apache.commons.logging.Log; 037import org.apache.commons.logging.LogFactory; 038 039/** 040 * @author <a href="mailto:[email protected]">Bogdan Stefanescu</a> 041 */ 042public class Mailer { 043 044 private static final Log log = LogFactory.getLog(Mailer.class); 045 046 protected Properties config; 047 048 protected volatile Session session; 049 050 protected Authenticator auth; 051 052 /** 053 * The JNDI session name. If not null JNDI will be used to lookup the default session, otherwise local configuration 054 * (through {@link #config}) will be used to create a session. 055 */ 056 protected final String sessionName; 057 058 /** 059 * Create a mailer that can be configured using the API. 060 * 061 * @see #setAuthenticator(Authenticator) 062 * @see #setCredentials(String, String) 063 * @see #setServer(String) 064 */ 065 public Mailer() { 066 this(null, new Properties()); 067 } 068 069 /** 070 * Create a mailer that use the given properties to configure the session. 071 * 072 * @param config 073 */ 074 public Mailer(Properties config) { 075 this(null, config); 076 } 077 078 /** 079 * Create a mailer using a session that lookup for the session in JNDI under the given session name. 080 * 081 * @param sessionName 082 */ 083 public Mailer(String sessionName) { 084 this(sessionName, new Properties()); 085 } 086 087 /** 088 * Create a mailer using a session that lookup for the session in JNDI under the given session name. If the JNDI 089 * binding doesn't exists use the given properties to cinfiugure the session. 090 * 091 * @param sessionName 092 * @param config 093 */ 094 public Mailer(String sessionName, Properties config) { 095 this.config = config; 096 this.sessionName = sessionName; 097 final String user = config.getProperty("mail.smtp.user"); 098 final String pass = config.getProperty("mail.smtp.password"); 099 if (user != null && pass != null) { 100 setAuthenticator(new Authenticator() { 101 @Override 102 protected PasswordAuthentication getPasswordAuthentication() { 103 return new PasswordAuthentication(user, pass); 104 } 105 }); 106 } 107 } 108 109 public void setServer(String host) { 110 setServer(host, "25", false); 111 } 112 113 public void setServer(String host, boolean ssl) { 114 setServer(host, ssl ? "465" : "25", ssl); 115 } 116 117 /** 118 * Set the SMTP server address to use 119 * 120 * @param host 121 * @param port 122 */ 123 public void setServer(String host, String port) { 124 setServer(host, port, false); 125 } 126 127 public void setServer(String host, String port, boolean ssl) { 128 if (ssl) { 129 if (port == null) { 130 port = "465"; 131 } 132 config.put("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory"); 133 config.put("mail.smtp.ssl.checkserveridentity", "true"); 134 config.put("mail.smtp.socketFactory.fallback", "false"); 135 config.put("mail.smtp.socketFactory.port", port); 136 137 } else if (port == null) { 138 port = "25"; 139 } 140 config.setProperty("mail.smtp.host", host); 141 config.setProperty("mail.smtp.port", port); 142 session = null; 143 } 144 145 /** 146 * Set SMTP credential 147 * 148 * @param user 149 * @param pass 150 */ 151 public void setCredentials(final String user, final String pass) { 152 config.setProperty("mail.smtp.auth", "true"); 153 auth = new Authenticator() { 154 @Override 155 protected PasswordAuthentication getPasswordAuthentication() { 156 return new PasswordAuthentication(user, pass); 157 } 158 }; 159 session = null; 160 } 161 162 public void setAuthenticator(Authenticator auth) { 163 config.setProperty("mail.smtp.auth", "true"); 164 this.auth = auth; 165 session = null; 166 } 167 168 public void setDebug(boolean debug) { 169 config.setProperty("mail.debug", Boolean.toString(debug)); 170 } 171 172 public Session getSession() { 173 if (session == null) { 174 synchronized (this) { 175 if (session == null) { 176 if (sessionName != null) { 177 try { 178 InitialContext ic = new InitialContext(); 179 session = (Session) ic.lookup(sessionName); 180 } catch (NamingException e) { 181 log.warn("Failed to lookup mail session using JNDI name " + sessionName 182 + ". Falling back on local configuration."); 183 session = Session.getInstance(config, auth); 184 } 185 } else { 186 session = Session.getInstance(config, auth); 187 } 188 } 189 } 190 } 191 return session; 192 } 193 194 public Properties getConfiguration() { 195 return config; 196 } 197 198 public void setConfiguration(Properties config) { 199 this.config = config; 200 } 201 202 public void loadConfiguration(InputStream in) throws IOException { 203 config.load(in); 204 } 205 206 public void send(MimeMessage message) throws MessagingException { 207 Transport.send(message); 208 } 209 210 public Message newMessage() { 211 return new Message(getSession()); 212 } 213 214 /** 215 * Send a single email. 216 */ 217 public void sendEmail(String from, String to, String subject, String body) throws MessagingException { 218 // Here, no Authenticator argument is used (it is null). 219 // Authenticators are used to prompt the user for user 220 // name and password. 221 MimeMessage message = new MimeMessage(getSession()); 222 // the "from" address may be set in code, or set in the 223 // config file under "mail.from" ; here, the latter style is used 224 message.setFrom(new InternetAddress(from)); 225 message.addRecipient(javax.mail.Message.RecipientType.TO, new InternetAddress(to)); 226 message.setSubject(subject); 227 message.setText(body); 228 Transport.send(message); 229 } 230 231 public static class Message extends MimeMessage { 232 233 public static enum AS { 234 FROM, TO, CC, BCC, REPLYTO 235 } 236 237 public Message(Session session) { 238 super(session); 239 } 240 241 public Message(Session session, InputStream in) throws MessagingException { 242 super(session, in); 243 } 244 245 public Message addTo(String to) throws MessagingException { 246 addRecipient(javax.mail.Message.RecipientType.TO, new InternetAddress(to)); 247 return this; 248 } 249 250 public Message addCc(String cc) throws MessagingException { 251 addRecipient(javax.mail.Message.RecipientType.CC, new InternetAddress(cc)); 252 return this; 253 } 254 255 public Message addBcc(String bcc) throws MessagingException { 256 addRecipient(javax.mail.Message.RecipientType.BCC, new InternetAddress(bcc)); 257 return this; 258 } 259 260 public Message addFrom(String from) throws MessagingException { 261 addFrom(new InternetAddress[] { new InternetAddress(from) }); 262 return this; 263 } 264 265 public void addInfoInMessageHeader(String address, AS as) throws MessagingException { 266 switch (as) { 267 case FROM: 268 addFrom(address); 269 break; 270 case TO: 271 addTo(address); 272 break; 273 case CC: 274 addCc(address); 275 break; 276 case BCC: 277 addBcc(address); 278 break; 279 case REPLYTO: 280 Address[] oldValue = getReplyTo(); 281 Address[] replyToValue; 282 if (getReplyTo() == null) { 283 replyToValue = new Address[1]; 284 } else { 285 replyToValue = new Address[oldValue.length + 1]; 286 } 287 for (int i = 0; i < oldValue.length; i++) { 288 replyToValue[i] = oldValue[i]; 289 } 290 replyToValue[oldValue.length] = new InternetAddress(address); 291 setReplyTo(replyToValue); 292 break; 293 default: 294 throw new MessagingException("Unknown header info " + as.toString()); 295 } 296 } 297 298 public Message setFrom(String from) throws MessagingException { 299 setFrom(new InternetAddress(from)); 300 return this; 301 } 302 303 public void send() throws MessagingException { 304 Transport.send(this); 305 } 306 307 } 308 309}