You are viewing a plain text version of this content. The canonical link for it is here.
Posted to server-dev@james.apache.org by pg...@apache.org on 2002/10/01 01:21:05 UTC

cvs commit: jakarta-james/src/java/org/apache/james/smtpserver SMTPHandler.java

pgoldstein    2002/09/30 16:21:05

  Modified:    src/java/org/apache/james/smtpserver SMTPHandler.java
  Log:
  A variety of changes to the SMTP handler that are designed to neaten the code as well as
  improve adherence to the RFC 2821 specification.
  
  Revision  Changes    Path
  1.28      +643 -432  jakarta-james/src/java/org/apache/james/smtpserver/SMTPHandler.java
  
  Index: SMTPHandler.java
  ===================================================================
  RCS file: /home/cvs/jakarta-james/src/java/org/apache/james/smtpserver/SMTPHandler.java,v
  retrieving revision 1.27
  retrieving revision 1.28
  diff -u -r1.27 -r1.28
  --- SMTPHandler.java	26 Sep 2002 01:15:23 -0000	1.27
  +++ SMTPHandler.java	30 Sep 2002 23:21:04 -0000	1.28
  @@ -6,6 +6,7 @@
    * the LICENSE file.
    */
   package org.apache.james.smtpserver;
  +
   import org.apache.avalon.cornerstone.services.connection.ConnectionHandler;
   import org.apache.avalon.cornerstone.services.scheduler.PeriodicTimeTrigger;
   import org.apache.avalon.cornerstone.services.scheduler.Target;
  @@ -55,20 +56,11 @@
   
       // Keys used to store/lookup data in the internal state hash map
   
  -    private final static String SERVER_NAME = "SERVER_NAME";   // Local server name
  -    private final static String SERVER_TYPE = "SERVER_TYPE";   // SMTP Software Type
  -    private final static String REMOTE_NAME = "REMOTE_NAME";   // Remote host name
  -    private final static String REMOTE_IP = "REMOTE_IP";       // Remote IP address
  -    private final static String NAME_GIVEN = "NAME_GIVEN";     // Remote host name provided by
  -                                                              // client
       private final static String CURRENT_HELO_MODE = "CURRENT_HELO_MODE"; // HELO or EHLO
       private final static String SENDER = "SENDER_ADDRESS";     // Sender's email address 
       private final static String MESG_FAILED = "MESG_FAILED";   // Message failed flag
       private final static String MESG_SIZE = "MESG_SIZE";       // The size of the message
       private final static String RCPT_VECTOR = "RCPT_VECTOR";   // The message recipients
  -    private final static String SMTP_ID = "SMTP_ID";           // The SMTP ID associated with
  -                                                              // the connection
  -    private final static String AUTH = "AUTHENTICATED";        // The authenticated user id
   
       /**
        * The character array that indicates termination of an SMTP connection
  @@ -85,47 +77,175 @@
        */
       private final static RFC822DateFormat rfc822DateFormat = new RFC822DateFormat();
   
  -    private Socket socket;    // The TCP/IP socket over which the SMTP 
  -                              // dialogue is occurring
  +    /**
  +     * The text string for the SMTP HELO command.
  +     */
  +    private final static String COMMAND_HELO = "HELO";
  +
  +    /**
  +     * The text string for the SMTP EHLO command.
  +     */
  +    private final static String COMMAND_EHLO = "EHLO";
  +
  +    /**
  +     * The text string for the SMTP AUTH command.
  +     */
  +    private final static String COMMAND_AUTH = "AUTH";
  +
  +    /**
  +     * The text string for the SMTP MAIL command.
  +     */
  +    private final static String COMMAND_MAIL = "MAIL";
  +
  +    /**
  +     * The text string for the SMTP RCPT command.
  +     */
  +    private final static String COMMAND_RCPT = "RCPT";
  +
  +    /**
  +     * The text string for the SMTP NOOP command.
  +     */
  +    private final static String COMMAND_NOOP = "NOOP";
  +
  +    /**
  +     * The text string for the SMTP RSET command.
  +     */
  +    private final static String COMMAND_RSET = "RSET";
  +
  +    /**
  +     * The text string for the SMTP DATA command.
  +     */
  +    private final static String COMMAND_DATA = "DATA";
  +
  +    /**
  +     * The text string for the SMTP QUIT command.
  +     */
  +    private final static String COMMAND_QUIT = "QUIT";
  +
  +    /**
  +     * The text string for the SMTP HELP command.
  +     */
  +    private final static String COMMAND_HELP = "HELP";
  +
  +    /**
  +     * The text string for the SMTP VRFY command.
  +     */
  +    private final static String COMMAND_VRFY = "VRFY";
  +
  +    /**
  +     * The text string for the SMTP EXPN command.
  +     */
  +    private final static String COMMAND_EXPN = "EXPN";
  +
  +    /**
  +     * 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 text string for the SMTP MAIL command SIZE option.
  +     */
  +    private final static String MAIL_OPTION_SIZE = "SIZE";
  +
  +
  +    /**
  +     * The TCP/IP socket over which the SMTP 
  +     * dialogue is occurring.
  +     */
  +    private Socket socket;
  +
  +    /**
  +     * The incoming stream of bytes coming from the socket.
  +     */
  +    private InputStream in;
   
  -    private InputStream in;     // The incoming stream of bytes coming from the socket.
  -    private PrintWriter out;    // The writer to which outgoing messages are written.
  +    /**
  +     * The writer to which outgoing messages are written.
  +     */
  +    private PrintWriter out;
   
       /**
        * A Reader wrapper for the incoming stream of bytes coming from the socket.
        */
       private BufferedReader inReader;
   
  -    private String remoteHost;  // The remote host name obtained by lookup on the socket.
  -    private String remoteIP;    // The remote IP address of the socket.
  -    private String smtpID;      // The id associated with this particular SMTP interaction.
  -
  -    private boolean authRequired = false;    // Whether authentication is required to use
  -                                             // this SMTP server.
  -
  -    private boolean verifyIdentity = false;  // Whether the server verifies that the user
  -                                             // actually sending an email matches the
  -                                             // authentication credentials attached to the
  -                                             // SMTP interaction.
  -
  -    private long maxmessagesize = 0;         // The maximum message size allowed by this
  -                                             // SMTP server.  The default value, 0, means
  -                                             // no limit.
  -
  -    private int lengthReset = 20000;         // The number of bytes to read before resetting
  -                                             // the connection timeout timer.  Defaults to
  -                                             // 20 seconds.
  +    /**
  +     * The remote host name obtained by lookup on the socket.
  +     */
  +    private String remoteHost;
  +
  +    /**
  +     * The remote IP address of the socket.
  +     */
  +    private String remoteIP;
  +
  +    /**
  +     * The user name of the authenticated user associated with this SMTP transaction.
  +     */
  +    private String authenticatedUser;
  +
  +    /**
  +     * The id associated with this particular SMTP interaction.
  +     */
  +    private String smtpID;
  +
  +    /**
  +     * Whether authentication is required to use
  +     * this SMTP server.
  +     */
  +    private boolean authRequired = false;
  +
  +
  +    /**
  +     * Whether the server verifies that the user
  +     * actually sending an email matches the
  +     * authentication credentials attached to the
  +     * SMTP interaction.
  +     */
  +    private boolean verifyIdentity = false;
  +
  +
  +    /**
  +     * The maximum message size allowed by this SMTP server.  The default
  +     * value, 0, means no limit.
  +     */
  +    private long maxmessagesize = 0;
  +
  +    /**
  +     * The number of bytes to read before resetting the connection timeout
  +     * timer.  Defaults to 20,000 bytes.
  +     */
  +    private int lengthReset = 20000;
                                       
  -    private TimeScheduler scheduler;    // The scheduler used to handle timeouts for the SMTP
  -                                        // interaction
  +    /**
  +     * The scheduler used to handle timeouts for the SMTP interaction.
  +     */
  +    private TimeScheduler scheduler;
   
  -    private UsersRepository users;      // The user repository for this server - used to authenticate
  -                                        // users.
  +    /**
  +     * The user repository for this server - used to authenticate
  +     * users.
  +     */
  +    private UsersRepository users;
   
  -    private MailServer mailServer;      // The internal mail server service
  +    /**
  +     * The internal mail server service.
  +     */
  +    private MailServer mailServer;
   
  -    private HashMap state = new HashMap();  // The hash map that holds variables for the SMTP
  -                                            // session in progress.
  +    /**
  +     * The hash map that holds variables for the SMTP message transfer in progress.
  +     *
  +     * This hash map should only be used to store variable set in a particular
  +     * set of sequential MAIL-RCPT-DATA commands, as described in RFC 2821.  Per
  +     * connection values should be stored as member variables in this class.
  +     */
  +    private HashMap state = new HashMap();
   
       /**
        * Pass the <code>ComponentManager</code> to the <code>composer</code>.
  @@ -214,7 +334,7 @@
   
           try {
               // Initially greet the connector
  -            // Format is:  Sat,  24 Jan 1998 13:16:09 -0500
  +            // Format is:  Sat, 24 Jan 1998 13:16:09 -0500
   
               final PeriodicTimeTrigger trigger = new PeriodicTimeTrigger( timeout, -1 );
               scheduler.addTrigger( this.toString(), trigger, this );
  @@ -227,11 +347,9 @@
                       .append(") ready ")
                       .append(rfc822DateFormat.format(new Date()));
               String responseString = responseBuffer.toString();
  -            out.println(responseString);
  -            out.flush();
  -            logResponseString(responseString);
  +            writeLoggedFlushedResponse(responseString);
   
  -            while (parseCommand(inReader.readLine())) {
  +            while (parseCommand(readCommandLine())) {
                   scheduler.resetTrigger(this.toString());
               }
               getLogger().debug("Closing socket.");
  @@ -287,6 +405,8 @@
        * being used as a timeout, so the method simply closes the connection.
        *
        * @param triggerName the name of the trigger
  +     *
  +     * TODO: Remove this interface from the class and change the mechanism
        */
       public void targetTriggered(final String triggerName) {
           getLogger().error("Connection timeout on socket");
  @@ -294,26 +414,83 @@
               out.println("Connection timeout. Closing connection");
               socket.close();
           } catch (IOException e) {
  +            // Ignored
           }
       }
   
       /**
  +     * This method logs at a "DEBUG" level the response string that 
  +     * was sent to the SMTP client.  The method is provided largely
  +     * as syntactic sugar to neaten up the code base.  It is declared
  +     * private and final to encourage compiler inlining.
  +     *
  +     * @param responseString the response string sent to the client
  +     */
  +    private final void logResponseString(String responseString) {
  +        if (getLogger().isDebugEnabled()) {
  +            getLogger().debug("Sent: " + responseString);
  +        }
  +    }
  +
  +    /**
  +     * Write and flush a response string.  The response is also logged.
  +     * Should be used for the last line of a multi-line response or
  +     * for a single line response.
  +     *
  +     * @param responseString the response string sent to the client
  +     */
  +    final void writeLoggedFlushedResponse(String responseString) {
  +        out.println(responseString);
  +        out.flush();
  +        logResponseString(responseString);
  +    }
  +
  +    /**
  +     * Write a response string.  The response is also logged. 
  +     * Used for multi-line responses.
  +     *
  +     * @param responseString the response string sent to the client
  +     */
  +    final void writeLoggedResponse(String responseString) {
  +        out.println(responseString);
  +        out.flush();
  +        logResponseString(responseString);
  +    }
  +
  +    /**
  +     * Reads a line of characters off the command line.
  +     *
  +     * @return the trimmed input line
  +     * @throws IOException if an exception is generated reading in the input characters
  +     */
  +    final String readCommandLine() throws IOException {
  +        return inReader.readLine().trim();
  +    }
  +
  +    /**
  +     * Sets the user name associated with this SMTP interaction.
  +     *
  +     * @param userID the user name
  +     */
  +    private void setUser(String userID) {
  +        authenticatedUser = userID;
  +    }
  +
  +    /**
  +     * Returns the user name associated with this SMTP interaction.
  +     *
  +     * @return the user name
  +     */
  +    private String getUser() {
  +        return authenticatedUser;
  +    }
  +
  +    /**
        * Resets message-specific, but not authenticated user, state.
        *
        */
       private void resetState() {
  -        String user = (String) state.get(AUTH);
           state.clear();
  -        state.put(SERVER_NAME, this.helloName);
  -        state.put(SERVER_TYPE, SOFTWARE_TYPE);
  -        state.put(REMOTE_NAME, remoteHost);
  -        state.put(REMOTE_IP, remoteIP);
  -        state.put(SMTP_ID, smtpID);
  -        // seems that after authenticating an smtp client sends
  -        // a RSET, so we need to remember that they are authenticated
  -        if (user != null) {
  -            state.put(AUTH, user);
  -        }
       }
   
       /**
  @@ -324,52 +501,56 @@
        * to parse the raw command string to determine exactly which handler should
        * be called.  It returns true if expecting additional commands, false otherwise.
        *
  -     * @param commandRaw the raw command string passed in over the socket
  +     * @param rawCommand the raw command string passed in over the socket
        *
        * @return whether additional commands are expected.
        */
  -    private boolean parseCommand(String command) throws Exception {
  +    private boolean parseCommand(String rawCommand) throws Exception {
           String argument = null;
  -        String argument1 = null;
           boolean returnValue = true;
  +        String command = rawCommand;
   
  -        if (command == null) {
  +        if (rawCommand == null) {
               return false;
           }
           if ((state.get(MESG_FAILED) == null) && (getLogger().isDebugEnabled())) {
               getLogger().debug("Command received: " + command);
           }
  -        command = command.trim();
  -        if (command.indexOf(" ") > 0) {
  -            argument = command.substring(command.indexOf(" ") + 1);
  -            command = command.substring(0, command.indexOf(" "));
  -            if (argument.indexOf(":") > 0) {
  -                argument1 = argument.substring(argument.indexOf(":") + 1);
  -                argument = argument.substring(0, argument.indexOf(":"));
  -            }
  +        int spaceIndex = command.indexOf(" ");
  +        if (spaceIndex > 0) {
  +            argument = command.substring(spaceIndex + 1);
  +            command = command.substring(0, spaceIndex);
           }
           command = command.toUpperCase(Locale.US);
  -        if (command.equals("HELO")) {
  -            doHELO(command, argument, argument1);
  -        } else if (command.equals("EHLO")) {
  -            doEHLO(command, argument, argument1);
  -        } else if (command.equals("AUTH")) {
  -            doAUTH(command, argument, argument1);
  -        } else if (command.equals("MAIL")) {
  -            doMAIL(command, argument, argument1);
  -        } else if (command.equals("RCPT")) {
  -            doRCPT(command, argument, argument1);
  -        } else if (command.equals("NOOP")) {
  -            doNOOP(command, argument, argument1);
  -        } else if (command.equals("RSET")) {
  -            doRSET(command, argument, argument1);
  -        } else if (command.equals("DATA")) {
  -            doDATA(command, argument, argument1);
  -        } else if (command.equals("QUIT")) {
  -            doQUIT(command, argument, argument1);
  +        if (command.equals(COMMAND_HELO)) {
  +            doHELO(argument);
  +        } else if (command.equals(COMMAND_EHLO)) {
  +            doEHLO(argument);
  +        } else if (command.equals(COMMAND_AUTH)) {
  +            doAUTH(argument);
  +        } else if (command.equals(COMMAND_MAIL)) {
  +            doMAIL(argument);
  +        } else if (command.equals(COMMAND_RCPT)) {
  +            doRCPT(argument);
  +        } else if (command.equals(COMMAND_NOOP)) {
  +            doNOOP(argument);
  +        } else if (command.equals(COMMAND_RSET)) {
  +            doRSET(argument);
  +        } else if (command.equals(COMMAND_DATA)) {
  +            doDATA(argument);
  +        } else if (command.equals(COMMAND_QUIT)) {
  +            doQUIT(argument);
               returnValue = false;
  +        } else if (command.equals(COMMAND_VRFY)) {
  +            doVRFY(argument);
  +        } else if (command.equals(COMMAND_EXPN)) {
  +            doEXPN(argument);
  +        } else if (command.equals(COMMAND_HELP)) {
  +            doHELP(argument);
           } else {
  -            doUnknownCmd(command, argument, argument1);
  +            if (state.get(MESG_FAILED) == null)  {
  +                doUnknownCmd(command, argument);
  +            }
           }
           return returnValue;
       }
  @@ -379,26 +560,16 @@
        * Responds with a greeting and informs the client whether
        * client authentication is required.
        *
  -     * @param command the command parsed by the parseCommand method
  -     * @argument the first argument parsed by the parseCommand method
  -     * @argument1 the second argument parsed by the parseCommand method
  +     * @param argument the argument passed in with the command by the SMTP client
        */
  -    private void doHELO(String command, String argument, String argument1) {
  +    private void doHELO(String argument) {
           String responseString = null;
  -        if (state.containsKey(CURRENT_HELO_MODE)) {
  -            StringBuffer responseBuffer =
  -                    new StringBuffer(96)
  -                            .append("250 ")
  -                            .append(state.get(SERVER_NAME))
  -                            .append(" Duplicate HELO");
  -            responseString = responseBuffer.toString();
  -            out.println(responseString);
  -        } else if (argument == null) {
  -            responseString = "501 domain address required: " + command;
  -            out.println(responseString);
  +        if (argument == null) {
  +            responseString = "501 Domain address required: " + COMMAND_HELO;
  +            writeLoggedFlushedResponse(responseString);
           } else {
  -            state.put(CURRENT_HELO_MODE, command);
  -            state.put(NAME_GIVEN, argument);
  +            resetState();
  +            state.put(CURRENT_HELO_MODE, COMMAND_HELO);
               StringBuffer responseBuffer = new StringBuffer(256);
               if (authRequired) {
                   //This is necessary because we're going to do a multiline response
  @@ -406,13 +577,13 @@
               } else {
                   responseBuffer.append("250 ");
               }
  -            responseBuffer.append(state.get(SERVER_NAME))
  +            responseBuffer.append(helloName)
                             .append(" Hello ")
                             .append(argument)
                             .append(" (")
  -                          .append(state.get(REMOTE_NAME))
  +                          .append(remoteHost)
                             .append(" [")
  -                          .append(state.get(REMOTE_IP))
  +                          .append(remoteIP)
                             .append("])");
               responseString = responseBuffer.toString();
               out.println(responseString);
  @@ -431,31 +602,20 @@
        * Responds with a greeting and informs the client whether
        * client authentication is required.
        *
  -     * @param command the command parsed by the parseCommand method
  -     * @argument the first argument parsed by the parseCommand method
  -     * @argument1 the second argument parsed by the parseCommand method
  +     * @param argument the argument passed in with the command by the SMTP client
        */
  -    private void doEHLO(String command, String argument, String argument1) {
  +    private void doEHLO(String argument) {
           String responseString = null;
  -        if (state.containsKey(CURRENT_HELO_MODE)) {
  -            StringBuffer responseBuffer = 
  -                new StringBuffer(96)
  -                        .append("250 ")
  -                        .append(state.get(SERVER_NAME))
  -                        .append(" Duplicate EHLO");
  -            responseString = responseBuffer.toString();
  -            out.println(responseString);
  -        } else if (argument == null) {
  -            responseString = "501 domain address required: " + command;
  -            out.println(responseString);
  +        if (argument == null) {
  +            responseString = "501 Domain address required: " + COMMAND_EHLO;
  +            writeLoggedFlushedResponse(responseString);
           } else {
  -            state.put(CURRENT_HELO_MODE, command);
  -            state.put(NAME_GIVEN, argument);
  +            resetState();
  +            state.put(CURRENT_HELO_MODE, COMMAND_EHLO);
               // Extension defined in RFC 1870
               if (maxmessagesize > 0) {
                   responseString = "250-SIZE " + maxmessagesize;
  -                out.println(responseString);
  -                logResponseString(responseString);
  +                writeLoggedResponse(responseString);
               }
               StringBuffer responseBuffer = new StringBuffer(256);
               if (authRequired) {
  @@ -464,182 +624,204 @@
               } else {
                   responseBuffer.append("250 ");
               }
  -            responseBuffer.append(state.get(SERVER_NAME))
  +            responseBuffer.append(helloName)
                              .append(" Hello ")
                              .append(argument)
                              .append(" (")
  -                           .append(state.get(REMOTE_NAME))
  +                           .append(remoteHost)
                              .append(" [")
  -                           .append(state.get(REMOTE_IP))
  +                           .append(remoteIP)
                              .append("])");
               responseString = responseBuffer.toString();
  -            out.println(responseString);
               if (authRequired) {
  -                logResponseString(responseString);
  +                writeLoggedResponse(responseString);
                   responseString = "250 AUTH LOGIN PLAIN";
  -                out.println(responseString);
               }
  +            writeLoggedFlushedResponse(responseString);
           }
  -        out.flush();
  -        logResponseString(responseString);
       }
   
       /**
        * Handler method called upon receipt of a AUTH command.
        * Handles client authentication to the SMTP server.
        *
  -     * @param command the command parsed by the parseCommand method
  -     * @argument the first argument parsed by the parseCommand method
  -     * @argument1 the second argument parsed by the parseCommand method
  +     * @param argument the argument passed in with the command by the SMTP client
        */
  -    private void doAUTH(String command, String argument, String argument1)
  +    private void doAUTH(String argument)
               throws Exception {
           String responseString = null;
  -        if (state.containsKey(AUTH)) {
  +        if (getUser() != null) {
               responseString = "503 User has previously authenticated. "
                           + " Further authentication is not required!";
  -            out.println(responseString);
  +            writeLoggedFlushedResponse(responseString);
           } else if (argument == null) {
               responseString = "501 Usage: AUTH (authentication type) <challenge>";
  -            out.println(responseString);
  +            writeLoggedFlushedResponse(responseString);
           } else {
  -            if ((argument1 == null) && (argument.indexOf(" ") > 0)) {
  -                argument1 = argument.substring(argument.indexOf(" ")+1);
  +            String initialResponse = null;
  +            if ((argument != null) && (argument.indexOf(" ") > 0)) {
  +                initialResponse = argument.substring(argument.indexOf(" ") + 1);
                   argument = argument.substring(0,argument.indexOf(" "));
               }
  -            argument = argument.toUpperCase(Locale.US);
  -            if (argument.equals("PLAIN")) {
  -                String userpass = null, user = null, pass = null;
  -                StringTokenizer authTokenizer;
  -                if (argument1 == null) {
  -                    responseString = "334 OK. Continue authentication";
  -                    out.println(responseString);
  -                    out.flush();
  -                    logResponseString(responseString);
  -                    userpass = inReader.readLine().trim();
  -                } else {
  -                    userpass = argument1.trim();
  -                }
  -                try {
  -                    if (userpass != null) {
  -                        userpass = Base64.decodeAsString(userpass);
  -                    }
  -                    if (userpass != null) {
  -                        authTokenizer = new StringTokenizer(userpass, "\0");
  -                        user = authTokenizer.nextToken();
  -                        pass = authTokenizer.nextToken();
  -                    }
  -                }
  -                catch (Exception e) {
  -                    // Ignored - this exception in parsing will be dealt
  -                    // with in the if clause below
  -                }
  -                // Authenticate user
  -                if ((user == null) || (pass == null)) {
  -                    responseString = "501 Could not decode parameters for AUTH PLAIN";
  -                    out.println(responseString);
  -                    out.flush();
  -                } else if (users.test(user, pass)) {
  -                    state.put(AUTH, user);
  -                    responseString = "235 Authentication Successful";
  -                    out.println(responseString);
  -                    getLogger().info("AUTH method PLAIN succeeded");
  -                } else {
  -                    responseString = "535 Authentication Failed";
  -                    out.println(responseString);
  -                    getLogger().error("AUTH method PLAIN failed");
  -                }
  -                logResponseString(responseString);
  +            String authType = argument.toUpperCase(Locale.US);
  +            if (authType.equals(AUTH_TYPE_PLAIN)) {
  +                doPlainAuth(initialResponse);
                   return;
  -            } else if (argument.equals("LOGIN")) {
  -                String user = null, pass = null;
  -                if (argument1 == null) {
  -                    responseString = "334 VXNlcm5hbWU6"; // base64 encoded "Username:"
  -                    out.println(responseString);
  -                    out.flush();
  -                    logResponseString(responseString);
  -                    user = inReader.readLine().trim();
  -                } else {
  -                    user = argument1.trim();
  -                }
  -                if (user != null) {
  -                    try {
  -                        user = Base64.decodeAsString(user);
  -                    } catch (Exception e) {
  -                        // Ignored - this parse error will be
  -                        // addressed in the if clause below
  -                        user = null;
  -                    }
  -                }
  -                responseString = "334 UGFzc3dvcmQ6"; // base64 encoded "Password:"
  -                out.println(responseString);
  -                out.flush();
  -                logResponseString(responseString);
  -                pass = inReader.readLine().trim();
  -                if (pass != null) {
  -                    try {
  -                        pass = Base64.decodeAsString(pass);
  -                    } catch (Exception e) {
  -                        // Ignored - this parse error will be
  -                        // addressed in the if clause below
  -                        pass = null;
  -                    }
  -                }
  -                // Authenticate user
  -                if ((user == null) || (pass == null)) {
  -                    responseString = "501 Could not decode parameters for AUTH LOGIN";
  -                    out.println(responseString);
  -                } else if (users.test(user, pass)) {
  -                    state.put(AUTH, user);
  -                    responseString = "235 Authentication Successful";
  -                    out.println(responseString);
  -                    getLogger().info("AUTH method LOGIN succeeded");
  -                } else {
  -                    responseString = "535 Authentication Failed";
  -                    out.println(responseString);
  -                    getLogger().error("AUTH method LOGIN failed");
  -                }
  -                out.flush();
  -                logResponseString(responseString);
  +            } else if (authType.equals(AUTH_TYPE_LOGIN)) {
  +                doLoginAuth(initialResponse);
                   return;
               } else {
  -                responseString = "504 Unrecognized Authentication Type";
  -                out.println(responseString);
  -                logResponseString(responseString);
  -                if (getLogger().isErrorEnabled()) {
  -                    StringBuffer errorBuffer =
  -                        new StringBuffer(128)
  -                            .append("AUTH method ")
  -                            .append(argument)
  -                            .append(" is an unrecognized authentication type");
  -                    getLogger().error(errorBuffer.toString());
  -                }
  +                doUnknownAuth(authType, initialResponse);
                   return;
               }
           }
  -        out.flush();
  -        logResponseString(responseString);
  +    }
  +
  +    /**
  +     * Carries out the Plain AUTH SASL exchange.
  +     *
  +     * @param initialResponse the initial response line passed in with the AUTH command
  +     */
  +    private void doPlainAuth(String initialResponse)
  +            throws IOException {
  +        String userpass = null, user = null, pass = null, responseString = null;
  +        if (initialResponse == null) {
  +            responseString = "334 OK. Continue authentication";
  +            writeLoggedFlushedResponse(responseString);
  +            userpass = readCommandLine();
  +        } else {
  +            userpass = initialResponse.trim();
  +        }
  +        try {
  +            if (userpass != null) {
  +                userpass = Base64.decodeAsString(userpass);
  +            }
  +            if (userpass != null) {
  +                StringTokenizer authTokenizer = new StringTokenizer(userpass, "\0");
  +                user = authTokenizer.nextToken();
  +                pass = authTokenizer.nextToken();
  +            }
  +        }
  +        catch (Exception e) {
  +            // Ignored - this exception in parsing will be dealt
  +            // with in the if clause below
  +        }
  +        // Authenticate user
  +        if ((user == null) || (pass == null)) {
  +            responseString = "501 Could not decode parameters for AUTH PLAIN";
  +            writeLoggedFlushedResponse(responseString);
  +        } else if (users.test(user, pass)) {
  +            setUser(user);
  +            responseString = "235 Authentication Successful";
  +            writeLoggedFlushedResponse(responseString);
  +            getLogger().info("AUTH method PLAIN succeeded");
  +        } else {
  +            responseString = "535 Authentication Failed";
  +            writeLoggedFlushedResponse(responseString);
  +            getLogger().error("AUTH method PLAIN failed");
  +        }
  +        return;
  +    }
  +
  +    /**
  +     * Carries out the Login AUTH SASL exchange.
  +     *
  +     * @param initialResponse the initial response line passed in with the AUTH command
  +     */
  +    private void doLoginAuth(String initialResponse)
  +            throws IOException {
  +        String user = null, pass = null, responseString = null;
  +        if (initialResponse == null) {
  +            responseString = "334 VXNlcm5hbWU6"; // base64 encoded "Username:"
  +            writeLoggedFlushedResponse(responseString);
  +            user = readCommandLine();
  +        } else {
  +            user = initialResponse.trim();
  +        }
  +        if (user != null) {
  +            try {
  +                user = Base64.decodeAsString(user);
  +            } catch (Exception e) {
  +                // Ignored - this parse error will be
  +                // addressed in the if clause below
  +                user = null;
  +            }
  +        }
  +        responseString = "334 UGFzc3dvcmQ6"; // base64 encoded "Password:"
  +        writeLoggedFlushedResponse(responseString);
  +        pass = readCommandLine();
  +        if (pass != null) {
  +            try {
  +                pass = Base64.decodeAsString(pass);
  +            } catch (Exception e) {
  +                // Ignored - this parse error will be
  +                // addressed in the if clause below
  +                pass = null;
  +            }
  +        }
  +        // Authenticate user
  +        if ((user == null) || (pass == null)) {
  +            responseString = "501 Could not decode parameters for AUTH LOGIN";
  +        } else if (users.test(user, pass)) {
  +            setUser(user);
  +            responseString = "235 Authentication Successful";
  +            if (getLogger().isDebugEnabled()) {
  +                // TODO: Make this string a more useful debug message
  +                getLogger().debug("AUTH method LOGIN succeeded");
  +            }
  +        } else {
  +            responseString = "535 Authentication Failed";
  +            // TODO: Make this string a more useful error message
  +            getLogger().error("AUTH method LOGIN failed");
  +        }
  +        writeLoggedFlushedResponse(responseString);
  +        return;
  +    }
  +
  +    /**
  +     * Handles the case of an unrecognized auth type.
  +     *
  +     * @param authType the unknown auth type
  +     * @param initialResponse the initial response line passed in with the AUTH command
  +     */
  +    private void doUnknownAuth(String authType, String initialResponse) {
  +        String responseString = "504 Unrecognized Authentication Type";
  +        writeLoggedFlushedResponse(responseString);
  +        if (getLogger().isErrorEnabled()) {
  +            StringBuffer errorBuffer =
  +                new StringBuffer(128)
  +                    .append("AUTH method ")
  +                        .append(authType)
  +                        .append(" is an unrecognized authentication type");
  +            getLogger().error(errorBuffer.toString());
  +        }
  +        return;
       }
   
       /**
        * Handler method called upon receipt of a MAIL command.
        * Sets up handler to deliver mail as the stated sender.
        *
  -     * @param command the command parsed by the parseCommand method
  -     * @argument the first argument parsed by the parseCommand method
  -     * @argument1 the second argument parsed by the parseCommand method
  +     * @param argument the argument passed in with the command by the SMTP client
        */
  -    private void doMAIL(String command, String argument, String argument1) {
  +    private void doMAIL(String argument) {
           String responseString = null;
  +
  +        String sender = null;
  +        if ((argument != null) && (argument.indexOf(":") > 0)) {
  +            int colonIndex = argument.indexOf(":");
  +            sender = argument.substring(colonIndex + 1);
  +            argument = argument.substring(0, colonIndex);
  +        }
           if (state.containsKey(SENDER)) {
               responseString = "503 Sender already specified";
  -            out.println(responseString);
  +            writeLoggedFlushedResponse(responseString);
           } else if (argument == null || !argument.toUpperCase(Locale.US).equals("FROM")
  -                   || argument1 == null) {
  +                   || sender == null) {
               responseString = "501 Usage: MAIL FROM:<sender>";
  -            out.println(responseString);
  +            writeLoggedFlushedResponse(responseString);
           } else {
  -            String sender = argument1.trim();
  +            sender = sender.trim();
               int lastChar = sender.lastIndexOf('>');
               // Check to see if any options are present and, if so, whether they are correctly formatted
               // (separated from the closing angle bracket by a ' ').
  @@ -662,37 +844,9 @@
   
                       // Handle the SIZE extension keyword
   
  -                    // TODO: Encapsulate option logic in a method
  -                    if (mailOptionName.startsWith("SIZE")) {
  -                        int size = 0;
  -                        try {
  -                            size = Integer.parseInt(mailOptionValue);
  -                        } catch (NumberFormatException pe) {
  -                            // This is a malformed option value.  We ignore it
  -                            // and proceed to the next option.
  -                            continue;
  -                        }
  -                        if (getLogger().isDebugEnabled()) {
  -                            StringBuffer debugBuffer = 
  -                                new StringBuffer(128)
  -                                    .append("MAIL command option SIZE received with value ")
  -                                    .append(size)
  -                                    .append(".");
  -                            getLogger().debug(debugBuffer.toString());
  -                        }
  -                        if ((maxmessagesize > 0) && (size > maxmessagesize)) {
  -                            // Let the client know that the size limit has been hit.
  -                            responseString = "552 Message size exceeds fixed maximum message size";
  -                            out.println(responseString);
  -                            out.flush();
  -    
  -                            logResponseString(responseString);
  -                            getLogger().error(responseString);
  +                    if (mailOptionName.startsWith(MAIL_OPTION_SIZE)) {
  +                        if (!(doMailSize(mailOptionValue))) {
                               return;
  -                        } else {
  -                            // put the message size in the message state so it can be used
  -                            // later to restrict messages for user quotas, etc.
  -                            state.put(MESG_SIZE, new Integer(size));
                           }
                       } else {
                           // Unexpected option attached to the Mail command
  @@ -709,9 +863,8 @@
                   }
               }
               if (!sender.startsWith("<") || !sender.endsWith(">")) {
  -                responseString = "501 Syntax error in parameters or arguments";
  -                out.println(responseString);
  -                logResponseString(responseString);
  +                responseString = "501 Syntax error in MAIL command";
  +                writeLoggedFlushedResponse(responseString);
                   if (getLogger().isErrorEnabled()) {
                       StringBuffer errorBuffer =
                           new StringBuffer(128)
  @@ -734,9 +887,8 @@
                   try {
                       senderAddress = new MailAddress(sender);
                   } catch (Exception pe) {
  -                    responseString = "501 Syntax error in parameters or arguments";
  -                    out.println(responseString);
  -                    logResponseString(responseString);
  +                    responseString = "501 Syntax error in sender address";
  +                    writeLoggedFlushedResponse(responseString);
                       if (getLogger().isErrorEnabled()) {
                           StringBuffer errorBuffer = 
                               new StringBuffer(256)
  @@ -756,35 +908,89 @@
                           .append(sender)
                           .append("> OK");
               responseString = responseBuffer.toString();
  -            out.println(responseString);
  +            writeLoggedFlushedResponse(responseString);
           }
  -        out.flush();
  -        logResponseString(responseString);
       }
   
       /**
  +     * Handles the SIZE MAIL option.
  +     *
  +     * @param mailOptionValue the option string passed in with the SIZE option
  +     * @returns true if further options should be processed, false otherwise
  +     */
  +    private boolean doMailSize(String mailOptionValue) {
  +        int size = 0;
  +        try {
  +            size = Integer.parseInt(mailOptionValue);
  +        } catch (NumberFormatException pe) {
  +            // This is a malformed option value.  We return an error
  +            String responseString = "501 Syntactically incorrect value for SIZE parameter";
  +            writeLoggedFlushedResponse(responseString);
  +            getLogger().error("Rejected syntactically incorrect value for SIZE parameter.");
  +            return true;
  +        }
  +        if (getLogger().isDebugEnabled()) {
  +            StringBuffer debugBuffer = 
  +                new StringBuffer(128)
  +                    .append("MAIL command option SIZE received with value ")
  +                    .append(size)
  +                    .append(".");
  +                    getLogger().debug(debugBuffer.toString());
  +        }
  +        if ((maxmessagesize > 0) && (size > maxmessagesize)) {
  +            // Let the client know that the size limit has been hit.
  +            String responseString = "552 Message size exceeds fixed maximum message size";
  +            writeLoggedFlushedResponse(responseString);
  +            StringBuffer errorBuffer =
  +                new StringBuffer(256)
  +                    .append("Rejected message from ")
  +                    .append(state.get(SENDER).toString())
  +                    .append(" from host ")
  +                    .append(remoteHost)
  +                    .append(" of size ")
  +                    .append(size)
  +                    .append(" exceeding system maximum message size of ")
  +                    .append(maxmessagesize)
  +                    .append("based on SIZE option.");
  +            getLogger().error(errorBuffer.toString());
  +            return true;
  +        } else {
  +            // put the message size in the message state so it can be used
  +            // later to restrict messages for user quotas, etc.
  +            state.put(MESG_SIZE, new Integer(size));
  +        }
  +        return false;
  +    }
  +
  +
  +    /**
        * Handler method called upon receipt of a RCPT command.
        * Reads recipient.  Does some connection validation.
        *
  -     * @param command the command parsed by the parseCommand method
  -     * @argument the first argument parsed by the parseCommand method
  -     * @argument1 the second argument parsed by the parseCommand method
  +     * @param argument the argument passed in with the command by the SMTP client
        */
  -    private void doRCPT(String command, String argument, String argument1) {
  +    private void doRCPT(String argument) {
           String responseString = null;
  +
  +        String recipient = null;
  +        if ((argument != null) && (argument.indexOf(":") > 0)) {
  +            int colonIndex = argument.indexOf(":");
  +            recipient = argument.substring(colonIndex + 1);
  +            argument = argument.substring(0, colonIndex);
  +        }
           if (!state.containsKey(SENDER)) {
               responseString = "503 Need MAIL before RCPT";
  -            out.println(responseString);
  +            writeLoggedFlushedResponse(responseString);
           } else if (argument == null || !argument.toUpperCase(Locale.US).equals("TO")
  -                   || argument1 == null) {
  +                   || recipient == null) {
               responseString = "501 Usage: RCPT TO:<recipient>";
  -            out.println(responseString);
  +            writeLoggedFlushedResponse(responseString);
           } else {
               Collection rcptColl = (Collection) state.get(RCPT_VECTOR);
               if (rcptColl == null) {
                   rcptColl = new Vector();
               }
  -            String recipient = argument1.trim();
  +            recipient = recipient.trim();
               int lastChar = recipient.lastIndexOf('>');
               // Check to see if any options are present and, if so, whether they are correctly formatted
               // (separated from the closing angle bracket by a ' ').
  @@ -818,9 +1024,7 @@
               }
               if (!recipient.startsWith("<") || !recipient.endsWith(">")) {
                   responseString = "501 Syntax error in parameters or arguments";
  -                out.println(responseString);
  -                out.flush();
  -                logResponseString(responseString);
  +                writeLoggedFlushedResponse(responseString);
                   if (getLogger().isErrorEnabled()) {
                       StringBuffer errorBuffer = 
                           new StringBuffer(192)
  @@ -840,9 +1044,8 @@
               try {
                   recipientAddress = new MailAddress(recipient);
               } catch (Exception pe) {
  -                responseString = "501 Syntax error in parameters or arguments";
  -                out.println(responseString);
  -                logResponseString(responseString);
  +                responseString = "501 Syntax error in recipient address";
  +                writeLoggedFlushedResponse(responseString);
   
                   if (getLogger().isErrorEnabled()) {
                       StringBuffer errorBuffer = 
  @@ -858,42 +1061,25 @@
               if (authRequired) {
                   // Make sure the mail is being sent locally if not
                   // authenticated else reject.
  -                if (!state.containsKey(AUTH)) {
  +                if (getUser() == null) {
                       String toDomain = recipientAddress.getHost();
                       if (!mailServer.isLocalServer(toDomain)) {
                           responseString = "530 Authentication Required";
  -                        out.println(responseString);
  -                        logResponseString(responseString);
  -                        getLogger().error("Authentication is required for mail request");
  +                        writeLoggedFlushedResponse(responseString);
  +                        getLogger().error("Rejected message - authentication is required for mail request");
                           return;
                       }
                   } else {
                       // Identity verification checking
                       if (verifyIdentity) {
  -                        String authUser = ((String) state.get(AUTH)).toLowerCase(Locale.US);
  +                        String authUser = (getUser()).toLowerCase(Locale.US);
                           MailAddress senderAddress = (MailAddress) state.get(SENDER);
                           boolean domainExists = false;
   
  -                        if (!authUser.equals(senderAddress.getUser())) {
  +                        if ((!authUser.equals(senderAddress.getUser())) ||
  +                            (!mailServer.isLocalServer(senderAddress.getHost()))) {
                               responseString = "503 Incorrect Authentication for Specified Email Address";
  -                            out.println(responseString);
  -                            logResponseString(responseString);
  -                            if (getLogger().isErrorEnabled()) {
  -                                StringBuffer errorBuffer =
  -                                    new StringBuffer(128)
  -                                        .append("User ")
  -                                        .append(authUser)
  -                                        .append(" authenticated, however tried sending email as ")
  -                                        .append(senderAddress);
  -                                getLogger().error(errorBuffer.toString());
  -                            }
  -                            return;
  -                        }
  -                        if (!mailServer.isLocalServer(
  -                                           senderAddress.getHost())) {
  -                            responseString = "503 Incorrect Authentication for Specified Email Address";
  -                            out.println(responseString);
  -                            logResponseString(responseString);
  +                            writeLoggedFlushedResponse(responseString);
                               if (getLogger().isErrorEnabled()) {
                                   StringBuffer errorBuffer =
                                       new StringBuffer(128)
  @@ -916,40 +1102,36 @@
                           .append(recipient)
                           .append("> OK");
               responseString = responseBuffer.toString();
  -            out.println(responseString);
  +            writeLoggedFlushedResponse(responseString);
           }
  -        out.flush();
  -        logResponseString(responseString);
       }
   
       /**
        * Handler method called upon receipt of a NOOP command.
        * Just sends back an OK and logs the command.
        *
  -     * @param command the command parsed by the parseCommand method
  -     * @argument the first argument parsed by the parseCommand method
  -     * @argument1 the second argument parsed by the parseCommand method
  +     * @param argument the argument passed in with the command by the SMTP client
        */
  -    private void doNOOP(String command, String argument, String argument1) {
  +    private void doNOOP(String argument) {
           String responseString = "250 OK";
  -        out.println(responseString);
  -        logResponseString(responseString);
  +        writeLoggedFlushedResponse(responseString);
       }
   
       /**
        * Handler method called upon receipt of a RSET command.
        * Resets message-specific, but not authenticated user, state.
        *
  -     * @param command the command parsed by the parseCommand method
  -     * @argument the first argument parsed by the parseCommand method
  -     * @argument1 the second argument parsed by the parseCommand method
  +     * @param argument the argument passed in with the command by the SMTP client
        */
  -    private void doRSET(String command, String argument, String argument1) {
  -        resetState();
  -        String responseString = "250 OK";
  -        out.println(responseString);
  -        out.flush();
  -        logResponseString(responseString);
  +    private void doRSET(String argument) {
  +        String responseString = "";
  +        if ((argument == null) || (argument.length() == 0)) {
  +            resetState();
  +            responseString = "250 OK";
  +        } else {
  +            responseString = "500 Unexpected argument provided with RSET command";
  +        }
  +        writeLoggedFlushedResponse(responseString);
       }
   
       /**
  @@ -957,23 +1139,23 @@
        * Reads in message data, creates header, and delivers to
        * mail server service for delivery.
        *
  -     * @param command the command parsed by the parseCommand method
  -     * @argument the first argument parsed by the parseCommand method
  -     * @argument1 the second argument parsed by the parseCommand method
  +     * @param argument the argument passed in with the command by the SMTP client
        */
  -    private void doDATA(String command, String argument, String argument1) {
  +    private void doDATA(String argument) {
           String responseString = null;
  +        if ((argument != null) && (argument.length() > 0)) {
  +            responseString = "500 Unexpected argument provided with DATA command";
  +            writeLoggedFlushedResponse(responseString);
  +        }
           if (!state.containsKey(SENDER)) {
               responseString = "503 No sender specified";
  -            out.println(responseString);
  +            writeLoggedFlushedResponse(responseString);
           } else if (!state.containsKey(RCPT_VECTOR)) {
               responseString = "503 No recipients specified";
  -            out.println(responseString);
  +            writeLoggedFlushedResponse(responseString);
           } else {
               responseString = "354 Ok Send data ending with <CRLF>.<CRLF>";
  -            out.println(responseString);
  -            out.flush();
  -            logResponseString(responseString);
  +            writeLoggedFlushedResponse(responseString);
               try {
                   // Setup the input stream to notify the scheduler periodically
                   InputStream msgIn =
  @@ -993,11 +1175,11 @@
                       }
                       msgIn = new SizeLimitedInputStream(msgIn, maxmessagesize);
                   }
  -                //Removes the dot stuffing
  +                // Removes the dot stuffing
                   msgIn = new SMTPInputStream(msgIn);
  -                //Parse out the message headers
  +                // Parse out the message headers
                   MailHeaders headers = new MailHeaders(msgIn);
  -                // if headers do not contains minimum REQUIRED headers fields,
  +                // If headers do not contains minimum REQUIRED headers fields,
                   // add them
                   if (!headers.isSet(RFC2822Headers.DATE)) {
                       headers.setHeader(RFC2822Headers.DATE, rfc822DateFormat.format(new Date()));
  @@ -1005,7 +1187,7 @@
                   if (!headers.isSet(RFC2822Headers.FROM) && state.get(SENDER) != null) {
                       headers.setHeader(RFC2822Headers.FROM, state.get(SENDER).toString());
                   }
  -                //Determine the Return-Path
  +                // Determine the Return-Path
                   String returnPath = headers.getHeader(RFC2822Headers.RETURN_PATH, "\r\n");
                   headers.removeHeader(RFC2822Headers.RETURN_PATH);
                   if (returnPath == null) {
  @@ -1020,19 +1202,19 @@
                           returnPath = returnPathBuffer.toString();
                       }
                   }
  -                //We will rebuild the header object to put Return-Path and our
  -                //  Received message at the top
  +                // We will rebuild the header object to put Return-Path and our
  +                // Received header at the top
                   Enumeration headerLines = headers.getAllHeaderLines();
                   headers = new MailHeaders();
  -                //Put the Return-Path first
  +                // Put the Return-Path first
                   headers.addHeaderLine(RFC2822Headers.RETURN_PATH + ": " + returnPath);
  -                //Put our Received header next
  +                // Put our Received header next
                   StringBuffer headerLineBuffer = 
                       new StringBuffer(128)
                               .append(RFC2822Headers.RECEIVED + ": from ")
  -                            .append(state.get(REMOTE_NAME))
  +                            .append(remoteHost)
                               .append(" ([")
  -                            .append(state.get(REMOTE_IP))
  +                            .append(remoteIP)
                               .append("])");
   
                   headers.addHeaderLine(headerLineBuffer.toString());
  @@ -1044,12 +1226,12 @@
                               .append(" (")
                               .append(SOFTWARE_TYPE)
                               .append(") with SMTP ID ")
  -                            .append(state.get(SMTP_ID));
  +                            .append(smtpID);
   
                   if (((Collection) state.get(RCPT_VECTOR)).size() == 1) {
  -                    //Only indicate a recipient if they're the only recipient
  -                    //(prevents email address harvesting and large headers in
  -                    // bulk email)
  +                    // Only indicate a recipient if they're the only recipient
  +                    // (prevents email address harvesting and large headers in
  +                    //  bulk email)
                       headers.addHeaderLine(headerLineBuffer.toString());
                       headerLineBuffer = 
                           new StringBuffer(256)
  @@ -1058,13 +1240,13 @@
                                   .append(">;");
                       headers.addHeaderLine(headerLineBuffer.toString());
                   } else {
  -                    //Put the ; on the end of the 'by' line
  +                    // Put the ; on the end of the 'by' line
                       headerLineBuffer.append(";");
                       headers.addHeaderLine(headerLineBuffer.toString());
                   }
                   headers.addHeaderLine("          " + rfc822DateFormat.format(new Date()));
   
  -                //Add all the original message headers back in next
  +                // Add all the original message headers back in next
                   while (headerLines.hasMoreElements()) {
                       headers.addHeaderLine((String) headerLines.nextElement());
                   }
  @@ -1075,104 +1257,133 @@
                           (MailAddress) state.get(SENDER),
                           (Vector) state.get(RCPT_VECTOR),
                           new SequenceInputStream(headersIn, msgIn));
  -                // if the message size limit has been set, we'll
  +                // If the message size limit has been set, we'll
                   // call mail.getSize() to force the message to be
                   // loaded. Need to do this to enforce the size limit
                   if (maxmessagesize > 0) {
                       mail.getMessageSize();
                   }
  -                mail.setRemoteHost((String) state.get(REMOTE_NAME));
  -                mail.setRemoteAddr((String) state.get(REMOTE_IP));
  +                mail.setRemoteHost(remoteHost);
  +                mail.setRemoteAddr(remoteIP);
                   mailServer.sendMail(mail);
  +                if (getLogger().isDebugEnabled()) {
  +                    getLogger().debug("Successfully sent mail to spool: " + mail.getName());
  +                }
               } catch (MessagingException me) {
  -                //Grab any exception attached to this one.
  +                // Grab any exception attached to this one.
                   Exception e = me.getNextException();
  -                //If there was an attached exception, and it's a
  -                //MessageSizeException
  +                // If there was an attached exception, and it's a
  +                // MessageSizeException
                   if (e != null && e instanceof MessageSizeException) {
                       // Add an item to the state to suppress
                       // logging of extra lines of data
                       // that are sent after the size limit has
                       // been hit.
                       state.put(MESG_FAILED, Boolean.TRUE);
  -                    //then let the client know that the size
  -                    //limit has been hit.
  +                    // then let the client know that the size
  +                    // limit has been hit.
                       responseString = "552 Error processing message: "
                                   + e.getMessage();
  +                    StringBuffer errorBuffer =
  +                        new StringBuffer(256)
  +                            .append("Rejected message from ")
  +                            .append(state.get(SENDER).toString())
  +                            .append(" from host ")
  +                            .append(remoteHost)
  +                            .append(" exceeding system maximum message size of ")
  +                            .append(maxmessagesize);
  +                    getLogger().error(errorBuffer.toString());
                   } else {
                       responseString = "451 Error processing message: "
                                   + me.getMessage();
  +                    getLogger().error("Unknown error occurred while processing DATA.", me);
                   }
  -                out.println(responseString);
  -                out.flush();
  -                logResponseString(responseString);
  -                getLogger().error(responseString);
  +                writeLoggedFlushedResponse(responseString);
                   return;
               }
  -            getLogger().info("Mail sent to Mail Server");
               resetState();
               responseString = "250 Message received";
  -            out.println(responseString);
  +            writeLoggedFlushedResponse(responseString);
           }
  -        out.flush();
  -        logResponseString(responseString);
       }
   
       /**
  -     * Handler method called upon receipt of a QUIT command.
  -     * This method informs the client that the connection is
  -     * closing.
  +     * Handler method called upon receipt of a VRFY command.
  +     * This method informs the client that the command is
  +     * not implemented.
        *
  -     * @param command the command parsed by the parseCommand method
  -     * @argument the first argument parsed by the parseCommand method
  -     * @argument1 the second argument parsed by the parseCommand method
  +     * @param argument the argument passed in with the command by the SMTP client
        */
  -    private void doQUIT(String command, String argument, String argument1) {
  -        StringBuffer responseBuffer =
  -            new StringBuffer(128)
  -                    .append("221 ")
  -                    .append(state.get(SERVER_NAME))
  -                    .append(" Service closing transmission channel");
  -        String responseString = responseBuffer.toString();
  -        out.println(responseString);
  -        out.flush();
  -        logResponseString(responseString);
  +    private void doVRFY(String argument) {
  +
  +        String responseString = "502 VRFY is not supported";
  +        writeLoggedFlushedResponse(responseString);
       }
   
       /**
  -     * Handler method called upon receipt of an unrecognized command.
  -     * Returns an error response and logs the command.
  +     * Handler method called upon receipt of a EXPN command.
  +     * This method informs the client that the command is
  +     * not implemented.
  +     *
  +     * @param argument the argument passed in with the command by the SMTP client
  +     */
  +    private void doEXPN(String argument) {
  +
  +        String responseString = "502 EXPN is not supported";
  +        writeLoggedFlushedResponse(responseString);
  +    }
  +
  +    /**
  +     * Handler method called upon receipt of a HELP command.
  +     * This method informs the client that the command is
  +     * not implemented.
  +     *
  +     * @param argument the argument passed in with the command by the SMTP client
  +     */
  +    private void doHELP(String argument) {
  +
  +        String responseString = "502 HELP is not supported";
  +        writeLoggedFlushedResponse(responseString);
  +    }
  +
  +    /**
  +     * Handler method called upon receipt of a QUIT command.
  +     * This method informs the client that the connection is
  +     * closing.
        *
  -     * @param command the command parsed by the parseCommand method
  -     * @argument the first argument parsed by the parseCommand method
  -     * @argument1 the second argument parsed by the parseCommand method
  +     * @param argument the argument passed in with the command by the SMTP client
        */
  -    private void doUnknownCmd(String command, String argument, String argument1) {
  -        if (state.get(MESG_FAILED) == null) {
  +    private void doQUIT(String argument) {
  +
  +        String responseString = "";
  +        if ((argument == null) || (argument.length() == 0)) {
               StringBuffer responseBuffer =
                   new StringBuffer(128)
  -                        .append("500 ")
  -                        .append(state.get(SERVER_NAME))
  -                        .append(" Syntax error, command unrecognized: ")
  -                        .append(command);
  -            String responseString = responseBuffer.toString();
  -            out.println(responseString);
  -            out.flush();
  -            logResponseString(responseString);
  +                        .append("221 ")
  +                        .append(helloName)
  +                        .append(" Service closing transmission channel");
  +            responseString = responseBuffer.toString();
  +        } else {
  +            responseString = "500 Unexpected argument provided with QUIT command";
           }
  +        writeLoggedFlushedResponse(responseString);
       }
   
       /**
  -     * This method logs at a "DEBUG" level the response string that 
  -     * was sent to the SMTP client.  The method is provided largely
  -     * as syntactic sugar to neaten up the code base.  It is declared
  -     * private and final to encourage compiler inlining.
  +     * Handler method called upon receipt of an unrecognized command.
  +     * Returns an error response and logs the command.
        *
  -     * @param responseString the response string sent to the client
  +     * @param command the command parsed by the SMTP client
  +     * @param argument the argument passed in with the command by the SMTP client
        */
  -    private final void logResponseString(String responseString) {
  -        if (getLogger().isDebugEnabled()) {
  -            getLogger().debug("Sent: " + responseString);
  -        }
  +    private void doUnknownCmd(String command, String argument) {
  +        StringBuffer responseBuffer =
  +            new StringBuffer(128)
  +                    .append("500 ")
  +                    .append(helloName)
  +                    .append(" Syntax error, command unrecognized: ")
  +                    .append(command);
  +        String responseString = responseBuffer.toString();
  +        writeLoggedFlushedResponse(responseString);
       }
   }
  
  
  

--
To unsubscribe, e-mail:   <ma...@jakarta.apache.org>
For additional commands, e-mail: <ma...@jakarta.apache.org>