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:bs@nuxeo.com">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.socketFactory.fallback", "false"); 134 config.put("mail.smtp.socketFactory.port", port); 135 136 } else if (port == null) { 137 port = "25"; 138 } 139 config.setProperty("mail.smtp.host", host); 140 config.setProperty("mail.smtp.port", port); 141 session = null; 142 } 143 144 /** 145 * Set SMTP credential 146 * 147 * @param user 148 * @param pass 149 */ 150 public void setCredentials(final String user, final String pass) { 151 config.setProperty("mail.smtp.auth", "true"); 152 auth = new Authenticator() { 153 @Override 154 protected PasswordAuthentication getPasswordAuthentication() { 155 return new PasswordAuthentication(user, pass); 156 } 157 }; 158 session = null; 159 } 160 161 public void setAuthenticator(Authenticator auth) { 162 config.setProperty("mail.smtp.auth", "true"); 163 this.auth = auth; 164 session = null; 165 } 166 167 public void setDebug(boolean debug) { 168 config.setProperty("mail.debug", Boolean.toString(debug)); 169 } 170 171 public Session getSession() { 172 if (session == null) { 173 synchronized (this) { 174 if (session == null) { 175 if (sessionName != null) { 176 try { 177 InitialContext ic = new InitialContext(); 178 session = (Session) ic.lookup(sessionName); 179 } catch (NamingException e) { 180 log.warn("Failed to lookup mail session using JNDI name " + sessionName 181 + ". Falling back on local configuration."); 182 session = Session.getInstance(config, auth); 183 } 184 } else { 185 session = Session.getInstance(config, auth); 186 } 187 } 188 } 189 } 190 return session; 191 } 192 193 public Properties getConfiguration() { 194 return config; 195 } 196 197 public void setConfiguration(Properties config) { 198 this.config = config; 199 } 200 201 public void loadConfiguration(InputStream in) throws IOException { 202 config.load(in); 203 } 204 205 public void send(MimeMessage message) throws MessagingException { 206 Transport.send(message); 207 } 208 209 public Message newMessage() { 210 return new Message(getSession()); 211 } 212 213 /** 214 * Send a single email. 215 */ 216 public void sendEmail(String from, String to, String subject, String body) throws MessagingException { 217 // Here, no Authenticator argument is used (it is null). 218 // Authenticators are used to prompt the user for user 219 // name and password. 220 MimeMessage message = new MimeMessage(getSession()); 221 // the "from" address may be set in code, or set in the 222 // config file under "mail.from" ; here, the latter style is used 223 message.setFrom(new InternetAddress(from)); 224 message.addRecipient(javax.mail.Message.RecipientType.TO, new InternetAddress(to)); 225 message.setSubject(subject); 226 message.setText(body); 227 Transport.send(message); 228 } 229 230 public static class Message extends MimeMessage { 231 232 public static enum AS { 233 FROM, TO, CC, BCC, REPLYTO 234 } 235 236 public Message(Session session) { 237 super(session); 238 } 239 240 public Message(Session session, InputStream in) throws MessagingException { 241 super(session, in); 242 } 243 244 public Message addTo(String to) throws MessagingException { 245 addRecipient(javax.mail.Message.RecipientType.TO, new InternetAddress(to)); 246 return this; 247 } 248 249 public Message addCc(String cc) throws MessagingException { 250 addRecipient(javax.mail.Message.RecipientType.CC, new InternetAddress(cc)); 251 return this; 252 } 253 254 public Message addBcc(String bcc) throws MessagingException { 255 addRecipient(javax.mail.Message.RecipientType.BCC, new InternetAddress(bcc)); 256 return this; 257 } 258 259 public Message addFrom(String from) throws MessagingException { 260 addFrom(new InternetAddress[] { new InternetAddress(from) }); 261 return this; 262 } 263 264 public void addInfoInMessageHeader(String address, AS as) throws MessagingException { 265 switch (as) { 266 case FROM: 267 addFrom(address); 268 break; 269 case TO: 270 addTo(address); 271 break; 272 case CC: 273 addCc(address); 274 break; 275 case BCC: 276 addBcc(address); 277 break; 278 case REPLYTO: 279 Address[] oldValue = getReplyTo(); 280 Address[] replyToValue; 281 if (getReplyTo() == null) { 282 replyToValue = new Address[1]; 283 } else { 284 replyToValue = new Address[oldValue.length + 1]; 285 } 286 for (int i = 0; i < oldValue.length; i++) { 287 replyToValue[i] = oldValue[i]; 288 } 289 replyToValue[oldValue.length] = new InternetAddress(address); 290 setReplyTo(replyToValue); 291 break; 292 default: 293 throw new MessagingException("Unknown header info " + as.toString()); 294 } 295 } 296 297 public Message setFrom(String from) throws MessagingException { 298 setFrom(new InternetAddress(from)); 299 return this; 300 } 301 302 public void send() throws MessagingException { 303 Transport.send(this); 304 } 305 306 } 307 308}