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.framework.service.ServiceException;
21  import org.apache.avalon.framework.service.ServiceManager;
22  import org.apache.james.Constants;
23  import org.apache.james.services.JamesUser;
24  import org.apache.james.services.User;
25  import org.apache.james.services.UsersRepository;
26  import org.apache.james.services.UsersStore;
27  import org.apache.mailet.RFC2822Headers;
28  
29  import org.apache.mailet.GenericMailet;
30  import org.apache.mailet.Mail;
31  import org.apache.mailet.MailAddress;
32  
33  import javax.mail.MessagingException;
34  import javax.mail.internet.MimeMessage;
35  
36  import java.util.Collection;
37  import java.util.HashSet;
38  import java.util.Iterator;
39  import java.util.LinkedList;
40  import java.util.Vector;
41  
42  /***
43   * Receives a Mail from JamesSpoolManager and takes care of delivery of the
44   * message to local inboxes.
45   * 
46   * Available configurations are:
47   * 
48   * <enableAliases>true</enableAliases>: specify wether the user aliases should
49   * be looked up or not. Default is false.
50   * 
51   * <enableForwarding>true</enableForwarding>: enable the forwarding. Default to
52   * false.
53   * 
54   * <usersRepository>LocalAdmins</usersRepository>: specific users repository
55   * name. Default to empty. If empty does lookup the default userRepository.
56   */
57  public class UsersRepositoryAliasingForwarding extends GenericMailet {
58  
59      /***
60       * The user repository for this mail server. Contains all the users with
61       * inboxes on this server.
62       */
63      private UsersRepository usersRepository;
64  
65      /***
66       * Whether to enable aliasing for users on this server
67       */
68      private boolean enableAliases;
69  
70      /***
71       * Whether to enable forwarding for users on this server
72       */
73      private boolean enableForwarding;
74  
75      /***
76       * Whether to ignore case when looking up user names on this server
77       */
78      private boolean ignoreCase;
79  
80      /***
81       * Delivers a mail to a local mailbox.
82       * 
83       * @param mail
84       *            the mail being processed
85       * 
86       * @throws MessagingException
87       *             if an error occurs while storing the mail
88       */
89      public void service(Mail mail) throws MessagingException {
90          Collection recipients = mail.getRecipients();
91          Collection errors = new Vector();
92  
93          MimeMessage message = mail.getMessage();
94  
95          // Set Return-Path and remove all other Return-Path headers from the
96          // message
97          // This only works because there is a placeholder inserted by
98          // MimeMessageWrapper
99          message
100                 .setHeader(RFC2822Headers.RETURN_PATH,
101                         (mail.getSender() == null ? "<>" : "<"
102                                 + mail.getSender() + ">"));
103 
104         Collection newRecipients = new LinkedList();
105         for (Iterator i = recipients.iterator(); i.hasNext();) {
106             MailAddress recipient = (MailAddress) i.next();
107             try {
108                 String username = processMail(mail.getSender(), recipient,
109                         message);
110 
111                 // if the username is null or changed we remove it from the
112                 // remaining recipients
113                 if (username == null) {
114                     i.remove();
115                 } else if (!username.equals(recipient.getUser())) {
116                     i.remove();
117                     // if the username has been changed we add a new recipient
118                     // with the new name.
119                     newRecipients.add(new MailAddress(username, recipient
120                             .getHost()));
121                 }
122 
123             } catch (Exception ex) {
124                 getMailetContext().log("Error while storing mail.", ex);
125                 errors.add(recipient);
126             }
127         }
128 
129         if (newRecipients.size() > 0) {
130             recipients.addAll(newRecipients);
131         }
132 
133         if (!errors.isEmpty()) {
134             // If there were errors, we redirect the email to the ERROR
135             // processor.
136             // In order for this server to meet the requirements of the SMTP
137             // specification, mails on the ERROR processor must be returned to
138             // the sender. Note that this email doesn't include any details
139             // regarding the details of the failure(s).
140             // In the future we may wish to address this.
141             getMailetContext().sendMail(mail.getSender(), errors, message,
142                     Mail.ERROR);
143         }
144 
145         if (recipients.size() == 0) {
146             // We always consume this message
147             mail.setState(Mail.GHOST);
148         }
149     }
150 
151     /***
152      * Return a string describing this mailet.
153      * 
154      * @return a string describing this mailet
155      */
156     public String getMailetInfo() {
157         return "Local User Aliasing and Forwarding Mailet";
158     }
159 
160     /***
161      * Return null when the mail should be GHOSTed, the username string when it
162      * should be changed due to the ignoreUser configuration.
163      * 
164      * @param sender
165      * @param recipient
166      * @param message
167      * @throws MessagingException
168      */
169     public String processMail(MailAddress sender, MailAddress recipient,
170             MimeMessage message) throws MessagingException {
171         String username;
172         if (recipient == null) {
173             throw new IllegalArgumentException(
174                     "Recipient for mail to be spooled cannot be null.");
175         }
176         if (message == null) {
177             throw new IllegalArgumentException(
178                     "Mail message to be spooled cannot be null.");
179         }
180         if (ignoreCase) {
181             String originalUsername = recipient.getUser();
182             username = usersRepository.getRealName(originalUsername);
183             if (username == null) {
184                 StringBuffer errorBuffer = new StringBuffer(128).append(
185                         "The inbox for user ").append(originalUsername).append(
186                         " was not found on this server.");
187                 throw new MessagingException(errorBuffer.toString());
188             }
189         } else {
190             username = recipient.getUser();
191         }
192         User user;
193         if (enableAliases || enableForwarding) {
194             user = usersRepository.getUserByName(username);
195             if (user instanceof JamesUser) {
196                 if (enableAliases && ((JamesUser) user).getAliasing()) {
197                     username = ((JamesUser) user).getAlias();
198                 }
199                 // Forwarding takes precedence over local aliases
200                 if (enableForwarding && ((JamesUser) user).getForwarding()) {
201                     MailAddress forwardTo = ((JamesUser) user).getForwardingDestination();
202                     if (forwardTo == null) {
203                         StringBuffer errorBuffer = new StringBuffer(128)
204                                 .append("Forwarding was enabled for ")
205                                 .append(username)
206                                 .append(
207                                         " but no forwarding address was set for this account.");
208                         throw new MessagingException(errorBuffer.toString());
209                     }
210                     Collection recipients = new HashSet();
211                     recipients.add(forwardTo);
212                     try {
213                         getMailetContext().sendMail(sender, recipients, message);
214                         StringBuffer logBuffer = new StringBuffer(128).append(
215                                 "Mail for ").append(username).append(
216                                 " forwarded to ").append(forwardTo.toString());
217                         getMailetContext().log(logBuffer.toString());
218                         return null;
219                     } catch (MessagingException me) {
220                         StringBuffer logBuffer = new StringBuffer(128).append(
221                                 "Error forwarding mail to ").append(
222                                 forwardTo.toString()).append(
223                                 "attempting local delivery");
224                         getMailetContext().log(logBuffer.toString());
225                         throw me;
226                     }
227                 }
228             }
229         }
230         return username;
231     }
232 
233     /***
234      * @see org.apache.mailet.GenericMailet#init()
235      */
236     public void init() throws MessagingException {
237         super.init();
238         ServiceManager compMgr = (ServiceManager) getMailetContext()
239                 .getAttribute(Constants.AVALON_COMPONENT_MANAGER);
240 
241         UsersStore usersStore;
242         try {
243             usersStore = (UsersStore) compMgr.lookup(UsersStore.ROLE);
244 
245 
246             enableAliases = new Boolean(getInitParameter("enableAliases",
247                     getMailetContext().getAttribute(Constants.DEFAULT_ENABLE_ALIASES).toString()
248                     )).booleanValue();
249             enableForwarding = new Boolean(getInitParameter("enableForwarding",
250                     getMailetContext().getAttribute(Constants.DEFAULT_ENABLE_FORWARDING).toString()
251                     )).booleanValue();
252             ignoreCase = new Boolean(getInitParameter("ignoreCase",
253                     getMailetContext().getAttribute(Constants.DEFAULT_IGNORE_USERNAME_CASE).toString()
254                     )).booleanValue();
255             
256             String userRep = getInitParameter("usersRepository");
257             if (userRep == null || userRep.length() == 0) {
258                 try {
259                     usersRepository = (UsersRepository) compMgr
260                             .lookup(UsersRepository.ROLE);
261                 } catch (ServiceException e) {
262                     log("Failed to retrieve UsersRepository component:"
263                             + e.getMessage());
264                 }
265             } else {
266                 usersRepository = usersStore.getRepository(userRep);
267             }
268 
269         } catch (ServiceException cnfe) {
270             log("Failed to retrieve UsersStore component:" + cnfe.getMessage());
271         }
272 
273     }
274 
275 }