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