View Javadoc

1   /************************************************************************
2    * Copyright (c) 2000-2006 The Apache Software Foundation.             *
3    * All rights reserved.                                                *
4    * ------------------------------------------------------------------- *
5    * Licensed under the Apache License, Version 2.0 (the "License"); you *
6    * may not use this file except in compliance with the License. You    *
7    * may obtain a copy of the License at:                                *
8    *                                                                     *
9    *     http://www.apache.org/licenses/LICENSE-2.0                      *
10   *                                                                     *
11   * Unless required by applicable law or agreed to in writing, software *
12   * distributed under the License is distributed on an "AS IS" BASIS,   *
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or     *
14   * implied.  See the License for the specific language governing       *
15   * permissions and limitations under the License.                      *
16   ***********************************************************************/
17  
18  package org.apache.james.transport.mailets;
19  
20  import org.apache.avalon.cornerstone.services.store.Store;
21  import org.apache.avalon.framework.configuration.DefaultConfiguration;
22  import org.apache.avalon.framework.container.ContainerUtil;
23  import org.apache.avalon.framework.service.ServiceException;
24  import org.apache.avalon.framework.service.ServiceManager;
25  import org.apache.james.Constants;
26  import org.apache.james.James;
27  import org.apache.james.core.MailImpl;
28  import org.apache.james.services.MailRepository;
29  import org.apache.james.services.MailServer;
30  import org.apache.mailet.RFC2822Headers;
31  
32  import org.apache.mailet.GenericMailet;
33  import org.apache.mailet.Mail;
34  import org.apache.mailet.MailAddress;
35  
36  import javax.mail.Header;
37  import javax.mail.MessagingException;
38  import javax.mail.internet.MimeMessage;
39  import javax.mail.internet.InternetHeaders;
40  
41  import java.util.Collection;
42  import java.util.Enumeration;
43  import java.util.HashSet;
44  import java.util.Iterator;
45  import java.util.Vector;
46  
47  /***
48   * Receives a Mail from JamesSpoolManager and takes care of delivery of the
49   * message to local inboxes or a specific repository.
50   * 
51   * Differently from LocalDelivery this does not lookup the UserRepository This
52   * simply store the message in a repository named like the local part of the
53   * recipient address.
54   * 
55   * If no repository is specified then this fallback to MailServer.getUserInbox.
56   * Otherwise you can add your own configuration for the repository
57   * 
58   * e.g: <repositoryUrl>file://var/spool/userspools/</repositoryUrl>
59   * <repositoryType>SPOOL</repositoryType>
60   * 
61   * <repositoryUrl>file://var/mail/inboxes/</repositoryUrl> <repositoryType>MAIL</repositoryType>
62   * 
63   * Header "Delivered-To" can be added to every message adding the
64   * <addDeliveryHeader>Delivered-To</addDeliveryHeader>
65   * 
66   */
67  public class ToMultiRepository extends GenericMailet {
68      /***
69       * The number of mails generated. Access needs to be synchronized for thread
70       * safety and to ensure that all threads see the latest value.
71       */
72      private static long count;
73  
74      /***
75       * The mailserver reference
76       */
77      private MailServer mailServer;
78  
79      /***
80       * The mailstore
81       */
82      private Store store;
83  
84      /***
85       * The optional repositoryUrl
86       */
87      private String repositoryUrl;
88  
89      /***
90       * The optional repositoryType
91       */
92      private String repositoryType;
93  
94      /***
95       * The delivery header
96       */
97      private String deliveryHeader;
98  
99      /***
100      * resetReturnPath
101      */
102     private boolean resetReturnPath;
103 
104     /***
105      * Delivers a mail to a local mailbox.
106      * 
107      * @param mail
108      *            the mail being processed
109      * 
110      * @throws MessagingException
111      *             if an error occurs while storing the mail
112      */
113     public void service(Mail mail) throws MessagingException {
114         Collection recipients = mail.getRecipients();
115         Collection errors = new Vector();
116 
117         MimeMessage message = null;
118         if (deliveryHeader != null || resetReturnPath) {
119             message = mail.getMessage();
120         }
121 
122         if (resetReturnPath) {
123             // Set Return-Path and remove all other Return-Path headers from the
124             // message
125             // This only works because there is a placeholder inserted by
126             // MimeMessageWrapper
127             message.setHeader(RFC2822Headers.RETURN_PATH,
128                     (mail.getSender() == null ? "<>" : "<" + mail.getSender()
129                             + ">"));
130         }
131 
132         Enumeration headers;
133         InternetHeaders deliveredTo = new InternetHeaders();
134         if (deliveryHeader != null) {
135             // Copy any Delivered-To headers from the message
136             headers = message
137                     .getMatchingHeaders(new String[] { deliveryHeader });
138             while (headers.hasMoreElements()) {
139                 Header header = (Header) headers.nextElement();
140                 deliveredTo.addHeader(header.getName(), header.getValue());
141             }
142         }
143 
144         for (Iterator i = recipients.iterator(); i.hasNext();) {
145             MailAddress recipient = (MailAddress) i.next();
146             try {
147                 if (deliveryHeader != null) {
148                     // Add qmail's de facto standard Delivered-To header
149                     message.addHeader(deliveryHeader, recipient.toString());
150                 }
151 
152                 storeMail(mail.getSender(), recipient, message);
153 
154                 if (deliveryHeader != null) {
155                     if (i.hasNext()) {
156                         // Remove headers but leave all placeholders
157                         message.removeHeader(deliveryHeader);
158                         headers = deliveredTo.getAllHeaders();
159                         // And restore any original Delivered-To headers
160                         while (headers.hasMoreElements()) {
161                             Header header = (Header) headers.nextElement();
162                             message.addHeader(header.getName(), header
163                                     .getValue());
164                         }
165                     }
166                 }
167             } catch (Exception ex) {
168                 getMailetContext().log("Error while storing mail.", ex);
169                 errors.add(recipient);
170             }
171         }
172 
173         if (!errors.isEmpty()) {
174             // If there were errors, we redirect the email to the ERROR
175             // processor.
176             // In order for this server to meet the requirements of the SMTP
177             // specification, mails on the ERROR processor must be returned to
178             // the sender. Note that this email doesn't include any details
179             // regarding the details of the failure(s).
180             // In the future we may wish to address this.
181             getMailetContext().sendMail(mail.getSender(), errors, mail.getMessage(),
182                     Mail.ERROR);
183         }
184         // We always consume this message
185         mail.setState(Mail.GHOST);
186     }
187 
188     /***
189      * Return a string describing this mailet.
190      * 
191      * @return a string describing this mailet
192      */
193     public String getMailetInfo() {
194         return "ToMultiRepository Mailet";
195     }
196 
197     /***
198      * 
199      * @param sender
200      * @param recipient
201      * @param message
202      * @throws MessagingException
203      */
204     public void storeMail(MailAddress sender, MailAddress recipient,
205             MimeMessage message) throws MessagingException {
206         String username;
207         if (recipient == null) {
208             throw new IllegalArgumentException(
209                     "Recipient for mail to be spooled cannot be null.");
210         }
211         if (message == null) {
212             throw new IllegalArgumentException(
213                     "Mail message to be spooled cannot be null.");
214         }
215         username = recipient.getUser();
216 
217         Collection recipients = new HashSet();
218         recipients.add(recipient);
219         MailImpl mail = new MailImpl(getId(), sender, recipients, message);
220         try {
221             MailRepository userInbox = getRepository(username);
222             if (userInbox == null) {
223                 StringBuffer errorBuffer = new StringBuffer(128).append(
224                         "The repository for user ").append(username).append(
225                         " was not found on this server.");
226                 throw new MessagingException(errorBuffer.toString());
227             }
228             userInbox.store(mail);
229         } finally {
230             mail.dispose();
231         }
232     }
233 
234     /***
235      * Return a new mail id.
236      * 
237      * @return a new mail id
238      */
239     public String getId() {
240         long localCount = -1;
241         synchronized (James.class) {
242             localCount = count++;
243         }
244         StringBuffer idBuffer = new StringBuffer(64).append("Mail").append(
245                 System.currentTimeMillis()).append("-").append(localCount);
246         return idBuffer.toString();
247     }
248 
249     /***
250      * @see org.apache.mailet.GenericMailet#init()
251      */
252     public void init() throws MessagingException {
253         super.init();
254         ServiceManager compMgr = (ServiceManager) getMailetContext()
255                 .getAttribute(Constants.AVALON_COMPONENT_MANAGER);
256 
257         try {
258             // Instantiate the a MailRepository for outgoing mails
259             mailServer = (MailServer) compMgr.lookup(MailServer.ROLE);
260         } catch (ServiceException cnfe) {
261             log("Failed to retrieve MailServer component:" + cnfe.getMessage());
262         } catch (Exception e) {
263             log("Failed to retrieve MailServer component:" + e.getMessage());
264         }
265 
266         try {
267             // Instantiate the a MailRepository for outgoing mails
268             store = (Store) compMgr.lookup(Store.ROLE);
269         } catch (ServiceException cnfe) {
270             log("Failed to retrieve Store component:" + cnfe.getMessage());
271         } catch (Exception e) {
272             log("Failed to retrieve Store component:" + e.getMessage());
273         }
274 
275         repositoryUrl = getInitParameter("repositoryUrl");
276         if (repositoryUrl != null) {
277             repositoryType = getInitParameter("repositoryType");
278             if (repositoryType == null)
279                 repositoryType = "MAIL";
280         }
281 
282         deliveryHeader = getInitParameter("addDeliveryHeader");
283         String resetReturnPathString = getInitParameter("resetReturnPath");
284         resetReturnPath = "true".equalsIgnoreCase(resetReturnPathString);
285     }
286 
287     /***
288      * Get the user inbox: if the repositoryUrl is null then get the userinbox
289      * from the mailserver, otherwise lookup the store with the given 
290      * repositoryurl/type
291      *   
292      * @param userName
293      * @return
294      */
295     private MailRepository getRepository(String userName) {
296         MailRepository userInbox;
297         if (repositoryUrl == null) {
298             userInbox = mailServer.getUserInbox(userName);
299         } else {
300             StringBuffer destinationBuffer = new StringBuffer(192).append(
301                     repositoryUrl).append(userName).append("/");
302             String destination = destinationBuffer.toString();
303             DefaultConfiguration mboxConf = new DefaultConfiguration(
304                     "repository", "generated:ToMultiRepository.getUserInbox()");
305             mboxConf.setAttribute("destinationURL", destination);
306             mboxConf.setAttribute("type", repositoryType);
307             try {
308                 userInbox = (MailRepository) store.select(mboxConf);
309             } catch (Exception e) {
310                 log("Cannot open repository " + e);
311                 userInbox = null;
312             }
313         }
314         return userInbox;
315     }
316 
317 }