james-server-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From nor...@apache.org
Subject svn commit: r905220 [3/4] - in /james/protocols/trunk: ./ smtp/ smtp/src/ smtp/src/main/ smtp/src/main/java/ smtp/src/main/java/org/ smtp/src/main/java/org/apache/ smtp/src/main/java/org/apache/james/ smtp/src/main/java/org/apache/james/dsn/ smtp/src/m...
Date Mon, 01 Feb 2010 09:29:58 GMT
Added: james/protocols/trunk/smtp/src/main/java/org/apache/james/smtpserver/protocol/core/VrfyCmdHandler.java
URL: http://svn.apache.org/viewvc/james/protocols/trunk/smtp/src/main/java/org/apache/james/smtpserver/protocol/core/VrfyCmdHandler.java?rev=905220&view=auto
==============================================================================
--- james/protocols/trunk/smtp/src/main/java/org/apache/james/smtpserver/protocol/core/VrfyCmdHandler.java (added)
+++ james/protocols/trunk/smtp/src/main/java/org/apache/james/smtpserver/protocol/core/VrfyCmdHandler.java Mon Feb  1 09:29:55 2010
@@ -0,0 +1,63 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+
+
+package org.apache.james.smtpserver.protocol.core;
+
+import java.util.ArrayList;
+import java.util.Collection;
+
+import org.apache.james.dsn.DSNStatus;
+import org.apache.james.protocols.api.CommandHandler;
+import org.apache.james.protocols.api.Request;
+import org.apache.james.protocols.api.Response;
+import org.apache.james.smtpserver.protocol.SMTPResponse;
+import org.apache.james.smtpserver.protocol.SMTPRetCode;
+import org.apache.james.smtpserver.protocol.SMTPSession;
+
+/**
+  * Command handler for handling VRFY command
+  */
+public class VrfyCmdHandler implements CommandHandler<SMTPSession> {
+
+    private final String COMMAND_NAME = "VRFY";
+
+    /**
+     * Handler method called upon receipt of a VRFY command.
+     * This method informs the client that the command is
+     * not implemented.
+     *
+     */
+    public Response onCommand(SMTPSession session, Request request) {
+        return new SMTPResponse(SMTPRetCode.UNIMPLEMENTED_COMMAND, 
+                DSNStatus.getStatus(DSNStatus.PERMANENT,DSNStatus.SYSTEM_NOT_CAPABLE)+" VRFY is not supported");
+    }
+    
+    /**
+     * @see org.apache.james.smtpserver.protocol.CommandHandler#getImplCommands()
+     */
+    public Collection<String> getImplCommands() {
+        Collection<String> implCommands = new ArrayList<String>();
+        implCommands.add(COMMAND_NAME);
+        
+        return implCommands;
+    }
+
+}

Added: james/protocols/trunk/smtp/src/main/java/org/apache/james/smtpserver/protocol/core/WelcomeMessageHandler.java
URL: http://svn.apache.org/viewvc/james/protocols/trunk/smtp/src/main/java/org/apache/james/smtpserver/protocol/core/WelcomeMessageHandler.java?rev=905220&view=auto
==============================================================================
--- james/protocols/trunk/smtp/src/main/java/org/apache/james/smtpserver/protocol/core/WelcomeMessageHandler.java (added)
+++ james/protocols/trunk/smtp/src/main/java/org/apache/james/smtpserver/protocol/core/WelcomeMessageHandler.java Mon Feb  1 09:29:55 2010
@@ -0,0 +1,69 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+
+package org.apache.james.smtpserver.protocol.core;
+
+import java.util.Date;
+
+import org.apache.james.protocols.api.ConnectHandler;
+import org.apache.james.smtpserver.protocol.SMTPResponse;
+import org.apache.james.smtpserver.protocol.SMTPRetCode;
+import org.apache.james.smtpserver.protocol.SMTPSession;
+import org.apache.mailet.base.RFC822DateFormat;
+
+/**
+ * This ConnectHandler print the greeting on connecting
+ */
+public class WelcomeMessageHandler implements ConnectHandler<SMTPSession> {
+
+    /**
+     * Static RFC822DateFormat used to generate date headers
+     */
+    private final static RFC822DateFormat rfc822DateFormat = new RFC822DateFormat();
+
+    /**
+     * @see org.apache.james.smtpserver.protocol.ConnectHandler#onConnect(SMTPSession)
+     */
+    public void onConnect(SMTPSession session) {
+        String smtpGreeting = session.getSMTPGreeting();
+
+        SMTPResponse welcomeResponse;
+        // if no greeting was configured use a default
+        if (smtpGreeting == null) {
+            // Initially greet the connector
+            // Format is:  Sat, 24 Jan 1998 13:16:09 -0500
+            welcomeResponse = new SMTPResponse(SMTPRetCode.SERVICE_READY,
+                          new StringBuilder(256)
+                          .append(session.getHelloName())
+                          .append(" SMTP Server (")
+                          .append(getProductName())
+                          .append(") ready ")
+                          .append(rfc822DateFormat.format(new Date())));
+        } else {
+            welcomeResponse = new SMTPResponse(SMTPRetCode.SERVICE_READY,smtpGreeting);
+        }
+        session.writeResponse(welcomeResponse);
+    }
+    
+    protected String getProductName() {
+        return "JAMES SMTP Server";
+    }
+
+}

