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.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}