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}