Added: james/protocols/trunk/smtp/src/main/java/org/apache/james/smtpserver/protocol/core/esmtp/AuthCmdHandler.java
URL: http://svn.apache.org/viewvc/james/protocols/trunk/smtp/src/main/java/org/apache/james/smtpserver/protocol/core/esmtp/AuthCmdHandler.java?rev=905220&view=auto
==============================================================================
--- james/protocols/trunk/smtp/src/main/java/org/apache/james/smtpserver/protocol/core/esmtp/AuthCmdHandler.java (added)
+++ james/protocols/trunk/smtp/src/main/java/org/apache/james/smtpserver/protocol/core/esmtp/AuthCmdHandler.java Mon Feb  1 09:29:55 2010
@@ -0,0 +1,489 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+
+
+package org.apache.james.smtpserver.protocol.core.esmtp;
+
+import java.io.UnsupportedEncodingException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Locale;
+import java.util.StringTokenizer;
+
+import org.apache.commons.codec.binary.Base64;
+
+import org.apache.james.dsn.DSNStatus;
+import org.apache.james.protocols.api.CommandHandler;
+import org.apache.james.protocols.api.ExtensibleHandler;
+import org.apache.james.protocols.api.LineHandler;
+import org.apache.james.protocols.api.Request;
+import org.apache.james.protocols.api.Response;
+import org.apache.james.protocols.api.WiringException;
+import org.apache.james.smtpserver.protocol.SMTPResponse;
+import org.apache.james.smtpserver.protocol.SMTPRetCode;
+import org.apache.james.smtpserver.protocol.SMTPSession;
+import org.apache.james.smtpserver.protocol.hook.AuthHook;
+import org.apache.james.smtpserver.protocol.hook.HookResult;
+import org.apache.james.smtpserver.protocol.hook.HookResultHook;
+import org.apache.james.smtpserver.protocol.hook.HookReturnCode;
+import org.apache.james.smtpserver.protocol.hook.MailParametersHook;
+
+
+/**
+ * handles AUTH command
+ * 
+ * Note: we could extend this to use java5 sasl standard libraries and provide client
+ * support against a server implemented via non-james specific hooks.
+ * This would allow us to reuse hooks between imap4/pop3/smtp and eventually different
+ * system (simple pluggabilty against external authentication services).
+ */
+public class AuthCmdHandler
+    implements CommandHandler<SMTPSession>, EhloExtension, ExtensibleHandler, MailParametersHook {
+
+    private abstract class AbstractSMTPLineHandler implements LineHandler<SMTPSession> {
+
+        public void onLine(SMTPSession session, byte[] l) {
+            SMTPResponse res;
+            try {
+                res = handleCommand(session, new String(l,"US-ASCII"));
+                session.popLineHandler();
+                session.writeResponse(res);
+            } catch (UnsupportedEncodingException e) {
+                e.printStackTrace();
+            }
+
+           
+        }
+
+        private SMTPResponse handleCommand(SMTPSession session, String line) {
+            // See JAMES-939
+            
+            // According to RFC2554:
+            // "If the client wishes to cancel an authentication exchange, it issues a line with a single "*".
+            // If the server receives such an answer, it MUST reject the AUTH
+            // command by sending a 501 reply."
+            if (line.equals("*\r\n")) {
+                return new SMTPResponse(SMTPRetCode.SYNTAX_ERROR_ARGUMENTS, DSNStatus.getStatus(DSNStatus.PERMANENT, DSNStatus.SECURITY_AUTH) + " Authentication aborted");
+            }
+            return onCommand(session, line);
+        }
+
+        protected abstract SMTPResponse onCommand(SMTPSession session, String l);
+    }
+
+
+
+    /**
+     * The text string for the SMTP AUTH type PLAIN.
+     */
+    private final static String AUTH_TYPE_PLAIN = "PLAIN";
+
+    /**
+     * The text string for the SMTP AUTH type LOGIN.
+     */
+    private final static String AUTH_TYPE_LOGIN = "LOGIN";
+
+    /**
+     * The AuthHooks
+     */
+    private List<AuthHook> hooks;
+    
+    private List rHooks;
+    
+    /**
+     * handles AUTH command
+     *
+     */
+    public Response onCommand(SMTPSession session, Request request) {
+        return doAUTH(session, request.getArgument());
+    }
+
+
+
+    /**
+     * Handler method called upon receipt of a AUTH command.
+     * Handles client authentication to the SMTP server.
+     *
+     * @param session SMTP session
+     * @param argument the argument passed in with the command by the SMTP client
+     */
+    private SMTPResponse doAUTH(SMTPSession session, String argument) {
+        if (session.getUser() != null) {
+            return new SMTPResponse(SMTPRetCode.BAD_SEQUENCE, DSNStatus.getStatus(DSNStatus.PERMANENT,DSNStatus.DELIVERY_OTHER)+" User has previously authenticated. "
+                    + " Further authentication is not required!");
+        } else if (argument == null) {
+            return new SMTPResponse(SMTPRetCode.SYNTAX_ERROR_ARGUMENTS, DSNStatus.getStatus(DSNStatus.PERMANENT,DSNStatus.DELIVERY_INVALID_ARG)+" Usage: AUTH (authentication type) <challenge>");
+        } else {
+            String initialResponse = null;
+            if ((argument != null) && (argument.indexOf(" ") > 0)) {
+                initialResponse = argument.substring(argument.indexOf(" ") + 1);
+                argument = argument.substring(0,argument.indexOf(" "));
+            }
+            String authType = argument.toUpperCase(Locale.US);
+            if (authType.equals(AUTH_TYPE_PLAIN)) {
+                String userpass;
+                if (initialResponse == null) {
+                    session.pushLineHandler(new AbstractSMTPLineHandler() {
+                        protected SMTPResponse onCommand(SMTPSession session, String l) {
+                            return doPlainAuthPass(session, l);
+                        }
+                    });
+                    return new SMTPResponse(SMTPRetCode.AUTH_READY, "OK. Continue authentication");
+                } else {
+                    userpass = initialResponse.trim();
+                    return doPlainAuthPass(session, userpass);
+                }
+            } else if (authType.equals(AUTH_TYPE_LOGIN)) {
+                
+                if (initialResponse == null) {
+                    session.pushLineHandler(new AbstractSMTPLineHandler() {
+                        protected SMTPResponse onCommand(SMTPSession session, String l) {
+                            return doLoginAuthPass(session, l);
+                        }
+                    });
+                    return new SMTPResponse(SMTPRetCode.AUTH_READY, "VXNlcm5hbWU6"); // base64 encoded "Username:"
+                } else {
+                    String user = initialResponse.trim();
+                    return doLoginAuthPass(session, user);
+                }
+            } else {
+                return doUnknownAuth(session, authType, initialResponse);
+            }
+        }
+    }
+
+    /**
+     * Carries out the Plain AUTH SASL exchange.
+     *
+     * According to RFC 2595 the client must send: [authorize-id] \0 authenticate-id \0 password.
+     *
+     * >>> AUTH PLAIN dGVzdAB0ZXN0QHdpei5leGFtcGxlLmNvbQB0RXN0NDI=
+     * Decoded: test\000test@wiz.example.com\000tEst42
+     *
+     * >>> AUTH PLAIN dGVzdAB0ZXN0AHRFc3Q0Mg==
+     * Decoded: test\000test\000tEst42
+     *
+     * @param session SMTP session object
+     * @param initialResponse the initial response line passed in with the AUTH command
+     */
+    private SMTPResponse doPlainAuthPass(SMTPSession session, String userpass) {
+        String user = null, pass = null;
+        try {
+            if (userpass != null) {
+                userpass = new String(Base64.decodeBase64(userpass));
+            }
+            if (userpass != null) {
+                /*  See: RFC 2595, Section 6
+                    The mechanism consists of a single message from the client to the
+                    server.  The client sends the authorization identity (identity to
+                    login as), followed by a US-ASCII NUL character, followed by the
+                    authentication identity (identity whose password will be used),
+                    followed by a US-ASCII NUL character, followed by the clear-text
+                    password.  The client may leave the authorization identity empty to
+                    indicate that it is the same as the authentication identity.
+
+                    The server will verify the authentication identity and password with
+                    the system authentication database and verify that the authentication
+                    credentials permit the client to login as the authorization identity.
+                    If both steps succeed, the user is logged in.
+                */
+                StringTokenizer authTokenizer = new StringTokenizer(userpass, "\0");
+                String authorize_id = authTokenizer.nextToken();  // Authorization Identity
+                user = authTokenizer.nextToken();                 // Authentication Identity
+                try {
+                    pass = authTokenizer.nextToken();             // Password
+                }
+                catch (java.util.NoSuchElementException _) {
+                    // If we got here, this is what happened.  RFC 2595
+                    // says that "the client may leave the authorization
+                    // identity empty to indicate that it is the same as
+                    // the authentication identity."  As noted above,
+                    // that would be represented as a decoded string of
+                    // the form: "\0authenticate-id\0password".  The
+                    // first call to nextToken will skip the empty
+                    // authorize-id, and give us the authenticate-id,
+                    // which we would store as the authorize-id.  The
+                    // second call will give us the password, which we
+                    // think is the authenticate-id (user).  Then when
+                    // we ask for the password, there are no more
+                    // elements, leading to the exception we just
+                    // caught.  So we need to move the user to the
+                    // password, and the authorize_id to the user.
+                    pass = user;
+                    user = authorize_id;
+                }
+
+                authTokenizer = null;
+            }
+        }
+        catch (Exception e) {
+            // Ignored - this exception in parsing will be dealt
+            // with in the if clause below
+        }
+        // Authenticate user
+        return doAuthTest(session, user, pass, "PLAIN");
+    }
+
+    /**
+     * Carries out the Login AUTH SASL exchange.
+     *
+     * @param session SMTP session object
+     * @param initialResponse the initial response line passed in with the AUTH command
+     */
+    private SMTPResponse doLoginAuthPass(SMTPSession session, String user) {
+        if (user != null) {
+            try {
+                user = new String(Base64.decodeBase64(user));
+            } catch (Exception e) {
+                // Ignored - this parse error will be
+                // addressed in the if clause below
+                user = null;
+            }
+        }
+        session.pushLineHandler(new AbstractSMTPLineHandler() {
+
+            private String user;
+
+            public LineHandler<SMTPSession> setUser(String user) {
+                this.user = user;
+                return this;
+            }
+
+            protected SMTPResponse onCommand(SMTPSession session, String l) {
+                return doLoginAuthPassCheck(session, user, l);
+            }
+            
+        }.setUser(user));
+        return new SMTPResponse(SMTPRetCode.AUTH_READY, "UGFzc3dvcmQ6"); // base64 encoded "Password:"
+    }
+    
+    private SMTPResponse doLoginAuthPassCheck(SMTPSession session, String user, String pass) {
+        if (pass != null) {
+            try {
+                pass = new String(Base64.decodeBase64(pass));
+            } catch (Exception e) {
+                // Ignored - this parse error will be
+                // addressed in the if clause below
+                pass = null;
+            }
+        }
+        // Authenticate user
+        return doAuthTest(session, user, pass, "LOGIN");
+    }
+
+
+
+    /**
+     * @param session
+     * @param user
+     * @param pass
+     * @param authType
+     * @return
+     */
+    private SMTPResponse doAuthTest(SMTPSession session, String user, String pass, String authType) {
+        if ((user == null) || (pass == null)) {
+            return new SMTPResponse(SMTPRetCode.SYNTAX_ERROR_ARGUMENTS,"Could not decode parameters for AUTH "+authType);
+        }
+
+        SMTPResponse res = null;
+        
+        List<AuthHook> hooks = getHooks();
+        
+        if (hooks != null) {
+            int count = hooks.size();
+            for (int i = 0; i < count; i++) {
+                AuthHook rawHook = hooks.get(i);
+                session.getLogger().debug("executing  hook " + rawHook);
+                
+                HookResult hRes = rawHook.doAuth(session, user, pass);
+                
+                if (rHooks != null) {
+                    for (int i2 = 0; i2 < rHooks.size(); i2++) {
+                        Object rHook = rHooks.get(i2);
+                        session.getLogger().debug("executing  hook " + rHook);
+                    
+                        hRes = ((HookResultHook) rHook).onHookResult(session, hRes, rHook);
+                    }
+                }
+                
+                res = calcDefaultSMTPResponse(hRes);
+                
+                if (res != null) {
+                    if (SMTPRetCode.AUTH_FAILED.equals(res.getRetCode())) {
+                        session.getLogger().error("AUTH method "+authType+" failed");
+                    } else if (SMTPRetCode.AUTH_OK.equals(res.getRetCode())) {
+                        if (session.getLogger().isDebugEnabled()) {
+                            // TODO: Make this string a more useful debug message
+                            session.getLogger().debug("AUTH method "+authType+" succeeded");
+                        }
+                    }
+                    return res;
+                }
+            }
+        }
+
+        res = new SMTPResponse(SMTPRetCode.AUTH_FAILED, "Authentication Failed");
+        // TODO: Make this string a more useful error message
+        session.getLogger().error("AUTH method "+authType+" failed");
+        return res;
+    }
+
+
+    /**
+     * Calculate the SMTPResponse for the given result
+     * 
+     * @param result the HookResult which should converted to SMTPResponse
+     * @return the calculated SMTPResponse for the given HookReslut
+     */
+    protected SMTPResponse calcDefaultSMTPResponse(HookResult result) {
+        if (result != null) {
+            int rCode = result.getResult();
+            String smtpRetCode = result.getSmtpRetCode();
+            String smtpDesc = result.getSmtpDescription();
+    
+            if (rCode == HookReturnCode.DENY) {
+                if (smtpRetCode == null)
+                    smtpRetCode = SMTPRetCode.AUTH_FAILED;
+                if (smtpDesc == null)
+                    smtpDesc = "Authentication Failed";
+    
+                return new SMTPResponse(smtpRetCode, smtpDesc);
+            } else if (rCode == HookReturnCode.DENYSOFT) {
+                if (smtpRetCode == null)
+                    smtpRetCode = SMTPRetCode.LOCAL_ERROR;
+                if (smtpDesc == null)
+                    smtpDesc = "Temporary problem. Please try again later";
+    
+                return new SMTPResponse(smtpRetCode, smtpDesc);
+            } else if (rCode == HookReturnCode.OK) {
+                if (smtpRetCode == null)
+                    smtpRetCode = SMTPRetCode.AUTH_OK;
+                if (smtpDesc == null)
+                    smtpDesc = "Authentication Succesfull";
+    
+                return new SMTPResponse(smtpRetCode, smtpDesc);
+            } else {
+                // Return null as default
+                return null;
+            }
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * Handles the case of an unrecognized auth type.
+     *
+     * @param session SMTP session object
+     * @param authType the unknown auth type
+     * @param initialResponse the initial response line passed in with the AUTH command
+     */
+    private SMTPResponse doUnknownAuth(SMTPSession session, String authType, String initialResponse) {
+        if (session.getLogger().isErrorEnabled()) {
+            StringBuilder errorBuffer =
+                new StringBuilder(128)
+                    .append("AUTH method ")
+                        .append(authType)
+                        .append(" is an unrecognized authentication type");
+            session.getLogger().error(errorBuffer.toString());
+        }
+        return new SMTPResponse(SMTPRetCode.PARAMETER_NOT_IMPLEMENTED, "Unrecognized Authentication Type");
+    }
+
+
+
+    /**
+     * @see org.apache.james.smtpserver.protocol.CommandHandler#getImplCommands()
+     */
+    public Collection<String> getImplCommands() {
+        Collection<String> implCommands = new ArrayList<String>();
+        implCommands.add("AUTH");
+        
+        return implCommands;
+    }
+
+    /**
+     * @see org.apache.james.smtpserver.protocol.core.esmtp.EhloExtension#getImplementedEsmtpFeatures(org.apache.james.smtpserver.protocol.SMTPSession)
+     */
+    public List<String> getImplementedEsmtpFeatures(SMTPSession session) {
+        if (session.isAuthSupported()) {
+            List<String> resp = new LinkedList<String>();
+            resp.add("AUTH LOGIN PLAIN");
+            resp.add("AUTH=LOGIN PLAIN");
+            return resp;
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * @see org.apache.james.api.protocol.ExtensibleHandler#getMarkerInterfaces()
+     */
+    public List<Class<?>> getMarkerInterfaces() {
+        List<Class<?>> classes = new ArrayList<Class<?>>(1);
+        classes.add(AuthHook.class);
+        return classes;
+    }
+
+
+    /**
+     * @see org.apache.james.api.protocol.ExtensibleHandler#wireExtensions(java.lang.Class, java.util.List)
+     */
+    public void wireExtensions(Class interfaceName, List extension) throws WiringException {
+        if (AuthHook.class.equals(interfaceName)) {
+            this.hooks = extension;
+            // If no AuthHook is configured then we revert to the default LocalUsersRespository check
+            if (hooks == null || hooks.size() == 0) {
+                throw new WiringException("AuthCmdHandler used without AuthHooks");
+            }
+        } else if (HookResultHook.class.equals(interfaceName)) {
+            this.rHooks = extension;
+        }
+    }
+    
+
+    /**
+     * Return a list which holds all hooks for the cmdHandler
+     * 
+     * @return
+     */
+    protected List<AuthHook> getHooks() {
+        return hooks;
+    }
+
+    /**
+     * @see org.apache.james.smtpserver.protocol.hook.MailParametersHook#doMailParameter(org.apache.james.smtpserver.protocol.SMTPSession, java.lang.String, java.lang.String)
+     */
+    public HookResult doMailParameter(SMTPSession session, String paramName, String paramValue) {
+        // Ignore the AUTH command.
+        // TODO we should at least check for correct syntax and put the result in session
+        return new HookResult(HookReturnCode.DECLINED);
+    }
+
+    /**
+     * @see org.apache.james.smtpserver.protocol.hook.MailParametersHook#getMailParamNames()
+     */
+    public String[] getMailParamNames() {
+        return new String[] { "AUTH" };
+    }
+
+}

Added: james/protocols/trunk/smtp/src/main/java/org/apache/james/smtpserver/protocol/core/esmtp/EhloCmdHandler.java
URL: http://svn.apache.org/viewvc/james/protocols/trunk/smtp/src/main/java/org/apache/james/smtpserver/protocol/core/esmtp/EhloCmdHandler.java?rev=905220&view=auto
==============================================================================
--- james/protocols/trunk/smtp/src/main/java/org/apache/james/smtpserver/protocol/core/esmtp/EhloCmdHandler.java (added)
+++ james/protocols/trunk/smtp/src/main/java/org/apache/james/smtpserver/protocol/core/esmtp/EhloCmdHandler.java Mon Feb  1 09:29:55 2010
@@ -0,0 +1,169 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.smtpserver.protocol.core.esmtp;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import org.apache.james.dsn.DSNStatus;
+import org.apache.james.smtpserver.protocol.SMTPResponse;
+import org.apache.james.smtpserver.protocol.SMTPRetCode;
+import org.apache.james.smtpserver.protocol.SMTPSession;
+import org.apache.james.smtpserver.protocol.core.AbstractHookableCmdHandler;
+import org.apache.james.smtpserver.protocol.hook.HeloHook;
+import org.apache.james.smtpserver.protocol.hook.HookResult;
+
+/**
+ * Handles EHLO command
+ */
+public class EhloCmdHandler extends AbstractHookableCmdHandler<HeloHook> {
+
+    /**
+     * The name of the command handled by the command handler
+     */
+    private final static String COMMAND_NAME = "EHLO";
+
+    private List<EhloExtension> ehloExtensions;
+
+    /**
+     * Handler method called upon receipt of a EHLO command. Responds with a
+     * greeting and informs the client whether client authentication is
+     * required.
+     * 
+     * @param session
+     *            SMTP session object
+     * @param argument
+     *            the argument passed in with the command by the SMTP client
+     */
+    private SMTPResponse doEHLO(SMTPSession session, String argument) {
+        SMTPResponse resp = new SMTPResponse(SMTPRetCode.MAIL_OK, new StringBuilder(session.getHelloName()).append(" Hello ").append(argument)
+                .append(" (").append(session.getRemoteHost()).append(" [")
+                .append(session.getRemoteIPAddress()).append("])"));
+        
+        session.getConnectionState().put(SMTPSession.CURRENT_HELO_MODE,
+                COMMAND_NAME);
+
+        processExtensions(session, resp);
+
+        resp.appendLine("PIPELINING");
+        resp.appendLine("ENHANCEDSTATUSCODES");
+        // see http://issues.apache.org/jira/browse/JAMES-419
+        resp.appendLine("8BITMIME");
+ 
+        return resp;
+
+    }
+
+    /**
+     * @see org.apache.james.smtpserver.protocol.CommandHandler#getImplCommands()
+     */
+    public Collection<String> getImplCommands() {
+        Collection<String> implCommands = new ArrayList<String>();
+        implCommands.add(COMMAND_NAME);
+
+        return implCommands;
+    }
+
+    /**
+     * @see org.apache.james.api.protocol.ExtensibleHandler#getMarkerInterfaces()
+     */
+    public List<Class<?>> getMarkerInterfaces() {
+        List<Class<?>> classes = super.getMarkerInterfaces();
+        classes.add(EhloExtension.class);
+        return classes;
+    }
+
+    /**
+     * @see org.apache.james.api.protocol.ExtensibleHandler#wireExtensions(java.lang.Class,
+     *      java.util.List)
+     */
+    public void wireExtensions(Class interfaceName, List extension) {
+        super.wireExtensions(interfaceName, extension);
+        if (EhloExtension.class.equals(interfaceName)) {
+            this.ehloExtensions = extension;
+        }
+    }
+
+    /**
+     * Process the ehloExtensions
+     * 
+     * @param session SMTPSession 
+     * @param resp SMTPResponse
+     */
+    private void processExtensions(SMTPSession session, SMTPResponse resp) {
+        if (ehloExtensions != null) {
+            int count = ehloExtensions.size();
+            for (int i = 0; i < count; i++) {
+                List<String> lines = ((EhloExtension) ehloExtensions.get(i))
+                        .getImplementedEsmtpFeatures(session);
+                if (lines != null) {
+                    for (int j = 0; j < lines.size(); j++) {
+                        resp.appendLine(lines.get(j));
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * @see org.apache.james.smtpserver.protocol.core.AbstractHookableCmdHandler#doCoreCmd(org.apache.james.smtpserver.protocol.SMTPSession,
+     *      java.lang.String, java.lang.String)
+     */
+    protected SMTPResponse doCoreCmd(SMTPSession session, String command,
+            String parameters) {
+        return doEHLO(session, parameters);
+    }
+
+    /**
+     * @see org.apache.james.smtpserver.protocol.core.AbstractHookableCmdHandler#doFilterChecks(org.apache.james.smtpserver.protocol.SMTPSession,
+     *      java.lang.String, java.lang.String)
+     */
+    protected SMTPResponse doFilterChecks(SMTPSession session, String command,
+            String parameters) {
+        session.resetState();
+
+        if (parameters == null) {
+            return new SMTPResponse(SMTPRetCode.SYNTAX_ERROR_ARGUMENTS,
+                    DSNStatus.getStatus(DSNStatus.PERMANENT,
+                            DSNStatus.DELIVERY_INVALID_ARG)
+                            + " Domain address required: " + COMMAND_NAME);
+        } else {
+            // store provided name
+            session.getState().put(SMTPSession.CURRENT_HELO_NAME, parameters);
+            return null;
+        }
+    }
+
+    /**
+     * @see org.apache.james.smtpserver.protocol.core.AbstractHookableCmdHandler#getHookInterface()
+     */
+    protected Class<HeloHook> getHookInterface() {
+        return HeloHook.class;
+    }
+
+    /**
+     * @see org.apache.james.smtpserver.protocol.core.AbstractHookableCmdHandler#callHook(java.lang.Object, org.apache.james.smtpserver.protocol.SMTPSession, java.lang.String)
+     */
+    protected HookResult callHook(HeloHook rawHook, SMTPSession session, String parameters) {
+        return rawHook.doHelo(session, parameters);
+    }
+
+}

Added: james/protocols/trunk/smtp/src/main/java/org/apache/james/smtpserver/protocol/core/esmtp/EhloExtension.java
URL: http://svn.apache.org/viewvc/james/protocols/trunk/smtp/src/main/java/org/apache/james/smtpserver/protocol/core/esmtp/EhloExtension.java?rev=905220&view=auto
==============================================================================
--- james/protocols/trunk/smtp/src/main/java/org/apache/james/smtpserver/protocol/core/esmtp/EhloExtension.java (added)
+++ james/protocols/trunk/smtp/src/main/java/org/apache/james/smtpserver/protocol/core/esmtp/EhloExtension.java Mon Feb  1 09:29:55 2010
@@ -0,0 +1,33 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.smtpserver.protocol.core.esmtp;
+
+import org.apache.james.smtpserver.protocol.SMTPSession;
+
+import java.util.List;
+
+/**
+ * Must be implemented by handlers that add new ESMTP EHLO keyworkds
+ */
+public interface EhloExtension {
+     
+    List<String> getImplementedEsmtpFeatures(SMTPSession session);
+    
+}

Added: james/protocols/trunk/smtp/src/main/java/org/apache/james/smtpserver/protocol/core/esmtp/MailSizeEsmtpExtension.java
URL: http://svn.apache.org/viewvc/james/protocols/trunk/smtp/src/main/java/org/apache/james/smtpserver/protocol/core/esmtp/MailSizeEsmtpExtension.java?rev=905220&view=auto
==============================================================================
--- james/protocols/trunk/smtp/src/main/java/org/apache/james/smtpserver/protocol/core/esmtp/MailSizeEsmtpExtension.java (added)
+++ james/protocols/trunk/smtp/src/main/java/org/apache/james/smtpserver/protocol/core/esmtp/MailSizeEsmtpExtension.java Mon Feb  1 09:29:55 2010
@@ -0,0 +1,183 @@
+/**
+ * 
+ */
+package org.apache.james.smtpserver.protocol.core.esmtp;
+
+import java.util.LinkedList;
+import java.util.List;
+
+import org.apache.james.dsn.DSNStatus;
+import org.apache.james.protocols.api.LineHandler;
+import org.apache.james.smtpserver.protocol.MailEnvelope;
+import org.apache.james.smtpserver.protocol.SMTPRetCode;
+import org.apache.james.smtpserver.protocol.SMTPSession;
+import org.apache.james.smtpserver.protocol.core.DataLineFilter;
+import org.apache.james.smtpserver.protocol.hook.HookResult;
+import org.apache.james.smtpserver.protocol.hook.HookReturnCode;
+import org.apache.james.smtpserver.protocol.hook.MailParametersHook;
+import org.apache.james.smtpserver.protocol.hook.MessageHook;
+
+/**
+ * Handle the ESMTP SIZE extension.
+ */
+public class MailSizeEsmtpExtension implements MailParametersHook, EhloExtension, DataLineFilter, MessageHook {
+
+    private final static String MESG_SIZE = "MESG_SIZE"; // The size of the
+    private final static String MESG_FAILED = "MESG_FAILED";   // Message failed flag
+
+
+    /**
+     * @see org.apache.james.smtpserver.protocol.hook.MailParametersHook#doMailParameter(org.apache.james.smtpserver.protocol.SMTPSession, java.lang.String, java.lang.String)
+     */
+    public HookResult doMailParameter(SMTPSession session, String paramName,
+            String paramValue) {
+        HookResult res = doMailSize(session, paramValue,
+                (String) session.getState().get(SMTPSession.SENDER));
+        return res;
+    }
+
+    /**
+     * @see org.apache.james.smtpserver.protocol.hook.MailParametersHook#getMailParamNames()
+     */
+    public String[] getMailParamNames() {
+        return new String[] { "SIZE" };
+    }
+
+    /**
+     * @see org.apache.james.smtpserver.protocol.core.esmtp.EhloExtension#getImplementedEsmtpFeatures(org.apache.james.smtpserver.protocol.SMTPSession)
+     */
+    public List<String> getImplementedEsmtpFeatures(SMTPSession session) {
+        LinkedList<String> resp = new LinkedList<String>();
+        // Extension defined in RFC 1870
+        long maxMessageSize = session.getMaxMessageSize();
+        if (maxMessageSize > 0) {
+            resp.add("SIZE " + maxMessageSize);
+        }
+        return resp;
+    }
+
+
+    /**
+     * Handles the SIZE MAIL option.
+     * 
+     * @param session
+     *            SMTP session object
+     * @param mailOptionValue
+     *            the option string passed in with the SIZE option
+     * @param tempSender
+     *            the sender specified in this mail command (for logging
+     *            purpose)
+     * @return true if further options should be processed, false otherwise
+     */
+    private HookResult doMailSize(SMTPSession session,
+            String mailOptionValue, String tempSender) {
+        int size = 0;
+        try {
+            size = Integer.parseInt(mailOptionValue);
+        } catch (NumberFormatException pe) {
+            session.getLogger().error("Rejected syntactically incorrect value for SIZE parameter.");
+            
+            // This is a malformed option value. We return an error
+            return new HookResult(HookReturnCode.DENY, 
+                    SMTPRetCode.SYNTAX_ERROR_ARGUMENTS,
+                    DSNStatus.getStatus(DSNStatus.PERMANENT,
+                            DSNStatus.DELIVERY_INVALID_ARG)
+                            + " Syntactically incorrect value for SIZE parameter");
+        }
+        if (session.getLogger().isDebugEnabled()) {
+            StringBuilder debugBuffer = new StringBuilder(128).append(
+                    "MAIL command option SIZE received with value ").append(
+                    size).append(".");
+            session.getLogger().debug(debugBuffer.toString());
+        }
+        long maxMessageSize = session.getMaxMessageSize();
+        if ((maxMessageSize > 0) && (size > maxMessageSize)) {
+            // Let the client know that the size limit has been hit.
+            StringBuilder errorBuffer = new StringBuilder(256).append(
+                    "Rejected message from ").append(
+                    tempSender != null ? tempSender : null).append(
+                    " from host ").append(session.getRemoteHost()).append(" (")
+                    .append(session.getRemoteIPAddress()).append(") of size ")
+                    .append(size).append(
+                            " exceeding system maximum message size of ")
+                    .append(maxMessageSize).append("based on SIZE option.");
+            session.getLogger().error(errorBuffer.toString());
+
+            return new HookResult(HookReturnCode.DENY, SMTPRetCode.QUOTA_EXCEEDED, DSNStatus
+                    .getStatus(DSNStatus.PERMANENT,
+                            DSNStatus.SYSTEM_MSG_TOO_BIG)
+                    + " Message size exceeds fixed maximum message size");
+        } else {
+            // put the message size in the message state so it can be used
+            // later to restrict messages for user quotas, etc.
+            session.getState().put(MESG_SIZE, new Integer(size));
+        }
+        return null;
+    }
+
+
+    /*
+     * (non-Javadoc)
+     * @see org.apache.james.smtpserver.protocol.core.DataLineFilter#onLine(org.apache.james.smtpserver.protocol.SMTPSession, byte[], org.apache.james.api.protocol.LineHandler)
+     */
+    public void onLine(SMTPSession session, byte[] line, LineHandler<SMTPSession> next) {
+        Boolean failed = (Boolean) session.getState().get(MESG_FAILED);
+        // If we already defined we failed and sent a reply we should simply
+        // wait for a CRLF.CRLF to be sent by the client.
+        if (failed != null && failed.booleanValue()) {
+            // TODO
+        } else {
+            if (line.length == 3 && line[0] == 46) {
+                next.onLine(session, line);
+            } else {
+                Long currentSize = (Long) session.getState().get("CURRENT_SIZE");
+                Long newSize;
+                if (currentSize == null) {
+                    newSize = new Long(line.length);
+                } else {
+                    newSize = new Long(currentSize.intValue()+line.length);
+                }
+                
+                if (session.getMaxMessageSize() > 0 && newSize.intValue() > session.getMaxMessageSize()) {
+                    // Add an item to the state to suppress
+                    // logging of extra lines of data
+                    // that are sent after the size limit has
+                    // been hit.
+                    session.getState().put(MESG_FAILED, Boolean.TRUE);
+                    // then let the client know that the size
+                    // limit has been hit.
+                    next.onLine(session, ".\r\n".getBytes());
+                } else {
+                    next.onLine(session, line);
+                }
+                
+                session.getState().put("CURRENT_SIZE", newSize);
+            }
+        }
+    }
+
+    /**
+     * @see org.apache.james.smtpserver.protocol.hook.MessageHook#onMessage(org.apache.james.smtpserver.protocol.SMTPSession, org.apache.james.smtpserver.protocol.MailEnvelopeImpl)
+     */
+    public HookResult onMessage(SMTPSession session, MailEnvelope mail) {
+        Boolean failed = (Boolean) session.getState().get(MESG_FAILED);
+        if (failed != null && failed.booleanValue()) {
+            HookResult response = new HookResult(HookReturnCode.DENY, SMTPRetCode.QUOTA_EXCEEDED,DSNStatus.getStatus(DSNStatus.PERMANENT,
+                    DSNStatus.SYSTEM_MSG_TOO_BIG) + " Maximum message size exceeded");
+  
+            StringBuilder errorBuffer = new StringBuilder(256).append(
+                    "Rejected message from ").append(
+                    session.getState().get(SMTPSession.SENDER).toString())
+                    .append(" from host ").append(session.getRemoteHost())
+                    .append(" (").append(session.getRemoteIPAddress())
+                    .append(") exceeding system maximum message size of ")
+                    .append(
+                            session.getMaxMessageSize());
+            session.getLogger().error(errorBuffer.toString());
+            return response;
+        } else {
+            return new HookResult(HookReturnCode.DECLINED);
+        }
+    }
+
+}

Added: james/protocols/trunk/smtp/src/main/java/org/apache/james/smtpserver/protocol/core/esmtp/StartTlsCmdHandler.java
URL: http://svn.apache.org/viewvc/james/protocols/trunk/smtp/src/main/java/org/apache/james/smtpserver/protocol/core/esmtp/StartTlsCmdHandler.java?rev=905220&view=auto
==============================================================================
--- james/protocols/trunk/smtp/src/main/java/org/apache/james/smtpserver/protocol/core/esmtp/StartTlsCmdHandler.java (added)
+++ james/protocols/trunk/smtp/src/main/java/org/apache/james/smtpserver/protocol/core/esmtp/StartTlsCmdHandler.java Mon Feb  1 09:29:55 2010
@@ -0,0 +1,107 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.smtpserver.protocol.core.esmtp;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import org.apache.james.dsn.DSNStatus;
+import org.apache.james.protocols.api.CommandHandler;
+import org.apache.james.protocols.api.Request;
+import org.apache.james.protocols.api.Response;
+import org.apache.james.smtpserver.protocol.SMTPResponse;
+import org.apache.james.smtpserver.protocol.SMTPRetCode;
+import org.apache.james.smtpserver.protocol.SMTPSession;
+
+/**
+ * Handles STARTTLS command
+ */
+public class StartTlsCmdHandler implements CommandHandler<SMTPSession>, EhloExtension {
+	/**
+	 * The name of the command handled by the command handler
+	 */
+	private final static String COMMAND_NAME = "STARTTLS";
+
+	/**
+	 * @see org.apache.james.smtpserver.protocol.CommandHandler#getImplCommands()
+	 */
+	public Collection<String> getImplCommands() {
+		Collection<String> commands = new ArrayList<String>();
+		commands.add(COMMAND_NAME);
+		return commands;
+	}
+
+	/**
+	 * Handler method called upon receipt of a STARTTLS command. Resets
+	 * message-specific, but not authenticated user, state.
+	 * 
+	 */
+    public Response onCommand(SMTPSession session, Request request) {
+		SMTPResponse response = null;
+		String command = request.getCommand();
+		String parameters = request.getArgument();
+		if (session.isStartTLSSupported()) {
+			if (session.isTLSStarted()) {
+				response = new SMTPResponse("500", DSNStatus.getStatus(DSNStatus.PERMANENT, DSNStatus.DELIVERY_INVALID_CMD) + " TLS already active RFC2487 5.2");
+			} else {
+				if ((parameters == null) || (parameters.length() == 0)) {
+					response = new SMTPResponse("220", DSNStatus.getStatus(DSNStatus.SUCCESS, DSNStatus.UNDEFINED_STATUS) + " Ready to start TLS");
+				} else {
+					response = new SMTPResponse("501 "+ DSNStatus.getStatus(DSNStatus.PERMANENT, DSNStatus.DELIVERY_INVALID_ARG) + " Syntax error (no parameters allowed) with STARTTLS command");
+				}
+				session.writeResponse(response);
+				try {
+					if (!session.isTLSStarted()) {
+						session.startTLS();
+						// force reset
+						session.resetState();
+					}
+				} catch (IOException e) {
+					return new SMTPResponse(SMTPRetCode.LOCAL_ERROR,"TLS not available due to temporary reason");
+				}
+			}
+			
+		} else {
+	        StringBuilder result = new StringBuilder();
+	        result.append(DSNStatus.getStatus(DSNStatus.PERMANENT, DSNStatus.DELIVERY_INVALID_CMD))
+	                      .append(" Command ")
+	                      .append(command)
+	                      .append(" unrecognized.");
+	        response =  new SMTPResponse(SMTPRetCode.SYNTAX_ERROR_COMMAND_UNRECOGNIZED, result);
+		}
+		return null;
+	}
+
+	/**
+	 * @see org.apache.james.smtpserver.protocol.core.esmtp.EhloExtension#getImplementedEsmtpFeatures(org.apache.james.smtpserver.protocol.SMTPSession)
+	 */
+	public List<String> getImplementedEsmtpFeatures(SMTPSession session) {
+		List<String> esmtpextensions = new ArrayList<String>();
+		// SMTP STARTTLS
+		if (!session.isTLSStarted() && session.isStartTLSSupported()) {
+			esmtpextensions.add("STARTTLS");
+		}
+		return esmtpextensions;
+
+	}
+
+}

Added: james/protocols/trunk/smtp/src/main/java/org/apache/james/smtpserver/protocol/core/fastfail/AbstractGreylistHandler.java
URL: http://svn.apache.org/viewvc/james/protocols/trunk/smtp/src/main/java/org/apache/james/smtpserver/protocol/core/fastfail/AbstractGreylistHandler.java?rev=905220&view=auto
==============================================================================
--- james/protocols/trunk/smtp/src/main/java/org/apache/james/smtpserver/protocol/core/fastfail/AbstractGreylistHandler.java (added)
+++ james/protocols/trunk/smtp/src/main/java/org/apache/james/smtpserver/protocol/core/fastfail/AbstractGreylistHandler.java Mon Feb  1 09:29:55 2010
@@ -0,0 +1,215 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.smtpserver.protocol.core.fastfail;
+
+import java.util.Iterator;
+
+import org.apache.james.dsn.DSNStatus;
+import org.apache.james.smtpserver.protocol.SMTPRetCode;
+import org.apache.james.smtpserver.protocol.SMTPSession;
+import org.apache.james.smtpserver.protocol.hook.HookResult;
+import org.apache.james.smtpserver.protocol.hook.HookReturnCode;
+import org.apache.james.smtpserver.protocol.hook.RcptHook;
+import org.apache.mailet.MailAddress;
+
+
+/**
+ * Abstract base class which implement GreyListing. 
+ * 
+ *
+ */
+public abstract class AbstractGreylistHandler implements RcptHook {
+
+    /** 1 hour */
+    private long tempBlockTime = 3600000;
+
+    /** 36 days */
+    private long autoWhiteListLifeTime = 3110400000L;
+
+    /** 4 hours */
+    private long unseenLifeTime = 14400000;
+
+
+    
+    public void setUnseenLifeTime(long unseenLifeTime) {
+        this.unseenLifeTime = unseenLifeTime;
+    }
+    
+    public void setAutoWhiteListLifeTime(long autoWhiteListLifeTime) {
+        this.autoWhiteListLifeTime = autoWhiteListLifeTime;
+    }
+    
+    public void setTempBlockTime(long tempBlockTime) {
+        this.tempBlockTime = tempBlockTime;
+    }
+
+
+    private HookResult doGreyListCheck(SMTPSession session, MailAddress senderAddress, MailAddress recipAddress) {
+        String recip = "";
+        String sender = "";
+
+        if (recipAddress != null) recip = recipAddress.toString();
+        if (senderAddress != null) sender = senderAddress.toString();
+    
+        long time = System.currentTimeMillis();
+        String ipAddress = session.getRemoteIPAddress();
+        
+        try {
+            long createTimeStamp = 0;
+            int count = 0;
+            
+            // get the timestamp when he triplet was last seen
+            Iterator<String> data = getGreyListData(ipAddress, sender, recip);
+            
+            if (data.hasNext()) {
+                createTimeStamp = Long.parseLong(data.next());
+                count = Integer.parseInt(data.next());
+            }
+            
+            session.getLogger().debug("Triplet " + ipAddress + " | " + sender + " | " + recip  +" -> TimeStamp: " + createTimeStamp);
+
+
+            // if the timestamp is bigger as 0 we have allready a triplet stored
+            if (createTimeStamp > 0) {
+                long acceptTime = createTimeStamp + tempBlockTime;
+        
+                if ((time < acceptTime) && (count == 0)) {
+                    return new HookResult(HookReturnCode.DENYSOFT, SMTPRetCode.LOCAL_ERROR, DSNStatus.getStatus(DSNStatus.TRANSIENT, DSNStatus.NETWORK_DIR_SERVER) 
+                        + " Temporary rejected: Reconnect to fast. Please try again later");
+                } else {
+                    
+                    session.getLogger().debug("Update triplet " + ipAddress + " | " + sender + " | " + recip + " -> timestamp: " + time);
+                    
+                    // update the triplet..
+                    updateTriplet(ipAddress, sender, recip, count, time);
+
+                }
+            } else {
+                session.getLogger().debug("New triplet " + ipAddress + " | " + sender + " | " + recip );
+           
+                // insert a new triplet
+                insertTriplet(ipAddress, sender, recip, count, time);
+      
+                // Tempory block on new triplet!
+                return new HookResult(HookReturnCode.DENYSOFT, SMTPRetCode.LOCAL_ERROR, DSNStatus.getStatus(DSNStatus.TRANSIENT, DSNStatus.NETWORK_DIR_SERVER) 
+                    + " Temporary rejected: Please try again later");
+            }
+
+            // some kind of random cleanup process
+            if (Math.random() > 0.99) {
+                // cleanup old entries
+            
+                session.getLogger().debug("Delete old entries");
+            
+                cleanupAutoWhiteListGreyList(time - autoWhiteListLifeTime);
+                cleanupGreyList(time - unseenLifeTime);
+            }
+
+        } catch (Exception e) {
+            // just log the exception
+            session.getLogger().error("Error on greylist method: " + e.getMessage());
+        }
+        return new HookResult(HookReturnCode.DECLINED);
+    }
+
+    /**
+     * Get all necessary data for greylisting based on provided triplet
+     * 
+     * @param ipAddress
+     *            The ipAddress of the client
+     * @param sender
+     *            The mailFrom
+     * @param recip
+     *            The rcptTo
+     * @return data
+     *            The data
+     * @throws Exception
+     */
+    protected abstract  Iterator<String> getGreyListData(String ipAddress, String sender, String recip) throws Exception;
+
+    /**
+     * Insert new triplet in the store
+     * 
+     * @param ipAddress
+     *            The ipAddress of the client
+     * @param sender
+     *            The mailFrom
+     * @param recip
+     *            The rcptTo
+     * @param count
+     *            The count
+     * @param createTime
+     *            The createTime
+     * @throws SQLException
+     */
+    protected abstract void insertTriplet(String ipAddress, String sender, String recip, int count, long createTime)
+        throws Exception;
+
+    /**
+     * Update the triplet
+     * 
+     * 
+     * @param ipAddress
+     *            The ipAddress of the client
+     * @param sender
+     *            The mailFrom
+     * @param recip
+     *            The rcptTo
+     * @param count
+     *            The count
+     * @param time
+     *            the current time in ms
+     * @throws Exception
+     */
+    protected abstract void updateTriplet(String ipAddress, String sender, String recip, int count, long time) throws Exception;
+       
+
+    /**
+     * Cleanup the autowhitelist
+     * 
+     * @param time
+     *            The time which must be reached before delete the records
+     * @throws Exception
+     */
+    protected abstract void cleanupAutoWhiteListGreyList(long time)throws Exception;     
+
+    /**
+     * Delete old entries from the Greylist datarecord 
+     * 
+     * @param time
+     *            The time which must be reached before delete the records
+     * @throws Exception
+     */
+    protected abstract void cleanupGreyList(long time) throws Exception;
+
+  
+
+    /**
+     * @see org.apache.james.smtpserver.protocol.hook.RcptHook#doRcpt(org.apache.james.smtpserver.protocol.SMTPSession, org.apache.mailet.MailAddress, org.apache.mailet.MailAddress)
+     */
+    public HookResult doRcpt(SMTPSession session, MailAddress sender, MailAddress rcpt) {
+        if (!session.isRelayingAllowed()) {
+            return doGreyListCheck(session, sender,rcpt);
+        } else {
+            session.getLogger().info("IpAddress " + session.getRemoteIPAddress() + " is allowed to send. Skip greylisting.");
+        }
+        return new HookResult(HookReturnCode.DECLINED);
+    }
+}

Added: james/protocols/trunk/smtp/src/main/java/org/apache/james/smtpserver/protocol/core/fastfail/AbstractValidRcptHandler.java
URL: http://svn.apache.org/viewvc/james/protocols/trunk/smtp/src/main/java/org/apache/james/smtpserver/protocol/core/fastfail/AbstractValidRcptHandler.java?rev=905220&view=auto
==============================================================================
--- james/protocols/trunk/smtp/src/main/java/org/apache/james/smtpserver/protocol/core/fastfail/AbstractValidRcptHandler.java (added)
+++ james/protocols/trunk/smtp/src/main/java/org/apache/james/smtpserver/protocol/core/fastfail/AbstractValidRcptHandler.java Mon Feb  1 09:29:55 2010
@@ -0,0 +1,66 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+
+
+
+package org.apache.james.smtpserver.protocol.core.fastfail;
+
+import org.apache.james.dsn.DSNStatus;
+import org.apache.james.smtpserver.protocol.SMTPRetCode;
+import org.apache.james.smtpserver.protocol.SMTPSession;
+import org.apache.james.smtpserver.protocol.hook.HookResult;
+import org.apache.james.smtpserver.protocol.hook.HookReturnCode;
+import org.apache.james.smtpserver.protocol.hook.RcptHook;
+import org.apache.mailet.MailAddress;
+
+
+/**
+ * Handler which want todo an recipient check should extend this
+ *
+ */
+public abstract class AbstractValidRcptHandler implements RcptHook {
+
+    
+    /**
+     * @see org.apache.james.smtpserver.protocol.hook.RcptHook#doRcpt(org.apache.james.smtpserver.protocol.SMTPSession, org.apache.mailet.MailAddress, org.apache.mailet.MailAddress)
+     */
+    public HookResult doRcpt(SMTPSession session, MailAddress sender, MailAddress rcpt) {
+        
+        if (!session.isRelayingAllowed()) {
+            if (isValidRecipient(session, rcpt) == false) {
+                //user not exist
+                session.getLogger().info("Rejected message. Unknown user: " + rcpt.toString());
+                return new HookResult(HookReturnCode.DENY,SMTPRetCode.TRANSACTION_FAILED, DSNStatus.getStatus(DSNStatus.PERMANENT,DSNStatus.ADDRESS_MAILBOX) + " Unknown user: " + rcpt.toString());
+            }
+        } else {
+            session.getLogger().debug("Sender allowed");
+        }
+        return new HookResult(HookReturnCode.DECLINED);
+    }
+    
+  
+    /**
+     * Return true if email for the given recipient should get accepted
+     * 
+     * @param recipient
+     * @return isValid
+     */
+    protected abstract boolean isValidRecipient(SMTPSession session, MailAddress recipient);
+}

Added: james/protocols/trunk/smtp/src/main/java/org/apache/james/smtpserver/protocol/core/fastfail/DNSRBLHandler.java
URL: http://svn.apache.org/viewvc/james/protocols/trunk/smtp/src/main/java/org/apache/james/smtpserver/protocol/core/fastfail/DNSRBLHandler.java?rev=905220&view=auto
==============================================================================
--- james/protocols/trunk/smtp/src/main/java/org/apache/james/smtpserver/protocol/core/fastfail/DNSRBLHandler.java (added)
+++ james/protocols/trunk/smtp/src/main/java/org/apache/james/smtpserver/protocol/core/fastfail/DNSRBLHandler.java Mon Feb  1 09:29:55 2010
@@ -0,0 +1,201 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+
+
+package org.apache.james.smtpserver.protocol.core.fastfail;
+
+import java.util.Collection;
+import java.util.StringTokenizer;
+
+import org.apache.james.dsn.DSNStatus;
+import org.apache.james.protocols.api.ConnectHandler;
+import org.apache.james.smtpserver.protocol.DNSService;
+import org.apache.james.smtpserver.protocol.SMTPSession;
+import org.apache.james.smtpserver.protocol.hook.HookResult;
+import org.apache.james.smtpserver.protocol.hook.HookReturnCode;
+import org.apache.james.smtpserver.protocol.hook.RcptHook;
+import org.apache.mailet.MailAddress;
+
+/**
+  * Connect handler for DNSRBL processing
+  */
+public class DNSRBLHandler implements  ConnectHandler<SMTPSession>, RcptHook{
+
+    
+    /**
+     * The lists of rbl servers to be checked to limit spam
+     */
+    private String[] whitelist;
+    private String[] blacklist;
+    
+    private DNSService dnsService = null;
+    
+    private boolean getDetail = false;
+    
+    private String blocklistedDetail = null;
+    
+    public static final String RBL_BLOCKLISTED_MAIL_ATTRIBUTE_NAME = "org.apache.james.smtpserver.rbl.blocklisted";
+    
+    public static final String RBL_DETAIL_MAIL_ATTRIBUTE_NAME = "org.apache.james.smtpserver.rbl.detail";
+
+
+    /**
+     * Sets the DNS service.
+     * @param dnsService the dnsService to set
+     */
+    public final void setDNSService(DNSService dnsService) {
+        this.dnsService = dnsService;
+    }
+
+   
+    
+    /**
+     * check if the remote Ip address is block listed
+     *
+    **/
+    public void onConnect(SMTPSession session) {
+        checkDNSRBL(session, session.getRemoteIPAddress());
+    }
+    
+    /**
+     * Set the whitelist array
+     * 
+     * @param whitelist The array which contains the whitelist
+     */
+    public void setWhitelist(String[] whitelist) {
+        this.whitelist = whitelist;
+    }
+    
+    /**
+     * Set the blacklist array
+     * 
+     * @param blacklist The array which contains the blacklist
+     */
+    public void setBlacklist(String[] blacklist) {
+        this.blacklist = blacklist;
+    }
+
+    /**
+     * Set for try to get a TXT record for the blocked record. 
+     * 
+     * @param getDetail Set to ture for enable
+     */
+    public void setGetDetail(boolean getDetail) {
+        this.getDetail = getDetail;
+    }
+
+    /**
+     *
+     * This checks DNSRBL whitelists and blacklists.  If the remote IP is whitelisted
+     * it will be permitted to send e-mail, otherwise if the remote IP is blacklisted,
+     * the sender will only be permitted to send e-mail to postmaster (RFC 2821) or
+     * abuse (RFC 2142), unless authenticated.
+     */
+
+    public void checkDNSRBL(SMTPSession session, String ipAddress) {
+        
+        /*
+         * don't check against rbllists if the client is allowed to relay..
+         * This whould make no sense.
+         */
+        if (session.isRelayingAllowed()) {
+            session.getLogger().info("Ipaddress " + session.getRemoteIPAddress() + " is allowed to relay. Don't check it");
+            return;
+        }
+        
+        if (whitelist != null || blacklist != null) {
+            StringBuffer sb = new StringBuffer();
+            StringTokenizer st = new StringTokenizer(ipAddress, " .", false);
+            while (st.hasMoreTokens()) {
+                sb.insert(0, st.nextToken() + ".");
+            }
+            String reversedOctets = sb.toString();
+
+            if (whitelist != null) {
+                String[] rblList = whitelist;
+                for (int i = 0 ; i < rblList.length ; i++) try {
+                    dnsService.getByName(reversedOctets + rblList[i]);
+                    if (session.getLogger().isInfoEnabled()) {
+                        session.getLogger().info("Connection from " + ipAddress + " whitelisted by " + rblList[i]);
+                    }
+                    
+                    return;
+                } catch (java.net.UnknownHostException uhe) {
+                    if (session.getLogger().isDebugEnabled()) {
+                        session.getLogger().debug("IpAddress " + session.getRemoteIPAddress() + " not listed on " + rblList[i]);
+                    }
+                }
+            }
+
+            if (blacklist != null) {
+                String[] rblList = blacklist;
+                for (int i = 0 ; i < rblList.length ; i++) try {
+                    dnsService.getByName(reversedOctets + rblList[i]);
+                    if (session.getLogger().isInfoEnabled()) {
+                        session.getLogger().info("Connection from " + ipAddress + " restricted by " + rblList[i] + " to SMTP AUTH/postmaster/abuse.");
+                    }
+                    
+                    // we should try to retrieve details
+                    if (getDetail) {
+                        Collection<String> txt = dnsService.findTXTRecords(reversedOctets + rblList[i]);
+                        
+                        // Check if we found a txt record
+                        if (!txt.isEmpty()) {
+                            // Set the detail
+                            String blocklistedDetail = txt.iterator().next().toString();
+                            
+                            session.getConnectionState().put(RBL_DETAIL_MAIL_ATTRIBUTE_NAME, blocklistedDetail);
+                        }
+                    }
+                    
+                    session.getConnectionState().put(RBL_BLOCKLISTED_MAIL_ATTRIBUTE_NAME, "true");
+                    return;
+                } catch (java.net.UnknownHostException uhe) {
+                    // if it is unknown, it isn't blocked
+                    if (session.getLogger().isDebugEnabled()) {
+                        session.getLogger().debug("unknown host exception thrown:" + rblList[i]);
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * @see org.apache.james.smtpserver.protocol.hook.RcptHook#doRcpt(org.apache.james.smtpserver.protocol.SMTPSession, org.apache.mailet.MailAddress, org.apache.mailet.MailAddress)
+     */
+    public HookResult doRcpt(SMTPSession session, MailAddress sender, MailAddress rcpt) {
+        
+        if (!session.isRelayingAllowed()) {
+            String blocklisted = (String) session.getConnectionState().get(RBL_BLOCKLISTED_MAIL_ATTRIBUTE_NAME);
+    
+            if (blocklisted != null) { // was found in the RBL
+                if (blocklistedDetail == null) {
+                    return new HookResult(HookReturnCode.DENY,DSNStatus.getStatus(DSNStatus.PERMANENT,
+                            DSNStatus.SECURITY_AUTH)  + " Rejected: unauthenticated e-mail from " + session.getRemoteIPAddress() 
+                            + " is restricted.  Contact the postmaster for details.");
+                } else {
+                    return new HookResult(HookReturnCode.DENY,DSNStatus.getStatus(DSNStatus.PERMANENT,DSNStatus.SECURITY_AUTH) + " " + blocklistedDetail);
+                }
+               
+            }
+        }
+        return new HookResult(HookReturnCode.DECLINED);
+    }
+}

Added: james/protocols/trunk/smtp/src/main/java/org/apache/james/smtpserver/protocol/core/fastfail/MaxRcptHandler.java
URL: http://svn.apache.org/viewvc/james/protocols/trunk/smtp/src/main/java/org/apache/james/smtpserver/protocol/core/fastfail/MaxRcptHandler.java?rev=905220&view=auto
==============================================================================
--- james/protocols/trunk/smtp/src/main/java/org/apache/james/smtpserver/protocol/core/fastfail/MaxRcptHandler.java (added)
+++ james/protocols/trunk/smtp/src/main/java/org/apache/james/smtpserver/protocol/core/fastfail/MaxRcptHandler.java Mon Feb  1 09:29:55 2010
@@ -0,0 +1,61 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+
+
+package org.apache.james.smtpserver.protocol.core.fastfail;
+
+
+import org.apache.james.dsn.DSNStatus;
+import org.apache.james.smtpserver.protocol.SMTPRetCode;
+import org.apache.james.smtpserver.protocol.SMTPSession;
+import org.apache.james.smtpserver.protocol.hook.HookResult;
+import org.apache.james.smtpserver.protocol.hook.HookReturnCode;
+import org.apache.james.smtpserver.protocol.hook.RcptHook;
+import org.apache.mailet.MailAddress;
+
+public class MaxRcptHandler implements RcptHook {
+
+    private int maxRcpt = 0;
+
+
+    /**
+     * Set the max rcpt for wich should be accepted
+     * 
+     * @param maxRcpt
+     *            The max rcpt count
+     */
+    public void setMaxRcpt(int maxRcpt) {
+        this.maxRcpt = maxRcpt;
+    }
+   
+    /**
+     * @see org.apache.james.smtpserver.protocol.hook.RcptHook#doRcpt(org.apache.james.smtpserver.protocol.SMTPSession, org.apache.mailet.MailAddress, org.apache.mailet.MailAddress)
+     */
+    public HookResult doRcpt(SMTPSession session, MailAddress sender, MailAddress rcpt) {
+        if ((session.getRcptCount() + 1) > maxRcpt) {
+            session.getLogger().info("Maximum recipients of " + maxRcpt + " reached");
+            
+            return new HookResult(HookReturnCode.DENY, SMTPRetCode.SYSTEM_STORAGE_ERROR, DSNStatus.getStatus(DSNStatus.NETWORK, DSNStatus.DELIVERY_TOO_MANY_REC)
+                    + " Requested action not taken: max recipients reached");
+        } else {
+            return new HookResult(HookReturnCode.DECLINED);
+        }
+    }
+}

Added: james/protocols/trunk/smtp/src/main/java/org/apache/james/smtpserver/protocol/core/fastfail/ResolvableEhloHeloHandler.java
URL: http://svn.apache.org/viewvc/james/protocols/trunk/smtp/src/main/java/org/apache/james/smtpserver/protocol/core/fastfail/ResolvableEhloHeloHandler.java?rev=905220&view=auto
==============================================================================
--- james/protocols/trunk/smtp/src/main/java/org/apache/james/smtpserver/protocol/core/fastfail/ResolvableEhloHeloHandler.java (added)
+++ james/protocols/trunk/smtp/src/main/java/org/apache/james/smtpserver/protocol/core/fastfail/ResolvableEhloHeloHandler.java Mon Feb  1 09:29:55 2010
@@ -0,0 +1,126 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.smtpserver.protocol.core.fastfail;
+
+import java.net.UnknownHostException;
+
+
+import org.apache.james.dsn.DSNStatus;
+import org.apache.james.smtpserver.protocol.DNSService;
+import org.apache.james.smtpserver.protocol.SMTPRetCode;
+import org.apache.james.smtpserver.protocol.SMTPSession;
+import org.apache.james.smtpserver.protocol.hook.HeloHook;
+import org.apache.james.smtpserver.protocol.hook.HookResult;
+import org.apache.james.smtpserver.protocol.hook.HookReturnCode;
+import org.apache.james.smtpserver.protocol.hook.RcptHook;
+import org.apache.mailet.MailAddress;
+
+
+/**
+ * This CommandHandler can be used to reject not resolvable EHLO/HELO
+ */
+public class ResolvableEhloHeloHandler implements RcptHook, HeloHook {
+
+    public final static String BAD_EHLO_HELO = "BAD_EHLO_HELO";
+
+    protected DNSService dnsService = null;
+
+    /**
+     * Gets the DNS service.
+     * @return the dnsService
+     */
+    public final DNSService getDNSService() {
+        return dnsService;
+    }
+
+    /**
+     * Sets the DNS service.
+     * @param dnsService the dnsService to set
+     */
+    public final void setDNSService(DNSService dnsService) {
+        this.dnsService = dnsService;
+    }
+
+
+    /**
+     * Check if EHLO/HELO is resolvable
+     * 
+     * @param session
+     *            The SMTPSession
+     * @param argument
+     *            The argument
+     */
+    protected void checkEhloHelo(SMTPSession session, String argument) {
+        
+        if (isBadHelo(session, argument)) {
+            session.getState().put(BAD_EHLO_HELO, "true");
+        }
+    }
+    
+    /**
+     * @param session the SMTPSession
+     * @param argument the argument
+     * @return true if the helo is bad.
+     */
+    protected boolean isBadHelo(SMTPSession session, String argument) {
+        // try to resolv the provided helo. If it can not resolved do not
+        // accept it.
+        try {
+        	dnsService.getByName(argument);
+        } catch (UnknownHostException e) {
+            return true;
+        }
+        return false;
+        
+    }
+
+    /**
+     * @see org.apache.james.smtpserver.core.filter.fastfail.AbstractJunkHandler#check(org.apache.james.smtpserver.protocol.SMTPSession)
+     */
+    protected boolean check(SMTPSession session,MailAddress rcpt) {
+        // not reject it
+        if (session.getState().get(BAD_EHLO_HELO) == null) {
+            return false;
+        }
+
+        return true;
+    }
+
+    /**
+     * @see org.apache.james.smtpserver.protocol.hook.RcptHook#doRcpt(org.apache.james.smtpserver.protocol.SMTPSession, org.apache.mailet.MailAddress, org.apache.mailet.MailAddress)
+     */
+    public HookResult doRcpt(SMTPSession session, MailAddress sender, MailAddress rcpt) {
+        if (check(session,rcpt)) {
+            return new HookResult(HookReturnCode.DENY,SMTPRetCode.SYNTAX_ERROR_ARGUMENTS,DSNStatus.getStatus(DSNStatus.PERMANENT, DSNStatus.DELIVERY_INVALID_ARG)
+                    + " Provided EHLO/HELO " + session.getState().get(SMTPSession.CURRENT_HELO_NAME) + " can not resolved.");
+        } else {
+            return new HookResult(HookReturnCode.DECLINED);
+        }
+    }
+
+    /**
+     * @see org.apache.james.smtpserver.protocol.hook.HeloHook#doHelo(org.apache.james.smtpserver.protocol.SMTPSession, java.lang.String)
+     */
+    public HookResult doHelo(SMTPSession session, String helo) {
+        checkEhloHelo(session, helo);
+        return new HookResult(HookReturnCode.DECLINED);
+    }
+
+}

Added: james/protocols/trunk/smtp/src/main/java/org/apache/james/smtpserver/protocol/core/fastfail/ReverseEqualsEhloHeloHandler.java
URL: http://svn.apache.org/viewvc/james/protocols/trunk/smtp/src/main/java/org/apache/james/smtpserver/protocol/core/fastfail/ReverseEqualsEhloHeloHandler.java?rev=905220&view=auto
==============================================================================
--- james/protocols/trunk/smtp/src/main/java/org/apache/james/smtpserver/protocol/core/fastfail/ReverseEqualsEhloHeloHandler.java (added)
+++ james/protocols/trunk/smtp/src/main/java/org/apache/james/smtpserver/protocol/core/fastfail/ReverseEqualsEhloHeloHandler.java Mon Feb  1 09:29:55 2010
@@ -0,0 +1,47 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.smtpserver.protocol.core.fastfail;
+
+import org.apache.james.smtpserver.protocol.SMTPSession;
+
+
+import java.net.UnknownHostException;
+
+
+public class ReverseEqualsEhloHeloHandler extends ResolvableEhloHeloHandler {
+
+    /**
+     * @see org.apache.james.smtpserver.protocol.core.fastfail.ResolvableEhloHeloHandler#isBadHelo(org.apache.james.smtpserver.protocol.SMTPSession, java.lang.String)
+     */
+    protected boolean isBadHelo(SMTPSession session, String argument) {
+        try {
+            // get reverse entry
+            String reverse = dnsService.getHostName(dnsService.getByName(
+                    session.getRemoteIPAddress()));
+            if (!argument.equals(reverse)) {
+                return true;
+            }
+        } catch (UnknownHostException e) {
+            return true;
+        }
+        return false;
+    }
+    
+}



---------------------------------------------------------------------
To unsubscribe, e-mail: server-dev-unsubscribe@james.apache.org
For additional commands, e-mail: server-dev-help@james.apache.org


Mime
View raw message