001/*
002 * (C) Copyright 2006-2008 Nuxeo SAS (http://nuxeo.com/) and contributors.
003 *
004 * All rights reserved. This program and the accompanying materials
005 * are made available under the terms of the GNU Lesser General Public License
006 * (LGPL) version 2.1 which accompanies this distribution, and is available at
007 * http://www.gnu.org/licenses/lgpl.html
008 *
009 * This library is distributed in the hope that it will be useful,
010 * but WITHOUT ANY WARRANTY; without even the implied warranty of
011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
012 * Lesser General Public License for more details.
013 *
014 * Contributors:
015 *     Alexandre Russel
016 *
017 * $Id$
018 */
019
020package org.nuxeo.ecm.platform.mail.service;
021
022import java.util.HashMap;
023import java.util.Map;
024import java.util.Properties;
025
026import javax.mail.Address;
027import javax.mail.Folder;
028import javax.mail.Message;
029import javax.mail.MessagingException;
030import javax.mail.Session;
031import javax.mail.Store;
032import javax.mail.Transport;
033import javax.mail.internet.MimeMessage;
034
035import org.nuxeo.ecm.platform.ec.notification.email.EmailHelper;
036import org.nuxeo.ecm.platform.mail.action.MailBoxActions;
037import org.nuxeo.ecm.platform.mail.action.MailBoxActionsImpl;
038import org.nuxeo.ecm.platform.mail.action.MessageActionPipe;
039import org.nuxeo.ecm.platform.mail.action.MessageActionPipeDescriptor;
040import org.nuxeo.ecm.platform.mail.fetcher.PropertiesFetcher;
041import org.nuxeo.ecm.platform.mail.fetcher.PropertiesFetcherDescriptor;
042import org.nuxeo.runtime.api.Framework;
043import org.nuxeo.runtime.model.ComponentInstance;
044import org.nuxeo.runtime.model.DefaultComponent;
045
046/**
047 * @author Alexandre Russel
048 */
049public class MailServiceImpl extends DefaultComponent implements MailService {
050
051    private static final String SESSION_FACTORY = "sessionFactory";
052
053    private static final String PROPERTIES_FETCHER = "propertiesFetcher";
054
055    private static final String ACTION_PIPES = "actionPipes";
056
057    private final Map</* fetcher name */String, Class<? extends PropertiesFetcher>> fetchers = new HashMap<String, Class<? extends PropertiesFetcher>>();
058
059    private final Map</* session factory name */String, SessionFactoryDescriptor> sessionFactories = new HashMap<String, SessionFactoryDescriptor>();
060
061    private final Map</* session factory name */String, PropertiesFetcher> configuredFetchers = new HashMap<String, PropertiesFetcher>();
062
063    private final Map<String, MessageActionPipe> actionPipesRegistry = new HashMap<String, MessageActionPipe>();
064
065    private final Map<String, MessageActionPipeDescriptor> actionPipeDescriptorsRegistry = new HashMap<String, MessageActionPipeDescriptor>();
066
067    static {
068        setDecodeUTFFileNamesSystemProperty();
069    }
070
071    @Override
072    public void registerContribution(Object contribution, String extensionPoint, ComponentInstance contributor) {
073        if (extensionPoint.equals(SESSION_FACTORY)) {
074            SessionFactoryDescriptor descriptor = (SessionFactoryDescriptor) contribution;
075            registerSessionFactory(descriptor);
076        } else if (extensionPoint.equals(PROPERTIES_FETCHER)) {
077            PropertiesFetcherDescriptor descriptor = (PropertiesFetcherDescriptor) contribution;
078            fetchers.put(descriptor.getName(), descriptor.getFetcher());
079        } else if (extensionPoint.equals(ACTION_PIPES)) {
080            MessageActionPipeDescriptor descriptor = (MessageActionPipeDescriptor) contribution;
081            registerActionPipe(descriptor);
082        }
083    }
084
085    @Override
086    public void unregisterContribution(Object contribution, String extensionPoint, ComponentInstance contributor) {
087        // TODO deal with other extension points
088        if (extensionPoint.equals(ACTION_PIPES)) {
089            MessageActionPipeDescriptor descriptor = (MessageActionPipeDescriptor) contribution;
090            actionPipesRegistry.remove(descriptor.getName());
091        }
092    }
093
094    private void registerSessionFactory(SessionFactoryDescriptor descriptor) {
095        sessionFactories.put(descriptor.getName(), descriptor);
096    }
097
098    private void registerActionPipe(MessageActionPipeDescriptor descriptor) {
099        if (!descriptor.getOverride()) {
100            MessageActionPipeDescriptor existingDescriptor = actionPipeDescriptorsRegistry.get(descriptor.getName());
101            if (existingDescriptor != null) {
102                descriptor.merge(existingDescriptor);
103            }
104        }
105        actionPipeDescriptorsRegistry.put(descriptor.getName(), descriptor);
106        actionPipesRegistry.put(descriptor.getName(), descriptor.getPipe());
107    }
108
109    private static void setDecodeUTFFileNamesSystemProperty() {
110        String toDecodeTheFilenames = Framework.getRuntime().getProperty("mail.mime.decodefilename");
111        if (toDecodeTheFilenames == null) {
112            return;
113        }
114        toDecodeTheFilenames = toDecodeTheFilenames.trim().toLowerCase();
115        if (toDecodeTheFilenames.equals("true") || toDecodeTheFilenames.equals("yes")) {
116            System.setProperty("mail.mime.decodefilename", "true");
117            return;
118        }
119        System.setProperty("mail.mime.decodefilename", "false");
120    }
121
122    public Store getConnectedStore(String name) throws MessagingException {
123        return getConnectedStore(name, null);
124    }
125
126    public Store getConnectedStore(String name, Map<String, Object> context) throws MessagingException {
127        Properties props = getProperties(name, context);
128        Session session = newSession(props);
129        Store store = session.getStore();
130        store.connect(props.getProperty("user"), props.getProperty("password"));
131        return store;
132    }
133
134    private Properties getProperties(String name, Map<String, Object> map) {
135        return getFetcher(name).getProperties(map);
136    }
137
138    public Transport getConnectedTransport(String name) throws MessagingException {
139        return getConnectedTransport(name, null);
140    }
141
142    public Transport getConnectedTransport(String name, Map<String, Object> context) throws MessagingException {
143        Properties props = getProperties(name, context);
144        Session session = newSession(props);
145        Transport transport = session.getTransport();
146        transport.connect(props.getProperty("user"), props.getProperty("password"));
147        return transport;
148    }
149
150    public Session getSession(String name) {
151        return getSession(name, null);
152    }
153
154    public Session getSession(String name, Map<String, Object> context) {
155        Properties props = getProperties(name, context);
156        return newSession(props);
157    }
158
159    public MailBoxActions getMailBoxActions(String factoryName, String folderName) throws MessagingException {
160        return getMailBoxActions(factoryName, folderName, null);
161    }
162
163    public MailBoxActions getMailBoxActions(String factoryName, String folderName, Map<String, Object> context)
164            throws MessagingException {
165        Store store = getConnectedStore(factoryName, context);
166        Folder folder = store.getFolder(folderName);
167        return new MailBoxActionsImpl(folder, true);
168    }
169
170    public void sendMail(String text, String subject, String factory, Address[] recipients) {
171        sendMail(text, subject, factory, recipients, null);
172    }
173
174    public void sendMail(String text, String subject, String factory, Address[] recipients, Map<String, Object> context) {
175        Session session = getSession(factory, context);
176        Message message = new MimeMessage(session);
177        try {
178            message.setFrom();
179            message.setSubject(subject);
180            message.setRecipients(Message.RecipientType.TO, recipients);
181            message.setText(text);
182            Transport.send(message);
183        } catch (MessagingException e) {
184            throw new RuntimeException(e);
185        }
186    }
187
188    public PropertiesFetcher getFetcher(String name) {
189        PropertiesFetcher fetcher = configuredFetchers.get(name);
190        if (fetcher == null) {
191            String fetcherName = sessionFactories.get(name).getFetcherName();
192            Class<? extends PropertiesFetcher> clazz = fetchers.get(fetcherName);
193            SessionFactoryDescriptor descriptor = sessionFactories.get(name);
194            try {
195                fetcher = clazz.newInstance();
196            } catch (ReflectiveOperationException e) {
197                throw new RuntimeException(e);
198            }
199            fetcher.configureFetcher(descriptor.getProperties());
200            configuredFetchers.put(name, fetcher);
201        }
202        return fetcher;
203    }
204
205    public MessageActionPipe getPipe(String name) {
206        return actionPipesRegistry.get(name);
207    }
208
209    protected Session newSession(Properties props) {
210        return EmailHelper.newSession(props);
211    }
212}