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  package org.apache.james.smtpserver;
23  
24  import org.apache.avalon.framework.configuration.Configuration;
25  import org.apache.avalon.framework.configuration.ConfigurationException;
26  import org.apache.avalon.framework.container.ContainerUtil;
27  import org.apache.avalon.framework.service.ServiceException;
28  import org.apache.avalon.framework.service.ServiceManager;
29  import org.apache.james.Constants;
30  import org.apache.james.api.dnsservice.DNSService;
31  import org.apache.james.api.dnsservice.util.NetMatcher;
32  import org.apache.james.api.user.UsersRepository;
33  import org.apache.james.services.MailServer;
34  import org.apache.james.socket.AbstractJamesService;
35  import org.apache.james.socket.ProtocolHandler;
36  import org.apache.mailet.MailetContext;
37  
38  /**
39   * <p>Accepts SMTP connections on a server socket and dispatches them to SMTPHandlers.</p>
40   *
41   * <p>Also responsible for loading and parsing SMTP specific configuration.</p>
42   *
43   * @version 1.1.0, 06/02/2001
44   */
45  /*
46   * IMPORTANT: SMTPServer extends AbstractJamesService.  If you implement ANY
47   * lifecycle methods, you MUST call super.<method> as well.
48   */
49  public class SMTPServer extends AbstractJamesService implements SMTPServerMBean {
50  
51  
52      /**
53       * The handler chain - SMTPhandlers can lookup handlerchain to obtain
54       * Command handlers , Message handlers and connection handlers
55       */
56      SMTPHandlerChain handlerChain = new SMTPHandlerChain();
57  
58      /**
59       * The mailet context - we access it here to set the hello name for the Mailet API
60       */
61      MailetContext mailetcontext;
62  
63      /**
64       * The user repository for this server - used to authenticate
65       * users.
66       */
67      private UsersRepository users;
68  
69      /**
70       * The internal mail server service.
71       */
72      private MailServer mailServer;
73      
74      /**
75       * The DNSService to use for queries
76       */
77      private DNSService dnsServer;
78      
79      /**
80       * Whether authentication is required to use
81       * this SMTP server.
82       */
83      private final static int AUTH_DISABLED = 0;
84      private final static int AUTH_REQUIRED = 1;
85      private final static int AUTH_ANNOUNCE = 2;
86      private int authRequired = AUTH_DISABLED;
87  
88      /**
89       * Whether the server verifies that the user
90       * actually sending an email matches the
91       * authentication credentials attached to the
92       * SMTP interaction.
93       */
94      private boolean verifyIdentity = false;
95  
96      /**
97       * Whether the server needs helo to be send first
98       */
99      private boolean heloEhloEnforcement = false;
100     
101     /**
102      * SMTPGreeting to use 
103      */
104     private String smtpGreeting = null;
105     
106     /**
107      * This is a Network Matcher that should be configured to contain
108      * authorized networks that bypass SMTP AUTH requirements.
109      */
110     private NetMatcher authorizedNetworks = null;
111 
112     /**
113      * The maximum message size allowed by this SMTP server.  The default
114      * value, 0, means no limit.
115      */
116     private long maxMessageSize = 0;
117 
118     /**
119      * The number of bytes to read before resetting
120      * the connection timeout timer.  Defaults to
121      * 20 KB.
122      */
123     private int lengthReset = 20 * 1024;
124 
125     /**
126      * The configuration data to be passed to the handler
127      */
128     private SMTPHandlerConfigurationData theConfigData
129         = new SMTPHandlerConfigurationDataImpl();
130 
131     private ServiceManager serviceManager;
132 
133     private boolean addressBracketsEnforcement = true;
134 
135     /**
136      * @see org.apache.avalon.framework.service.Serviceable#service(ServiceManager)
137      */
138     public void service( final ServiceManager manager ) throws ServiceException {
139         super.service( manager );
140         serviceManager = manager;
141         mailetcontext = (MailetContext) manager.lookup(MailetContext.class.getName());
142         mailServer = (MailServer) manager.lookup(MailServer.ROLE);
143         users = (UsersRepository) manager.lookup(UsersRepository.ROLE);
144         dnsServer = (DNSService) manager.lookup(DNSService.ROLE); 
145     }
146 
147     /**
148      * @see org.apache.avalon.framework.configuration.Configurable#configure(Configuration)
149      */
150     public void configure(final Configuration configuration) throws ConfigurationException {
151         super.configure(configuration);
152         String hello = (String) mailetcontext.getAttribute(Constants.HELLO_NAME);
153         
154         if (isEnabled()) {
155             // TODO Remove this in next not backwards compatible release!
156             if (hello == null) mailetcontext.setAttribute(Constants.HELLO_NAME, helloName);
157             
158             Configuration handlerConfiguration = configuration.getChild("handler");
159             String authRequiredString = handlerConfiguration.getChild("authRequired").getValue("false").trim().toLowerCase();
160             if (authRequiredString.equals("true")) authRequired = AUTH_REQUIRED;
161             else if (authRequiredString.equals("announce")) authRequired = AUTH_ANNOUNCE;
162             else authRequired = AUTH_DISABLED;
163             verifyIdentity = handlerConfiguration.getChild("verifyIdentity").getValueAsBoolean(false);
164             if (authRequired != AUTH_DISABLED) {
165                 if (verifyIdentity) {
166                     getLogger().info("This SMTP server requires authentication and verifies that the authentication credentials match the sender address.");
167                 } else {
168                     getLogger().info("This SMTP server requires authentication, but doesn't verify that the authentication credentials match the sender address.");
169                 }
170             } else {
171                 getLogger().info("This SMTP server does not require authentication.");
172             }
173 
174             String authorizedAddresses = handlerConfiguration.getChild("authorizedAddresses").getValue(null);
175             if (authRequired == AUTH_DISABLED && authorizedAddresses == null) {
176                 /* if SMTP AUTH is not requred then we will use
177                  * authorizedAddresses to determine whether or not to
178                  * relay e-mail.  Therefore if SMTP AUTH is not
179                  * required, we will not relay e-mail unless the
180                  * sending IP address is authorized.
181                  *
182                  * Since this is a change in behavior for James v2,
183                  * create a default authorizedAddresses network of
184                  * 0.0.0.0/0, which matches all possible addresses, thus
185                  * preserving the current behavior.
186                  *
187                  * James v3 should require the <authorizedAddresses>
188                  * element.
189                  */
190                 authorizedAddresses = "0.0.0.0/0.0.0.0";
191             }
192 
193             if (authorizedAddresses != null) {
194                 java.util.StringTokenizer st = new java.util.StringTokenizer(authorizedAddresses, ", ", false);
195                 java.util.Collection networks = new java.util.ArrayList();
196                 while (st.hasMoreTokens()) {
197                     String addr = st.nextToken();
198                     networks.add(addr);
199                 }
200                 authorizedNetworks = new NetMatcher(networks,dnsServer);
201             }
202 
203             if (authorizedNetworks != null) {
204                 getLogger().info("Authorized addresses: " + authorizedNetworks.toString());
205             }
206 
207             // get the message size limit from the conf file and multiply
208             // by 1024, to put it in bytes
209             maxMessageSize = handlerConfiguration.getChild( "maxmessagesize" ).getValueAsLong( maxMessageSize ) * 1024;
210             if (maxMessageSize > 0) {
211                 getLogger().info("The maximum allowed message size is " + maxMessageSize + " bytes.");
212             } else {
213                 getLogger().info("No maximum message size is enforced for this server.");
214             }
215             // How many bytes to read before updating the timer that data is being transfered
216             lengthReset = configuration.getChild("lengthReset").getValueAsInteger(lengthReset);
217             if (lengthReset <= 0) {
218                 throw new ConfigurationException("The configured value for the idle timeout reset, " + lengthReset + ", is not valid.");
219             }
220             if (getLogger().isInfoEnabled()) {
221                 getLogger().info("The idle timeout will be reset every " + lengthReset + " bytes.");
222             }
223             
224             heloEhloEnforcement = handlerConfiguration.getChild("heloEhloEnforcement").getValueAsBoolean(true);
225             
226             if (authRequiredString.equals("true")) authRequired = AUTH_REQUIRED;
227             
228             // get the smtpGreeting
229             smtpGreeting = handlerConfiguration.getChild("smtpGreeting").getValue(null);
230             
231             addressBracketsEnforcement = handlerConfiguration.getChild("addressBracketsEnforcement").getValueAsBoolean(true);
232 
233             //set the logger
234             ContainerUtil.enableLogging(handlerChain,getLogger());
235            
236             try {
237                 ContainerUtil.service(handlerChain,serviceManager);
238             } catch (ServiceException e) {
239                 if (getLogger().isErrorEnabled()) {
240                     getLogger().error("Failed to service handlerChain",e);
241                 }
242                 throw new ConfigurationException("Failed to service handlerChain");
243             }
244             
245             //read from the XML configuration and create and configure each of the handlers
246             ContainerUtil.configure(handlerChain,handlerConfiguration.getChild("handlerchain"));
247 
248         } else {
249             // TODO Remove this in next not backwards compatible release!
250             if (hello == null) mailetcontext.setAttribute(Constants.HELLO_NAME, "localhost");
251         }
252     }
253     
254     /**
255      * @see org.apache.james.socket.AbstractJamesService#initialize()
256      */
257     public void initialize() throws Exception {
258         super.initialize();
259         ContainerUtil.initialize(handlerChain);
260     }
261 
262     /**
263      * @see org.apache.james.socket.AbstractJamesService#getDefaultPort()
264      */
265      protected int getDefaultPort() {
266         return 25;
267      }
268 
269     /**
270      * @see org.apache.james.socket.AbstractJamesService#getServiceType()
271      */
272     public String getServiceType() {
273         return "SMTP Service";
274     }
275 
276     /**
277      * @see org.apache.james.socket.AbstractJamesService#newProtocolHandlerInstance()
278      */
279     public ProtocolHandler newProtocolHandlerInstance() {
280         SMTPHandler theHandler = new SMTPHandler();
281         //pass the handler chain to every SMTPhandler
282         theHandler.setHandlerChain(handlerChain);
283         return theHandler;
284     }
285 
286     /**
287      * A class to provide SMTP handler configuration to the handlers
288      */
289     private class SMTPHandlerConfigurationDataImpl
290         implements SMTPHandlerConfigurationData {
291 
292         /**
293          * @see org.apache.james.smtpserver.SMTPHandlerConfigurationData#getHelloName()
294          */
295         public String getHelloName() {
296             if (SMTPServer.this.helloName == null) {
297                 return SMTPServer.this.mailServer.getHelloName();
298             } else {
299                 return SMTPServer.this.helloName;
300             }
301         }
302 
303         /**
304          * @see org.apache.james.smtpserver.SMTPHandlerConfigurationData#getResetLength()
305          */
306         public int getResetLength() {
307             return SMTPServer.this.lengthReset;
308         }
309 
310         /**
311          * @see org.apache.james.smtpserver.SMTPHandlerConfigurationData#getMaxMessageSize()
312          */
313         public long getMaxMessageSize() {
314             return SMTPServer.this.maxMessageSize;
315         }
316 
317         /**
318          * @see org.apache.james.smtpserver.SMTPHandlerConfigurationData#isAuthRequired(String)
319          */
320         public boolean isRelayingAllowed(String remoteIP) {
321             boolean relayingAllowed = false;
322             if (authorizedNetworks != null) {
323                 relayingAllowed = SMTPServer.this.authorizedNetworks.matchInetNetwork(remoteIP);
324             }
325             return relayingAllowed;
326         }
327 
328         /**
329          * @see org.apache.james.smtpserver.SMTPHandlerConfigurationData#isAuthRequired(String)
330          */
331         public boolean isAuthRequired(String remoteIP) {
332               if (SMTPServer.this.authRequired == AUTH_ANNOUNCE) return true;
333             boolean authRequired = SMTPServer.this.authRequired != AUTH_DISABLED;
334             if (authorizedNetworks != null) {
335                 authRequired = authRequired && !SMTPServer.this.authorizedNetworks.matchInetNetwork(remoteIP);
336             }
337             return authRequired;
338         }
339 
340         /**
341          * @see org.apache.james.smtpserver.SMTPHandlerConfigurationData#isAuthRequired()
342          */
343         public boolean isAuthRequired() {
344             return SMTPServer.this.authRequired != AUTH_DISABLED;
345         }
346 
347         /**
348          * @see org.apache.james.smtpserver.SMTPHandlerConfigurationData#isVerifyIdentity()
349          */
350         public boolean isVerifyIdentity() {
351             return SMTPServer.this.verifyIdentity;
352         }
353 
354         /**
355          * @see org.apache.james.smtpserver.SMTPHandlerConfigurationData#getMailServer()
356          */
357         public MailServer getMailServer() {
358             return SMTPServer.this.mailServer;
359         }
360 
361         /**
362          * @see org.apache.james.smtpserver.SMTPHandlerConfigurationData#getUsersRepository()
363          */
364         public UsersRepository getUsersRepository() {
365             return SMTPServer.this.users;
366         }
367         
368         /**
369          * @see org.apache.james.smtpserver.SMTPHandlerConfigurationData#useHeloEhloEnforcement()
370          */
371         public boolean useHeloEhloEnforcement() {
372             return SMTPServer.this.heloEhloEnforcement;
373         }
374         
375         
376         /**
377          * @see org.apache.james.smtpserver.SMTPHandlerConfigurationData#getSMTPGreeting()
378          */
379         public String getSMTPGreeting() {
380             return SMTPServer.this.smtpGreeting;
381         }
382 
383         /**
384          * @see org.apache.james.smtpserver.SMTPHandlerConfigurationData#useAddressBracketsEnforcement()
385          */
386     public boolean useAddressBracketsEnforcement() {
387         // TODO Auto-generated method stub
388         return SMTPServer.this.addressBracketsEnforcement;
389     }
390         
391         //TODO: IF we create here an interface to get DNSService
392         //      we should access it from the SMTPHandlers
393 
394     }
395 
396     /**
397      * @see org.apache.james.socket.AbstractJamesService#getConfigurationData()
398      */
399     protected Object getConfigurationData() {
400         return theConfigData;
401     }
402 }