KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > james > smtpserver > SMTPServer


1 /***********************************************************************
2  * Copyright (c) 2000-2004 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.smtpserver;
19
20 import org.apache.avalon.cornerstone.services.connection.ConnectionHandler;
21 import org.apache.avalon.excalibur.pool.DefaultPool;
22 import org.apache.avalon.excalibur.pool.HardResourceLimitingPool;
23 import org.apache.avalon.excalibur.pool.ObjectFactory;
24 import org.apache.avalon.excalibur.pool.Pool;
25 import org.apache.avalon.excalibur.pool.Poolable;
26 import org.apache.avalon.framework.activity.Disposable;
27 import org.apache.avalon.framework.activity.Initializable;
28 import org.apache.avalon.framework.component.ComponentException;
29 import org.apache.avalon.framework.component.ComponentManager;
30 import org.apache.avalon.framework.configuration.Configuration;
31 import org.apache.avalon.framework.configuration.ConfigurationException;
32 import org.apache.avalon.framework.component.Component;
33 import org.apache.avalon.framework.activity.Disposable;
34 import org.apache.avalon.framework.logger.LogEnabled;
35 import org.apache.james.Constants;
36 import org.apache.james.core.AbstractJamesService;
37 import org.apache.james.services.MailServer;
38 import org.apache.james.services.UsersRepository;
39 import org.apache.james.services.UsersStore;
40 import org.apache.james.util.NetMatcher;
41 import org.apache.james.util.watchdog.Watchdog;
42 import org.apache.james.util.watchdog.WatchdogFactory;
43 import org.apache.mailet.MailetContext;
44
45 /**
46  * <p>Accepts SMTP connections on a server socket and dispatches them to SMTPHandlers.</p>
47  *
48  * <p>Also responsible for loading and parsing SMTP specific configuration.</p>
49  *
50  * @version 1.1.0, 06/02/2001
51  */

52 /*
53  * IMPORTANT: SMTPServer extends AbstractJamesService. If you implement ANY
54  * lifecycle methods, you MUST call super.<method> as well.
55  */

