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