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 java.util.ArrayList;
23  import java.util.Collection;
24  import java.util.Iterator;
25  import java.util.LinkedList;
26  import java.util.Vector;
27  
28  import javax.mail.MessagingException;
29  import javax.mail.internet.MimeMessage;
30  
31  import org.apache.james.Constants;
32  import org.apache.mailet.Mail;
33  import org.apache.mailet.MailAddress;
34  import org.apache.mailet.base.GenericMailet;
35  import org.apache.mailet.base.RFC2822Headers;
36  
37  /**
38   * Abstract base class which should get extended by classes which handle mapping
39   * operations based on VirtualUserTable implementations
40   * 
41   *
42   */
43  public abstract class AbstractVirtualUserTableMailet extends GenericMailet {
44      
45      /*
46       * (non-Javadoc)
47       * @see org.apache.mailet.base.GenericMailet#service(org.apache.mailet.Mail)
48       */
49      public void service(Mail mail) throws MessagingException {
50          Collection recipients = mail.getRecipients();
51          Collection errors = new Vector();
52  
53          MimeMessage message = mail.getMessage();
54  
55          // Set Return-Path and remove all other Return-Path headers from the
56          // message
57          // This only works because there is a placeholder inserted by
58          // MimeMessageWrapper
59          message
60                  .setHeader(RFC2822Headers.RETURN_PATH,
61                          (mail.getSender() == null ? "<>" : "<"
62                                  + mail.getSender() + ">"));
63  
64          Collection newRecipients = new LinkedList();
65          for (Iterator i = recipients.iterator(); i.hasNext();) {
66              MailAddress recipient = (MailAddress) i.next();
67              try {
68                  Collection usernames = processMail(mail.getSender(), recipient,
69                          message);
70  
71                  // if the username is null or changed we remove it from the
72                  // remaining recipients
73                  if (usernames == null) {
74                      i.remove();
75                  } else {
76                      i.remove();
77                      // if the username has been changed we add a new recipient
78                      // with the new name.
79                      newRecipients.addAll(usernames);
80                  }
81  
82              } catch (Exception ex) {
83                  getMailetContext().log("Error while storing mail.", ex);
84                  errors.add(recipient);
85              }
86          }
87  
88          if (newRecipients.size() > 0) {
89              recipients.addAll(newRecipients);
90          }
91  
92          if (!errors.isEmpty()) {
93              // If there were errors, we redirect the email to the ERROR
94              // processor.
95              // In order for this server to meet the requirements of the SMTP
96              // specification, mails on the ERROR processor must be returned to
97              // the sender. Note that this email doesn't include any details
98              // regarding the details of the failure(s).
99              // In the future we may wish to address this.
100             getMailetContext().sendMail(mail.getSender(), errors, message,
101                     Mail.ERROR);
102         }
103 
104         if (recipients.size() == 0) {
105             // We always consume this message
106             mail.setState(Mail.GHOST);
107         }
108     }
109     
110     /**
111      * Handle the given mappings to map the original recipient to the right one
112      * 
113      * @param mappings a collection of mappings for the given recipient
114      * @param sender the sender of the mail
115      * @param recipient the original recipient of the email
116      * @param message the mail message
117      * @return a collection of mapped recpient addresses
118      * 
119      * @throws MessagingException
120      */
121     protected Collection handleMappings(Collection mappings, MailAddress sender, MailAddress recipient,
122             MimeMessage message) throws MessagingException {
123         Iterator i = mappings.iterator();
124         Collection remoteRecipients = new ArrayList();
125         Collection localRecipients = new ArrayList();
126         while (i.hasNext()) {
127             String rcpt = (String) i.next();
128 
129             if (rcpt.indexOf("@") < 0) {
130                 // the mapping contains no domain name, use the default domain
131                 rcpt = rcpt + "@" + getMailetContext().getAttribute(Constants.DEFAULT_DOMAIN);
132             }
133 
134             MailAddress nextMap = new MailAddress(rcpt);
135             if (getMailetContext().isLocalServer(nextMap.getHost())) {
136                 localRecipients.add(nextMap);
137             } else {
138                 remoteRecipients.add(nextMap);
139             }
140         }
141 
142         if (remoteRecipients.size() > 0) {
143             try {
144                 getMailetContext().sendMail(sender, remoteRecipients, message);
145                 StringBuffer logBuffer = new StringBuffer(128).append("Mail for ").append(recipient).append(" forwarded to ");
146                 for (Iterator j = remoteRecipients.iterator(); j.hasNext();) {
147                     logBuffer.append(j.next());
148                     if (j.hasNext())
149                         logBuffer.append(", ");
150                 }
151                 getMailetContext().log(logBuffer.toString());
152                 return null;
153             } catch (MessagingException me) {
154                 StringBuffer logBuffer = new StringBuffer(128).append("Error forwarding mail to ");
155                 for (Iterator j = remoteRecipients.iterator(); j.hasNext();) {
156                     logBuffer.append(j.next());
157                     if (j.hasNext())
158                         logBuffer.append(", ");
159                 }
160                 logBuffer.append("attempting local delivery");
161 
162                 getMailetContext().log(logBuffer.toString());
163                 throw me;
164             }
165         }
166 
167         if (localRecipients.size() > 0) {
168             return localRecipients;
169         } else {
170             return null;
171         }
172     }
173     
174     /**
175      * Process the mail 
176      * 
177      * @param sender the sender of the mail
178      * @param recipient the recipient of the mail
179      * @param message the mail message
180      * @return collection of recipients
181      * 
182      * @throws MessagingException
183      */
184     public abstract Collection processMail(MailAddress sender, MailAddress recipient,
185             MimeMessage message) throws MessagingException;
186 }