Index: jakarta-james/src/java/org/apache/james/smtpserver/SMTPHandler.java
===================================================================
RCS file: /home/cvs/jakarta-james/src/java/org/apache/james/smtpserver/SMTPHandler.java,v
retrieving revision 1.27
diff -u -r1.27 SMTPHandler.java
--- jakarta-james/src/java/org/apache/james/smtpserver/SMTPHandler.java	26 Sep 2002 01:15:23 -0000	1.27
+++ jakarta-james/src/java/org/apache/james/smtpserver/SMTPHandler.java	30 Sep 2002 18:51:42 -0000
@@ -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;
@@ -41,7 +42,7 @@
  * @author Danny Angus <danny@thought.co.uk>
  * @author Peter M. Goldstein <farsight@alum.mit.edu>
  *
- * @version This is $Revision: 1.27 $
+ * @version This is $Revision: 1.26 $
  */
 public class SMTPHandler
     extends BaseConnectionHandler
@@ -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,172 @@
      */
     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";
 
-    private InputStream in;     // The incoming stream of bytes coming from the socket.
-    private PrintWriter out;    // The writer to which outgoing messages are written.
+    /**
+     * 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;
+
+    /**
+     * 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.
+     */
+    private HashMap state = new HashMap();
 
     /**
      * Pass the <code>ComponentManager</code> to the <code>composer</code>.
@@ -214,7 +331,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 +344,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 +402,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 +411,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 +498,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 +557,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 +574,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 +599,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 +621,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 ' ').
@@ -663,7 +842,7 @@
                     // Handle the SIZE extension keyword
 
                     // TODO: Encapsulate option logic in a method
-                    if (mailOptionName.startsWith("SIZE")) {
+                    if (mailOptionName.startsWith(MAIL_OPTION_SIZE)) {
                         int size = 0;
                         try {
                             size = Integer.parseInt(mailOptionValue);
@@ -683,10 +862,7 @@
                         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);
+                            writeLoggedFlushedResponse(responseString);
                             getLogger().error(responseString);
                             return;
                         } else {
@@ -709,9 +885,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 +909,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 +930,78 @@
                         .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(responseString);
+            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);
+            getLogger().error(responseString);
+            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 +1035,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 +1055,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 +1072,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);
+                        writeLoggedFlushedResponse(responseString);
                         getLogger().error("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())) {
-                            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())) {
+                        if ((!authUser.equals(senderAddress.getUser())) ||
+                            (!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 +1113,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 +1150,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 +1186,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 +1198,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 +1213,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 +1237,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 +1251,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 +1268,124 @@
                         (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();
                 } else {
                     responseString = "451 Error processing message: "
                                 + me.getMessage();
                 }
-                out.println(responseString);
-                out.flush();
-                logResponseString(responseString);
+                writeLoggedFlushedResponse(responseString);
                 getLogger().error(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 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 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 argument the argument passed in with the command by the SMTP client
+     */
+    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);
     }
 }

