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  
21  
22  
23  package org.apache.james.smtpserver.core.filter.fastfail;
24  
25  import java.util.ArrayList;
26  import java.util.Collection;
27  import java.util.Iterator;
28  import java.util.StringTokenizer;
29  
30  import org.apache.avalon.framework.configuration.Configurable;
31  import org.apache.avalon.framework.configuration.Configuration;
32  import org.apache.avalon.framework.configuration.ConfigurationException;
33  import org.apache.avalon.framework.logger.AbstractLogEnabled;
34  import org.apache.avalon.framework.service.ServiceException;
35  import org.apache.avalon.framework.service.ServiceManager;
36  import org.apache.avalon.framework.service.Serviceable;
37  import org.apache.james.api.vut.ErrorMappingException;
38  import org.apache.james.api.vut.VirtualUserTable;
39  import org.apache.james.api.vut.VirtualUserTableStore;
40  import org.apache.james.dsn.DSNStatus;
41  import org.apache.james.smtpserver.CommandHandler;
42  import org.apache.james.smtpserver.SMTPSession;
43  import org.apache.mailet.MailAddress;
44  import org.apache.oro.text.regex.MalformedPatternException;
45  import org.apache.oro.text.regex.Pattern;
46  import org.apache.oro.text.regex.Perl5Compiler;
47  import org.apache.oro.text.regex.Perl5Matcher;
48  
49  /**
50   * Handler which reject invalid recipients
51   */
52  public class ValidRcptHandler extends AbstractLogEnabled implements CommandHandler, Configurable, Serviceable {
53      
54      private Collection recipients = new ArrayList();
55      private Collection domains = new ArrayList();
56      private Collection regex = new ArrayList();
57      private boolean vut = true;
58      private VirtualUserTable table;
59      private String tableName = null;
60      
61      /**
62       * @see org.apache.avalon.framework.service.Serviceable#service(org.apache.avalon.framework.service.ServiceManager)
63       */
64      public void service(ServiceManager arg0) throws ServiceException {
65          if (tableName == null || tableName.equals("")) {
66              table = (VirtualUserTable) arg0.lookup(VirtualUserTable.ROLE); 
67          } else {
68              table = ((VirtualUserTableStore) arg0.lookup(VirtualUserTableStore.ROLE)).getTable(tableName);
69          }
70      }
71      
72      /**
73       * @see org.apache.avalon.framework.configuration.Configurable#configure(Configuration)
74       */
75      public void configure(Configuration arg0) throws ConfigurationException {
76          Configuration recipientsConfig = arg0.getChild("validRecipients");
77          if (recipientsConfig != null) {
78              setValidRecipients(recipientsConfig.getValue());
79          }
80          
81          Configuration domainConfig = arg0.getChild("validDomains");        
82          if (domainConfig != null) {
83              setValidDomains(domainConfig.getValue());
84          }
85          
86          Configuration regexConfig = arg0.getChild("validRegexPattern");        
87          if (regexConfig != null) {
88              try {
89                  setValidRegex(regexConfig.getValue());
90              } catch(MalformedPatternException mpe) {
91                  throw new ConfigurationException("Malformed pattern: ", mpe);
92              }
93          }
94          Configuration vutConfig = arg0.getChild("enableVirtualUserTable");
95          
96          if (vutConfig != null) {
97              vut = vutConfig.getValueAsBoolean(true);    
98          }
99          Configuration tableConfig = arg0.getChild("table");
100         
101         if (tableConfig != null) {
102             tableName = tableConfig.getValue(null);   
103         }
104     }
105     
106     /**
107      * Set the valid recipients. 
108      * 
109      * @param recip The valid recipients. Commaseperated list
110      */
111     public void setValidRecipients(String recip) {
112         StringTokenizer st = new StringTokenizer(recip, ", ", false);
113         
114         while (st.hasMoreTokens()) {
115             String recipient = st.nextToken().toLowerCase();
116             
117             getLogger().debug("Add recipient to valid recipients: " + recipient);
118             recipients.add(recipient);
119         }
120     }
121 
122     /**
123      * Set the domains for which every rcpt will be accepted. 
124      * 
125      * @param dom The valid domains. Commaseperated list
126      */
127     public void setValidDomains(String dom) {
128         StringTokenizer st = new StringTokenizer(dom, ", ", false);
129         
130         while (st.hasMoreTokens()) {
131             String domain = st.nextToken().toLowerCase();
132             getLogger().debug("Add domain to valid domains: " + domain);
133             domains.add(domain);
134         }  
135     }
136     
137     /**
138      * 
139      * @param reg 
140      * @throws MalformedPatternException
141      */
142     public void setValidRegex(String reg) throws MalformedPatternException {
143         Perl5Compiler compiler = new Perl5Compiler();
144         
145         StringTokenizer st = new StringTokenizer(reg, ", ", false);
146         
147         while (st.hasMoreTokens()) {
148             String patternString = st.nextToken().trim();
149 
150             getLogger().debug("Add regex to valid regex: " + patternString);
151             
152             Pattern pattern = compiler.compile(patternString, Perl5Compiler.READ_ONLY_MASK);
153             regex.add(pattern);
154 
155         }  
156     }
157     
158     public void setVirtualUserTableSupport(boolean vut) {
159         this.vut = vut;
160     }
161 
162     /**
163      * @see org.apache.james.smtpserver.CommandHandler#getImplCommands()
164      */
165     public Collection getImplCommands() {
166         Collection c = new ArrayList();
167         c.add("RCPT");
168             
169         return c;
170     }
171 
172     /**
173      * @see org.apache.james.smtpserver.CommandHandler#onCommand(SMTPSession)
174      */
175     public void onCommand(SMTPSession session) {
176         if (!session.isRelayingAllowed() && !(session.isAuthRequired() && session.getUser() != null)) {
177             checkValidRcpt(session);
178         } else {
179             getLogger().debug("Sender allowed");
180         }
181     }
182     
183     
184     
185     /**
186      * Check if the recipient should be accepted
187      * 
188      * @param session The SMTPSession
189      */
190     private void checkValidRcpt(SMTPSession session) {
191         MailAddress rcpt = (MailAddress) session.getState().get(SMTPSession.CURRENT_RECIPIENT);
192         boolean invalidUser = true;
193 
194         if (session.getConfigurationData().getUsersRepository().contains(rcpt.getUser()) == true || recipients.contains(rcpt.toString().toLowerCase()) || domains.contains(rcpt.getHost().toLowerCase())) {
195             invalidUser = false;
196         }
197 
198         // check if an valid virtual mapping exists
199         if (invalidUser == true  && vut == true) {
200             try {
201                 Collection targetString = table.getMappings(rcpt.getUser(), rcpt.getHost());
202         
203                 if (targetString != null && targetString.isEmpty() == false) {
204                     invalidUser = false;
205                 }
206             } catch (ErrorMappingException e) {
207         
208                 String responseString = e.getMessage();
209                 
210                 getLogger().info("Rejected message. Reject Message: " + responseString);
211             
212                 session.writeResponse(responseString);
213                 session.setStopHandlerProcessing(true);
214             }
215         }
216         
217         if (invalidUser == true && !regex.isEmpty()) {
218             Iterator reg = regex.iterator();
219             Perl5Matcher matcher  = new Perl5Matcher();
220             
221             while (reg.hasNext()) {
222                 if (matcher.matches(rcpt.toString(), (Pattern) reg.next())) {
223                     // regex match
224                     invalidUser = false;
225                     break;
226                 }
227             }
228         }
229     
230         if (invalidUser == true) {
231             //user not exist
232             String responseString = "554 " + DSNStatus.getStatus(DSNStatus.PERMANENT,DSNStatus.ADDRESS_MAILBOX) + " Unknown user: " + rcpt.toString();
233         
234             getLogger().info("Rejected message. Unknown user: " + rcpt.toString());
235         
236             session.writeResponse(responseString);
237             session.setStopHandlerProcessing(true);
238         }
239     }
240 }