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}