56 public class SMTPServer extends AbstractJamesService implements Component, SMTPServerMBean {
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      * Whether authentication is required to use
76      * this SMTP server.
77      */

78     private boolean authRequired = false;
79
80     /**
81      * Whether the server verifies that the user
82      * actually sending an email matches the
83      * authentication credentials attached to the
84      * SMTP interaction.
85      */

86     private boolean verifyIdentity = false;
87
88     /**
89      * This is a Network Matcher that should be configured to contain
90      * authorized networks that bypass SMTP AUTH requirements.
91      */

92     private NetMatcher authorizedNetworks = null;
93
94     /**
95      * The maximum message size allowed by this SMTP server. The default
96      * value, 0, means no limit.
97      */

98     private long maxMessageSize = 0;
99
100     /**
101      * The number of bytes to read before resetting
102      * the connection timeout timer. Defaults to
103      * 20 KB.
104      */

105     private int lengthReset = 20 * 1024;
106
107     /**
108      * The pool used to provide SMTP Handler objects
109      */

110     private Pool theHandlerPool = null;
111
112     /**
113      * The pool used to provide SMTP Handler objects
114      */

115     private ObjectFactory theHandlerFactory = new SMTPHandlerFactory();
116
117     /**
118      * The factory used to generate Watchdog objects
119      */

120     private WatchdogFactory theWatchdogFactory;
121
122     /**
123      * The configuration data to be passed to the handler
124      */

125     private SMTPHandlerConfigurationData theConfigData
126         = new SMTPHandlerConfigurationDataImpl();
127
128     /**
129      * @see org.apache.avalon.framework.component.Composable#compose(ComponentManager)
130      */

131     public void compose(final ComponentManager componentManager) throws ComponentException {
132         super.compose(componentManager);
133         mailetcontext = (MailetContext) componentManager.lookup("org.apache.mailet.MailetContext");
134         mailServer = (MailServer) componentManager.lookup("org.apache.james.services.MailServer");
135         UsersStore usersStore =
136             (UsersStore) componentManager.lookup("org.apache.james.services.UsersStore");
137         users = usersStore.getRepository("LocalUsers");
138         if (users == null) {
139             throw new ComponentException("The user repository could not be found.");
140         }
141     }
142
143     /**
144      * @see org.apache.avalon.framework.configuration.Configurable#configure(Configuration)
145      */

146     public void configure(final Configuration configuration) throws ConfigurationException {
147         super.configure(configuration);
148         if (isEnabled()) {
149             mailetcontext.setAttribute(Constants.HELLO_NAME, helloName);
150             Configuration handlerConfiguration = configuration.getChild("handler");
151             authRequired = handlerConfiguration.getChild("authRequired").getValueAsBoolean(false);
152             verifyIdentity = handlerConfiguration.getChild("verifyIdentity").getValueAsBoolean(false);
153             if (authRequired) {
154                 if (verifyIdentity) {
155                     getLogger().info("This SMTP server requires authentication and verifies that the authentication credentials match the sender address.");
156                 } else {
157                     getLogger().info("This SMTP server requires authentication, but doesn't verify that the authentication credentials match the sender address.");
158                 }
159             } else {
160                 getLogger().info("This SMTP server does not require authentication.");
161             }
162
163             String JavaDoc authorizedAddresses = handlerConfiguration.getChild("authorizedAddresses").getValue(null);
164             if (!authRequired && authorizedAddresses == null) {
165                 /* if SMTP AUTH is not requred then we will use
166                  * authorizedAddresses to determine whether or not to
167                  * relay e-mail. Therefore if SMTP AUTH is not
168                  * required, we will not relay e-mail unless the
169                  * sending IP address is authorized.
170                  *
171                  * Since this is a change in behavior for James v2,
172                  * create a default authorizedAddresses network of
173                  * 0.0.0.0/0, which matches all possible addresses, thus
174                  * preserving the current behavior.
175                  *
176                  * James v3 should require the <authorizedAddresses>
177                  * element.
178                  */

179                 authorizedAddresses = "0.0.0.0/0.0.0.0";
180             }
181
182             if (authorizedAddresses != null) {
183                 java.util.StringTokenizer JavaDoc st = new java.util.StringTokenizer JavaDoc(authorizedAddresses, ", ", false);
184                 java.util.Collection JavaDoc networks = new java.util.ArrayList JavaDoc();
185                 while (st.hasMoreTokens()) {
186                     String JavaDoc addr = st.nextToken();
187                     networks.add(addr);
188                 }
189                 authorizedNetworks = new NetMatcher(networks);
190             }
191
192             if (authorizedNetworks != null) {
193                 getLogger().info("Authorized addresses: " + authorizedNetworks.toString());
194             }
195
196             // get the message size limit from the conf file and multiply
197
// by 1024, to put it in bytes
198
maxMessageSize = handlerConfiguration.getChild( "maxmessagesize" ).getValueAsLong( maxMessageSize ) * 1024;
199             if (maxMessageSize > 0) {
200                 getLogger().info("The maximum allowed message size is " + maxMessageSize + " bytes.");
201             } else {
202                 getLogger().info("No maximum message size is enforced for this server.");
203             }
204             // How many bytes to read before updating the timer that data is being transfered
205
lengthReset = configuration.getChild("lengthReset").getValueAsInteger(lengthReset);
206             if (lengthReset <= 0) {
207                 throw new ConfigurationException("The configured value for the idle timeout reset, " + lengthReset + ", is not valid.");
208             }
209             if (getLogger().isInfoEnabled()) {
210                 getLogger().info("The idle timeout will be reset every " + lengthReset + " bytes.");
211             }
212         } else {
213             mailetcontext.setAttribute(Constants.HELLO_NAME, "localhost");
214         }
215     }
216
217     /**
218      * @see org.apache.avalon.framework.activity.Initializable#initialize()
219      */

220     public void initialize() throws Exception JavaDoc {
221         super.initialize();
222         if (!isEnabled()) {
223             return;
224         }
225
226         if (connectionLimit != null) {
227             theHandlerPool = new HardResourceLimitingPool(theHandlerFactory, 5, connectionLimit.intValue());
228             if (getLogger().isDebugEnabled()) {
229                 getLogger().debug("Using a bounded pool for SMTP handlers with upper limit " + connectionLimit.intValue());
230             }
231         } else {
232             // NOTE: The maximum here is not a real maximum. The handler pool will continue to
233
// provide handlers beyond this value.
234
theHandlerPool = new DefaultPool(theHandlerFactory, null, 5, 30);
235             getLogger().debug("Using an unbounded pool for SMTP handlers.");
236         }
237         if (theHandlerPool instanceof LogEnabled) {
238             ((LogEnabled)theHandlerPool).enableLogging(getLogger());
239         }
240         if (theHandlerPool instanceof Initializable) {
241             ((Initializable)theHandlerPool).initialize();
242         }
243
244         theWatchdogFactory = getWatchdogFactory();
245     }
246
247     /**
248      * @see org.apache.james.core.AbstractJamesService#getDefaultPort()
249      */

250      protected int getDefaultPort() {
251         return 25;
252      }
253
254     /**
255      * @see org.apache.james.core.AbstractJamesService#getServiceType()
256      */

257     public String JavaDoc getServiceType() {
258         return "SMTP Service";
259     }
260
261     /**
262      * @see org.apache.avalon.cornerstone.services.connection.AbstractHandlerFactory#newHandler()
263      */

264     protected ConnectionHandler newHandler()
265             throws Exception JavaDoc {
266         SMTPHandler theHandler = (SMTPHandler)theHandlerPool.get();
267
268         if (getLogger().isDebugEnabled()) {
269             getLogger().debug("Getting SMTPHandler from pool.");
270         }
271         Watchdog theWatchdog = theWatchdogFactory.getWatchdog(theHandler.getWatchdogTarget());
272
273         theHandler.setConfigurationData(theConfigData);
274
275         theHandler.setWatchdog(theWatchdog);
276         return theHandler;
277     }
278
279     /**
280      * @see org.apache.avalon.cornerstone.services.connection.ConnectionHandlerFactory#releaseConnectionHandler(ConnectionHandler)
281      */

282     public void releaseConnectionHandler( ConnectionHandler connectionHandler ) {
283         if (!(connectionHandler instanceof SMTPHandler)) {
284             throw new IllegalArgumentException JavaDoc("Attempted to return non-SMTPHandler to pool.");
285         }
286         if (getLogger().isDebugEnabled()) {
287             getLogger().debug("Returning SMTPHandler to pool.");
288         }
289         theHandlerPool.put((Poolable)connectionHandler);
290     }
291
292     /**
293      * The factory for producing handlers.
294      */

295     private static class SMTPHandlerFactory
296         implements ObjectFactory {
297
298         /**
299          * @see org.apache.avalon.excalibur.pool.ObjectFactory#newInstance()
300          */

301         public Object JavaDoc newInstance() throws Exception JavaDoc {
302             return new SMTPHandler();
303         }
304
305         /**
306          * @see org.apache.avalon.excalibur.pool.ObjectFactory#getCreatedClass()
307          */

308         public Class JavaDoc getCreatedClass() {
309             return SMTPHandler.class;
310         }
311
312         /**
313          * @see org.apache.avalon.excalibur.pool.ObjectFactory#decommision(Object)
314          */

315         public void decommission( Object JavaDoc object ) throws Exception JavaDoc {
316             return;
317         }
318     }
319
320     /**
321      * A class to provide SMTP handler configuration to the handlers
322      */

323     private class SMTPHandlerConfigurationDataImpl
324         implements SMTPHandlerConfigurationData {
325
326         /**
327          * @see org.apache.james.smtpserver.SMTPHandlerConfigurationData#getHelloName()
328          */

329         public String JavaDoc getHelloName() {
330             return SMTPServer.this.helloName;
331         }
332
333         /**
334          * @see org.apache.james.smtpserver.SMTPHandlerConfigurationData#getResetLength()
335          */

336         public int getResetLength() {
337             return SMTPServer.this.lengthReset;
338         }
339
340         /**
341          * @see org.apache.james.smtpserver.SMTPHandlerConfigurationData#getMaxMessageSize()
342          */

343         public long getMaxMessageSize() {
344             return SMTPServer.this.maxMessageSize;
345         }
346
347         /**
348          * @see org.apache.james.smtpserver.SMTPHandlerConfigurationData#isAuthRequired(String)
349          */

350         public boolean isRelayingAllowed(String JavaDoc remoteIP) {
351             boolean relayingAllowed = false;
352             if (authorizedNetworks != null) {
353                 relayingAllowed = SMTPServer.this.authorizedNetworks.matchInetNetwork(remoteIP);
354             }
355             return relayingAllowed;
356         }
357
358         /**
359          * @see org.apache.james.smtpserver.SMTPHandlerConfigurationData#isAuthRequired(String)
360          */

361         public boolean isAuthRequired(String JavaDoc remoteIP) {
362             boolean authRequired = SMTPServer.this.authRequired;
363             if (authorizedNetworks != null) {
364                 authRequired = authRequired && !SMTPServer.this.authorizedNetworks.matchInetNetwork(remoteIP);
365             }
366             return authRequired;
367         }
368
369         /**
370          * @see org.apache.james.smtpserver.SMTPHandlerConfigurationData#isAuthRequired()
371          */

372         public boolean isAuthRequired() {
373             return SMTPServer.this.authRequired;
374         }
375
376         /**
377          * @see org.apache.james.smtpserver.SMTPHandlerConfigurationData#isVerifyIdentity()
378          */

379         public boolean isVerifyIdentity() {
380             return SMTPServer.this.verifyIdentity;
381         }
382
383         /**
384          * @see org.apache.james.smtpserver.SMTPHandlerConfigurationData#getMailServer()
385          */

386         public MailServer getMailServer() {
387             return SMTPServer.this.mailServer;
388         }
389
390         /**
391          * @see org.apache.james.smtpserver.SMTPHandlerConfigurationData#getUsersRepository()
392          */

393         public UsersRepository getUsersRepository() {
394             return SMTPServer.this.users;
395         }
396     }
397 }
398
Popular Tags