001/*
002 * (C) Copyright 2006-2019 Nuxeo (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;
033
034import org.nuxeo.mail.MailSessionBuilder;
035
036import static org.nuxeo.mail.MailConstants.CONFIGURATION_MAIL_DEBUG;
037import static org.nuxeo.mail.MailConstants.CONFIGURATION_MAIL_SMTP_AUTH;
038import static org.nuxeo.mail.MailConstants.CONFIGURATION_MAIL_SMTP_HOST;
039import static org.nuxeo.mail.MailConstants.CONFIGURATION_MAIL_SMTP_PASSWORD;
040import static org.nuxeo.mail.MailConstants.CONFIGURATION_MAIL_SMTP_PORT;
041import static org.nuxeo.mail.MailConstants.CONFIGURATION_MAIL_SMTP_USER;
042
043/**
044 * @author <a href="mailto:bs@nuxeo.com">Bogdan Stefanescu</a>
045 */
046public class Mailer {
047
048    protected Properties config;
049
050    protected volatile Session session;
051
052    /**
053     * @deprecated since 11.1, not used anymore
054     */
055    @Deprecated(since = "11.1")
056    protected Authenticator auth;
057
058    /**
059     * The JNDI session name. If not null JNDI will be used to lookup the default session, otherwise local configuration
060     * (through {@link #config}) will be used to create a session.
061     */
062    protected final String sessionName;
063
064    /**
065     * Create a mailer that can be configured using the API.
066     *
067     * @see #setAuthenticator(Authenticator)
068     * @see #setCredentials(String, String)
069     * @see #setServer(String)
070     * @deprecated since 11.1, use other constructors instead
071     */
072    @Deprecated(since = "11.1")
073    public Mailer() {
074        this(null, new Properties());
075    }
076
077    /**
078     * Create a mailer that use the given properties to configure the session.
079     */
080    public Mailer(Properties config) {
081        this(null, config);
082    }
083
084    /**
085     * Create a mailer using a session that lookup for the session in JNDI under the given session name.
086     */
087    public Mailer(String sessionName) {
088        this(sessionName, new Properties());
089    }
090
091    /**
092     * Create a mailer using a session that lookup for the session in JNDI under the given session name. If the JNDI
093     * binding doesn't exists use the given properties to cinfiugure the session.
094     */
095    public Mailer(String sessionName, Properties config) {
096        this.config = config;
097        this.sessionName = sessionName;
098        final String user = config.getProperty(CONFIGURATION_MAIL_SMTP_USER);
099        final String pass = config.getProperty(CONFIGURATION_MAIL_SMTP_PASSWORD);
100        if (user != null && pass != null) {
101            this.auth = new Authenticator() {
102                @Override
103                protected PasswordAuthentication getPasswordAuthentication() {
104                    return new PasswordAuthentication(user, pass);
105                }
106            };
107        }
108    }
109
110    public void setServer(String host) {
111        setServer(host, "25", false);
112    }
113
114    public void setServer(String host, boolean ssl) {
115        setServer(host, ssl ? "465" : "25", ssl);
116    }
117
118    /**
119     * Set the SMTP server address to use
120     */
121    public void setServer(String host, String port) {
122        setServer(host, port, false);
123    }
124
125    public void setServer(String host, String port, boolean ssl) {
126        if (ssl) {
127            if (port == null) {
128                port = "465";
129            }
130            config.put("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory");
131            config.put("mail.smtp.ssl.checkserveridentity", "true");
132            config.put("mail.smtp.socketFactory.fallback", "false");
133            config.put("mail.smtp.socketFactory.port", port);
134
135        } else if (port == null) {
136            port = "25";
137        }
138        config.setProperty(CONFIGURATION_MAIL_SMTP_HOST, host);
139        config.setProperty(CONFIGURATION_MAIL_SMTP_PORT, port);
140        session = null;
141    }
142
143    /**
144     * Set SMTP credentials.
145     *
146     * @deprecated since 11.1, use {@code mail.${protocol}.user} and {@code mail.${protocol}.password} instead
147     */
148    @Deprecated(since = "11.1")
149    public void setCredentials(final String user, final String pass) {
150        config.setProperty(CONFIGURATION_MAIL_SMTP_AUTH, "true");
151        config.setProperty(CONFIGURATION_MAIL_SMTP_USER, user);
152        config.setProperty(CONFIGURATION_MAIL_SMTP_PASSWORD, user);
153        auth = new Authenticator() {
154            @Override
155            protected PasswordAuthentication getPasswordAuthentication() {
156                return new PasswordAuthentication(user, pass);
157            }
158        };
159        session = null;
160    }
161
162    /**
163     * @deprecated since 11.1, this method does nothing, use {@code mail.${protocol}.user} and
164     *             {@code mail.${protocol}.password} instead
165     */
166    @Deprecated(since = "11.1")
167    public void setAuthenticator(Authenticator auth) {
168        config.setProperty(CONFIGURATION_MAIL_SMTP_AUTH, "true");
169        this.auth = auth;
170        session = null;
171    }
172
173    public void setDebug(boolean debug) {
174        config.setProperty(CONFIGURATION_MAIL_DEBUG, Boolean.toString(debug));
175    }
176
177    public Session getSession() {
178        if (session == null) {
179            synchronized (this) {
180                if (session == null) {
181                    if (sessionName != null) {
182                        session = MailSessionBuilder.fromJndi(sessionName).fallbackOn(config).build();
183                    } else {
184                        session = MailSessionBuilder.fromProperties(config).build();
185                    }
186                }
187            }
188        }
189        return session;
190    }
191
192    public Properties getConfiguration() {
193        return config;
194    }
195
196    public void setConfiguration(Properties config) {
197        this.config = config;
198    }
199
200    public void loadConfiguration(InputStream in) throws IOException {
201        config.load(in);
202    }
203
204    public void send(MimeMessage message) throws MessagingException {
205        Transport.send(message);
206    }
207
208    public Message newMessage() {
209        return new Message(getSession());
210    }
211
212    /**
213     * Send a single email.
214     */
215    public void sendEmail(String from, String to, String subject, String body) throws MessagingException {
216        // Here, no Authenticator argument is used (it is null).
217        // Authenticators are used to prompt the user for user
218        // name and password.
219        MimeMessage message = new MimeMessage(getSession());
220        // the "from" address may be set in code, or set in the
221        // config file under "mail.from" ; here, the latter style is used
222        message.setFrom(new InternetAddress(from));
223        message.addRecipient(javax.mail.Message.RecipientType.TO, new InternetAddress(to));
224        message.setSubject(subject);
225        message.setText(body);
226        Transport.send(message);
227    }
228
229    public static class Message extends MimeMessage {
230
231        public enum AS {
232            FROM, TO, CC, BCC, REPLYTO
233        }
234
235        public Message(Session session) {
236            super(session);
237        }
238
239        public Message(Session session, InputStream in) throws MessagingException {
240            super(session, in);
241        }
242
243        public Message addTo(String to) throws MessagingException {
244            addRecipient(javax.mail.Message.RecipientType.TO, new InternetAddress(to));
245            return this;
246        }
247
248        public Message addCc(String cc) throws MessagingException {
249            addRecipient(javax.mail.Message.RecipientType.CC, new InternetAddress(cc));
250            return this;
251        }
252
253        public Message addBcc(String bcc) throws MessagingException {
254            addRecipient(javax.mail.Message.RecipientType.BCC, new InternetAddress(bcc));
255            return this;
256        }
257
258        public Message addFrom(String from) throws MessagingException {
259            addFrom(new InternetAddress[] { new InternetAddress(from) });
260            return this;
261        }
262
263        public void addInfoInMessageHeader(String address, AS as) throws MessagingException {
264            switch (as) {
265            case FROM:
266                addFrom(address);
267                break;
268            case TO:
269                addTo(address);
270                break;
271            case CC:
272                addCc(address);
273                break;
274            case BCC:
275                addBcc(address);
276                break;
277            case REPLYTO:
278                Address[] oldValue = getReplyTo();
279                Address[] replyToValue;
280                if (getReplyTo() == null) {
281                    replyToValue = new Address[1];
282                } else {
283                    replyToValue = new Address[oldValue.length + 1];
284                }
285                for (int i = 0; i < oldValue.length; i++) {
286                    replyToValue[i] = oldValue[i];
287                }
288                replyToValue[oldValue.length] = new InternetAddress(address);
289                setReplyTo(replyToValue);
290                break;
291            default:
292                throw new MessagingException("Unknown header info " + as.toString());
293            }
294        }
295
296        public Message setFrom(String from) throws MessagingException {
297            setFrom(new InternetAddress(from));
298            return this;
299        }
300
301        public void send() throws MessagingException {
302            Transport.send(this);
303        }
304
305    }
306
307}