Index: java/org/apache/james/smtpserver/SMTPHandler.java
===================================================================
--- java/org/apache/james/smtpserver/SMTPHandler.java	(revision 468922)
+++ java/org/apache/james/smtpserver/SMTPHandler.java	(working copy)
@@ -24,6 +24,7 @@
 import org.apache.avalon.framework.container.ContainerUtil;
 import org.apache.james.Constants;
 import org.apache.james.core.AbstractJamesHandler;
+import org.apache.james.util.CRLFDelimitedByteBuffer;
 import org.apache.james.util.CRLFTerminatedReader;
 import org.apache.james.util.watchdog.Watchdog;
 import org.apache.mailet.Mail;
@@ -31,6 +32,8 @@
 
 import java.io.IOException;
 import java.io.InputStream;
+import java.io.UnsupportedEncodingException;
+import java.net.Socket;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Date;
@@ -35,6 +38,7 @@
 import java.util.Collection;
 import java.util.Date;
 import java.util.HashMap;
+import java.util.LinkedList;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
@@ -48,7 +52,7 @@
  */
 public class SMTPHandler
     extends AbstractJamesHandler
-    implements SMTPSession {
+    implements SMTPSession, LineHandler {
 
     /**
      * The constants to indicate the current processing mode of the session
@@ -161,6 +165,16 @@
     private StringBuffer responseBuffer = new StringBuffer(256);
     
     private boolean stopHandlerProcessing = false;
+    
+    /**
+     * If not null every line is sent to this command handler instead
+     * of the default "command parsing -> dipatching" procedure.
+     */
+    private LinkedList lineHandlers;
+    /**
+     * Manage inputstream as a bytebuffer
+     */
+    private CRLFDelimitedByteBuffer bytebufferHandler;
 
     /**
      * Set the configuration data for the handler
@@ -175,6 +189,11 @@
         }
     }
     
+    protected void initHandler(Socket connection) throws IOException {
+        super.initHandler(connection);
+        bytebufferHandler = new CRLFDelimitedByteBuffer();
+    }
+
     /**
      * @see org.apache.james.core.AbstractJamesHandler#handleProtocol()
      */
@@ -251,40 +270,16 @@
           mode = COMMAND_MODE;
 
           //parse the command
-          String cmdString =  readCommandLine();
-          if (cmdString == null) {
+          byte[] line =  readCommandLine();
+          if (line == null) {
               break;
           }
-          int spaceIndex = cmdString.indexOf(" ");
-          if (spaceIndex > 0) {
-              curCommandName = cmdString.substring(0, spaceIndex);
-              curCommandArgument = cmdString.substring(spaceIndex + 1);
-          } else {
-              curCommandName = cmdString;
-          }
-          curCommandName = curCommandName.toUpperCase(Locale.US);
 
-          //fetch the command handlers registered to the command
-          List commandHandlers = handlerChain.getCommandHandlers(curCommandName);
-          if(commandHandlers == null) {
-              //end the session
-              break;
-          } else {
-              int count = commandHandlers.size();
-              for(int i = 0; i < count; i++) {
-                  setStopHandlerProcessing(false);
-                  ((CommandHandler)commandHandlers.get(i)).onCommand(this);
-                  
-                  theWatchdog.reset();
-                  
-                  //if the response is received, stop processing of command handlers
-                  if(mode != COMMAND_MODE || getStopHandlerProcessing()) {
-                      break;
-                  }
-              }
+          System.err.println(mode+">>> "+new String(line, "US-ASCII"));
 
-          }
-
+          ((LineHandler) lineHandlers.getLast()).onLine(this, line);
+          theWatchdog.reset();
+          
           //handle messages
           if(mode == MESSAGE_RECEIVED_MODE) {
               try {
@@ -321,6 +316,47 @@
         getLogger().debug("Closing socket.");
     }
 
+    public void onLine(SMTPSession session, byte[] line) {
+        String cmdString;
+        try {
+            cmdString = new String(line, "US-ASCII");
+            if (cmdString != null) {
+                cmdString = cmdString.trim();
+            }
+            
+            int spaceIndex = cmdString.indexOf(" ");
+            if (spaceIndex > 0) {
+                curCommandName = cmdString.substring(0, spaceIndex);
+                curCommandArgument = cmdString.substring(spaceIndex + 1);
+            } else {
+                curCommandName = cmdString;
+            }
+            curCommandName = curCommandName.toUpperCase(Locale.US);
+
+            //fetch the command handlers registered to the command
+            List commandHandlers = handlerChain.getCommandHandlers(curCommandName);
+            if(commandHandlers == null) {
+                //end the session
+                sessionEnded = true;
+            } else {
+                int count = commandHandlers.size();
+                for(int i = 0; i < count; i++) {
+                    setStopHandlerProcessing(false);
+                    ((CommandHandler)commandHandlers.get(i)).onCommand(this);
+                    
+                    //if the response is received, stop processing of command handlers
+                    if(mode != COMMAND_MODE || getStopHandlerProcessing()) {
+                        break;
+                    }
+                }
+
+            }        
+        } catch (UnsupportedEncodingException e) {
+            // TODO Define what to do
+            e.printStackTrace();
+        }
+    }
+    
     /**
      * Resets the handler data to a basic state.
      */
@@ -382,6 +418,7 @@
      */
     public void setMail(Mail mail) {
         this.mail = mail;
+        System.err.println(">>>>>> SETMAIL "+lineHandlers.size());
         this.mode = MESSAGE_RECEIVED_MODE;
     }
 
@@ -422,6 +459,10 @@
             recipients.clear();
         }
         state.clear();
+        // empty any previous line handler and add self (command dispatcher)
+        // as the default.
+        lineHandlers = null;
+        pushLineHandler(this);
     }
 
     /**
@@ -498,13 +539,14 @@
     /**
      * @see org.apache.james.smtpserver.SMTPSession#readCommandLine()
      */
-    public final String readCommandLine() throws IOException {
+    public final byte[] readCommandLine() throws IOException {
         for (;;) try {
-            String commandLine = inReader.readLine();
-            if (commandLine != null) {
-                commandLine = commandLine.trim();
+            if (bytebufferHandler.isEmpty()) {
+                byte[] buffer = new byte[1000];
+                int length = in.read(buffer);
+                bytebufferHandler.write(buffer, length);
             }
-            return commandLine;
+            return bytebufferHandler.read();
         } catch (CRLFTerminatedReader.TerminationException te) {
             writeLoggedFlushedResponse("501 Syntax error at character position " + te.position() + ". CR and LF must be CRLF paired.  See RFC 2821 #2.7.1.");
         } catch (CRLFTerminatedReader.LineLengthExceededException llee) {
@@ -576,4 +618,17 @@
         return connectionState;
     }
 
+    public void popLineHandler() {
+        if (lineHandlers != null) {
+            lineHandlers.removeLast();
+        }
+    }
+
+    public void pushLineHandler(LineHandler lineHandler) {
+        if (lineHandlers == null) {
+            lineHandlers = new LinkedList();
+        }
+        lineHandlers.addLast(lineHandler);
+    }
+
 }
Index: java/org/apache/james/smtpserver/SMTPSession.java
===================================================================
--- java/org/apache/james/smtpserver/SMTPSession.java	(revision 468922)
+++ java/org/apache/james/smtpserver/SMTPSession.java	(working copy)
@@ -57,7 +57,7 @@
      * @return the trimmed input line
      * @throws IOException if an exception is generated reading in the input characters
      */
-    String readCommandLine() throws IOException;
+    // String readCommandLine() throws IOException;
 
 
     /**
@@ -253,5 +253,16 @@
      */
     Map getConnectionState();
 
+    /**
+     * Put a new line handler in the chain
+     * @param overrideCommandHandler
+     */
+    void pushLineHandler(LineHandler overrideCommandHandler);
+    
+    /**
+     * Pop the last command handler 
+     */
+    void popLineHandler();
+
 }
 
Index: java/org/apache/james/smtpserver/core/AuthCmdHandler.java
===================================================================
--- java/org/apache/james/smtpserver/core/AuthCmdHandler.java	(revision 468922)
+++ java/org/apache/james/smtpserver/core/AuthCmdHandler.java	(working copy)
@@ -22,6 +22,7 @@
 package org.apache.james.smtpserver.core;
 
 import org.apache.james.smtpserver.CommandHandler;
+import org.apache.james.smtpserver.LineHandler;
 import org.apache.james.smtpserver.SMTPSession;
 import org.apache.james.util.mail.dsn.DSNStatus;
 import org.apache.avalon.framework.logger.AbstractLogEnabled;
@@ -31,7 +32,7 @@
 import java.util.Locale;
 import java.util.StringTokenizer;
 import org.apache.james.util.Base64;
-import java.io.IOException;
+import java.io.UnsupportedEncodingException;
 
 
 /**
@@ -50,7 +51,7 @@
      * The text string for the SMTP AUTH type LOGIN.
      */
     private final static String AUTH_TYPE_LOGIN = "LOGIN";
-
+    
     /**
      * handles AUTH command
      *
@@ -94,10 +95,48 @@
             }
             String authType = argument.toUpperCase(Locale.US);
             if (authType.equals(AUTH_TYPE_PLAIN)) {
-                doPlainAuth(session, initialResponse);
+                String userpass;
+                if (initialResponse == null) {
+                    responseString = "334 OK. Continue authentication";
+                    session.pushLineHandler(new LineHandler() {
+
+                        public void onLine(SMTPSession session, byte[] line) {
+                            try {
+                                doPlainAuthPass(session, new String(line, "US-ASCII"));
+                            } catch (UnsupportedEncodingException e) {
+                                // TODO should never happen
+                                e.printStackTrace();
+                            }
+                        }
+                        
+                    });
+                    session.writeResponse(responseString);
+                } else {
+                    userpass = initialResponse.trim();
+                    doPlainAuthPass(session, userpass);
+                }
                 return;
             } else if (authType.equals(AUTH_TYPE_LOGIN)) {
-                doLoginAuth(session, initialResponse);
+                
+                if (initialResponse == null) {
+                    responseString = "334 VXNlcm5hbWU6"; // base64 encoded "Username:"
+                    session.pushLineHandler(new LineHandler() {
+
+                        public void onLine(SMTPSession session, byte[] line) {
+                            try {
+                                doLoginAuthPass(session, new String(line, "US-ASCII"), true);
+                            } catch (UnsupportedEncodingException e) {
+                                // TODO should never happen
+                                e.printStackTrace();
+                            }
+                        }
+                        
+                    });
+                    session.writeResponse(responseString);
+                } else {
+                    String user = initialResponse.trim();
+                    doLoginAuthPass(session, user, false);
+                }
                 return;
             } else {
                 doUnknownAuth(session, authType, initialResponse);
@@ -120,16 +159,8 @@
      * @param session SMTP session object
      * @param initialResponse the initial response line passed in with the AUTH command
      */
-    private void doPlainAuth(SMTPSession session, String initialResponse)
-            throws IOException {
-        String userpass = null, user = null, pass = null, responseString = null;
-        if (initialResponse == null) {
-            responseString = "334 OK. Continue authentication";
-            session.writeResponse(responseString);
-            userpass = session.readCommandLine();
-        } else {
-            userpass = initialResponse.trim();
-        }
+    private void doPlainAuthPass(SMTPSession session, String userpass) {
+        String user = null, pass = null, responseString = null;
         try {
             if (userpass != null) {
                 userpass = Base64.decodeAsString(userpass);
@@ -182,6 +213,7 @@
             // Ignored - this exception in parsing will be dealt
             // with in the if clause below
         }
+        session.popLineHandler();
         // Authenticate user
         if ((user == null) || (pass == null)) {
             responseString = "501 Could not decode parameters for AUTH PLAIN";
@@ -205,16 +237,8 @@
      * @param session SMTP session object
      * @param initialResponse the initial response line passed in with the AUTH command
      */
-    private void doLoginAuth(SMTPSession session, String initialResponse)
-            throws IOException {
-        String user = null, pass = null, responseString = null;
-        if (initialResponse == null) {
-            responseString = "334 VXNlcm5hbWU6"; // base64 encoded "Username:"
-            session.writeResponse(responseString);
-            user = session.readCommandLine();
-        } else {
-            user = initialResponse.trim();
-        }
+    private void doLoginAuthPass(SMTPSession session, String user, boolean inCustomeHandler) {
+        String responseString = null;
         if (user != null) {
             try {
                 user = Base64.decodeAsString(user);
@@ -225,8 +249,33 @@
             }
         }
         responseString = "334 UGFzc3dvcmQ6"; // base64 encoded "Password:"
+        if (inCustomeHandler) {
+            session.popLineHandler();
+        }
+        session.pushLineHandler(new LineHandler() {
+
+            private String user;
+
+            public void onLine(SMTPSession session, byte[] line) {
+                try {
+                    doLoginAuthPassCheck(session, user, new String(line, "US-ASCII"));
+                } catch (UnsupportedEncodingException e) {
+                    // TODO should never happen
+                    e.printStackTrace();
+                }
+            }
+
+            public LineHandler setUser(String user) {
+                this.user = user;
+                return this;
+            }
+            
+        }.setUser(user));
         session.writeResponse(responseString);
-        pass = session.readCommandLine();
+    }
+    
+    private void doLoginAuthPassCheck(SMTPSession session, String user, String pass) {
+        String responseString = null;
         if (pass != null) {
             try {
                 pass = Base64.decodeAsString(pass);
@@ -251,6 +300,8 @@
             // TODO: Make this string a more useful error message
             getLogger().error("AUTH method LOGIN failed");
         }
+        session.popLineHandler();
+        session.popLineHandler();
         session.writeResponse(responseString);
         return;
     }
Index: java/org/apache/james/smtpserver/core/DataCmdHandler.java
===================================================================
--- java/org/apache/james/smtpserver/core/DataCmdHandler.java	(revision 487826)
+++ java/org/apache/james/smtpserver/core/DataCmdHandler.java	(working copy)
@@ -27,6 +27,7 @@
 import org.apache.james.core.MailImpl;
 import org.apache.james.fetchmail.ReaderInputStream;
 import org.apache.james.smtpserver.CommandHandler;
+import org.apache.james.smtpserver.LineHandler;
 import org.apache.james.smtpserver.MessageSizeException;
 import org.apache.james.smtpserver.SMTPSession;
 import org.apache.james.smtpserver.SizeLimitedInputStream;
@@ -43,8 +44,12 @@
 import java.io.ByteArrayInputStream;
 import java.io.IOException;
 import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.PipedInputStream;
+import java.io.PipedOutputStream;
 import java.io.SequenceInputStream;
 import java.io.StringReader;
+import java.io.UnsupportedEncodingException;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Date;
@@ -103,12 +108,84 @@
      * @param argument the argument passed in with the command by the SMTP client
      */
     private void doDATA(SMTPSession session, String argument) {
-
         String responseString = null;
         responseString = "354 Ok Send data ending with <CRLF>.<CRLF>";
+        
+        PipedInputStream messageIn = new PipedInputStream();
+        Thread t = new Thread() {
+            private PipedInputStream in;
+            private SMTPSession session;
+
+            public void run() {
+                System.err.println("run");
+                handleStream(session, in);
+            }
+
+            public Thread setParam(SMTPSession session, PipedInputStream in) {
+                this.in = in;
+                this.session = session;
+                return this;
+            }
+        }.setParam(session, messageIn);
+        
+        t.start();
+        
+        OutputStream out;
+        try {
+            out = new PipedOutputStream(messageIn);
+            session.pushLineHandler(new LineHandler() {
+
+                private OutputStream out;
+                private Thread worker;
+
+                public void onLine(SMTPSession session, byte[] line) {
+                    try {
+                        System.err.println("onLine ["+new String(line, "US-ASCII")+"]");
+                    } catch (UnsupportedEncodingException e1) {
+                        // TODO Auto-generated catch block
+                        e1.printStackTrace();
+                    }
+                    try {
+                        out.write(line);
+//                        out.write(13);
+//                        out.write(10);
+                        out.flush();
+                        // 46 is "."
+                        if (line.length == 3 && line[0] == 46) {
+                            try {
+                                worker.join();
+                            } catch (InterruptedException e) {
+                                // TODO Auto-generated catch block
+                                e.printStackTrace();
+                            }
+                        }
+                        
+                    } catch (IOException e) {
+                        // TODO Define what we have to do here!
+                        e.printStackTrace();
+                    }
+                }
+
+                public LineHandler setParam(OutputStream out, Thread t) {
+                    this.out = out;
+                    this.worker = t;
+                    return this;
+                };
+            
+            }.setParam(out,t));
+        } catch (IOException e1) {
+            // TODO Define what to do.
+            e1.printStackTrace();
+        }
+        
         session.writeResponse(responseString);
-        InputStream msgIn = new CharTerminatedInputStream(session
-                .getInputStream(), SMTPTerminator);
+        
+    }
+    
+
+    public void handleStream(SMTPSession session, InputStream stream) {
+        String responseString;
+        InputStream msgIn = new CharTerminatedInputStream(stream, SMTPTerminator);
         try {
             msgIn = new BytesReadResetInputStream(msgIn, session.getWatchdog(),
                     session.getConfigurationData().getResetLength());
@@ -131,8 +208,11 @@
             // Parse out the message headers
             MailHeaders headers = new MailHeaders(msgIn);
             headers = processMailHeaders(session, headers);
+            System.err.print("processMailheaders DONE");
             processMail(session, headers, msgIn);
+            System.err.print("processMail DONE");
             headers = null;
+            System.err.print("handleStream DONE");
         } catch (MessagingException me) {
             // Grab any exception attached to this one.
             Exception e = me.getNextException();
@@ -168,6 +248,7 @@
                 getLogger().error(
                         "Unknown error occurred while processing DATA.", me);
             }
+            session.popLineHandler();
             session.writeResponse(responseString);
             return;
         } finally {
@@ -311,6 +392,8 @@
                 mail.setAttribute(SMTP_AUTH_NETWORK_NAME,"true");
             }
             
+            session.popLineHandler();
+            
             session.setMail(mail);
         } catch (MessagingException me) {
             // if we get here, it means that we received a
@@ -356,4 +439,5 @@
         return implCommands;
     }
 
+
 }
Index: java/org/apache/james/smtpserver/LineHandler.java
===================================================================
--- java/org/apache/james/smtpserver/LineHandler.java	
+++ java/org/apache/james/smtpserver/LineHandler.java	
@@ -0,0 +1,32 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.smtpserver;
+
+/**
+ * Custom line handlers must implement this interface
+ */
+public interface LineHandler {
+     
+    /**
+     * Handle the command
+    **/
+    void onLine(SMTPSession session, byte[] line);
+    
+}
Index: java/org/apache/james/util/CRLFDelimitedByteBuffer.java
===================================================================
--- java/org/apache/james/util/CRLFDelimitedByteBuffer.java	
+++ java/org/apache/james/util/CRLFDelimitedByteBuffer.java	
@@ -0,0 +1,315 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+
+package org.apache.james.util;
+
+/*
+ * A simple, synchronized, queue of CRLF-delimited lines.
+ *
+ * NOTA BENE: as bytes arrive, they are buffered internally up to a
+ * configured maximum.  The maximum is intended to reflect a line length
+ * limiter, but there is a potential corner case that should be
+ * understood.  If the working buffer is almost full, and a new packet
+ * arrives with enough data to overflow the buffer, the code will
+ * consider that an error and discard the entire working buffer, even
+ * though the beginning of the new packet might have terminated the
+ * currently accumulating line.  And the reported position of the
+ * overflow is based upon the buffer position before scanning for lines,
+ * not an actualy line length (perhaps we need not even report the
+ * position).  Since the purpose for this maximum is to prevent memory
+ * flooding attacks, this does not appear to be a particularly critical
+ * corner case.  We simply need to make sure that the working buffer is
+ * at least twice the size of the maximum desired line length.
+ *
+ * After buffering the incoming data, it is scanned for CRLF.  As each
+ * line is found, it is moved to an ArrayList of Line objects.  When all
+ * data has been scanned, any remaining bytes are shifted within the
+ * working buffer to prepare for the next packet.
+ *
+ * The code enforces CRLF pairing (RFC 2821 #2.7.1).  The Line object,
+ * which is for internal use only, can hold the bytes for each line or
+ * record an exception (line termination or line length) associated
+ * with processing the data that would otherwise have.  The exceptions
+ * are rethrown during the read operation, rather than during the write
+ * operation, so that the order of responses perserves the order of
+ * input.
+ *
+ * This code does not handle dot stuffing.  Dot Stuffing, Message size
+ * limiting, and buffering of the message in a file are all expected to
+ * be performed by the I/O handler associated with the DATA accumulation
+ * state.
+ */
+
+public class CRLFDelimitedByteBuffer {
+	static public class TerminationException extends java.io.IOException {
+		private int where;
+		public TerminationException(int where) {
+			super();
+			this.where = where;
+		}
+
+		public TerminationException(String s, int where) {
+			super(s);
+			this.where = where;
+		}
+
+		public int position() {
+			return where;
+		}
+	}
+
+	static public class LineLengthExceededException extends java.io.IOException {
+		public LineLengthExceededException(String s) {
+			super(s);
+		}
+	}
+
+	public CRLFDelimitedByteBuffer() {
+		this(2048);
+	}
+
+	public CRLFDelimitedByteBuffer(int maxLineLength) {
+		lines = new java.util.ArrayList();
+		workLine = new byte[maxLineLength];
+	}
+
+	synchronized public boolean isEmpty() {
+		return lines.isEmpty();
+	}
+
+	synchronized public byte[] read() throws LineLengthExceededException, TerminationException {
+		return lines.isEmpty() ? null : ((Line) lines.remove(0)).getBytes();
+	}
+
+	synchronized public String readString() throws LineLengthExceededException, TerminationException {
+		if (lines.isEmpty()) return null;
+		else {
+			byte[] bytes = ((Line) lines.remove(0)).getBytes();
+			try {
+				return new String(bytes, "US-ASCII");
+			} catch (java.io.UnsupportedEncodingException uee) {
+				return new String(bytes);
+			}
+		}
+	}
+
+	synchronized public void write(byte[] data, int length) {
+		if (canFit(length)) {
+			System.arraycopy(data, 0, workLine, writeindex, length);
+			writeindex += length;
+			buildlines();
+		}
+	}
+
+	synchronized public void write(byte data) {
+		if (canFit(1)) {
+			workLine[writeindex++] = data;
+			buildlines();
+		}
+	}
+
+	synchronized public void write(String s) {
+		write(s.getBytes(), s.getBytes().length);
+	}
+
+	private boolean canFit(int length) {
+		if (writeindex + length > workLine.length) {
+			reset();
+			lines.add(new Line(new LineLengthExceededException("Exceeded maximum line length")));
+			return false;
+		} else return true;
+	}
+
+	static private class Line {
+		java.io.IOException e;
+		byte[] bytes;
+
+		public Line(byte[] data) {
+			bytes = data;
+		}
+
+		public Line(String data) {
+			bytes = data.getBytes();
+		}
+
+		public Line(java.io.IOException e) {
+			this.e = e;
+		}
+
+		public Line(byte[] data, int offset, int length) {
+			bytes = new byte[length];
+			System.arraycopy(data, offset, bytes, 0, length);
+		}
+
+		public byte[] getBytes() throws LineLengthExceededException, TerminationException {
+			if (e != null) {
+				if (e instanceof LineLengthExceededException) throw (LineLengthExceededException) e;
+				else  throw (TerminationException) e;
+			}
+			return bytes;
+		}
+	}
+
+	private java.util.ArrayList lines;
+
+	private byte[] workLine;
+	private int writeindex = 0;
+	private int readindex = 0;
+	private int scanindex = 0;      // current index for matching within the working buffer
+
+	private void reset() {
+		writeindex = 0;
+		readindex = 0;
+		scanindex = 0;
+	}
+
+	private void shift() {
+		System.arraycopy(workLine, readindex, workLine, 0, writeindex - readindex);
+		writeindex -= readindex;
+		scanindex -= readindex;
+		readindex = 0;
+	}
+
+	private void buildlines() {
+		for (; scanindex < writeindex; scanindex++) {
+			if (workLine[scanindex] == '\n') {
+				final int pos = scanindex;
+				reset();
+				lines.add(new Line(new TerminationException("\"bare\" LF in data stream.", pos)));
+				break;
+			} else if (workLine[scanindex] == '\r') {
+				if (scanindex+1 == writeindex) break;
+				else if (workLine[++scanindex] == '\n') {
+					lines.add(new Line(workLine, readindex, scanindex - readindex + 1));
+					readindex = scanindex + 1;
+				} else {
+					final int pos = scanindex - 1;
+					reset();
+					lines.add(new Line(new TerminationException("\"bare\" CR in data stream.", pos)));
+					break;
+				}
+			}
+		}
+
+		if (readindex != 0) shift();
+	}
+
+	/*** THE CODE BELOW IS PURELY FOR TESTING ***/
+	/*
+	synchronized private void status() {
+		System.out.println("\n--------------------------------------------------\n");
+		if (lines.isEmpty()) System.out.println("Lines: None");
+		else {
+			System.out.println("Lines:");
+			java.util.Iterator i = lines.iterator();
+			while (i.hasNext()) {
+				Line line = (Line) i.next();
+				try {
+					System.out.println("\tData[" + line.getBytes().length + "]: " + new String(line.getBytes(), 0, line.getBytes().length, "US-ASCII"));
+				} catch (java.io.UnsupportedEncodingException uee) {
+				} catch (TerminationException te) {
+					System.out.println("\tSyntax error at character position " + te.position() + ". CR and LF must be CRLF paired.  See RFC 2821 #2.7.1.");
+				} catch (LineLengthExceededException llee) {
+					System.out.println("\tLine length exceeded. See RFC 2821 #4.5.3.1.");
+				}
+			}
+		}
+
+		System.out.println("Buffer Status:");
+		System.out.println("\tworkline length: " + workLine.length);
+		System.out.println("\tnext read index: " + readindex);
+		System.out.println("\tnext scan index: " + scanindex);
+		System.out.println("\tnext write index: " + writeindex);
+
+		try {
+			System.out.println("\tOld data: " + new String(workLine, 0, readindex, "US-ASCII"));
+			System.out.println("\tData: " + new String(workLine, readindex, writeindex - readindex, "US-ASCII"));
+		} catch (java.io.UnsupportedEncodingException uee) {
+			System.err.println(uee);
+		}
+		System.out.println("\n--------------------------------------------------\n");
+	}
+
+
+	static public void main(String[] args) throws java.io.IOException {
+		CRLFDelimitedByteBuffer dbb = new CRLFDelimitedByteBuffer();
+		dbb.status();
+		dbb.write("Hello"); dbb.status();
+		dbb.write(" "); dbb.status();
+		dbb.write("World."); dbb.status();
+		dbb.write("\r"); dbb.status();
+		dbb.write("\n"); dbb.status();
+		dbb.write("\r\n"); dbb.status();
+		dbb.write("\n"); dbb.status();
+		dbb.write("\r\r"); dbb.status();
+		dbb.write("stuff\n"); dbb.status();
+		dbb.write("morestuff\r\r"); dbb.status();
+		for (int i = 0; i < 2500; i++) dbb.write("\0"); dbb.status();
+
+		while (!dbb.isEmpty()) {
+			try {
+				byte[] line = dbb.read();
+				System.out.println("Read line[" + line.length + "]: " + new String(line, 0, line.length, "US-ASCII"));
+			} catch (java.io.UnsupportedEncodingException uee) {
+			} catch (TerminationException te) {
+				System.out.println("Syntax error at character position " + te.position() + ". CR and LF must be CRLF paired.  See RFC 2821 #2.7.1.");
+			} catch (LineLengthExceededException llee) {
+				System.out.println("Line length exceeded. See RFC 2821 #4.5.3.1.");
+			}
+		}
+		dbb.status();
+
+		dbb.write("This is a test.\015\012.... Three dots\015\012.\015\012");
+		dbb.status();
+
+		while (!dbb.isEmpty()) {
+			try {
+				byte[] line = dbb.read();
+				System.out.println("Read line[" + line.length + "]: " + new String(line, 0, line.length, "US-ASCII"));
+			} catch (java.io.UnsupportedEncodingException uee) {
+			} catch (TerminationException te) {
+				System.out.println("Syntax error at character position " + te.position() + ". CR and LF must be CRLF paired.  See RFC 2821 #2.7.1.");
+			} catch (LineLengthExceededException llee) {
+				System.out.println("Line length exceeded. See RFC 2821 #4.5.3.1.");
+			}
+		}
+		dbb.status();
+
+		dbb.write("This is"); dbb.status();
+		dbb.write(" a tes"); dbb.status();
+		dbb.write("t.\015"); dbb.status();
+		dbb.write("\012..."); dbb.status();
+		dbb.write(". Three dot"); dbb.status();
+		dbb.write("s\015\012.\015\012"); dbb.status();
+
+		while (!dbb.isEmpty()) {
+			try {
+				String text = dbb.readString();
+				System.out.println ("read : " + text);
+				dbb.status();
+			} catch (TerminationException te) {
+				System.out.println("Syntax error at character position " + te.position() + ". CR and LF must be CRLF paired.  See RFC 2821 #2.7.1.");
+			} catch (LineLengthExceededException llee) {
+				System.out.println("Line length exceeded. See RFC 2821 #4.5.3.1.");
+			}
+		}
+	}
+	*/
+}
Index: test/org/apache/james/smtpserver/AbstractSMTPSession.java
===================================================================
--- test/org/apache/james/smtpserver/AbstractSMTPSession.java	(revision 475939)
+++ test/org/apache/james/smtpserver/AbstractSMTPSession.java	(working copy)
@@ -242,4 +242,12 @@
         throw new UnsupportedOperationException("Unimplemented Stub Method");
     }
 
+    public void popLineHandler() {
+        throw new UnsupportedOperationException("Unimplemented Stub Method");
+    }
+
+    public void pushLineHandler(LineHandler overrideCommandHandler) {
+        throw new UnsupportedOperationException("Unimplemented Stub Method");
+    }
+
 }
Index: test/org/apache/james/smtpserver/SMTPServerTest.java
===================================================================
--- test/org/apache/james/smtpserver/SMTPServerTest.java	(revision 475939)
+++ test/org/apache/james/smtpserver/SMTPServerTest.java	(working copy)
@@ -1003,6 +1003,8 @@
         wr.write("1234567810123456782012345678301234567840123456785012345678601234567870123456788012345678901234567100");
         wr.write("1234567810123456782012345678301234567840123456785012345678601234567870123456788012345678901234567100");
         wr.write("1234567810123456782012345678301234567840123456785012345678601234567870123456788012345678901234567100");
+        wr.write("1234567810123456782012345678301234567840123456785012345678601234567870123456788012345678901234567100\r\n");
+        // add a CRLF in the middle, to be sure we don't use more than 1000 bytes by RFC.
         wr.write("1234567810123456782012345678301234567840123456785012345678601234567870123456788012345678901234567100");
         wr.write("1234567810123456782012345678301234567840123456785012345678601234567870123456788012345678901234567100");
         wr.write("1234567810123456782012345678301234567840123456785012345678601234567870123456788012345678901234567100");
@@ -1008,8 +1010,7 @@
         wr.write("1234567810123456782012345678301234567840123456785012345678601234567870123456788012345678901234567100");
         wr.write("1234567810123456782012345678301234567840123456785012345678601234567870123456788012345678901234567100");
         wr.write("1234567810123456782012345678301234567840123456785012345678601234567870123456788012345678901234567100");
-        wr.write("1234567810123456782012345678301234567840123456785012345678601234567870123456788012345678901234567100");
-        wr.write("1234567810123456782012\r\n"); // 1022 + CRLF = 1024
+        wr.write("12345678101234567820\r\n"); // 500 + CRLF + 520 + CRLF = 1024
         wr.close();
         
         assertTrue(smtpProtocol.completePendingCommand());
