View Javadoc

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