james-server-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From darr...@apache.org
Subject cvs commit: jakarta-james/proposals/imap2/test/org/apache/james/test AbstractProtocolTest.java FileProtocolSessionBuilder.java ProtocolSession.java
Date Tue, 03 Dec 2002 14:00:14 GMT
darrell     2002/12/03 06:00:14

  Modified:    proposals/imap2 build-test.xml build.xml
               proposals/imap2/java/org/apache/james/imapserver
                        ImapHandler.java ImapHost.java
                        ImapRequestHandler.java ImapRequestLineReader.java
                        ImapResponse.java JamesImapHost.java
               proposals/imap2/java/org/apache/james/imapserver/commands
                        AppendCommand.java CommandParser.java
                        CommandTemplate.java ImapCommandFactory.java
                        SelectCommand.java StatusCommand.java
                        SubscribeCommand.java UnsubscribeCommand.java
               proposals/imap2/java/org/apache/james/imapserver/store
                        ImapMailbox.java InMemoryStore.java
                        MailboxException.java MessageFlags.java
               proposals/imap2/test/org/apache/james/imapserver Append.test
                        CommandParserTest.java Create.test Delete.test
                        ExamineEmpty.test FetchSingleMessage.test
                        ImapMailboxTest.java ListMailboxes.test
                        SelectEmpty.test Status.test StringArgs.test
                        Subscribe.test
               proposals/imap2/test/org/apache/james/test
                        AbstractProtocolTest.java
                        FileProtocolSessionBuilder.java
                        ProtocolSession.java
  Added:       proposals/imap2/java/org/apache/james/imapserver/commands
                        CheckCommand.java CloseCommand.java
                        CopyCommand.java ExpungeCommand.java
                        FetchCommand.java IdSet.java SearchCommand.java
                        StoreCommand.java UidCommand.java
                        UidEnabledCommand.java
               proposals/imap2/java/org/apache/james/imapserver/store
                        ImapMessage1.java ImapMessageAttributes.java
                        SimpleImapMessage.java SimpleMessageAttributes.java
               proposals/imap2/test/org/apache/james/imapserver
                        AppendExpunge.test Check.test Copy.test
                        Expunge.test Noop.test Search.test
                        SelectedStateCleanup.test SelectedStateSetup.test
                        Store.test TestCommandsInAuthenticatedState.java
                        TestCommandsInNonAuthenticatedState.java
                        TestCompound.java
                        TestOtherCommandsInSelectedState.java
                        TestSelectedCommandsInSelectedState.java Uid.test
                        ValidAuthenticated.test ValidNonAuthenticated.test
                        ValidSelected.test
  Removed:     proposals/imap2/java/org/apache/james/imapserver/store
                        ImapMessage.java
               proposals/imap2/test/org/apache/james/imapserver
                        AuthenticateAuthenticated.test
                        LoginAuthenticated.test Lsub.test Subscribe2.test
                        Test.test TestAuthenticated.java
                        TestNonAuthenticated.java TestSelected.java
  Log:
  IMAP Proposal updates.
  
  * Added the remaining missing commands with (partly) working implementations.
    All commands now have semi-functional implementations, as well as a simple
    unit test for each command.
  
  * Modified ImapRequestLineReader to take an InputStream rather than a Reader,
    so we can have complete control over encoding, line-feeds etc.
  
  * Cleaned up build-test.xml. Made sure that tests run completely both locally
    (no socket connection) and remotely (against running instance of James).
  
  Revision  Changes    Path
  1.4       +20 -102   jakarta-james/proposals/imap2/build-test.xml
  
  Index: build-test.xml
  ===================================================================
  RCS file: /home/cvs/jakarta-james/proposals/imap2/build-test.xml,v
  retrieving revision 1.3
  retrieving revision 1.4
  diff -u -r1.3 -r1.4
  --- build-test.xml	26 Nov 2002 14:53:47 -0000	1.3
  +++ build-test.xml	3 Dec 2002 14:00:11 -0000	1.4
  @@ -112,32 +112,11 @@
                   <exclude name="**/*.java"/>
               </fileset>
           </copy>
  -
  -        <!-- Load custom tasks -->
  -        <!--
  -            <taskdef classname="org.apache.james.test.JamesTask" name="james">
  -                <classpath>
  -                    <path location="${build.dir}/lib/james.bar"/>
  -                    <path location="${build.test.classes}"/>
  -                    <fileset dir="${lib.dir}">
  -                        <include name="*.jar"/>
  -                        <include name="logkit-1.0.jar"/>
  -                        <include name="avalon-scratchpad-20011122.jar"/>
  -                        <include name="avalon-excalibur-20011120.jar"/>
  -                        <include name="avalon-framework-20011115.jar"/>
  -                        <include name="phoenix-engine-20011230.jar"/>
  -                        <include name="phoenix-client-20011230.jar"/>
  -                    </fileset>
  -                </classpath>
  -            </taskdef>
  -        -->
  -
       </target>
   
  -    <!-- Run the full set of Imap tests -->
  -    <target name="testimap" depends="unit-tests, protocol-tests"/>
  -
  -    <target name="unit-tests" depends="compile">
  +    <!-- Run the full set of Imap tests against local components -->
  +    <target name="unit-tests" depends="compile"
  +            description="Run the full set of IMAP tests against local components.">
           <junit fork="yes">
               <classpath>
                   <pathelement location="${build.test.classes}"/>
  @@ -145,16 +124,21 @@
               </classpath>
               <sysproperty key="runTestsLocal" value="true"/>
               <formatter type="plain" usefile="false"/>
  -            <test name="org.apache.james.imapserver.ImapHostTest"/>
               <test name="org.apache.james.imapserver.CommandParserTest"/>
  +            <test name="org.apache.james.imapserver.ImapHostTest"/>
               <test name="org.apache.james.imapserver.ImapStoreTest"/>
  -            <test name="org.apache.james.imapserver.TestNonAuthenticated"/>
  -            <test name="org.apache.james.imapserver.TestAuthenticated"/>
  +            <test name="org.apache.james.imapserver.ImapMailboxTest"/>
  +            <test name="org.apache.james.imapserver.TestCommandsInNonAuthenticatedState"/>
  +            <test name="org.apache.james.imapserver.TestCommandsInAuthenticatedState"/>
  +            <test name="org.apache.james.imapserver.TestOtherCommandsInSelectedState"/>
  +            <test name="org.apache.james.imapserver.TestSelectedCommandsInSelectedState"/>
  +            <test name="org.apache.james.imapserver.TestCompound"/>
           </junit>
       </target>
   
       <!-- Executes tests against a running instance of James -->
  -    <target name="protocol-tests" depends="testimap-init, testimap-nonauthenticated, testimap-authenticated"/>
  +    <target name="protocol-tests" depends="testimap-init, testimap-commands"
  +            description="Run the full set of IMAP protocol tests against a running instance of James. Note: James must be started manually."/>
   
       <!-- Initialises the IMAP server for running tests.
            Namely, creates the "imapuser" user, and sends 4 test messages to that user
  @@ -169,12 +153,12 @@
               <sysproperty key="runTestsLocal" value="false"/>
               <formatter type="plain" usefile="false"/>
               <test name="org.apache.james.remotemanager.InitialImapUsersTest"/>
  -            <test name="org.apache.james.imapserver.InitialMail"/>
  +<!--            <test name="org.apache.james.imapserver.InitialMail"/>-->
           </junit>
       </target>
   
  -    <!-- Tests IMAP commands valid in the NonAuthenticated state -->
  -    <target name="testimap-nonauthenticated" depends="compile">
  +    <!-- Tests IMAP commands against a running instance of James -->
  +    <target name="testimap-commands" depends="compile">
           <junit fork="yes">
               <classpath>
                   <pathelement location="${build.test.classes}"/>
  @@ -182,78 +166,12 @@
               </classpath>
               <sysproperty key="runTestsLocal" value="false"/>
               <formatter type="plain" usefile="false"/>
  -            <test name="org.apache.james.imapserver.TestNonAuthenticated"/>
  +            <test name="org.apache.james.imapserver.TestCommandsInNonAuthenticatedState"/>
  +            <test name="org.apache.james.imapserver.TestCommandsInAuthenticatedState"/>
  +            <test name="org.apache.james.imapserver.TestOtherCommandsInSelectedState"/>
  +            <test name="org.apache.james.imapserver.TestSelectedCommandsInSelectedState"/>
  +            <test name="org.apache.james.imapserver.TestCompound"/>
           </junit>
       </target>
  -
  -    <!-- Tests IMAP commands valid in the Authenticated state -->
  -    <target name="testimap-authenticated" depends="compile">
  -        <junit fork="yes">
  -            <classpath>
  -                <pathelement location="${build.test.classes}"/>
  -                <path refid="test.class.path"/>
  -            </classpath>
  -            <sysproperty key="runTestsLocal" value="false"/>
  -            <formatter type="plain" usefile="false"/>
  -            <test name="org.apache.james.imapserver.TestAuthenticated"/>
  -        </junit>
  -    </target>
  -
  -    <!-- Tests IMAP commands valid in the selected state -->
  -    <target name="testimap-selected" depends="compile">
  -        <junit fork="yes">
  -            <classpath>
  -                <pathelement location="${build.test.classes}"/>
  -                <path refid="test.class.path"/>
  -            </classpath>
  -            <sysproperty key="runTestsLocal" value="false"/>
  -            <formatter type="plain" usefile="false"/>
  -            <test name="org.apache.james.imapserver.TestSelected"/>
  -        </junit>
  -    </target>
  -
  -    <target name="test-remote-manager" depends="compile">
  -        <junit fork="yes">
  -            <classpath>
  -                <pathelement location="${build.test.classes}"/>
  -                <path refid="test.class.path"/>
  -            </classpath>
  -            <sysproperty key="runTestsLocall" value="false"/>
  -            <formatter type="plain" usefile="false"/>
  -            <test name="org.apache.james.remotemanager.TestRemoteManager"/>
  -        </junit>
  -    </target>
  -
  -
  -    <!-- The following tasks attempt to use ANT to start/stop James. They don't really work at the moment. -->
  -    <target name="test-james" depends="clean-james, start-james, testimap, stop-james"/>
  -
  -    <target name="start-james" depends="compile">
  -        <james action="start"/>
  -    </target>
  -
  -    <target name="stop-james" depends="compile">
  -        <james action="stop"/>
  -    </target>
  -
  -    <target name="start-stop" depends="compile">
  -        <james action="start-stop">
  -            <classpath>
  -                <path location="${build.dir}/lib/james.bar"/>
  -                <path location="${build.test.classes}"/>
  -                <fileset dir="${lib.dir}">
  -                    <include name="*.bar"/>
  -                    <include name="*.jar"/>
  -                    <include name="logkit-1.0.jar"/>
  -                    <include name="avalon-scratchpad-20011122.jar"/>
  -                    <include name="avalon-excalibur-20011120.jar"/>
  -                    <include name="avalon-framework-20011115.jar"/>
  -                    <include name="phoenix-engine-20011230.jar"/>
  -                    <include name="phoenix-client-20011230.jar"/>
  -                </fileset>
  -            </classpath>
  -        </james>
  -    </target>
  -
   </project>
   
  
  
  
  1.3       +2 -2      jakarta-james/proposals/imap2/build.xml
  
  Index: build.xml
  ===================================================================
  RCS file: /home/cvs/jakarta-james/proposals/imap2/build.xml,v
  retrieving revision 1.2
  retrieving revision 1.3
  diff -u -r1.2 -r1.3
  --- build.xml	26 Nov 2002 14:53:47 -0000	1.2
  +++ build.xml	3 Dec 2002 14:00:11 -0000	1.3
  @@ -217,7 +217,7 @@
          ===================================================================
                                     compile
          ===================================================================
  -  -->
  +    -->
       <target name="compile" depends="prepare,prepare-jdbc3" description="Compiles the source.">
           <!-- First compile the main James tree, leaving out any files that
                overlap with the IMAP proposal. -->
  
  
  
  1.4       +10 -3     jakarta-james/proposals/imap2/java/org/apache/james/imapserver/ImapHandler.java
  
  Index: ImapHandler.java
  ===================================================================
  RCS file: /home/cvs/jakarta-james/proposals/imap2/java/org/apache/james/imapserver/ImapHandler.java,v
  retrieving revision 1.3
  retrieving revision 1.4
  diff -u -r1.3 -r1.4
  --- ImapHandler.java	26 Nov 2002 14:53:47 -0000	1.3
  +++ ImapHandler.java	3 Dec 2002 14:00:11 -0000	1.4
  @@ -33,6 +33,7 @@
   import java.io.PrintWriter;
   import java.io.Writer;
   import java.io.Reader;
  +import java.io.InputStream;
   import java.net.Socket;
   
   /**
  @@ -79,6 +80,11 @@
       private BufferedReader in;
   
       /**
  +     * The socket's input stream.
  +     */
  +    private InputStream ins;
  +
  +    /**
        * The writer to which outgoing messages are written.
        */
       private PrintWriter out;
  @@ -175,6 +181,7 @@
               synchronized ( this ) {
                   handlerThread = Thread.currentThread();
               }
  +            ins = socket.getInputStream();
               in = new BufferedReader( new InputStreamReader( socket.getInputStream(), "ASCII" ), 512 );
               remoteIP = socket.getInetAddress().getHostAddress();
               remoteHost = socket.getInetAddress().getHostName();
  @@ -208,7 +215,7 @@
           try {
               outs = new BufferedOutputStream( socket.getOutputStream(), 1024 );
               out = new InternetPrintWriter( outs, true );
  -            ImapResponse untaggedResponse = new ImapResponse( out );
  +            ImapResponse response = new ImapResponse( outs );
   
               // Write welcome message
               StringBuffer responseBuffer =
  @@ -217,7 +224,7 @@
                       .append( " Server " )
                       .append( theConfigData.getHelloName() )
                       .append( " ready" );
  -            untaggedResponse.okResponse( null, responseBuffer.toString() );
  +            response.okResponse( null, responseBuffer.toString() );
   
               session = new ImapSessionImpl( theConfigData.getImapHost(),
                                              theConfigData.getUsersRepository(),
  @@ -226,7 +233,7 @@
                                              socket.getInetAddress().getHostAddress());
   
               theWatchdog.start();
  -            while ( requestHandler.handleRequest( in, out, session ) ) {
  +            while ( requestHandler.handleRequest( ins, outs, session ) ) {
                   theWatchdog.reset();
               }
               theWatchdog.stop();
  
  
  
  1.3       +10 -1     jakarta-james/proposals/imap2/java/org/apache/james/imapserver/ImapHost.java
  
  Index: ImapHost.java
  ===================================================================
  RCS file: /home/cvs/jakarta-james/proposals/imap2/java/org/apache/james/imapserver/ImapHost.java,v
  retrieving revision 1.2
  retrieving revision 1.3
  diff -u -r1.2 -r1.3
  --- ImapHost.java	27 Nov 2002 03:24:19 -0000	1.2
  +++ ImapHost.java	3 Dec 2002 14:00:11 -0000	1.3
  @@ -11,6 +11,7 @@
   import org.apache.james.imapserver.store.ImapMailbox;
   import org.apache.james.imapserver.store.MailboxException;
   
  +import javax.mail.search.SearchTerm;
   import java.util.Collection;
   
   /**
  @@ -243,6 +244,14 @@
        */
       void unsubscribe( User user, String mailbox )
               throws MailboxException;
  +
  +    int[] expunge( ImapMailbox mailbox ) throws MailboxException;
  +
  +    long[] search( SearchTerm searchTerm, ImapMailbox mailbox );
  +
  +    void copyMessage( long uid, ImapMailbox currentMailbox, ImapMailbox toMailbox )
  +            throws MailboxException;
  +
   
   }
   
  
  
  
  1.2       +7 -7      jakarta-james/proposals/imap2/java/org/apache/james/imapserver/ImapRequestHandler.java
  
  Index: ImapRequestHandler.java
  ===================================================================
  RCS file: /home/cvs/jakarta-james/proposals/imap2/java/org/apache/james/imapserver/ImapRequestHandler.java,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- ImapRequestHandler.java	26 Nov 2002 14:53:47 -0000	1.1
  +++ ImapRequestHandler.java	3 Dec 2002 14:00:11 -0000	1.2
  @@ -11,8 +11,8 @@
   import org.apache.james.imapserver.commands.CommandParser;
   import org.apache.james.imapserver.commands.ImapCommand;
   
  -import java.io.Reader;
  -import java.io.PrintWriter;
  +import java.io.InputStream;
  +import java.io.OutputStream;
   
   /**
    *
  @@ -36,12 +36,12 @@
        *
        * @return whether additional commands are expected.
        */
  -    public boolean handleRequest( Reader reader,
  -                                  PrintWriter writer,
  +    public boolean handleRequest( InputStream input,
  +                                  OutputStream output,
                                     ImapSession session )
               throws ProtocolException
       {
  -        ImapRequestLineReader request = new ImapRequestLineReader( reader, writer );
  +        ImapRequestLineReader request = new ImapRequestLineReader( input, output );
           try {
               request.nextChar();
           }
  @@ -49,7 +49,7 @@
               return false;
           }
   
  -        ImapResponse response = new ImapResponse( writer );
  +        ImapResponse response = new ImapResponse( output );
   
           doProcessRequest( request, response, session );
   
  
  
  
  1.4       +31 -17    jakarta-james/proposals/imap2/java/org/apache/james/imapserver/ImapRequestLineReader.java
  
  Index: ImapRequestLineReader.java
  ===================================================================
  RCS file: /home/cvs/jakarta-james/proposals/imap2/java/org/apache/james/imapserver/ImapRequestLineReader.java,v
  retrieving revision 1.3
  retrieving revision 1.4
  diff -u -r1.3 -r1.4
  --- ImapRequestLineReader.java	27 Nov 2002 12:26:45 -0000	1.3
  +++ ImapRequestLineReader.java	3 Dec 2002 14:00:11 -0000	1.4
  @@ -7,13 +7,9 @@
    */
   package org.apache.james.imapserver;
   
  -
  -import java.io.BufferedReader;
   import java.io.IOException;
  -import java.io.Reader;
  -import java.io.Writer;
  -import java.io.BufferedWriter;
  -import java.util.StringTokenizer;
  +import java.io.InputStream;
  +import java.io.OutputStream;
   
   /**
    * Wraps the client input reader with a bunch of convenience methods, allowing lookahead=1
  @@ -26,16 +22,16 @@
    */
   public class ImapRequestLineReader
   {
  -    private Reader reader;
  -    private Writer writer;
  +    private InputStream input;
  +    private OutputStream output;
   
       private boolean nextSeen = false;
       private char nextChar; // unknown
   
  -    ImapRequestLineReader( Reader reader, Writer writer )
  +    ImapRequestLineReader( InputStream input, OutputStream output )
       {
  -        this.reader = reader;
  -        this.writer = writer;
  +        this.input = input;
  +        this.output = output;
       }
   
       /**
  @@ -73,7 +69,7 @@
               int next = -1;
   
               try {
  -                next = reader.read();
  +                next = input.read();
               }
               catch ( IOException e ) {
                   throw new ProtocolException( "Error reading from stream." );
  @@ -113,6 +109,7 @@
   
           // Check if we found extra characters.
           if ( next != '\n' ) {
  +            // TODO debug log here and other exceptions
               throw new ProtocolException( "Expected end-of-line, found more characters.");
           }
       }
  @@ -140,14 +137,28 @@
        * @param holder A char array which will be filled with chars read from the underlying reader.
        * @throws ProtocolException If a char can't be read into each array element.
        */
  -    public void read( char[] holder ) throws ProtocolException
  +    public void read( byte[] holder ) throws ProtocolException
       {
  -        try {
  -            reader.read( holder );
  +        int readTotal = 0;
  +        try
  +        {
  +            while ( readTotal < holder.length )
  +            {
  +                int count = 0;
  +                count = input.read( holder, readTotal, holder.length - readTotal );
  +                if ( count == -1 ) {
  +                    throw new ProtocolException( "Unexpectd end of stream." );
  +                }
  +                readTotal += count;
  +            }
  +            // Unset the next char.
  +            nextSeen = false;
  +            nextChar = 0;
           }
           catch ( IOException e ) {
               throw new ProtocolException( "Error reading from stream." );
           }
  +
       }
   
       /**
  @@ -158,7 +169,10 @@
               throws ProtocolException
       {
           try {
  -            writer.write( "+\n" );
  +            output.write( '+' );
  +            output.write( '\r' );
  +            output.write( '\n' );
  +            output.flush();
           }
           catch ( IOException e ) {
               throw new ProtocolException("Unexpected exception in sending command continuation request.");
  
  
  
  1.2       +22 -2     jakarta-james/proposals/imap2/java/org/apache/james/imapserver/ImapResponse.java
  
  Index: ImapResponse.java
  ===================================================================
  RCS file: /home/cvs/jakarta-james/proposals/imap2/java/org/apache/james/imapserver/ImapResponse.java,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- ImapResponse.java	22 Nov 2002 02:09:50 -0000	1.1
  +++ ImapResponse.java	3 Dec 2002 14:00:11 -0000	1.2
  @@ -9,8 +9,10 @@
   
   import org.apache.james.imapserver.commands.ImapCommand;
   import org.apache.james.imapserver.store.MessageFlags;
  +import org.apache.james.util.InternetPrintWriter;
   
   import java.io.PrintWriter;
  +import java.io.OutputStream;
   
   /**
    * Class providing methods to send response messages from the server
  @@ -21,9 +23,9 @@
       private PrintWriter writer;
       private String tag = UNTAGGED;
   
  -    public ImapResponse( PrintWriter writer )
  +    public ImapResponse( OutputStream output )
       {
  -        this.writer = writer;
  +        this.writer = new InternetPrintWriter( output, true );
       }
   
       public void setTag( String tag )
  @@ -165,6 +167,23 @@
           end();
       }
   
  +    public void expungeResponse( int msn )
  +    {
  +        untagged();
  +        message( msn );
  +        message( "EXPUNGE" );
  +        end();
  +    }
  +
  +    public void fetchResponse( int msn, String msgData )
  +    {
  +        untagged();
  +        message( msn );
  +        message( "FETCH" );
  +        message( "(" + msgData + ")" );
  +        end();
  +    }
  +
       public void commandResponse( ImapCommand command, String message )
       {
           untagged();
  @@ -242,6 +261,7 @@
       private void end()
       {
           writer.println();
  +        writer.flush();
       }
   
   }
  
  
  
  1.4       +77 -2     jakarta-james/proposals/imap2/java/org/apache/james/imapserver/JamesImapHost.java
  
  Index: JamesImapHost.java
  ===================================================================
  RCS file: /home/cvs/jakarta-james/proposals/imap2/java/org/apache/james/imapserver/JamesImapHost.java,v
  retrieving revision 1.3
  retrieving revision 1.4
  diff -u -r1.3 -r1.4
  --- JamesImapHost.java	27 Nov 2002 03:24:19 -0000	1.3
  +++ JamesImapHost.java	3 Dec 2002 14:00:11 -0000	1.4
  @@ -12,7 +12,14 @@
   import org.apache.james.imapserver.store.InMemoryStore;
   import org.apache.james.imapserver.store.ImapMailbox;
   import org.apache.james.imapserver.store.MailboxException;
  +import org.apache.james.imapserver.store.SimpleImapMessage;
  +import org.apache.avalon.framework.logger.AbstractLogEnabled;
  +import org.apache.avalon.framework.logger.ConsoleLogger;
   
  +import examples.messages;
  +
  +import javax.mail.search.SearchTerm;
  +import javax.mail.internet.MimeMessage;
   import java.util.ArrayList;
   import java.util.Collection;
   import java.util.Iterator;
  @@ -30,14 +37,20 @@
    * @version $Revision$
    */
   public class JamesImapHost
  +        extends AbstractLogEnabled
           implements ImapHost, ImapConstants
   {
       private ImapStore store;
       private MailboxSubscriptions subscriptions;
   
  +    /**
  +     * Hack constructor which creates an in-memory store, and creates a console logger.
  +     */
       public JamesImapHost()
       {
  +        enableLogging( new ConsoleLogger() );
           store = new InMemoryStore();
  +        setupLogger( store );
           subscriptions = new MailboxSubscriptions();
       }
   
  @@ -137,7 +150,12 @@
           ImapMailbox toDelete = getMailbox( user, mailboxName, true );
   
           if ( store.getChildren( toDelete ).isEmpty() ) {
  -            // Does this delete all messages?
  +            long[] uids = toDelete.getMessageUids();
  +            for ( int i = 0; i < uids.length; i++ ) {
  +                long uid = uids[i];
  +                SimpleImapMessage imapMessage = toDelete.getMessage( uid );
  +                toDelete.deleteMessage( imapMessage.getUid() );
  +            }
               store.deleteMailbox( toDelete );
           }
           else {
  @@ -245,6 +263,63 @@
       {
           ImapMailbox mailbox = getMailbox( user, mailboxName, true );
           subscriptions.unsubscribe( user, mailbox );
  +    }
  +
  +    public int[] expunge( ImapMailbox mailbox )
  +            throws MailboxException
  +    {
  +        ArrayList deletedMessages = new ArrayList();
  +
  +        long[] uids = mailbox.getMessageUids();
  +        for ( int i = 0; i < uids.length; i++ ) {
  +            long uid = uids[i];
  +            SimpleImapMessage message = mailbox.getMessage( uid );
  +            if ( message.getFlags().isDeleted() ) {
  +                deletedMessages.add( message );
  +            }
  +        }
  +
  +        int[] ids = new int[ deletedMessages.size() ];
  +        for ( int i = 0; i < ids.length; i++ ) {
  +            SimpleImapMessage imapMessage = ( SimpleImapMessage ) deletedMessages.get( i );
  +            long uid = imapMessage.getUid();
  +            int msn = mailbox.getMsn( uid );
  +            ids[i] = msn;
  +            mailbox.deleteMessage( uid );
  +        }
  +
  +        return ids;
  +    }
  +
  +    public long[] search( SearchTerm searchTerm, ImapMailbox mailbox )
  +    {
  +        ArrayList matchedMessages = new ArrayList();
  +
  +        long[] allUids = mailbox.getMessageUids();
  +        for ( int i = 0; i < allUids.length; i++ ) {
  +            long uid = allUids[i];
  +            SimpleImapMessage message = mailbox.getMessage( uid );
  +            if ( searchTerm.match( message.getMimeMessage() ) ) {
  +                matchedMessages.add( message );
  +            }
  +        }
  +
  +        long[] matchedUids = new long[ matchedMessages.size() ];
  +        for ( int i = 0; i < matchedUids.length; i++ ) {
  +            SimpleImapMessage imapMessage = ( SimpleImapMessage ) matchedMessages.get( i );
  +            long uid = imapMessage.getUid();
  +            matchedUids[i] = uid;
  +        }
  +        return matchedUids;
  +    }
  +
  +    public void copyMessage( long uid, ImapMailbox currentMailbox, ImapMailbox toMailbox )
  +            throws MailboxException
  +    {
  +        SimpleImapMessage message = currentMailbox.getMessage( uid );
  +        toMailbox.createMessage( message.getMimeMessage(),
  +                                 message.getFlags(),
  +                                 message.getInternalDate() );
       }
   
       /**
  
  
  
  1.2       +9 -7      jakarta-james/proposals/imap2/java/org/apache/james/imapserver/commands/AppendCommand.java
  
  Index: AppendCommand.java
  ===================================================================
  RCS file: /home/cvs/jakarta-james/proposals/imap2/java/org/apache/james/imapserver/commands/AppendCommand.java,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- AppendCommand.java	27 Nov 2002 12:26:46 -0000	1.1
  +++ AppendCommand.java	3 Dec 2002 14:00:11 -0000	1.2
  @@ -31,7 +31,7 @@
    *
    * @version $Revision$
    */
  -class AppendCommand extends CommandTemplate
  +class AppendCommand extends AuthenticatedStateCommand
   {
       public static final String NAME = "APPEND";
       public static final String ARGS = "<mailbox> [<flag_list>] [<date_time>] literal";
  @@ -56,11 +56,13 @@
           MimeMessage message = parser.mimeMessage( request );
           parser.endLine( request );
   
  -        ImapMailbox mailbox = getMailbox( mailboxName, session );
  -        if ( mailbox == null ) {
  -            session.unsolicitedResponses( response );
  -            response.commandFailed( this, "TRYCREATE", "No such mailbox.");
  -            return;
  +        ImapMailbox mailbox = null;
  +        try {
  +            mailbox = getMailbox( mailboxName, session, true );
  +        }
  +        catch ( MailboxException e ) {
  +            e.setResponseCode( "TRYCREATE" );
  +            throw e;
           }
   
           mailbox.createMessage( message, flags, datetime );
  
  
  
  1.4       +120 -8    jakarta-james/proposals/imap2/java/org/apache/james/imapserver/commands/CommandParser.java
  
  Index: CommandParser.java
  ===================================================================
  RCS file: /home/cvs/jakarta-james/proposals/imap2/java/org/apache/james/imapserver/commands/CommandParser.java,v
  retrieving revision 1.3
  retrieving revision 1.4
  diff -u -r1.3 -r1.4
  --- CommandParser.java	27 Nov 2002 12:26:46 -0000	1.3
  +++ CommandParser.java	3 Dec 2002 14:00:11 -0000	1.4
  @@ -14,6 +14,7 @@
   import org.apache.james.util.Assert;
   
   import java.util.Date;
  +import java.util.TimeZone;
   import java.text.DateFormat;
   import java.text.SimpleDateFormat;
   import java.text.ParseException;
  @@ -106,6 +107,7 @@
   
       /**
        * Reads a "date-time" argument from the request.
  +     * TODO handle timezones properly
        */
       public Date dateTime( ImapRequestLineReader request ) throws ProtocolException
       {
  @@ -118,7 +120,7 @@
               throw new ProtocolException( "DateTime values must be quoted." );
           }
   
  -        DateFormat dateFormat = new SimpleDateFormat( "dd-MMM-yyyy HH:mm:ss ZZ" );
  +        DateFormat dateFormat = new SimpleDateFormat( "dd-MMM-yyyy hh:mm:ss zzzz" );
           try {
               return dateFormat.parse( dateString );
           }
  @@ -129,6 +131,7 @@
   
       /**
        * Reads a "date" argument from the request.
  +     * TODO handle timezones properly
        */
       public Date date( ImapRequestLineReader request ) throws ProtocolException
       {
  @@ -213,20 +216,36 @@
   
           // Consume the '}' and the newline
           consumeChar( request, '}' );
  -        consumeChar( request, '\n' );
  +        consumeCRLF( request );
   
           if ( synchronizedLiteral ) {
               request.commandContinuationRequest();
           }
   
           int size = Integer.parseInt( digits.toString() );
  -        char[] buffer = new char[size];
  +        byte[] buffer = new byte[size];
           request.read( buffer );
   
           return new String( buffer );
       }
   
       /**
  +     * Consumes a CRLF from the request.
  +     * TODO we're being liberal, the spec insists on \r\n for new lines.
  +     * @param request
  +     * @throws ProtocolException
  +     */
  +    private void consumeCRLF( ImapRequestLineReader request )
  +            throws ProtocolException
  +    {
  +        char next = request.nextChar();
  +        if ( next != '\n' ) {
  +            consumeChar( request, '\r' );
  +        }
  +        consumeChar( request, '\n' );
  +    }
  +
  +    /**
        * Consumes the next character in the request, checking that it matches the
        * expected one. This method should be used when the
        */
  @@ -280,13 +299,48 @@
       /**
        * Reads a "flags" argument from the request.
        */
  -    public MessageFlags flagList( ImapRequestLineReader request )
  +    public MessageFlags flagList( ImapRequestLineReader request ) throws ProtocolException
       {
  -        // TODO implement
  -        return null;
  +        MessageFlags flags = new MessageFlags();
  +        request.nextWordChar();
  +        consumeChar( request, '(' );
  +        CharacterValidator validator = new NoopCharValidator();
  +        String nextWord = consumeWord( request, validator );
  +        while ( ! nextWord.endsWith(")" ) ) {
  +            setFlag( nextWord, flags );
  +            nextWord = consumeWord( request, validator );
  +        }
  +        // Got the closing ")", may be attached to a word.
  +        if ( nextWord.length() > 1 ) {
  +            setFlag( nextWord.substring(0, nextWord.length() - 1 ), flags );
  +        }
  +
  +        return flags;
  +        }
  +
  +    public void setFlag( String flagString, MessageFlags flags ) throws ProtocolException
  +    {
  +        if ( flagString.equalsIgnoreCase( MessageFlags.ANSWERED ) ) {
  +            flags.setAnswered( true );
  +        }
  +        else if ( flagString.equalsIgnoreCase( MessageFlags.DELETED ) ) {
  +            flags.setDeleted( true );
  +        }
  +        else if ( flagString.equalsIgnoreCase( MessageFlags.DRAFT ) ) {
  +            flags.setDraft( true );
  +        }
  +        else if ( flagString.equalsIgnoreCase( MessageFlags.FLAGGED ) ) {
  +            flags.setFlagged( true );
  +        }
  +        else if ( flagString.equalsIgnoreCase( MessageFlags.SEEN ) ) {
  +            flags.setSeen( true );
  +        }
  +        else {
  +            throw new ProtocolException( "Invalid flag string." );
  +        }
       }
   
  -    /**
  +     /**
        * Reads an argument of type "number" from the request.
        */
       public long number( ImapRequestLineReader request ) throws ProtocolException
  @@ -341,6 +395,33 @@
       }
   
       /**
  +     * Reads a "message set" argument, and parses into an IdSet.
  +     * Currently only supports a single range of values.
  +     */
  +    public IdSet set( ImapRequestLineReader request )
  +            throws ProtocolException
  +    {
  +        CharacterValidator validator = new MessageSetCharValidator();
  +        String nextWord = consumeWord( request, validator );
  +
  +        int pos = nextWord.indexOf( ':' );
  +        try {
  +            if ( pos == -1 ) {
  +                long value = Long.parseLong( nextWord );
  +                return new HighLowIdSet( value, value );
  +            }
  +            else {
  +                long lowVal = Long.parseLong( nextWord.substring(0, pos ) );
  +                long highVal = Long.parseLong( nextWord.substring( pos + 1 ) );
  +                return new HighLowIdSet( lowVal, highVal );
  +            }
  +        }
  +        catch ( NumberFormatException e ) {
  +            throw new ProtocolException( "Invalid message set.");
  +        }
  +    }
  +
  +    /**
        * Provides the ability to ensure characters are part of a permitted set.
        */
       protected interface CharacterValidator
  @@ -393,6 +474,37 @@
           {
               if ( chr == '+' ) return false;
               return super.isValid( chr );
  +        }
  +    }
  +
  +    private class MessageSetCharValidator implements CharacterValidator
  +    {
  +        public boolean isValid( char chr )
  +        {
  +            return isDigit( chr ) ||
  +                    chr == ':' ||
  +                    chr == ',';
  +        }
  +
  +        private boolean isDigit( char chr )
  +        {
  +            return '0' <= chr && chr <= '9';
  +        }
  +    }
  +
  +    private class HighLowIdSet implements IdSet
  +    {
  +        private long lowVal;
  +        private long highVal;
  +
  +        public HighLowIdSet( long lowVal, long highVal )
  +        {
  +            this.lowVal = lowVal;
  +            this.highVal = highVal;
  +        }
  +
  +        public boolean includes( long value ) {
  +            return ( lowVal <= value ) && ( value <= highVal );
           }
       }
   
  
  
  
  1.4       +2 -2      jakarta-james/proposals/imap2/java/org/apache/james/imapserver/commands/CommandTemplate.java
  
  Index: CommandTemplate.java
  ===================================================================
  RCS file: /home/cvs/jakarta-james/proposals/imap2/java/org/apache/james/imapserver/commands/CommandTemplate.java,v
  retrieving revision 1.3
  retrieving revision 1.4
  diff -u -r1.3 -r1.4
  --- CommandTemplate.java	27 Nov 2002 12:26:46 -0000	1.3
  +++ CommandTemplate.java	3 Dec 2002 14:00:11 -0000	1.4
  @@ -57,7 +57,7 @@
               doProcess( request, response, session );
           }
           catch ( MailboxException e ) {
  -            response.commandFailed( this, e.getMessage() );
  +            response.commandFailed( this, e.getResponseCode(), e.getMessage() );
           }
           catch ( AuthorizationException e ) {
               String msg = "Authorization error: Lacking permissions to perform requested operation.";
  
  
  
  1.5       +19 -15    jakarta-james/proposals/imap2/java/org/apache/james/imapserver/commands/ImapCommandFactory.java
  
  Index: ImapCommandFactory.java
  ===================================================================
  RCS file: /home/cvs/jakarta-james/proposals/imap2/java/org/apache/james/imapserver/commands/ImapCommandFactory.java,v
  retrieving revision 1.4
  retrieving revision 1.5
  diff -u -r1.4 -r1.5
  --- ImapCommandFactory.java	27 Nov 2002 12:26:46 -0000	1.4
  +++ ImapCommandFactory.java	3 Dec 2002 14:00:12 -0000	1.5
  @@ -55,27 +55,28 @@
           _imapCommands.put( LsubCommand.NAME, LsubCommand.class );
           _imapCommands.put( StatusCommand.NAME, StatusCommand.class );
           _imapCommands.put( AppendCommand.NAME, AppendCommand.class );
  -        
  +
   //        // RFC2342 NAMESPACE
   //        _imapCommands.put( "NAMESPACE", NamespaceCommand.class );
  -//        // RFC2086 GETACL, SETACL, DELETEACL, LISTRIGHTS, MYRIGHTS
  +
  +        // RFC2086 GETACL, SETACL, DELETEACL, LISTRIGHTS, MYRIGHTS
   //        _imapCommands.put( "GETACL", GetAclCommand.class );
   //        _imapCommands.put( "SETACL", SetAclCommand.class );
   //        _imapCommands.put( "DELETEACL", DeleteAclCommand.class );
   //        _imapCommands.put( "LISTRIGHTS", ListRightsCommand.class );
   //        _imapCommands.put( "MYRIGHTS", MyRightsCommand.class );
  -//
  -//
  -//        // Commands only valid in SELECTED state.
  -//        // CHECK, CLOSE, EXPUNGE, SEARCH, FETCH, STORE, COPY, and UID
  -//        _imapCommands.put( "CHECK", CheckCommand.class );
  -//        _imapCommands.put( "CLOSE", CloseCommand.class );
  -//        _imapCommands.put( "COPY", CopyCommand.class );
  -//        _imapCommands.put( "EXPUNGE", ExpungeCommand.class );
  -//        _imapCommands.put( "SEARCH", SearchCommand.class );
  -//        _imapCommands.put( "FETCH", CommandFetch.class );
  -//        _imapCommands.put( "STORE", CommandStore.class );
  -//        _imapCommands.put( "UID", UidCommand.class );
  +
  +
  +        // Commands only valid in SELECTED state.
  +        // CHECK, CLOSE, EXPUNGE, SEARCH, FETCH, STORE, COPY, and UID
  +        _imapCommands.put( CheckCommand.NAME, CheckCommand.class );
  +        _imapCommands.put( CloseCommand.NAME, CloseCommand.class );
  +        _imapCommands.put( ExpungeCommand.NAME, ExpungeCommand.class );
  +        _imapCommands.put( CopyCommand.NAME, CopyCommand.class );
  +        _imapCommands.put( SearchCommand.NAME, SearchCommand.class );
  +        _imapCommands.put( FetchCommand.NAME, FetchCommand.class );
  +        _imapCommands.put( StoreCommand.NAME, StoreCommand.class );
  +        _imapCommands.put( UidCommand.NAME, UidCommand.class );
       }
   
       public ImapCommand getCommand( String commandName )
  @@ -96,6 +97,9 @@
               ImapCommand cmd = ( ImapCommand ) commandClass.newInstance();
               if ( cmd instanceof LogEnabled ) {
                   ( ( LogEnabled ) cmd ).enableLogging( getLogger() );
  +            }
  +            if ( cmd instanceof UidCommand ) {
  +                ( ( UidCommand) cmd ).setCommandFactory( this );
               }
               return cmd;
           }
  
  
  
  1.4       +2 -2      jakarta-james/proposals/imap2/java/org/apache/james/imapserver/commands/SelectCommand.java
  
  Index: SelectCommand.java
  ===================================================================
  RCS file: /home/cvs/jakarta-james/proposals/imap2/java/org/apache/james/imapserver/commands/SelectCommand.java,v
  retrieving revision 1.3
  retrieving revision 1.4
  diff -u -r1.3 -r1.4
  --- SelectCommand.java	27 Nov 2002 02:24:09 -0000	1.3
  +++ SelectCommand.java	3 Dec 2002 14:00:12 -0000	1.4
  @@ -53,7 +53,7 @@
   
           int firstUnseen = mailbox.getFirstUnseen();
           if ( firstUnseen != 0 ) {
  -            int msnUnseen = mailbox.getIndex( firstUnseen );
  +            int msnUnseen = mailbox.getMsn( firstUnseen );
               response.okResponse( "UNSEEN " + msnUnseen,
                                    "Message " + msnUnseen + " is the first unseen" );
           }
  
  
  
  1.4       +3 -3      jakarta-james/proposals/imap2/java/org/apache/james/imapserver/commands/StatusCommand.java
  
  Index: StatusCommand.java
  ===================================================================
  RCS file: /home/cvs/jakarta-james/proposals/imap2/java/org/apache/james/imapserver/commands/StatusCommand.java,v
  retrieving revision 1.3
  retrieving revision 1.4
  diff -u -r1.3 -r1.4
  --- StatusCommand.java	27 Nov 2002 02:24:09 -0000	1.3
  +++ StatusCommand.java	3 Dec 2002 14:00:12 -0000	1.4
  @@ -15,13 +15,13 @@
   import org.apache.james.imapserver.store.MailboxException;
   
   /**
  - * Handles processeing for the NOOP imap command.
  + * Handles processeing for the STATUS imap command.
    *
    * @author  Darrell DeBoer <darrell@apache.org>
    *
    * @version $Revision$
    */
  -class StatusCommand extends CommandTemplate
  +class StatusCommand extends AuthenticatedStateCommand
   {
       public static final String NAME = "STATUS";
       public static final String ARGS = "<mailbox> ( <status-data-item>+ )";
  
  
  
  1.3       +2 -2      jakarta-james/proposals/imap2/java/org/apache/james/imapserver/commands/SubscribeCommand.java
  
  Index: SubscribeCommand.java
  ===================================================================
  RCS file: /home/cvs/jakarta-james/proposals/imap2/java/org/apache/james/imapserver/commands/SubscribeCommand.java,v
  retrieving revision 1.2
  retrieving revision 1.3
  diff -u -r1.2 -r1.3
  --- SubscribeCommand.java	27 Nov 2002 12:26:46 -0000	1.2
  +++ SubscribeCommand.java	3 Dec 2002 14:00:12 -0000	1.3
  @@ -21,7 +21,7 @@
    *
    * @version $Revision$
    */
  -class SubscribeCommand extends CommandTemplate
  +class SubscribeCommand extends AuthenticatedStateCommand
   {
       public static final String NAME = "SUBSCRIBE";
       public static final String ARGS = "<mailbox>";
  
  
  
  1.3       +2 -2      jakarta-james/proposals/imap2/java/org/apache/james/imapserver/commands/UnsubscribeCommand.java
  
  Index: UnsubscribeCommand.java
  ===================================================================
  RCS file: /home/cvs/jakarta-james/proposals/imap2/java/org/apache/james/imapserver/commands/UnsubscribeCommand.java,v
  retrieving revision 1.2
  retrieving revision 1.3
  diff -u -r1.2 -r1.3
  --- UnsubscribeCommand.java	27 Nov 2002 12:26:46 -0000	1.2
  +++ UnsubscribeCommand.java	3 Dec 2002 14:00:12 -0000	1.3
  @@ -21,7 +21,7 @@
    *
    * @version $Revision$
    */
  -class UnsubscribeCommand extends CommandTemplate
  +class UnsubscribeCommand extends AuthenticatedStateCommand
   {
       public static final String NAME = "UNSUBSCRIBE";
       public static final String ARGS = "<mailbox>";
  
  
  
  1.1                  jakarta-james/proposals/imap2/java/org/apache/james/imapserver/commands/CheckCommand.java
  
  Index: CheckCommand.java
  ===================================================================
  /*
   * Copyright (C) The Apache Software Foundation. All rights reserved.
   *
   * This software is published under the terms of the Apache Software License
   * version 1.1, a copy of which has been included with this distribution in
   * the LICENSE file.
   */
  package org.apache.james.imapserver.commands;
  
  import org.apache.james.imapserver.ImapRequestLineReader;
  import org.apache.james.imapserver.ImapResponse;
  import org.apache.james.imapserver.ImapSession;
  import org.apache.james.imapserver.ProtocolException;
  
  /**
   * Handles processeing for the CHECK imap command.
   *
   * @author  Darrell DeBoer <darrell@apache.org>
   *
   * @version $Revision: 1.1 $
   */
  class CheckCommand extends SelectedStateCommand
  {
      public static final String NAME = "CHECK";
      public static final String ARGS = null;
  
      /** @see CommandTemplate#doProcess */
      protected void doProcess( ImapRequestLineReader request,
                                ImapResponse response,
                                ImapSession session ) throws ProtocolException
      {
          parser.endLine( request );
          session.unsolicitedResponses( response );
          response.commandComplete( this );
      }
  
      /** @see ImapCommand#getName */
      public String getName()
      {
          return NAME;
      }
  
      /** @see CommandTemplate#getArgSyntax */
      public String getArgSyntax()
      {
          return ARGS;
      }
  }
  
  
  
  1.1                  jakarta-james/proposals/imap2/java/org/apache/james/imapserver/commands/CloseCommand.java
  
  Index: CloseCommand.java
  ===================================================================
  /*
   * Copyright (C) The Apache Software Foundation. All rights reserved.
   *
   * This software is published under the terms of the Apache Software License
   * version 1.1, a copy of which has been included with this distribution in
   * the LICENSE file.
   */
  package org.apache.james.imapserver.commands;
  
  import org.apache.james.imapserver.ImapRequestLineReader;
  import org.apache.james.imapserver.ImapResponse;
  import org.apache.james.imapserver.ImapSession;
  import org.apache.james.imapserver.ProtocolException;
  import org.apache.james.imapserver.ImapHost;
  import org.apache.james.imapserver.store.ImapMailbox;
  import org.apache.james.imapserver.store.MailboxException;
  
  /**
   * Handles processeing for the CHECK imap command.
   *
   * @author  Darrell DeBoer <darrell@apache.org>
   *
   * @version $Revision: 1.1 $
   */
  class CloseCommand extends SelectedStateCommand
  {
      public static final String NAME = "CLOSE";
      public static final String ARGS = null;
  
      /** @see CommandTemplate#doProcess */
      protected void doProcess( ImapRequestLineReader request,
                                ImapResponse response,
                                ImapSession session )
              throws ProtocolException, MailboxException
      {
          parser.endLine( request );
  
          if ( ! session.selectedIsReadOnly() ) {
              ImapMailbox mailbox = session.getSelected();
              ImapHost host = session.getHost();
              host.expunge( mailbox );
          }
          session.deselect();
  
          session.unsolicitedResponses( response );
          response.commandComplete( this );
      }
  
  
      /** @see ImapCommand#getName */
      public String getName()
      {
          return NAME;
      }
  
      /** @see CommandTemplate#getArgSyntax */
      public String getArgSyntax()
      {
          return ARGS;
      }
  }
  /*
  6.4.2.  CLOSE Command
  
     Arguments:  none
  
     Responses:  no specific responses for this command
  
     Result:     OK - close completed, now in authenticated state
                 NO - close failure: no mailbox selected
                 BAD - command unknown or arguments invalid
  
        The CLOSE command permanently removes from the currently selected
        mailbox all messages that have the \Deleted flag set, and returns
        to authenticated state from selected state.  No untagged EXPUNGE
        responses are sent.
  
        No messages are removed, and no error is given, if the mailbox is
        selected by an EXAMINE command or is otherwise selected read-only.
  
        Even if a mailbox is selected, a SELECT, EXAMINE, or LOGOUT
        command MAY be issued without previously issuing a CLOSE command.
        The SELECT, EXAMINE, and LOGOUT commands implicitly close the
        currently selected mailbox without doing an expunge.  However,
        when many messages are deleted, a CLOSE-LOGOUT or CLOSE-SELECT
  
        sequence is considerably faster than an EXPUNGE-LOGOUT or
        EXPUNGE-SELECT because no untagged EXPUNGE responses (which the
        client would probably ignore) are sent.
  
     Example:    C: A341 CLOSE
                 S: A341 OK CLOSE completed
  */
  
  
  
  1.1                  jakarta-james/proposals/imap2/java/org/apache/james/imapserver/commands/CopyCommand.java
  
  Index: CopyCommand.java
  ===================================================================
  /*
   * Copyright (C) The Apache Software Foundation. All rights reserved.
   *
   * This software is published under the terms of the Apache Software License
   * version 1.1, a copy of which has been included with this distribution in
   * the LICENSE file.
   */
  package org.apache.james.imapserver.commands;
  
  import org.apache.james.imapserver.ImapRequestLineReader;
  import org.apache.james.imapserver.ImapResponse;
  import org.apache.james.imapserver.ImapSession;
  import org.apache.james.imapserver.ProtocolException;
  import org.apache.james.imapserver.store.ImapMailbox;
  import org.apache.james.imapserver.store.MailboxException;
  import org.apache.james.imapserver.store.SimpleImapMessage;
  
  /**
   * Handles processeing for the COPY imap command.
   *
   * @author  Darrell DeBoer <darrell@apache.org>
   *
   * @version $Revision: 1.1 $
   */
  class CopyCommand extends SelectedStateCommand implements UidEnabledCommand
  {
      public static final String NAME = "COPY";
      public static final String ARGS = "<message-set> <mailbox>";
  
      /** @see CommandTemplate#doProcess */
      protected void doProcess( ImapRequestLineReader request,
                                ImapResponse response,
                                ImapSession session )
          throws ProtocolException, MailboxException
      {
          doProcess( request, response, session, false );
      }
  
      public void doProcess( ImapRequestLineReader request,
                                ImapResponse response,
                                ImapSession session,
                                boolean useUids)
              throws ProtocolException, MailboxException
      {
          IdSet idSet = parser.set( request );
          String mailboxName = parser.mailbox( request );
          parser.endLine( request );
  
          ImapMailbox currentMailbox = session.getSelected();
          ImapMailbox toMailbox;
          try {
              toMailbox = getMailbox( mailboxName, session, true );
          }
          catch ( MailboxException e ) {
              e.setResponseCode( "TRYCREATE" );
              throw e;
          }
  
          long[] uids = currentMailbox.getMessageUids();
          for ( int i = 0; i < uids.length; i++ ) {
              long uid = uids[i];
              boolean inSet;
              if ( useUids ) {
                  inSet = idSet.includes( uid );
              }
              else {
                  int msn = currentMailbox.getMsn( uid );
                  inSet = idSet.includes( msn );
              }
  
              if ( inSet ) {
                  session.getHost().copyMessage( uid, currentMailbox, toMailbox );
              }
          }
  
          session.unsolicitedResponses( response );
          response.commandComplete( this );
      }
  
      /** @see ImapCommand#getName */
      public String getName()
      {
          return NAME;
      }
  
      /** @see CommandTemplate#getArgSyntax */
      public String getArgSyntax()
      {
          return ARGS;
      }
  }
  
  
  
  1.1                  jakarta-james/proposals/imap2/java/org/apache/james/imapserver/commands/ExpungeCommand.java
  
  Index: ExpungeCommand.java
  ===================================================================
  /*
   * Copyright (C) The Apache Software Foundation. All rights reserved.
   *
   * This software is published under the terms of the Apache Software License
   * version 1.1, a copy of which has been included with this distribution in
   * the LICENSE file.
   */
  package org.apache.james.imapserver.commands;
  
  import org.apache.james.imapserver.ImapRequestLineReader;
  import org.apache.james.imapserver.ImapResponse;
  import org.apache.james.imapserver.ImapSession;
  import org.apache.james.imapserver.ProtocolException;
  import org.apache.james.imapserver.ImapHost;
  import org.apache.james.imapserver.store.ImapMailbox;
  import org.apache.james.imapserver.store.MailboxException;
  
  /**
   * Handles processeing for the EXPUNGE imap command.
   *
   * @author  Darrell DeBoer <darrell@apache.org>
   *
   * @version $Revision: 1.1 $
   */
  class ExpungeCommand extends SelectedStateCommand
  {
      public static final String NAME = "EXPUNGE";
      public static final String ARGS = null;
  
      /** @see CommandTemplate#doProcess */
      protected void doProcess( ImapRequestLineReader request,
                                ImapResponse response,
                                ImapSession session )
              throws ProtocolException, MailboxException
      {
          parser.endLine( request );
  
          if ( session.selectedIsReadOnly() ) {
              response.commandFailed( this, "Mailbox selected read only." );
          }
  
          ImapMailbox mailbox = session.getSelected();
          ImapHost host = session.getHost();
          int[] msns = host.expunge( mailbox );
          for ( int i = 0; i < msns.length; i++ ) {
              int msn = msns[i];
              response.expungeResponse( msn );
          }
  
          session.unsolicitedResponses( response );
          response.commandComplete( this );
      }
  
      /** @see ImapCommand#getName */
      public String getName()
      {
          return NAME;
      }
  
      /** @see CommandTemplate#getArgSyntax */
      public String getArgSyntax()
      {
          return ARGS;
      }
  }
  /*
  6.4.3.  EXPUNGE Command
  
     Arguments:  none
  
     Responses:  untagged responses: EXPUNGE
  
     Result:     OK - expunge completed
                 NO - expunge failure: can't expunge (e.g. permission
                      denied)
                 BAD - command unknown or arguments invalid
  
        The EXPUNGE command permanently removes from the currently
        selected mailbox all messages that have the \Deleted flag set.
        Before returning an OK to the client, an untagged EXPUNGE response
        is sent for each message that is removed.
  
     Example:    C: A202 EXPUNGE
                 S: * 3 EXPUNGE
                 S: * 3 EXPUNGE
                 S: * 5 EXPUNGE
                 S: * 8 EXPUNGE
                 S: A202 OK EXPUNGE completed
  
        Note: in this example, messages 3, 4, 7, and 11 had the
        \Deleted flag set.  See the description of the EXPUNGE
        response for further explanation.
  */
  
  
  
  
  1.1                  jakarta-james/proposals/imap2/java/org/apache/james/imapserver/commands/FetchCommand.java
  
  Index: FetchCommand.java
  ===================================================================
  /*
   * Copyright (C) The Apache Software Foundation. All rights reserved.
   *
   * This software is published under the terms of the Apache Software License
   * version 1.1, a copy of which has been included with this distribution in
   * the LICENSE file.
   */
  package org.apache.james.imapserver.commands;
  
  import org.apache.excalibur.util.StringUtil;
  import org.apache.james.core.MimeMessageWrapper;
  import org.apache.james.imapserver.ImapRequestLineReader;
  import org.apache.james.imapserver.ImapResponse;
  import org.apache.james.imapserver.ImapSession;
  import org.apache.james.imapserver.ProtocolException;
  import org.apache.james.imapserver.store.MailboxException;
  import org.apache.james.imapserver.store.MessageFlags;
  import org.apache.james.imapserver.store.SimpleImapMessage;
  import org.apache.james.imapserver.store.ImapMailbox;
  
  import javax.mail.MessagingException;
  import javax.mail.internet.MimeMessage;
  import java.io.ByteArrayOutputStream;
  import java.io.IOException;
  import java.util.ArrayList;
  import java.util.Collections;
  import java.util.Enumeration;
  import java.util.Iterator;
  import java.util.List;
  import java.util.Collection;
  
  /**
   * Handles processeing for the FETCH imap command.
   *
   * @author  Darrell DeBoer <darrell@apache.org>
   *
   * @version $Revision: 1.1 $
   */
  class FetchCommand extends SelectedStateCommand implements UidEnabledCommand
  {
      public static final String NAME = "FETCH";
      public static final String ARGS = "<message-set> <fetch-profile>";
  
      private FetchCommandParser parser = new FetchCommandParser();
  
      /** @see CommandTemplate#doProcess */
      protected void doProcess( ImapRequestLineReader request,
                                ImapResponse response,
                                ImapSession session )
              throws ProtocolException, MailboxException
      {
          doProcess( request, response, session, false );
      }
  
      public void doProcess( ImapRequestLineReader request,
                                ImapResponse response,
                                ImapSession session,
                                boolean useUids )
              throws ProtocolException, MailboxException
      {
          IdSet idSet = parser.set( request );
          FetchRequest fetch = parser.fetchRequest( request );
          parser.endLine( request );
  
          ImapMailbox mailbox = session.getSelected();
          long[] uids = mailbox.getMessageUids();
          for ( int i = 0; i < uids.length; i++ ) {
              long uid = uids[i];
              int msn = mailbox.getMsn( uid );
  
              if ( ( useUids && idSet.includes( uid ) ) ||
                   ( !useUids && idSet.includes( msn ) ) )
              {
                  SimpleImapMessage imapMessage = mailbox.getMessage( uid );
                  String msgData = outputMessage( fetch, imapMessage );
                  response.fetchResponse( msn, msgData );
              }
          }
  
          session.unsolicitedResponses( response );
          response.commandComplete( this );
      }
  
      private String outputMessage( FetchRequest fetch, SimpleImapMessage message )
              throws MailboxException, ProtocolException
      {
  
          StringBuffer response = new StringBuffer();
  
          List elements = fetch.getElements();
          for ( int i = 0; i < elements.size(); i++ ) {
              FetchElement fetchElement = ( FetchElement ) elements.get( i );
              if ( i > 0 ) {
                  response.append( SP );
              }
              response.append( fetchElement.getResponseName() );
              response.append( SP );
  
              if ( fetchElement == FetchElement.FLAGS ) {
                  MessageFlags flags = message.getFlags();
                  response.append( flags.format() );
              }
              else if ( fetchElement == FetchElement.INTERNALDATE ) {
                  // TODO format properly
                  String internalDate = message.getAttributes().getInternalDateAsString();
                  response.append( "\"" );
                  response.append( internalDate );
                  response.append ( "\"" );
              }
              else if ( fetchElement == FetchElement.RFC822_SIZE ) {
                  response.append( message.getAttributes().getSize() );
              }
              else if ( fetchElement == FetchElement.ENVELOPE ) {
                  response.append( message.getAttributes().getEnvelope() );
              }
              else if ( fetchElement == FetchElement.BODY ) {
                  response.append( message.getAttributes().getBodyStructure( false ) );
              }
              else if ( fetchElement == FetchElement.BODYSTRUCTURE ) {
                  response.append( message.getAttributes().getBodyStructure( true ) );
              }
              else if ( fetchElement == FetchElement.UID ) {
                  response.append( message.getUid() );
              }
              // Various mechanisms for returning message body.
              else if ( fetchElement instanceof BodyFetchElement ) {
                  BodyFetchElement bodyFetchElement = (BodyFetchElement)fetchElement;
                  String sectionSpecifier = bodyFetchElement.getParameters();
                  boolean peek = bodyFetchElement.isPeek();
  
                  MimeMessage mimeMessage = message.getMimeMessage();
                  try {
                      handleBodyFetch( mimeMessage, sectionSpecifier, response );
                  }
                  catch ( MessagingException e ) {
                      // TODO  chain exceptions
                      throw new MailboxException( e.getMessage() );
                  }
  
                  if ( ! peek ) {
                      message.getFlags().setSeen( true );
                      // TODO need to store this change.
                  }
              }
              else {
                  throw new ProtocolException( "Invalid fetch attribute" );
              }
          }
  
          return response.toString();
      }
  
  
      private void handleBodyFetch( MimeMessage mimeMessage,
                                    String sectionSpecifier,
                                    StringBuffer response )
              throws ProtocolException, MessagingException
      {
          if ( sectionSpecifier.length() == 0 ) {
              // TODO - need to use an InputStream from the response here.
              ByteArrayOutputStream bout = new ByteArrayOutputStream();
              try {
                  mimeMessage.writeTo(bout);
              }
              catch ( IOException e ) {
                  throw new ProtocolException( "Error reading message source" );
              }
              byte[] bytes = bout.toByteArray();
              addLiteral( bytes, response );
          }
          else if ( sectionSpecifier.equalsIgnoreCase( "HEADER" ) ) {
              Enumeration enum = mimeMessage.getAllHeaderLines();
              addHeaders( enum, response );
          }
          else if ( sectionSpecifier.startsWith( "HEADER.FIELDS.NOT " ) ) {
              String[] excludeNames = extractHeaderList( sectionSpecifier, "HEADER.FIELDS.NOT ".length() );
              Enumeration enum = mimeMessage.getNonMatchingHeaderLines( excludeNames );
              addHeaders( enum, response );
          }
          else if ( sectionSpecifier.startsWith( "HEADER.FIELDS " ) ) {
              String[] includeNames = extractHeaderList( sectionSpecifier, "HEADER.FIELDS ".length() );
              Enumeration enum = mimeMessage.getMatchingHeaderLines( includeNames );
              addHeaders( enum, response );
          }
          else if ( sectionSpecifier.equalsIgnoreCase( "MIME" ) ) {
              // TODO implement
              throw new ProtocolException( "MIME not yet implemented." );
          }
          else if ( sectionSpecifier.equalsIgnoreCase( "TEXT" ) ) {
              // TODO - need to use an InputStream from the response here.
              // TODO - this is a hack. To get just the body content, I'm using a null
              // input stream to take the headers. Need to have a way of ignoring headers.
              ByteArrayOutputStream headerOut = new ByteArrayOutputStream();
              ByteArrayOutputStream bodyOut = new ByteArrayOutputStream();
              try {
                  MimeMessageWrapper.writeTo( mimeMessage, headerOut, bodyOut );
                  byte[] bytes = bodyOut.toByteArray();
  
                  addLiteral( bytes, response );
  
              }
              catch ( IOException e ) {
                  throw new ProtocolException( "Error reading message source" );
              }
          }
          else {
              // Should be a part specifier followed by a section specifier.
              // See if there's a leading part specifier.
              // If so, get the number, get the part, and call this recursively.
              int dotPos = sectionSpecifier.indexOf( '.' );
              if ( dotPos == -1 ) {
                  throw new ProtocolException( "Malformed fetch attribute: " + sectionSpecifier );
              }
              int partNumber = Integer.parseInt( sectionSpecifier.substring( 0, dotPos ) );
              String partSectionSpecifier = sectionSpecifier.substring( dotPos + 1 );
  
              // TODO - get the MimePart of the mimeMessage, and call this method
              // with the new partSectionSpecifier.
  //        MimeMessage part;
  //        handleBodyFetch( part, partSectionSpecifier, response );
              throw new ProtocolException( "Mime parts not yet implemented for fetch." );
          }
  
      }
  
      private void addLiteral( byte[] bytes, StringBuffer response )
      {
          response.append('{' );
          response.append( bytes.length + 1 );
          response.append( '}' );
          response.append( "\r\n" );
  
          for ( int i = 0; i < bytes.length; i++ ) {
              byte b = bytes[i];
              response.append((char)b);
          }
      }
  
      // TODO should do this at parse time.
      private String[] extractHeaderList( String headerList, int prefixLen )
      {
          // Remove the trailing and leading ')('
          String tmp = headerList.substring( prefixLen + 1, headerList.length() - 1 );
          String[] headerNames = StringUtil.split( tmp, " " );
          return headerNames;
      }
  
      private void addHeaders( Enumeration enum, StringBuffer response )
      {
          List lines = new ArrayList();
          int count = 0;
          while (enum.hasMoreElements()) {
              String line = (String)enum.nextElement();
              count += line.length() + 2;
              lines.add(line);
          }
          response.append( '{' );
          response.append( count + 2 );
          response.append( '}' );
          response.append("\r\n");
  
          Iterator lit = lines.iterator();
          while (lit.hasNext()) {
              String line = (String)lit.next();
              response.append( line );
              response.append( "\r\n" );
          }
          response.append("\r\n");
      }
  
      /** @see ImapCommand#getName */
      public String getName()
      {
          return NAME;
      }
  
      /** @see CommandTemplate#getArgSyntax */
      public String getArgSyntax()
      {
          return ARGS;
      }
  
      private class FetchCommandParser extends CommandParser
      {
  
  
          public FetchRequest fetchRequest( ImapRequestLineReader request )
                  throws ProtocolException
          {
              FetchRequest fetch = new FetchRequest();
              CharacterValidator validator = new NoopCharValidator();
  
              char next = nextNonSpaceChar( request );
              consumeChar( request, '(' );
  
              next = nextNonSpaceChar( request );
              while ( next != ')' ) {
                  fetch.addElement( getNextElement( request ) );
                  next = nextNonSpaceChar( request );
              }
  
              consumeChar(request, ')');
  
              return fetch;
          }
  
          private FetchElement getNextElement( ImapRequestLineReader request)
                  throws ProtocolException
          {
              char next = nextCharInLine( request );
                  StringBuffer element = new StringBuffer();
                  while ( next != ' ' && next != '[' && next != ')' ) {
                      element.append(next);
                      request.consume();
                      next = nextCharInLine( request );
                  }
                  String name = element.toString();
                  // Simple elements with no '[]' parameters.
                  if ( next == ' ' || next == ')' ) {
                      if ( "FAST".equalsIgnoreCase( name ) ) {
                          return FetchElement.FAST;
                      }
                      if ( "FULL".equalsIgnoreCase( name ) ) {
                          return FetchElement.FULL;
                      }
                      if ( "ALL".equalsIgnoreCase( name ) ) {
                          return FetchElement.ALL;
                      }
                      if ( "FLAGS".equalsIgnoreCase( name ) ) {
                          return FetchElement.FLAGS;
                      }
                      if ( "RFC822.SIZE".equalsIgnoreCase( name ) ) {
                          return FetchElement.RFC822_SIZE;
                      }
                      if ( "ENVELOPE".equalsIgnoreCase( name ) ) {
                          return FetchElement.ENVELOPE;
                      }
                      if ( "INTERNALDATE".equalsIgnoreCase( name ) ) {
                          return FetchElement.INTERNALDATE;
                      }
                      if ( "BODY".equalsIgnoreCase( name ) ) {
                          return FetchElement.BODY;
                      }
                      if ( "BODYSTRUCTURE".equalsIgnoreCase( name ) ) {
                          return FetchElement.BODYSTRUCTURE;
                      }
                      if ( "UID".equalsIgnoreCase( name ) ) {
                          return FetchElement.UID;
                      }
                      if ( "RFC822".equalsIgnoreCase( name ) ) {
                          return new BodyFetchElement( "RFC822", "", false );
                      }
                      if ( "RFC822.HEADER".equalsIgnoreCase( name ) ) {
                          return new BodyFetchElement( "RFC822.HEADER", "HEADER", true );
                      }
                      if ( "RFC822.TEXT".equalsIgnoreCase( name ) ) {
                          return new BodyFetchElement( "RFC822.TEXT", "TEXT", false );
                      }
                      else {
                          throw new ProtocolException( "Invalid fetch attribute: " + name );
                      }
                  }
                  else {
                      consumeChar( request, '[' );
  
                      StringBuffer sectionIdentifier = new StringBuffer();
                      next = nextCharInLine( request );
                      while ( next != ']' ) {
                          sectionIdentifier.append( next );
                          request.consume();
                          next = nextCharInLine(request);
                      }
                      consumeChar( request, ']' );
  
                      String parameter = sectionIdentifier.toString();
  
                      if ( "BODY".equalsIgnoreCase( name ) ) {
                          return new BodyFetchElement( "BODY[" + parameter + "]", parameter, false );
                      }
                      if ( "BODY.PEEK".equalsIgnoreCase( name ) ) {
                          return new BodyFetchElement( "BODY[" + parameter + "]", parameter, true );
                      }
                      else {
                          throw new ProtocolException( "Invalid fetch attibute: " + name + "[]" );
                      }
                  }
              }
  
          private char nextCharInLine( ImapRequestLineReader request )
                  throws ProtocolException
          {
              char next = request.nextChar();
              if ( next == '\r' || next == '\n' ) {
                  throw new ProtocolException( "Unexpected end of line." );
              }
              return next;
          }
  
          private char nextNonSpaceChar( ImapRequestLineReader request )
                  throws ProtocolException
          {
              char next = request.nextChar();
              while ( next == ' ' ) {
                  request.consume();
                  next = request.nextChar();
              }
              return next;
          }
  
      }
  
  
      private static class FetchRequest
      {
          private ArrayList elements = new ArrayList();
          List getElements() {
              return Collections.unmodifiableList( elements );
          }
          void addElement( FetchElement element )
          {
              if ( element == FetchElement.ALL ) {
                  elements.add( FetchElement.FLAGS );
                  elements.add( FetchElement.INTERNALDATE );
                  elements.add( FetchElement.RFC822_SIZE );
                  elements.add( FetchElement.ENVELOPE );
              }
              else if ( element == FetchElement.FAST ) {
                  elements.add( FetchElement.FLAGS );
                  elements.add( FetchElement.INTERNALDATE );
                  elements.add( FetchElement.RFC822_SIZE );
             }
              else if ( element == FetchElement.FULL ) {
                  elements.add( FetchElement.FLAGS );
                  elements.add( FetchElement.INTERNALDATE );
                  elements.add( FetchElement.RFC822_SIZE );
                  elements.add( FetchElement.ENVELOPE );
                  elements.add( FetchElement.BODY );
              }
              else {
                  elements.add( element );
              }
          }
      }
  
      private static class FetchElement
      {
          public static final FetchElement FLAGS = new FetchElement( "FLAGS" );
          public static final FetchElement INTERNALDATE = new FetchElement( "INTERNALDATE" );
          public static final FetchElement RFC822_SIZE= new FetchElement( "RFC822.SIZE" );
          public static final FetchElement ENVELOPE = new FetchElement( "ENVELOPE" );
          public static final FetchElement BODY = new FetchElement( "BODY" );
          public static final FetchElement BODYSTRUCTURE = new FetchElement( "BODYSTRUCTURE" );
          public static final FetchElement UID = new FetchElement( "UID" );
  
          public static final FetchElement FAST = new FetchElement( "FAST" );
          public static final FetchElement FULL = new FetchElement( "FULL" );
          public static final FetchElement ALL = new FetchElement( "ALL" );
  
          String name;
  
          protected FetchElement( String name )
          {
              this.name = name;
          }
  
          public String getResponseName() {
              return this.name;
          }
      }
  
      private class BodyFetchElement extends FetchElement
      {
          private boolean peek;
          private String sectionIdentifier;
  
          public BodyFetchElement( String name, String sectionIdentifier, boolean peek )
          {
              super( name );
              this.sectionIdentifier = sectionIdentifier;
              this.peek = peek;
          }
  
          public String getParameters()
          {
              return this.sectionIdentifier;
          }
  
          public boolean isPeek()
          {
              return this.peek;
          }
      }
  
  }
  /*
  6.4.5.  FETCH Command
  
     Arguments:  message set
                 message data item names
  
     Responses:  untagged responses: FETCH
  
     Result:     OK - fetch completed
                 NO - fetch error: can't fetch that data
                 BAD - command unknown or arguments invalid
  
        The FETCH command retrieves data associated with a message in the
        mailbox.  The data items to be fetched can be either a single atom
        or a parenthesized list.
  
        The currently defined data items that can be fetched are:
  
        ALL            Macro equivalent to: (FLAGS INTERNALDATE
                       RFC822.SIZE ENVELOPE)
  
        BODY           Non-extensible form of BODYSTRUCTURE.
  
        BODY[<section>]<<partial>>
                       The text of a particular body section.  The section
                       specification is a set of zero or more part
                       specifiers delimited by periods.  A part specifier
                       is either a part number or one of the following:
                       HEADER, HEADER.FIELDS, HEADER.FIELDS.NOT, MIME, and
                       TEXT.  An empty section specification refers to the
                       entire message, including the header.
  
                       Every message has at least one part number.
                       Non-[MIME-IMB] messages, and non-multipart
                       [MIME-IMB] messages with no encapsulated message,
                       only have a part 1.
  
                       Multipart messages are assigned consecutive part
                       numbers, as they occur in the message.  If a
                       particular part is of type message or multipart,
                       its parts MUST be indicated by a period followed by
                       the part number within that nested multipart part.
  
                       A part of type MESSAGE/RFC822 also has nested part
                       numbers, referring to parts of the MESSAGE part's
                       body.
  
                       The HEADER, HEADER.FIELDS, HEADER.FIELDS.NOT, and
                       TEXT part specifiers can be the sole part specifier
                       or can be prefixed by one or more numeric part
                       specifiers, provided that the numeric part
                       specifier refers to a part of type MESSAGE/RFC822.
                       The MIME part specifier MUST be prefixed by one or
                       more numeric part specifiers.
  
                       The HEADER, HEADER.FIELDS, and HEADER.FIELDS.NOT
                       part specifiers refer to the [RFC-822] header of
                       the message or of an encapsulated [MIME-IMT]
                       MESSAGE/RFC822 message.  HEADER.FIELDS and
                       HEADER.FIELDS.NOT are followed by a list of
                       field-name (as defined in [RFC-822]) names, and
                       return a subset of the header.  The subset returned
                       by HEADER.FIELDS contains only those header fields
                       with a field-name that matches one of the names in
                       the list; similarly, the subset returned by
                       HEADER.FIELDS.NOT contains only the header fields
                       with a non-matching field-name.  The field-matching
                       is case-insensitive but otherwise exact.  In all
                       cases, the delimiting blank line between the header
                       and the body is always included.
  
                       The MIME part specifier refers to the [MIME-IMB]
                       header for this part.
  
                       The TEXT part specifier refers to the text body of
                       the message, omitting the [RFC-822] header.
  
  
                         Here is an example of a complex message
                         with some of its part specifiers:
  
                          HEADER     ([RFC-822] header of the message)
                          TEXT       MULTIPART/MIXED
                          1          TEXT/PLAIN
                          2          APPLICATION/OCTET-STREAM
                          3          MESSAGE/RFC822
                          3.HEADER   ([RFC-822] header of the message)
                          3.TEXT     ([RFC-822] text body of the message)
                          3.1        TEXT/PLAIN
                          3.2        APPLICATION/OCTET-STREAM
                          4          MULTIPART/MIXED
                          4.1        IMAGE/GIF
                          4.1.MIME   ([MIME-IMB] header for the IMAGE/GIF)
                          4.2        MESSAGE/RFC822
                          4.2.HEADER ([RFC-822] header of the message)
                          4.2.TEXT   ([RFC-822] text body of the message)
                          4.2.1      TEXT/PLAIN
                          4.2.2      MULTIPART/ALTERNATIVE
                          4.2.2.1    TEXT/PLAIN
                          4.2.2.2    TEXT/RICHTEXT
  
  
                       It is possible to fetch a substring of the
                       designated text.  This is done by appending an open
                       angle bracket ("<"), the octet position of the
                       first desired octet, a period, the maximum number
                       of octets desired, and a close angle bracket (">")
                       to the part specifier.  If the starting octet is
                       beyond the end of the text, an empty string is
                       returned.
  
                       Any partial fetch that attempts to read beyond the
                       end of the text is truncated as appropriate.  A
                       partial fetch that starts at octet 0 is returned as
                       a partial fetch, even if this truncation happened.
  
                            Note: this means that BODY[]<0.2048> of a
                            1500-octet message will return BODY[]<0>
                            with a literal of size 1500, not BODY[].
  
                            Note: a substring fetch of a
                            HEADER.FIELDS or HEADER.FIELDS.NOT part
                            specifier is calculated after subsetting
                            the header.
  
  
                       The \Seen flag is implicitly set; if this causes
                       the flags to change they SHOULD be included as part
                       of the FETCH responses.
  
        BODY.PEEK[<section>]<<partial>>
                       An alternate form of BODY[<section>] that does not
                       implicitly set the \Seen flag.
  
        BODYSTRUCTURE  The [MIME-IMB] body structure of the message.  This
                       is computed by the server by parsing the [MIME-IMB]
                       header fields in the [RFC-822] header and
                       [MIME-IMB] headers.
  
        ENVELOPE       The envelope structure of the message.  This is
                       computed by the server by parsing the [RFC-822]
                       header into the component parts, defaulting various
                       fields as necessary.
  
        FAST           Macro equivalent to: (FLAGS INTERNALDATE
                       RFC822.SIZE)
  
        FLAGS          The flags that are set for this message.
  
        FULL           Macro equivalent to: (FLAGS INTERNALDATE
                       RFC822.SIZE ENVELOPE BODY)
  
        INTERNALDATE   The internal date of the message.
  
        RFC822         Functionally equivalent to BODY[], differing in the
                       syntax of the resulting untagged FETCH data (RFC822
                       is returned).
  
        RFC822.HEADER  Functionally equivalent to BODY.PEEK[HEADER],
                       differing in the syntax of the resulting untagged
                       FETCH data (RFC822.HEADER is returned).
  
        RFC822.SIZE    The [RFC-822] size of the message.
  
        RFC822.TEXT    Functionally equivalent to BODY[TEXT], differing in
                       the syntax of the resulting untagged FETCH data
                       (RFC822.TEXT is returned).
  
        UID            The unique identifier for the message.
  
     Example:    C: A654 FETCH 2:4 (FLAGS BODY[HEADER.FIELDS (DATE FROM)])
                 S: * 2 FETCH ....
                 S: * 3 FETCH ....
                 S: * 4 FETCH ....
                 S: A654 OK FETCH completed
  
  
  7.4.2.  FETCH Response
  
     Contents:   message data
  
        The FETCH response returns data about a message to the client.
        The data are pairs of data item names and their values in
        parentheses.  This response occurs as the result of a FETCH or
        STORE command, as well as by unilateral server decision (e.g. flag
        updates).
  
        The current data items are:
  
        BODY           A form of BODYSTRUCTURE without extension data.
  
        BODY[<section>]<<origin_octet>>
                       A string expressing the body contents of the
                       specified section.  The string SHOULD be
                       interpreted by the client according to the content
                       transfer encoding, body type, and subtype.
  
                       If the origin octet is specified, this string is a
                       substring of the entire body contents, starting at
                       that origin octet.  This means that BODY[]<0> MAY
                       be truncated, but BODY[] is NEVER truncated.
  
                       8-bit textual data is permitted if a [CHARSET]
                       identifier is part of the body parameter
                       parenthesized list for this section.  Note that
                       headers (part specifiers HEADER or MIME, or the
                       header portion of a MESSAGE/RFC822 part), MUST be
                       7-bit; 8-bit characters are not permitted in
                       headers.  Note also that the blank line at the end
                       of the header is always included in header data.
  
                       Non-textual data such as binary data MUST be
                       transfer encoded into a textual form such as BASE64
                       prior to being sent to the client.  To derive the
                       original binary data, the client MUST decode the
                       transfer encoded string.
  
        BODYSTRUCTURE  A parenthesized list that describes the [MIME-IMB]
                       body structure of a message.  This is computed by
                       the server by parsing the [MIME-IMB] header fields,
                       defaulting various fields as necessary.
  
                       For example, a simple text message of 48 lines and
                       2279 octets can have a body structure of: ("TEXT"
                       "PLAIN" ("CHARSET" "US-ASCII") NIL NIL "7BIT" 2279
                       48)
  
                       Multiple parts are indicated by parenthesis
                       nesting.  Instead of a body type as the first
                       element of the parenthesized list there is a nested
                       body.  The second element of the parenthesized list
                       is the multipart subtype (mixed, digest, parallel,
                       alternative, etc.).
  
                       For example, a two part message consisting of a
                       text and a BASE645-encoded text attachment can have
                       a body structure of: (("TEXT" "PLAIN" ("CHARSET"
                       "US-ASCII") NIL NIL "7BIT" 1152 23)("TEXT" "PLAIN"
                       ("CHARSET" "US-ASCII" "NAME" "cc.diff")
                       "<960723163407.20117h@cac.washington.edu>"
                       "Compiler diff" "BASE64" 4554 73) "MIXED"))
  
                       Extension data follows the multipart subtype.
                       Extension data is never returned with the BODY
                       fetch, but can be returned with a BODYSTRUCTURE
                       fetch.  Extension data, if present, MUST be in the
                       defined order.
  
                       The extension data of a multipart body part are in
                       the following order:
  
                       body parameter parenthesized list
                          A parenthesized list of attribute/value pairs
                          [e.g. ("foo" "bar" "baz" "rag") where "bar" is
                          the value of "foo" and "rag" is the value of
                          "baz"] as defined in [MIME-IMB].
  
                       body disposition
                          A parenthesized list, consisting of a
                          disposition type string followed by a
                          parenthesized list of disposition
                          attribute/value pairs.  The disposition type and
                          attribute names will be defined in a future
                          standards-track revision to [DISPOSITION].
  
                       body language
                          A string or parenthesized list giving the body
                          language value as defined in [LANGUAGE-TAGS].
  
                       Any following extension data are not yet defined in
                       this version of the protocol.  Such extension data
                       can consist of zero or more NILs, strings, numbers,
                       or potentially nested parenthesized lists of such
                       data.  Client implementations that do a
                       BODYSTRUCTURE fetch MUST be prepared to accept such
                       extension data.  Server implementations MUST NOT
                       send such extension data until it has been defined
                       by a revision of this protocol.
  
                       The basic fields of a non-multipart body part are
                       in the following order:
  
                       body type
                          A string giving the content media type name as
                          defined in [MIME-IMB].
  
                       body subtype
                          A string giving the content subtype name as
                          defined in [MIME-IMB].
  
                       body parameter parenthesized list
                          A parenthesized list of attribute/value pairs
                          [e.g. ("foo" "bar" "baz" "rag") where "bar" is
                          the value of "foo" and "rag" is the value of
                          "baz"] as defined in [MIME-IMB].
  
                       body id
                          A string giving the content id as defined in
                          [MIME-IMB].
  
                       body description
                          A string giving the content description as
                          defined in [MIME-IMB].
  
                       body encoding
                          A string giving the content transfer encoding as
                          defined in [MIME-IMB].
  
                       body size
                          A number giving the size of the body in octets.
                          Note that this size is the size in its transfer
                          encoding and not the resulting size after any
                          decoding.
  
                       A body type of type MESSAGE and subtype RFC822
                       contains, immediately after the basic fields, the
                       envelope structure, body structure, and size in
                       text lines of the encapsulated message.
  
                       A body type of type TEXT contains, immediately
                       after the basic fields, the size of the body in
                       text lines.  Note that this size is the size in its
                       content transfer encoding and not the resulting
                       size after any decoding.
  
                       Extension data follows the basic fields and the
                       type-specific fields listed above.  Extension data
                       is never returned with the BODY fetch, but can be
                       returned with a BODYSTRUCTURE fetch.  Extension
                       data, if present, MUST be in the defined order.
  
                       The extension data of a non-multipart body part are
                       in the following order:
  
                       body MD5
                          A string giving the body MD5 value as defined in
                          [MD5].
  
                       body disposition
                          A parenthesized list with the same content and
                          function as the body disposition for a multipart
                          body part.
  
                       body language
                          A string or parenthesized list giving the body
                          language value as defined in [LANGUAGE-TAGS].
  
                       Any following extension data are not yet defined in
                       this version of the protocol, and would be as
                       described above under multipart extension data.
  
        ENVELOPE       A parenthesized list that describes the envelope
                       structure of a message.  This is computed by the
                       server by parsing the [RFC-822] header into the
                       component parts, defaulting various fields as
                       necessary.
  
                       The fields of the envelope structure are in the
                       following order: date, subject, from, sender,
                       reply-to, to, cc, bcc, in-reply-to, and message-id.
                       The date, subject, in-reply-to, and message-id
                       fields are strings.  The from, sender, reply-to,
                       to, cc, and bcc fields are parenthesized lists of
                       address structures.
  
                       An address structure is a parenthesized list that
                       describes an electronic mail address.  The fields
                       of an address structure are in the following order:
                       personal name, [SMTP] at-domain-list (source
                       route), mailbox name, and host name.
  
                       [RFC-822] group syntax is indicated by a special
                       form of address structure in which the host name
                       field is NIL.  If the mailbox name field is also
                       NIL, this is an end of group marker (semi-colon in
                       RFC 822 syntax).  If the mailbox name field is
                       non-NIL, this is a start of group marker, and the
                       mailbox name field holds the group name phrase.
  
                       Any field of an envelope or address structure that
                       is not applicable is presented as NIL.  Note that
                       the server MUST default the reply-to and sender
                       fields from the from field; a client is not
                       expected to know to do this.
  
        FLAGS          A parenthesized list of flags that are set for this
                       message.
  
        INTERNALDATE   A string representing the internal date of the
                       message.
  
        RFC822         Equivalent to BODY[].
  
        RFC822.HEADER  Equivalent to BODY.PEEK[HEADER].
  
        RFC822.SIZE    A number expressing the [RFC-822] size of the
                       message.
  
        RFC822.TEXT    Equivalent to BODY[TEXT].
  
        UID            A number expressing the unique identifier of the
                       message.
  
  
     Example:    S: * 23 FETCH (FLAGS (\Seen) RFC822.SIZE 44827)
  
  */
  
  
  
  1.1                  jakarta-james/proposals/imap2/java/org/apache/james/imapserver/commands/IdSet.java
  
  Index: IdSet.java
  ===================================================================
  /*
   * Copyright (C) The Apache Software Foundation. All rights reserved.
   *
   * This software is published under the terms of the Apache Software License
   * version 1.1, a copy of which has been included with this distribution in
   * the LICENSE file.
   */
  package org.apache.james.imapserver.commands;
  
  /**
   *
   * @author  Darrell DeBoer <darrell@apache.org>
   *
   * @version $Revision: 1.1 $
   */
  public interface IdSet
  {
      boolean includes( long value );
  }
  
  
  
  1.1                  jakarta-james/proposals/imap2/java/org/apache/james/imapserver/commands/SearchCommand.java
  
  Index: SearchCommand.java
  ===================================================================
  /*
   * Copyright (C) The Apache Software Foundation. All rights reserved.
   *
   * This software is published under the terms of the Apache Software License
   * version 1.1, a copy of which has been included with this distribution in
   * the LICENSE file.
   */
  package org.apache.james.imapserver.commands;
  
  import org.apache.james.imapserver.ImapRequestLineReader;
  import org.apache.james.imapserver.ImapResponse;
  import org.apache.james.imapserver.ImapSession;
  import org.apache.james.imapserver.ProtocolException;
  import org.apache.james.imapserver.store.ImapMailbox;
  import org.apache.james.imapserver.store.MailboxException;
  
  import javax.mail.search.SearchTerm;
  import javax.mail.Message;
  
  /**
   * Handles processeing for the SEARCH imap command.
   *
   * @author  Darrell DeBoer <darrell@apache.org>
   *
   * @version $Revision: 1.1 $
   */
  class SearchCommand extends SelectedStateCommand implements UidEnabledCommand
  {
      public static final String NAME = "SEARCH";
      public static final String ARGS = "<search term>";
  
      private SearchCommandParser parser = new SearchCommandParser();
  
      /** @see CommandTemplate#doProcess */
      protected void doProcess( ImapRequestLineReader request,
                                ImapResponse response,
                                ImapSession session )
              throws ProtocolException, MailboxException
      {
          doProcess( request, response, session, false );
      }
  
      public void doProcess( ImapRequestLineReader request,
                                ImapResponse response,
                                ImapSession session,
                                boolean useUids )
              throws ProtocolException, MailboxException
      {
          // Parse the search term from the request
          SearchTerm searchTerm = parser.searchTerm( request );
          parser.endLine( request );
  
          ImapMailbox mailbox = session.getSelected();
          long[] uids = session.getHost().search( searchTerm, mailbox );
          StringBuffer idList = new StringBuffer();
          for ( int i = 0; i < uids.length; i++ ) {
              if ( i > 0 ) {
                  idList.append( SP );
              }
              long uid = uids[i];
              if ( useUids ) {
                  idList.append( uid );
              }
              else {
                  int msn = mailbox.getMsn( uid );
                  idList.append( msn );
              }
          }
  
          response.commandResponse( this, idList.toString() );
  
          session.unsolicitedResponses( response );
          response.commandComplete( this );
      }
  
      /** @see ImapCommand#getName */
      public String getName()
      {
          return NAME;
      }
  
      /** @see CommandTemplate#getArgSyntax */
      public String getArgSyntax()
      {
          return ARGS;
      }
  
      private class SearchCommandParser extends CommandParser
      {
          /**
           * Parses the request argument into a valid search term.
           * Not yet implemented - all searches will return everything for now.
           * TODO implement search
           */
          public SearchTerm searchTerm( ImapRequestLineReader request )
                  throws ProtocolException
          {
              // Dummy implementation
              // Consume to the end of the line.
              char next = request.nextChar();
              while ( next != '\n' ) {
                  request.consume();
                  next = request.nextChar();
              }
  
              // Return a search term that matches everything.
              return new SearchTerm()
              {
                  public boolean match( Message message )
                  {
                      return true;
                  }
              };
          }
  
      }
  }
  /*
  6.4.4.  SEARCH Command
  
     Arguments:  OPTIONAL [CHARSET] specification
                 searching criteria (one or more)
  
     Responses:  REQUIRED untagged response: SEARCH
  
     Result:     OK - search completed
                 NO - search error: can't search that [CHARSET] or
                      criteria
                 BAD - command unknown or arguments invalid
  
        The SEARCH command searches the mailbox for messages that match
        the given searching criteria.  Searching criteria consist of one
        or more search keys.  The untagged SEARCH response from the server
        contains a listing of message sequence numbers corresponding to
        those messages that match the searching criteria.
  
        When multiple keys are specified, the result is the intersection
        (AND function) of all the messages that match those keys.  For
        example, the criteria DELETED FROM "SMITH" SINCE 1-Feb-1994 refers
        to all deleted messages from Smith that were placed in the mailbox
        since February 1, 1994.  A search key can also be a parenthesized
        list of one or more search keys (e.g. for use with the OR and NOT
        keys).
  
        Server implementations MAY exclude [MIME-IMB] body parts with
        terminal content media types other than TEXT and MESSAGE from
        consideration in SEARCH matching.
  
        The OPTIONAL [CHARSET] specification consists of the word
        "CHARSET" followed by a registered [CHARSET].  It indicates the
        [CHARSET] of the strings that appear in the search criteria.
        [MIME-IMB] content transfer encodings, and [MIME-HDRS] strings in
        [RFC-822]/[MIME-IMB] headers, MUST be decoded before comparing
        text in a [CHARSET] other than US-ASCII.  US-ASCII MUST be
        supported; other [CHARSET]s MAY be supported.  If the server does
        not support the specified [CHARSET], it MUST return a tagged NO
        response (not a BAD).
  
        In all search keys that use strings, a message matches the key if
        the string is a substring of the field.  The matching is case-
        insensitive.
  
        The defined search keys are as follows.  Refer to the Formal
        Syntax section for the precise syntactic definitions of the
        arguments.
  
        <message set>  Messages with message sequence numbers
                       corresponding to the specified message sequence
                       number set
  
        ALL            All messages in the mailbox; the default initial
                       key for ANDing.
  
        ANSWERED       Messages with the \Answered flag set.
  
        BCC <string>   Messages that contain the specified string in the
                       envelope structure's BCC field.
  
        BEFORE <date>  Messages whose internal date is earlier than the
                       specified date.
  
        BODY <string>  Messages that contain the specified string in the
                       body of the message.
  
        CC <string>    Messages that contain the specified string in the
                       envelope structure's CC field.
  
        DELETED        Messages with the \Deleted flag set.
  
        DRAFT          Messages with the \Draft flag set.
  
        FLAGGED        Messages with the \Flagged flag set.
  
        FROM <string>  Messages that contain the specified string in the
                       envelope structure's FROM field.
  
        HEADER <field-name> <string>
                       Messages that have a header with the specified
                       field-name (as defined in [RFC-822]) and that
                       contains the specified string in the [RFC-822]
                       field-body.
  
        KEYWORD <flag> Messages with the specified keyword set.
  
        LARGER <n>     Messages with an [RFC-822] size larger than the
                       specified number of octets.
  
        NEW            Messages that have the \Recent flag set but not the
                       \Seen flag.  This is functionally equivalent to
                       "(RECENT UNSEEN)".
  
        NOT <search-key>
                       Messages that do not match the specified search
                       key.
  
        OLD            Messages that do not have the \Recent flag set.
                       This is functionally equivalent to "NOT RECENT" (as
                       opposed to "NOT NEW").
  
        ON <date>      Messages whose internal date is within the
                       specified date.
  
        OR <search-key1> <search-key2>
                       Messages that match either search key.
  
        RECENT         Messages that have the \Recent flag set.
  
        SEEN           Messages that have the \Seen flag set.
  
        SENTBEFORE <date>
                       Messages whose [RFC-822] Date: header is earlier
                       than the specified date.
  
        SENTON <date>  Messages whose [RFC-822] Date: header is within the
                       specified date.
  
        SENTSINCE <date>
                       Messages whose [RFC-822] Date: header is within or
                       later than the specified date.
  
        SINCE <date>   Messages whose internal date is within or later
                       than the specified date.
  
        SMALLER <n>    Messages with an [RFC-822] size smaller than the
                       specified number of octets.
  
        SUBJECT <string>
                       Messages that contain the specified string in the
                       envelope structure's SUBJECT field.
  
        TEXT <string>  Messages that contain the specified string in the
                       header or body of the message.
  
        TO <string>    Messages that contain the specified string in the
                       envelope structure's TO field.
  
        UID <message set>
                       Messages with unique identifiers corresponding to
                       the specified unique identifier set.
  
        UNANSWERED     Messages that do not have the \Answered flag set.
  
        UNDELETED      Messages that do not have the \Deleted flag set.
  
        UNDRAFT        Messages that do not have the \Draft flag set.
  
        UNFLAGGED      Messages that do not have the \Flagged flag set.
  
        UNKEYWORD <flag>
                       Messages that do not have the specified keyword
                       set.
  
        UNSEEN         Messages that do not have the \Seen flag set.
  
     Example:    C: A282 SEARCH FLAGGED SINCE 1-Feb-1994 NOT FROM "Smith"
                 S: * SEARCH 2 84 882
                 S: A282 OK SEARCH completed
  
  
  
  7.2.5.  SEARCH Response
  
     Contents:   zero or more numbers
  
        The SEARCH response occurs as a result of a SEARCH or UID SEARCH
        command.  The number(s) refer to those messages that match the
        search criteria.  For SEARCH, these are message sequence numbers;
        for UID SEARCH, these are unique identifiers.  Each number is
        delimited by a space.
  
     Example:    S: * SEARCH 2 3 6
  
  */
  
  
  
  1.1                  jakarta-james/proposals/imap2/java/org/apache/james/imapserver/commands/StoreCommand.java
  
  Index: StoreCommand.java
  ===================================================================
  /*
   * Copyright (C) The Apache Software Foundation. All rights reserved.
   *
   * This software is published under the terms of the Apache Software License
   * version 1.1, a copy of which has been included with this distribution in
   * the LICENSE file.
   */
  package org.apache.james.imapserver.commands;
  
  import org.apache.james.imapserver.ImapRequestLineReader;
  import org.apache.james.imapserver.ImapResponse;
  import org.apache.james.imapserver.ImapSession;
  import org.apache.james.imapserver.ProtocolException;
  import org.apache.james.imapserver.store.MessageFlags;
  import org.apache.james.imapserver.store.ImapMailbox;
  import org.apache.james.imapserver.store.SimpleImapMessage;
  import org.apache.james.imapserver.store.MailboxException;
  
  /**
   * Handles processeing for the STORE imap command.
   *
   * @author  Darrell DeBoer <darrell@apache.org>
   *
   * @version $Revision: 1.1 $
   */
  class StoreCommand extends SelectedStateCommand implements UidEnabledCommand
  {
      public static final String NAME = "STORE";
      public static final String ARGS = "<Message-set> ['+'|'-']FLAG[.SILENT] <flag-list>";
  
      private final StoreCommandParser parser = new StoreCommandParser();
  
      /** @see CommandTemplate#doProcess */
      protected void doProcess( ImapRequestLineReader request,
                                ImapResponse response,
                                ImapSession session )
              throws ProtocolException, MailboxException
      {
          doProcess( request, response, session , false );
      }
  
      public void doProcess( ImapRequestLineReader request,
                                ImapResponse response,
                                ImapSession session,
                                boolean useUids )
              throws ProtocolException, MailboxException
      {
          IdSet idSet = parser.set( request );
          StoreDirective directive = parser.storeDirective( request );
          MessageFlags flags = parser.flagList( request );
          parser.endLine( request );
  
          ImapMailbox mailbox = session.getSelected();
          long[] uids = mailbox.getMessageUids();
          for ( int i = 0; i < uids.length; i++ ) {
              long uid = uids[i];
              int msn = mailbox.getMsn( uid );
  
              if ( ( useUids && idSet.includes( uid ) ) ||
                   ( !useUids && idSet.includes( msn ) ) )
              {
                  SimpleImapMessage imapMessage = mailbox.getMessage( uid );
                  storeFlags( imapMessage, directive, flags );
                  mailbox.updateMessage( imapMessage );
  
                  if ( ! directive.isSilent() ) {
                      StringBuffer out = new StringBuffer( "FLAGS " );
                      out.append( imapMessage.getFlags().format() );
                      response.fetchResponse( msn, out.toString() );
                  }
              }
          }
  
          session.unsolicitedResponses( response );
          response.commandComplete( this );
      }
  
      private void storeFlags( SimpleImapMessage imapMessage, StoreDirective directive, MessageFlags newFlags )
      {
          MessageFlags messageFlags = imapMessage.getFlags();
          if ( directive.getSign() == 0 ) {
              messageFlags.setAll( newFlags );
          }
          else if ( directive.getSign() < 0 ) {
              messageFlags.removeAll( newFlags );
          }
          else if ( directive.getSign() > 0 ) {
              messageFlags.addAll( newFlags );
          }
      }
  
      /** @see ImapCommand#getName */
      public String getName()
      {
          return NAME;
      }
  
      /** @see CommandTemplate#getArgSyntax */
      public String getArgSyntax()
      {
          return ARGS;
      }
  
      private class StoreCommandParser extends CommandParser
      {
          StoreDirective storeDirective( ImapRequestLineReader request ) throws ProtocolException
          {
              int sign = 0;
              boolean silent = false;
  
              char next = request.nextWordChar();
              if ( next == '+' ) {
                  sign = 1;
                  request.consume();
              }
              else if ( next == '-' ) {
                  sign = -1;
                  request.consume();
              }
              else {
                  sign = 0;
              }
  
              String directive = consumeWord( request, new NoopCharValidator() );
              if ( "FLAGS".equalsIgnoreCase( directive ) ) {
                  silent = false;
              }
              else if ( "FLAGS.SILENT".equalsIgnoreCase( directive ) ) {
                  silent = true;
              }
              else {
                  throw new ProtocolException( "Invalid Store Directive: '" + directive + "'" );
              }
              return new StoreDirective( sign, silent );
          }
      }
  
      private class StoreDirective
      {
          private int sign;
          private boolean silent;
  
          public StoreDirective( int sign, boolean silent )
          {
              this.sign = sign;
              this.silent = silent;
          }
  
          public int getSign()
          {
              return sign;
          }
  
          public boolean isSilent()
          {
              return silent;
          }
      }
  }
  /*
  6.4.6.  STORE Command
  
     Arguments:  message set
                 message data item name
                 value for message data item
  
     Responses:  untagged responses: FETCH
  
     Result:     OK - store completed
                 NO - store error: can't store that data
                 BAD - command unknown or arguments invalid
  
        The STORE command alters data associated with a message in the
        mailbox.  Normally, STORE will return the updated value of the
        data with an untagged FETCH response.  A suffix of ".SILENT" in
        the data item name prevents the untagged FETCH, and the server
        SHOULD assume that the client has determined the updated value
        itself or does not care about the updated value.
  
           Note: regardless of whether or not the ".SILENT" suffix was
           used, the server SHOULD send an untagged FETCH response if a
           change to a message's flags from an external source is
           observed.  The intent is that the status of the flags is
           determinate without a race condition.
  
        The currently defined data items that can be stored are:
  
        FLAGS <flag list>
                       Replace the flags for the message with the
                       argument.  The new value of the flags are returned
                       as if a FETCH of those flags was done.
  
        FLAGS.SILENT <flag list>
                       Equivalent to FLAGS, but without returning a new
                       value.
  
        +FLAGS <flag list>
                       Add the argument to the flags for the message.  The
                       new value of the flags are returned as if a FETCH
                       of those flags was done.
  
        +FLAGS.SILENT <flag list>
                       Equivalent to +FLAGS, but without returning a new
                       value.
  
        -FLAGS <flag list>
                       Remove the argument from the flags for the message.
                       The new value of the flags are returned as if a
                       FETCH of those flags was done.
  
        -FLAGS.SILENT <flag list>
                       Equivalent to -FLAGS, but without returning a new
                       value.
  
     Example:    C: A003 STORE 2:4 +FLAGS (\Deleted)
                 S: * 2 FETCH FLAGS (\Deleted \Seen)
                 S: * 3 FETCH FLAGS (\Deleted)
                 S: * 4 FETCH FLAGS (\Deleted \Flagged \Seen)
                 S: A003 OK STORE completed
  
  */
  
  
  
  1.1                  jakarta-james/proposals/imap2/java/org/apache/james/imapserver/commands/UidCommand.java
  
  Index: UidCommand.java
  ===================================================================
  /*
   * Copyright (C) The Apache Software Foundation. All rights reserved.
   *
   * This software is published under the terms of the Apache Software License
   * version 1.1, a copy of which has been included with this distribution in
   * the LICENSE file.
   */
  package org.apache.james.imapserver.commands;
  
  import org.apache.james.imapserver.ImapRequestLineReader;
  import org.apache.james.imapserver.ImapResponse;
  import org.apache.james.imapserver.ImapSession;
  import org.apache.james.imapserver.ProtocolException;
  import org.apache.james.imapserver.store.MailboxException;
  
  /**
   * Handles processeing for the UID imap command.
   *
   * @author  Darrell DeBoer <darrell@apache.org>
   *
   * @version $Revision: 1.1 $
   */
  class UidCommand extends SelectedStateCommand
  {
      public static final String NAME = "UID";
      public static final String ARGS = "<fetch-command>|<store-command>|<copy-command>|<search-command>";
  
      private ImapCommandFactory commandFactory;
  
      /** @see CommandTemplate#doProcess */
      protected void doProcess( ImapRequestLineReader request,
                                ImapResponse response,
                                ImapSession session )
              throws ProtocolException, MailboxException
      {
          String commandName = parser.atom( request );
          ImapCommand command = commandFactory.getCommand( commandName );
          if ( command == null ||
               ! (command instanceof UidEnabledCommand ) ) {
              throw new ProtocolException("Invalid UID command: '" + commandName + "'" );
          }
  
          ((UidEnabledCommand)command).doProcess( request, response, session, true );
      }
  
      /** @see ImapCommand#getName */
      public String getName()
      {
          return NAME;
      }
  
      /** @see CommandTemplate#getArgSyntax */
      public String getArgSyntax()
      {
          return ARGS;
      }
  
      public void setCommandFactory( ImapCommandFactory imapCommandFactory )
      {
          this.commandFactory = imapCommandFactory;
      }
  }
  
  
  
  1.1                  jakarta-james/proposals/imap2/java/org/apache/james/imapserver/commands/UidEnabledCommand.java
  
  Index: UidEnabledCommand.java
  ===================================================================
  /*
   * Copyright (C) The Apache Software Foundation. All rights reserved.
   *
   * This software is published under the terms of the Apache Software License
   * version 1.1, a copy of which has been included with this distribution in
   * the LICENSE file.
   */
  package org.apache.james.imapserver.commands;
  
  import org.apache.james.imapserver.ImapRequestLineReader;
  import org.apache.james.imapserver.ImapResponse;
  import org.apache.james.imapserver.ImapSession;
  import org.apache.james.imapserver.ProtocolException;
  import org.apache.james.imapserver.store.MailboxException;
  
  /**
   *
   * @author  Darrell DeBoer <darrell@apache.org>
   *
   * @version $Revision: 1.1 $
   */
  public interface UidEnabledCommand
  {
      void doProcess( ImapRequestLineReader request,
                                ImapResponse response,
                                ImapSession session,
                                boolean useUids)
              throws ProtocolException, MailboxException;
  }
  
  
  
  1.4       +8 -7      jakarta-james/proposals/imap2/java/org/apache/james/imapserver/store/ImapMailbox.java
  
  Index: ImapMailbox.java
  ===================================================================
  RCS file: /home/cvs/jakarta-james/proposals/imap2/java/org/apache/james/imapserver/store/ImapMailbox.java,v
  retrieving revision 1.3
  retrieving revision 1.4
  diff -u -r1.3 -r1.4
  --- ImapMailbox.java	27 Nov 2002 12:26:46 -0000	1.3
  +++ ImapMailbox.java	3 Dec 2002 14:00:12 -0000	1.4
  @@ -11,6 +11,7 @@
   import org.apache.james.core.MailImpl;
   
   import javax.mail.internet.MimeMessage;
  +import javax.mail.search.SearchTerm;
   import java.util.Date;
   import java.util.Collection;
   
  @@ -41,7 +42,7 @@
   
       int getFirstUnseen();
   
  -    int getIndex( int uid );
  +    int getMsn( long uid ) throws MailboxException;
   
       boolean isSelectable();
   
  @@ -49,15 +50,15 @@
   
       int getUnseenCount();
   
  -    ImapMessage createMessage( MimeMessage message, MessageFlags flags, Date internalDate );
  +    SimpleImapMessage createMessage( MimeMessage message, MessageFlags flags, Date internalDate );
   
  -    void storeMessage( ImapMessage message );
  +    void updateMessage( SimpleImapMessage message ) throws MailboxException;
   
       void store( MailImpl mail) throws Exception;
   
  -    ImapMessage getMessage( long uid );
  -
  -    Collection getMessages();
  +    SimpleImapMessage getMessage( long uid );
   
  +    long[] getMessageUids();
   
  +    void deleteMessage( long uid );
   }
  
  
  
  1.4       +69 -32    jakarta-james/proposals/imap2/java/org/apache/james/imapserver/store/InMemoryStore.java
  
  Index: InMemoryStore.java
  ===================================================================
  RCS file: /home/cvs/jakarta-james/proposals/imap2/java/org/apache/james/imapserver/store/InMemoryStore.java,v
  retrieving revision 1.3
  retrieving revision 1.4
  diff -u -r1.3 -r1.4
  --- InMemoryStore.java	27 Nov 2002 12:26:46 -0000	1.3
  +++ InMemoryStore.java	3 Dec 2002 14:00:12 -0000	1.4
  @@ -8,28 +8,34 @@
   package org.apache.james.imapserver.store;
   
   import org.apache.james.core.MailImpl;
  -import org.apache.james.imapserver.store.ImapStore;
   import org.apache.james.imapserver.ImapConstants;
  +import org.apache.avalon.framework.logger.AbstractLogEnabled;
  +import org.apache.avalon.framework.logger.LogKitLogger;
  +import org.apache.avalon.framework.logger.ConsoleLogger;
  +import org.apache.log.Logger;
   
   import javax.mail.internet.MimeMessage;
  +import javax.mail.search.SearchTerm;
   import java.util.ArrayList;
   import java.util.Collection;
   import java.util.Collections;
  -import java.util.Iterator;
  -import java.util.StringTokenizer;
   import java.util.Date;
  +import java.util.Iterator;
   import java.util.Map;
  -import java.util.HashMap;
  +import java.util.StringTokenizer;
  +import java.util.TreeMap;
   
   /**
    * A simple in-memory implementation of {@link ImapStore}, used for testing
    * and development. Note: this implementation does not persist *anything* to disk.
  - * 
  + *
    * @author  Darrell DeBoer <darrell@apache.org>
    *
    * @version $Revision$
    */
  -public class InMemoryStore implements ImapStore, ImapConstants
  +public class InMemoryStore
  +        extends AbstractLogEnabled
  +        implements ImapStore, ImapConstants
   {
       private RootMailbox rootMailbox = new RootMailbox();
       private static MessageFlags mailboxFlags = new MessageFlags();
  @@ -133,18 +139,22 @@
               String matchPattern = searchPattern.substring( lastDot + 1, searchPattern.length() - 1 );
   
               HierarchicalMailbox parent = ( HierarchicalMailbox ) getMailbox( parentName );
  -
  -            Iterator children = parent.getChildren().iterator();
  -            while ( children.hasNext() ) {
  -                HierarchicalMailbox child = ( HierarchicalMailbox ) children.next();
  -                if ( child.getName().startsWith( matchPattern ) ) {
  -                    mailboxes.add( child );
  -
  -                    if ( starIndex != -1 ) {
  -                        addAllChildren( child, mailboxes );
  +            // If the parent from the search pattern doesn't exist,
  +            // return empty.
  +            if ( parent != null ) {
  +                Iterator children = parent.getChildren().iterator();
  +                while ( children.hasNext() ) {
  +                    HierarchicalMailbox child = ( HierarchicalMailbox ) children.next();
  +                    if ( child.getName().startsWith( matchPattern ) ) {
  +                        mailboxes.add( child );
  +
  +                        if ( starIndex != -1 ) {
  +                            addAllChildren( child, mailboxes );
  +                        }
                       }
                   }
               }
  +
           }
           else {
               ImapMailbox mailbox = getMailbox( searchPattern );
  @@ -188,8 +198,8 @@
           protected String name;
           private boolean isSelectable = false;
   
  -        private Map mailMessages = new HashMap();
  -        private long nextUid = 1;
  +        private Map mailMessages = new TreeMap();
  +        private long nextUid = 10;
           private long uidValidity;
   
           public HierarchicalMailbox( HierarchicalMailbox parent,
  @@ -248,11 +258,6 @@
               return mailMessages.size();
           }
   
  -        public int getRecentCount()
  -        {
  -            return 0;
  -        }
  -
           public long getUidValidity()
           {
               return uidValidity;
  @@ -273,11 +278,24 @@
               return 0;
           }
   
  -        public int getIndex( int uid )
  +        public int getRecentCount()
           {
               return 0;
           }
   
  +        public int getMsn( long uid ) throws MailboxException
  +        {
  +            Collection keys = mailMessages.keySet();
  +            Iterator iterator = keys.iterator();
  +            for ( int msn = 1; iterator.hasNext(); msn++ ) {
  +                Long messageUid = ( Long ) iterator.next();
  +                if ( messageUid.longValue() == uid ) {
  +                    return msn;
  +                }
  +            }
  +            throw new MailboxException( "No such message." );
  +        }
  +
           public boolean isSelectable()
           {
               return isSelectable;
  @@ -288,23 +306,29 @@
               isSelectable = selectable;
           }
   
  -        public ImapMessage createMessage( MimeMessage message,
  +        public SimpleImapMessage createMessage( MimeMessage message,
                                             MessageFlags flags,
                                             Date internalDate )
           {
               long uid = nextUid;
  -            nextUid++;
  +            nextUid+=10;
   
  -            ImapMessage imapMessage = new ImapMessage( message, flags,
  +            SimpleImapMessage imapMessage = new SimpleImapMessage( message, flags,
                                                          internalDate, uid );
  +            setupLogger( imapMessage );
   
               mailMessages.put( new Long( uid ), imapMessage );
               return imapMessage;
           }
   
  -        public void storeMessage( ImapMessage message )
  +        public void updateMessage( SimpleImapMessage message )
  +                throws MailboxException
           {
  +
               Long key = new Long( message.getUid() );
  +            if ( ! mailMessages.containsKey( key ) ) {
  +                throw new MailboxException( "Message doesn't exist" );
  +            }
               mailMessages.put( key, message );
           }
   
  @@ -317,14 +341,27 @@
               createMessage( message, flags, internalDate );
           }
   
  -        public ImapMessage getMessage( long uid )
  +        public SimpleImapMessage getMessage( long uid )
  +        {
  +            return (SimpleImapMessage)mailMessages.get( new Long(uid ) );
  +        }
  +
  +        public long[] getMessageUids()
           {
  -            return (ImapMessage)mailMessages.get( new Long(uid ) );
  +            long[] uids = new long[ mailMessages.size() ];
  +            Collection keys = mailMessages.keySet();
  +            Iterator iterator = keys.iterator();
  +            for ( int i = 0; iterator.hasNext(); i++ ) {
  +                Long key = ( Long ) iterator.next();
  +                uids[i] = key.longValue();
  +            }
  +            return uids;
           }
   
  -        public Collection getMessages()
  +        public void deleteMessage( long uid )
           {
  -            return Collections.unmodifiableCollection( mailMessages.values() );
  +            mailMessages.remove( new Long( uid ) );
           }
  +
       }
   }
  
  
  
  1.2       +13 -1     jakarta-james/proposals/imap2/java/org/apache/james/imapserver/store/MailboxException.java
  
  Index: MailboxException.java
  ===================================================================
  RCS file: /home/cvs/jakarta-james/proposals/imap2/java/org/apache/james/imapserver/store/MailboxException.java,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- MailboxException.java	22 Nov 2002 02:09:52 -0000	1.1
  +++ MailboxException.java	3 Dec 2002 14:00:12 -0000	1.2
  @@ -37,6 +37,8 @@
       protected String status = null;
       protected String remoteServer = null;
   
  +    private String responseCode = null;
  +
       /**
        * Construct a new <code>MailboxException</code> instance.
        *
  @@ -64,7 +66,7 @@
        *
        * @param message The detail message for this exception (mandatory).
        * @param aStatus String constant indicating condition
  -     * @param sServer String indicating another server where Mailbox should be.
  +     * @param aServer String indicating another server where Mailbox should be.
        */
       public MailboxException( String message, String aStatus, String aServer )
       {
  @@ -87,5 +89,15 @@
       {
           return ( status.equals( ALREADY_EXISTS_REMOTELY )
                   || status.equals( IF_CREATED_REMOTE ) );
  +    }
  +
  +    public String getResponseCode()
  +    {
  +        return responseCode;
  +    }
  +
  +    public void setResponseCode( String responseCode )
  +    {
  +        this.responseCode = responseCode;
       }
   }
  
  
  
  1.3       +77 -35    jakarta-james/proposals/imap2/java/org/apache/james/imapserver/store/MessageFlags.java
  
  Index: MessageFlags.java
  ===================================================================
  RCS file: /home/cvs/jakarta-james/proposals/imap2/java/org/apache/james/imapserver/store/MessageFlags.java,v
  retrieving revision 1.2
  retrieving revision 1.3
  diff -u -r1.2 -r1.3
  --- MessageFlags.java	27 Nov 2002 12:26:46 -0000	1.2
  +++ MessageFlags.java	3 Dec 2002 14:00:12 -0000	1.3
  @@ -20,16 +20,23 @@
   public class MessageFlags
   {
   
  -    public static final int ANSWERED = 0;
  -    public static final int DELETED = 1;
  -    public static final int DRAFT = 2;
  -    public static final int FLAGGED = 3;
  -    public static final int RECENT = 4;
  -    public static final int SEEN = 5;
  +    public static final int ANSWERED_INDEX = 0;
  +    public static final int DELETED_INDEX = 1;
  +    public static final int DRAFT_INDEX = 2;
  +    public static final int FLAGGED_INDEX = 3;
  +    public static final int RECENT_INDEX = 4;
  +    public static final int SEEN_INDEX = 5;
   
       // Array does not include seen flag
       private boolean[] flags = new boolean[6];
   
  +
  +    public static final String ANSWERED = "\\ANSWERED";
  +    public static final String DELETED = "\\DELETED";
  +    public static final String DRAFT = "\\DRAFT";
  +    public static final String FLAGGED = "\\FLAGGED";
  +    public static final String SEEN = "\\SEEN";
  +
       public MessageFlags()
       {
           resetAll();
  @@ -47,22 +54,22 @@
       {
           StringBuffer buf = new StringBuffer();
           buf.append( "(" );
  -        if ( flags[ANSWERED] ) {
  +        if ( flags[ANSWERED_INDEX] ) {
               buf.append( "\\Answered " );
           }
  -        if ( flags[DELETED] ) {
  +        if ( flags[DELETED_INDEX] ) {
               buf.append( "\\Deleted " );
           }
  -        if ( flags[DRAFT] ) {
  +        if ( flags[DRAFT_INDEX] ) {
               buf.append( "\\Draft " );
           }
  -        if ( flags[FLAGGED] ) {
  +        if ( flags[FLAGGED_INDEX] ) {
               buf.append( "\\Flagged " );
           }
  -        if ( flags[RECENT] ) {
  +        if ( flags[RECENT_INDEX] ) {
               buf.append( "\\Recent " );
           }
  -        if ( flags[SEEN] ) {
  +        if ( flags[SEEN_INDEX] ) {
               buf.append( "\\Seen " );
           }
           // Remove the trailing space, if necessary.
  @@ -110,89 +117,124 @@
               return false;
           }
   
  -        if ( flagString.indexOf( "\\ANSWERED" ) != -1 ) {
  -            flags[ANSWERED] = modValue;
  +        if ( flagString.indexOf( ANSWERED ) != -1 ) {
  +            flags[ANSWERED_INDEX] = modValue;
           }
  -        if ( flagString.indexOf( "\\DELETED" ) != -1 ) {
  -            flags[DELETED] = modValue;
  +        if ( flagString.indexOf( DELETED ) != -1 ) {
  +            flags[DELETED_INDEX] = modValue;
           }
  -        if ( flagString.indexOf( "\\DRAFT" ) != -1 ) {
  -            flags[DRAFT] = modValue;
  +        if ( flagString.indexOf( DRAFT ) != -1 ) {
  +            flags[DRAFT_INDEX] = modValue;
           }
  -        if ( flagString.indexOf( "\\FLAGGED" ) != -1 ) {
  -            flags[FLAGGED] = modValue;
  +        if ( flagString.indexOf( FLAGGED ) != -1 ) {
  +            flags[FLAGGED_INDEX] = modValue;
           }
  -        if ( flagString.indexOf( "\\SEEN" ) != -1 ) {
  -            flags[SEEN] = modValue;
  +        if ( flagString.indexOf( SEEN ) != -1 ) {
  +            flags[SEEN_INDEX] = modValue;
           }
           return true;
       }
   
       public void setAnswered( boolean newState )
       {
  -        flags[ANSWERED] = newState;
  +        flags[ANSWERED_INDEX] = newState;
       }
   
       public boolean isAnswered()
       {
  -        return flags[ANSWERED];
  +        return flags[ANSWERED_INDEX];
       }
   
       public void setDeleted( boolean newState )
       {
  -        flags[DELETED] = newState;
  +        flags[DELETED_INDEX] = newState;
       }
   
       public boolean isDeleted()
       {
  -        return flags[DELETED];
  +        return flags[DELETED_INDEX];
       }
   
       public void setDraft( boolean newState )
       {
  -        flags[DRAFT] = newState;
  +        flags[DRAFT_INDEX] = newState;
       }
   
       public boolean isDraft()
       {
  -        return flags[DRAFT];
  +        return flags[DRAFT_INDEX];
       }
   
       public void setFlagged( boolean newState )
       {
  -        flags[FLAGGED] = newState;
  +        flags[FLAGGED_INDEX] = newState;
       }
   
       public boolean isFlagged()
       {
  -        return flags[FLAGGED];
  +        return flags[FLAGGED_INDEX];
       }
   
       public void setRecent( boolean newState )
       {
  -        flags[RECENT] = newState;
  +        flags[RECENT_INDEX] = newState;
       }
   
       public boolean isRecent()
       {
  -        return flags[RECENT];
  +        return flags[RECENT_INDEX];
       }
   
       public void setSeen( boolean newState )
       {
  -        flags[SEEN] = newState;
  +        flags[SEEN_INDEX] = newState;
       }
   
       public boolean isSeen()
       {
  -        return flags[SEEN];
  +        return flags[SEEN_INDEX];
       }
   
       public void setAll( boolean newState )
       {
  -        for ( int i = ANSWERED; i <= SEEN; i++ )
  +        for ( int i = ANSWERED_INDEX; i <= SEEN_INDEX; i++ )
           {
               flags[i] = newState;
  +        }
  +    }
  +
  +    public void setAll( MessageFlags newFlags ) {
  +        setAnswered( newFlags.isAnswered() );
  +        setDeleted( newFlags.isDeleted() );
  +        setDraft( newFlags.isDraft() );
  +        setFlagged( newFlags.isFlagged() );
  +        setSeen( newFlags.isSeen() );
  +    }
  +
  +    public void addAll( MessageFlags toAdd ) {
  +        setFlagState( toAdd, true );
  +    }
  +
  +    public void removeAll (MessageFlags toRemove) {
  +        setFlagState( toRemove, false );
  +    }
  +
  +    private void setFlagState( MessageFlags changes, boolean setState )
  +    {
  +        if ( changes.isAnswered() ) {
  +            setAnswered( setState );
  +        }
  +        if ( changes.isDeleted() ) {
  +            setDeleted( setState );
  +        }
  +        if ( changes.isDraft() ) {
  +            setDraft( setState );
  +        }
  +        if ( changes.isFlagged() ) {
  +            setFlagged( setState );
  +        }
  +        if ( changes.isSeen() ) {
  +            setSeen( setState );
           }
       }
   }
  
  
  
  1.1                  jakarta-james/proposals/imap2/java/org/apache/james/imapserver/store/ImapMessage1.java
  
  Index: ImapMessage1.java
  ===================================================================
  /*
   * Copyright (C) The Apache Software Foundation. All rights reserved.
   *
   * This software is published under the terms of the Apache Software License
   * version 1.1, a copy of which has been included with this distribution in
   * the LICENSE file.
   */
  package org.apache.james.imapserver.store;
  
  import javax.mail.internet.MimeMessage;
  import java.util.Date;
  
  /**
   *
   * @author  Darrell DeBoer <darrell@apache.org>
   *
   * @version $Revision: 1.1 $
   */
  public interface ImapMessage1
  {
      MimeMessage getMimeMessage();
  
      MessageFlags getFlags();
  
      Date getInternalDate();
  
      long getUid();
  
      ImapMessageAttributes getAttributes() throws MailboxException;
  }
  
  
  
  1.1                  jakarta-james/proposals/imap2/java/org/apache/james/imapserver/store/ImapMessageAttributes.java
  
  Index: ImapMessageAttributes.java
  ===================================================================
  /*
   * Copyright (C) The Apache Software Foundation. All rights reserved.
   *
   * This software is published under the terms of the Apache Software License
   * version 1.1, a copy of which has been included with this distribution in
   * the LICENSE file.
   */
  package org.apache.james.imapserver.store;
  
  import java.util.Date;
  
  /**
   * Interface for objects holding IMAP4rev1 Message Attributes. Message
   * Attributes should be set when a message enters a mailbox. Implementations
   * are encouraged to implement and store MessageAttributes apart from the
   * underlying message. This allows the Mailbox to respond to questions about
   * very large message without needing to access them directly.
   * <p> Note that the message in a mailbox have the same order using either
   * Message Sequence Numbers or UIDs.
   *
   * Reference: RFC 2060 - para 2.3
   * @author <a href="mailto:charles@benett1.demon.co.uk">Charles Benett</a>
   * @version 0.1 on 14 Dec 2000
   */
  public interface ImapMessageAttributes  {
  
      /**
       * Provides the date and time at which the message was received. In the
       * case of delivery by SMTP, this SHOULD be the date and time of final
       * delivery as defined for SMTP. In the case of messages copied from
       * another mailbox, it shuld be the internalDate of the source message. In
       * the case of messages Appended to the mailbox, example drafts,  the
       * internalDate is either specified in the Append command or is the
       * current dat and time at the time of the Append.
       *
       * @return Date imap internal date
       */
      Date getInternalDate();
  
      /**
       * Returns IMAP formatted String representation of Date
       */
      String getInternalDateAsString();
  
      /**
       * Provides the sizeof the message in octets.
       *
       * @return int number of octets in message.
       */
      int getSize();
  
      /**
       * Provides the Envelope structure information for this message. 
       * This is a parsed representation of the rfc-822 envelope information. 
       * This is not to be confused with the SMTP envelope!
       *
       * @return String satisfying envelope syntax in rfc 2060.
       */
      String getEnvelope();
  
      /**
       * Provides the Body Structure information for this message. 
       * This is a parsed representtion of the MIME structure of the message.
       *
       * @return String satisfying body syntax in rfc 2060.
       */
      String getBodyStructure( boolean includeExtensions );
  }
  
  
  
  
  
  1.1                  jakarta-james/proposals/imap2/java/org/apache/james/imapserver/store/SimpleImapMessage.java
  
  Index: SimpleImapMessage.java
  ===================================================================
  /*
   * Copyright (C) The Apache Software Foundation. All rights reserved.
   *
   * This software is published under the terms of the Apache Software License
   * version 1.1, a copy of which has been included with this distribution in
   * the LICENSE file.
   */
  package org.apache.james.imapserver.store;
  
  import org.apache.avalon.framework.logger.AbstractLogEnabled;
  
  import javax.mail.internet.MimeMessage;
  import javax.mail.MessagingException;
  import java.util.Date;
  
  /**
   * A mail message with all of the extra stuff that IMAP requires.
   * This is just a placeholder object, while I work out what's really required. A common
   * way of handling *all* messages needs to be available for James (maybe MailImpl?)
   * @author  Darrell DeBoer <darrell@apache.org>
   *
   * @version $Revision: 1.1 $
   */
  public class SimpleImapMessage
          extends AbstractLogEnabled implements ImapMessage1
  {
      private MimeMessage mimeMessage;
      private MessageFlags flags;
      private Date internalDate;
      private long uid;
      private SimpleMessageAttributes attributes;
  
      SimpleImapMessage( MimeMessage mimeMessage, MessageFlags flags,
                   Date internalDate, long uid )
      {
          this.mimeMessage = mimeMessage;
          this.flags = flags;
          this.internalDate = internalDate;
          this.uid = uid;
      }
  
      public MimeMessage getMimeMessage() {
          return mimeMessage;
      }
  
      public MessageFlags getFlags() {
          return flags;
      }
  
      public Date getInternalDate() {
          return internalDate;
      }
  
      public long getUid() {
          return uid;
      }
  
      public ImapMessageAttributes getAttributes() throws MailboxException
      {
          if ( attributes == null ) {
              attributes = new SimpleMessageAttributes();
              setupLogger( attributes );
              try {
                  attributes.setAttributesFor( mimeMessage );
              }
              catch ( MessagingException e ) {
                  throw new MailboxException( "Could not parse mime message." );
              }
          }
          return attributes;
      }
  }
  
  
  
  1.1                  jakarta-james/proposals/imap2/java/org/apache/james/imapserver/store/SimpleMessageAttributes.java
  
  Index: SimpleMessageAttributes.java
  ===================================================================
  /*
   * Copyright (C) The Apache Software Foundation. All rights reserved.
   *
   * This software is published under the terms of the Apache Software License
   * version 1.1, a copy of which has been included with this distribution in
   * the LICENSE file.
   */
  package org.apache.james.imapserver.store;
  
  import org.apache.avalon.framework.logger.AbstractLogEnabled;
  import org.apache.james.util.RFC822DateFormat;
  import org.apache.mailet.MailAddress;
  
  import javax.mail.BodyPart;
  import javax.mail.MessagingException;
  import javax.mail.internet.AddressException;
  import javax.mail.internet.InternetAddress;
  import javax.mail.internet.MimeMessage;
  import javax.mail.internet.MimeMultipart;
  import javax.mail.internet.MimePart;
  import javax.mail.internet.ParseException;
  import java.util.ArrayList;
  import java.util.Date;
  import java.util.HashSet;
  import java.util.Iterator;
  import java.util.List;
  import java.util.Set;
  
  /**
   * Attributes of a Message in IMAP4rev1 style. Message
   * Attributes should be set when a message enters a mailbox.
   * <p> Note that the message in a mailbox have the same order using either
   * Message Sequence Numbers or UIDs.
   * <p> reinitialize() must be called on deserialization to reset Logger
   *
   * Reference: RFC 2060 - para 2.3
   * @author <a href="mailto:sascha@kulawik.de">Sascha Kulawik</a>
   * @author <a href="mailto:charles@benett1.demon.co.uk">Charles Benett</a>
   * @version 0.2 on 04 Aug 2002
   */
  public class SimpleMessageAttributes
      extends AbstractLogEnabled
      implements ImapMessageAttributes
  {
  
      private final static String SP = " ";
      private final static String NIL = "NIL";
      private final static String Q = "\"";
      private final static String LB = "(";
      private final static String RB = ")";
      private final static boolean DEBUG = false;
      private final static String MULTIPART = "MULTIPART";
      private final static String MESSAGE = "MESSAGE";
  
      private int uid;
      private int messageSequenceNumber;
      private Date internalDate;
      private String internalDateString;
      private String bodyStructure;
      private String envelope;
      private int size;
      private int lineCount;
      public ImapMessageAttributes[] parts;
      private List headers;
  
      //rfc822 or MIME header fields
      //arrays only if multiple values allowed under rfc822
      private String subject;
      private String[] from;
      private String[] sender;
      private String[] replyTo;
      private String[] to;
      private String[] cc;
      private String[] bcc;
      private String[] inReplyTo;
      private String[] date;
      private String[] messageID;
      private String contentType;
      private String primaryType;   // parsed from contentType
      private String secondaryType; // parsed from contentType
      private Set parameters;      // parsed from contentType
      private String contentID;
      private String contentDesc;
      private String contentEncoding;
  
      SimpleMessageAttributes() {
      }
  
      void setAttributesFor(MimeMessage msg) throws MessagingException {
          size = msg.getSize();
          try {
              internalDate = msg.getSentDate();
          } catch (MessagingException me) {
              internalDate = new Date();
          }
  
          internalDateString = RFC822DateFormat.toString(internalDate); // not right format
          parseMimePart(msg);
          envelope = null;
          bodyStructure = null;
      }
  
      void setUID(int thisUID) {
          uid = thisUID;
      }
  
      /**
       * Parses key data items from a MimeMessage for seperate storage.
       * TODO this is a mess, and should be completely revamped.
       */
      void parseMimePart(MimePart part) {
          // Section 1 - Message Headers
          if (part instanceof MimeMessage) {
              try {
                  subject = ((MimeMessage)part).getSubject();
              } catch (MessagingException me) {
                  if (DEBUG) getLogger().debug("Messaging Exception for getSubject: " + me);
              }
          }
          try {
              from = part.getHeader("From");
          } catch (MessagingException me) {
              if (DEBUG) getLogger().debug("Messaging Exception for getHeader(From): " + me);
          }
          try {
              sender = part.getHeader("Sender");
          } catch (MessagingException me) {
              if (DEBUG) getLogger().debug("Messaging Exception for getHeader(Sender): " + me);
          }
          try {
              replyTo = part.getHeader("Reply To");
          } catch (MessagingException me) {
              if (DEBUG) getLogger().debug("Messaging Exception for getHeader(Reply To): " + me);
          }
          try {
              to = part.getHeader("To");
          } catch (MessagingException me) {
              if (DEBUG) getLogger().debug("Messaging Exception for getHeader(To): " + me);
          }
          try {
              cc = part.getHeader("Cc");
          } catch (MessagingException me) {
              if (DEBUG) getLogger().debug("Messaging Exception for getHeader(To): " + me);
          }
          try {
              bcc = part.getHeader("Bcc");
          } catch (MessagingException me) {
              if (DEBUG) getLogger().debug("Messaging Exception for getHeader(To): " + me);
          }
          try {
              inReplyTo = part.getHeader("In Reply To");
          } catch (MessagingException me) {
              if (DEBUG) getLogger().debug("Messaging Exception for getHeader(In Reply To): " + me);
          }
          try {
              date = part.getHeader("Date");
          } catch (MessagingException me) {
              if (DEBUG) getLogger().debug("Messaging Exception for getHeader(Date): " + me);
          }
          try {
              messageID = part.getHeader("Message-ID");
          } catch (MessagingException me) {
              if (DEBUG) getLogger().debug("Messaging Exception for getHeader(messageID): " + me);
          }
          String contentTypeLine = null;
          try {
              contentTypeLine = part.getContentType();
          } catch (MessagingException me) {
              if (DEBUG) getLogger().debug("Messaging Exception for getContentType(): " + me);
          }
          if (contentTypeLine !=null ) {
              decodeContentType(contentTypeLine);
          }
          try {
              contentID = part.getContentID();
          } catch (MessagingException me) {
              if (DEBUG) getLogger().debug("Messaging Exception for getContentUD(): " + me);
          }
          try {
              contentDesc = part.getDescription();
          } catch (MessagingException me) {
              if (DEBUG) getLogger().debug("Messaging Exception for getDescription(): " + me);
          }
          try {
              contentEncoding = part.getEncoding();
              // default value.
              if ( contentEncoding == null ) {
                  contentEncoding = "7BIT";
              }
          } catch (MessagingException me) {
              if (DEBUG) getLogger().debug("Messaging Exception for getEncoding(): " + me);
          }
          if (DEBUG) {
              try {
                  String contentDisposition = part.getDisposition();
              } catch (MessagingException me) {
                  getLogger().debug("Messaging Exception for getEncoding(): " + me);
              }
          }
  
          try {
              // TODO this doesn't work
              lineCount = part.getLineCount();
          } catch (MessagingException me) {
              if (DEBUG) getLogger().debug("Messaging Exception for getLineCount(): " + me);
          } catch (Exception e) {
              if (DEBUG) getLogger().debug("Exception for getLineCount(): " + e);
          }
  
          // Recurse through any embedded parts
          if (primaryType.equalsIgnoreCase(MULTIPART)) {
              MimeMultipart container;
              try {
                  container =(MimeMultipart) part.getContent();
                  int count = container.getCount();
                  parts = new SimpleMessageAttributes[count];
                  for (int i = 0; i < count ; i ++) {
                      BodyPart nextPart = container.getBodyPart(i);
  
                      if (nextPart instanceof MimePart) {
                          SimpleMessageAttributes partAttrs = new SimpleMessageAttributes();
                          setupLogger(partAttrs); // reset transient logger
                          partAttrs.parseMimePart((MimePart)nextPart);
                          parts[i] = partAttrs;
  
                      } else {
                          getLogger().info("Found a non-Mime bodyPart");
                      }
                  }
              } catch (Exception e) {
                  getLogger().debug("Messaging Exception for getContent(): " + e);
                  e.printStackTrace();
              }
          } else if (primaryType.equalsIgnoreCase("message")) {
              getLogger().info("This part contains an embedded message of subtype: " + secondaryType);
              getLogger().info("Uses java class: " + part.getClass().getName());
              if (secondaryType.equalsIgnoreCase("RFC822")) {
                  //try {
  
                      /*
                      MimeMessageWrapper message = new MimeMessageWrapper(part.getInputStream());
                      SimpleMessageAttributes msgAttrs = new SimpleMessageAttributes();
                      msgAttrs.setAttributesFor(message);
  
                      if (part instanceof MimeMessage) {
                          Comments out because I don't know what it should do here
                          MimeMessage msg1 = (MimeMessage) part;
                          MimeMessageWrapper message2 = new MimeMessageWrapper(msg1);
                          SimpleMessageAttributes msgAttrs2 = new SimpleMessageAttributes();
                          msgAttrs.setAttributesFor(message2);
                      }
  
                      parts = new SimpleMessageAttributes[1];
                      parts[0] = msgAttrs;
                      */
                  //} catch (Exception e) {
                  //getLogger().error("Error interpreting a message/rfc822: " + e);
                  //e.printStackTrace();
                  //}
              } else {
                  getLogger().info("Unknown subtype of message encountered.");
                  System.out.println("Unknown subtype of message encountered.");
              }
          }
          else {
              System.out.println("parseMimePart: its just a plain message");
          }
      }
  
      /**
       * Builds IMAP envelope String from pre-parsed data.
       */
      String parseEnvelope() {
          List response = new ArrayList();
          response.add( LB + Q + internalDateString + Q + SP);
          if (subject != null && (!subject.equals(""))) {
              response.add( Q +  subject + Q + SP );
          } else {
              response.add( NIL + SP );
          }
          if (from != null && from.length > 0) {
              response.add(LB);
              for (int i=0; i<from.length; i++) {
                  response.add(parseAddress( from[i]) );
              }
              response.add(RB);
          } else {
              response.add( NIL);
          }
          response.add(SP);
          if (sender != null && sender.length >0) {
              if (DEBUG) getLogger().debug("parsingEnvelope - sender[0] is: " + sender[0]);
              //Check for Netscape feature - sender is local part only
              if (sender[0].indexOf("@") == -1) {
                  response.add(LB + (String)response.get(3) + RB); //first From address
              } else {
                  response.add(LB);
                  for (int i=0; i<sender.length; i++) {
                      response.add( parseAddress(sender[i]));
                  }
                  response.add(RB);
              }
          } else {
              if (from != null && from.length > 0) {
                  response.add(LB + (String)response.get(3) + RB); //first From address
              } else {
                  response.add( NIL);
              }
          }
          response.add(SP);
          if (replyTo != null && replyTo.length >0) {
              if (replyTo[0].indexOf("@") == -1) {
                  response.add(LB + (String)response.get(3) + RB); //first From address
              } else {
                  response.add(LB);
                  for (int i=0; i<replyTo.length; i++) {
                      response.add( parseAddress(replyTo[i]));
                  }
                  response.add(RB);
              }
          } else {
              if (from != null && from.length > 0) {
                  response.add(LB + (String)response.get(3) + RB); //first From address
              } else {
                  response.add( NIL);
              }
          }
          response.add(SP);
          if (to != null && to.length >0) {
              response.add(LB);
              for (int i=0; i<to.length; i++) {
                  response.add( parseAddress(to[i]));
              }
              response.add(RB);
          } else {
              response.add( NIL);
          }
          response.add(SP);
          if (cc != null && cc.length >0) {
              response.add(LB);
              for (int i=0; i<cc.length; i++) {
                  response.add( parseAddress(cc[i]));
              }
              response.add(RB);
          } else {
              response.add( NIL);
          }
          response.add(SP);
          if (bcc != null && bcc.length >0) {
              response.add(LB);
              for (int i=0; i<bcc.length; i++) {
                  response.add( parseAddress(bcc[i]));
              }
              response.add(RB);
          } else {
              response.add( NIL);
          }
          response.add(SP);
          if (inReplyTo != null && inReplyTo.length>0) {
              response.add( inReplyTo[0]);
          } else {
              response.add( NIL);
          }
          response.add(SP);
          if (messageID != null && messageID.length>0) {
              response.add(Q + messageID[0] + Q);
          } else {
              response.add( NIL);
          }
          response.add(RB);
  
          StringBuffer buf = new StringBuffer(16 * response.size());
          for (int j=0; j<response.size(); j++) {
              buf.append((String)response.get(j));
          }
  
          return buf.toString();
      }
  
      /**
       * Parses a String email address to an IMAP address string.
       */
      String parseAddress(String address) {
          int comma = address.indexOf(",");
          StringBuffer buf = new StringBuffer();
          if (comma == -1) { //single address
              buf.append(LB);
              InternetAddress netAddr = null;
              try {
                  netAddr = new InternetAddress(address);
              } catch (AddressException ae) {
                  return null;
              }
              String personal = netAddr.getPersonal();
              if (personal != null && (!personal.equals(""))) {
                  buf.append(Q + personal + Q);
              } else {
                  buf.append( NIL);
              }
              buf.append( SP);
              buf.append( NIL) ; // should add route-addr
              buf.append( SP);
              try {
                  MailAddress mailAddr = new MailAddress(netAddr);
                  buf.append(Q + mailAddr.getUser() + Q);
                  buf.append(SP);
                  buf.append(Q + mailAddr.getHost() + Q);
              } catch (ParseException pe) {
                  buf.append( NIL + SP + NIL);
              }
              buf.append(RB);
          } else {
              buf.append(parseAddress(address.substring(0, comma)));
              buf.append(SP);
              buf.append(parseAddress(address.substring(comma + 1)));
          }
          return buf.toString();
      }
  
      /**
       * Decode a content Type header line into types and parameters pairs
       */
      void decodeContentType(String rawLine) {
          int slash = rawLine.indexOf("/");
          if( slash == -1){
              if (DEBUG) getLogger().debug("decoding ... no slash found");
              return;
          } else {
              primaryType = rawLine.substring(0, slash).trim();
          }
          int semicolon = rawLine.indexOf(";");
          if (semicolon == -1) {
              if (DEBUG) getLogger().debug("decoding ... no semicolon found");
              secondaryType = rawLine.substring(slash + 1).trim();
              return;
          }
          // have parameters
          parameters = new HashSet();
          secondaryType = rawLine.substring(slash + 1, semicolon).trim();
          int pos = semicolon;
          int nextsemi = rawLine.indexOf(";", pos+1);
          while (nextsemi != -1) {
              if (DEBUG) getLogger().debug("decoding ... found another semicolon");
              String param = rawLine.substring(pos + 1, nextsemi);
              int esign = param.indexOf("=") ;
              if (esign == -1) {
                  if (DEBUG) getLogger().debug("Whacky parameter found: " + param);
              } else {
                  String name = param.substring(0, esign).trim();
                  String value = param.substring(esign + 1).trim();
                  parameters.add(name + SP + value);
                  if (DEBUG) getLogger().debug("Found parameter: " + name + SP + value);
              }
              pos = nextsemi;
              nextsemi = rawLine.indexOf(";", pos +1);
          }
          String lastParam = rawLine.substring(pos + 1);
          int esign = lastParam.indexOf("=") ;
          if (esign == -1) {
              if (DEBUG) getLogger().debug("Whacky parameter found: " + lastParam);
          } else {
              String name = lastParam.substring(0, esign).trim();
              String value = lastParam.substring(esign + 1).trim();
              parameters.add(Q + name + Q + SP + Q + value + Q);
              if (DEBUG) getLogger().debug("Found parameter: " + name + SP + value);
          }
      }
  
      String parseBodyFields() {
          StringBuffer buf = new StringBuffer();
          if (parameters == null || parameters.isEmpty()) {
              buf.append(NIL);
          } else {
              buf.append(LB);
              Iterator it = parameters.iterator();
              while(it.hasNext()) {
                  buf.append((String)it.next());
              }
              buf.append(RB);
          }
          buf.append(SP);
          if(contentID == null) {
              buf.append(NIL);
          } else {
              buf.append(Q + contentID + Q);
          }
          buf.append(SP);
          if(contentDesc == null) {
              buf.append(NIL);
          } else {
              buf.append(Q + contentDesc + Q);
          }
          buf.append(SP);
          if(contentEncoding == null) {
              buf.append( NIL );
          } else {
              buf.append(Q + contentEncoding + Q);
          }
          buf.append(SP);
          buf.append(size);
          return buf.toString();
      }
  
      /**
       * Produce the IMAP formatted String for the BodyStructure of a pre-parsed MimeMessage
       * TODO handle extension elements - Content-disposition, Content-Language and other parameters.
       */
      String parseBodyStructure() {
          try {
              String fields = parseBodyFields();
              StringBuffer buf = new StringBuffer();
              buf.append(LB);
              if (primaryType.equalsIgnoreCase("Text")) {
                  buf.append("\"TEXT\" \"" );
                  buf.append( secondaryType.toUpperCase() );
                  buf.append( "\" ");
                  buf.append( fields );
                  buf.append( " " );
                  buf.append( lineCount );
  
                  // is:    * 1 FETCH (BODYSTRUCTURE ("Text" "plain" NIL NIL NIL NIL    4  -1))
                  // wants: * 1 FETCH (BODYSTRUCTURE ("text" "plain" NIL NIL NIL "8bit" 6  1  NIL NIL NIL))
                  // or:    * 1 FETCH (BODYSTRUCTURE ("text" "plain" NIL NIL NIL "7bit" 28 1 NIL NIL NIL))
  
              } else if  (primaryType.equalsIgnoreCase(MESSAGE) && secondaryType.equalsIgnoreCase("rfc822")) {
                  buf.append("\"MESSAGE\" \"RFC822\" ");
                  buf.append(fields + SP);
                  setupLogger(parts[0]); // reset transient logger
                  buf.append(parts[0].getEnvelope() + SP);
                  buf.append(parts[0].getBodyStructure( false ) + SP);
                  buf.append(lineCount);
              } else if (primaryType.equalsIgnoreCase(MULTIPART)) {
                  for (int i=0; i<parts.length; i++) {
                      setupLogger(parts[i]); // reset transient getLogger()
                      buf.append(parts[i].getBodyStructure( false ));
                  }
                  buf.append(SP + secondaryType);
              }
              buf.append(RB);
              return buf.toString();
          } catch (Exception e) {
              getLogger().error("Exception while parsing BodyStrucuture: " + e);
              e.printStackTrace();
              throw new RuntimeException("Exception in parseBodyStructure");
          }
      }
  
      /**
       * Provides the current Message Sequence Number for this message. MSNs
       * change when messages are expunged from the mailbox.
       *
       * @return int a positive non-zero integer
       */
      public int getMessageSequenceNumber() {
          return messageSequenceNumber;
      }
  
      void setMessageSequenceNumber(int newMsn) {
          messageSequenceNumber = newMsn;
      }
  
  
      /**
       * Provides the unique identity value for this message. UIDs combined with
       * a UIDValidity value form a unique reference for a message in a given
       * mailbox. UIDs persist across sessions unless the UIDValidity value is
       * incremented. UIDs are not copied if a message is copied to another
       * mailbox.
       *
       * @return int a 32-bit value
       */
      public int getUID() {
          return uid;
      }
  
      /**
       * Provides the date and time at which the message was received. In the
       * case of delivery by SMTP, this SHOULD be the date and time of final
       * delivery as defined for SMTP. In the case of messages copied from
       * another mailbox, it shuld be the internalDate of the source message. In
       * the case of messages Appended to the mailbox, example drafts,  the
       * internalDate is either specified in the Append command or is the
       * current dat and time at the time of the Append.
       *
       * @return Date imap internal date
       */
      public Date getInternalDate() {
          return internalDate;
      }
  
      public String getInternalDateAsString() {
          return internalDateString;
      }
  
      /**
       * Provides the sizeof the message in octets.
       *
       * @return int number of octets in message.
       */
      public int getSize() {
          return size;
      }
  
      /**
       * Provides the Envelope structure information for this message. This is a parsed representation of the rfc-822 envelope information. This is not to be confused with the SMTP envelope!
       *
       * @return String satisfying envelope syntax in rfc 2060.
       */
      public String getEnvelope() {
          return parseEnvelope();
      }
  
      /**
       * Provides the Body Structure information for this message. This is a parsed representtion of the MIME structure of the message.
       *
       * @return String satisfying body syntax in rfc 2060.
       */
      public String getBodyStructure( boolean includeExtensions ) {
          return parseBodyStructure();
      }
  }
  
  
  
  1.2       +30 -4     jakarta-james/proposals/imap2/test/org/apache/james/imapserver/Append.test
  
  Index: Append.test
  ===================================================================
  RCS file: /home/cvs/jakarta-james/proposals/imap2/test/org/apache/james/imapserver/Append.test,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- Append.test	27 Nov 2002 12:26:46 -0000	1.1
  +++ Append.test	3 Dec 2002 14:00:13 -0000	1.2
  @@ -1,12 +1,38 @@
  -C: A003 APPEND INBOX {309}
  +C: a1 CREATE appendBox
  +S: a1 OK CREATE completed
  +
  +C: A003 APPEND appendBox {310}
  +# Use command continuation request
  +S: \+
   C: Date: Mon, 7 Feb 1994 21:52:25 -0800 (PST)
   C: From: Fred Foobar <foobar@Blurdybloop.COM>
  -C: Subject: afternoon meeting
  +C: Subject: afternoon meeting 2
   C: To: mooch@owatagu.siam.edu
   C: Message-Id: <B27397-0100000@Blurdybloop.COM>
   C: MIME-Version: 1.0
   C: Content-Type: TEXT/PLAIN; CHARSET=US-ASCII
   C:
  -C: Hello Joe, do you think we can meet at 3:30 tomorrow?
  +C: Hello Joe, could we change that to 4:00pm tomorrow?
   C:
  -S: A003 OK APPEND completed
  \ No newline at end of file
  +S: A003 OK APPEND completed
  +
  +C: A003 APPEND appendBox (\Seen) "17-Jul-1996 02:44:25 -0700" {310+}
  +# use synchronising literal
  +C: Date: Mon, 7 Feb 1994 21:52:25 -0800 (PST)
  +C: From: Fred Foobar <foobar@Blurdybloop.COM>
  +C: Subject: afternoon meeting 2
  +C: To: mooch@owatagu.siam.edu
  +C: Message-Id: <B27397-0100000@Blurdybloop.COM>
  +C: MIME-Version: 1.0
  +C: Content-Type: TEXT/PLAIN; CHARSET=US-ASCII
  +C:
  +C: Hello Joe, could we change that to 4:00pm tomorrow?
  +C:
  +S: A003 OK APPEND completed
  +
  +C: a1 APPEND nosuchmailbox {13+}
  +C: 13 characters
  +S: a1 NO \[TRYCREATE\] APPEND failed\. No such mailbox\.
  +
  +C: a1 DELETE appendBox
  +S: a1 OK DELETE completed
  \ No newline at end of file
  
  
  
  1.4       +47 -12    jakarta-james/proposals/imap2/test/org/apache/james/imapserver/CommandParserTest.java
  
  Index: CommandParserTest.java
  ===================================================================
  RCS file: /home/cvs/jakarta-james/proposals/imap2/test/org/apache/james/imapserver/CommandParserTest.java,v
  retrieving revision 1.3
  retrieving revision 1.4
  diff -u -r1.3 -r1.4
  --- CommandParserTest.java	27 Nov 2002 01:59:22 -0000	1.3
  +++ CommandParserTest.java	3 Dec 2002 14:00:13 -0000	1.4
  @@ -14,6 +14,16 @@
   import java.io.BufferedReader;
   import java.io.StringReader;
   import java.io.StringWriter;
  +import java.io.InputStream;
  +import java.io.ByteArrayInputStream;
  +import java.io.ByteArrayOutputStream;
  +import java.io.UnsupportedEncodingException;
  +import java.util.Date;
  +import java.util.Calendar;
  +import java.util.SimpleTimeZone;
  +import java.util.TimeZone;
  +import java.text.SimpleDateFormat;
  +import java.text.DateFormat;
   
   /**
    * Tests for the {@link ImapRequestLineReader}.
  @@ -26,7 +36,7 @@
           extends TestCase
   {
       private CommandParser parser = new CommandParser();
  -    private StringWriter writer = new StringWriter();
  +    private ByteArrayOutputStream output = new ByteArrayOutputStream();
   
       public CommandParserTest( String s )
       {
  @@ -99,16 +109,16 @@
       public void testLiteral() throws Exception
       {
           // Synchronized literal.
  -        String test = "{24+}\nA \tsynchronized \nliteral {26}\nAn \tunsynchronized\nliteral";
  +        String test = "{24+}\r\nA \tsynchronized \nliteral {27}\r\nThe \tunsynchronized\nliteral";
           ImapRequestLineReader request = getRequest(test );
   
           assertEquals( "A \tsynchronized \nliteral", parser.astring( request ) );
           // Make sure we didn't get a command continuation response
  -        assertEquals( "", writer.getBuffer().toString() );
  +        assertEquals( "", getServerResponse() );
   
  -        assertEquals( "An \tunsynchronized\nliteral", parser.astring( request ) );
  +        assertEquals( "The \tunsynchronized\nliteral", parser.astring( request ) );
           // Make sure we got a command continuation response
  -        assertEquals( "+\n", writer.getBuffer().toString() );
  +        assertEquals( "+\r\n", getServerResponse() );
   
       }
   
  @@ -118,7 +128,7 @@
        */
       public void testAstring() throws Exception
       {
  -        String testRequest = "atom at.om \"quoted\" \"\" {6+}\n\"here\"";
  +        String testRequest = "atom at.om \"quoted\" \"\" {6+}\r\n\"here\"";
           ImapRequestLineReader request = getRequest( testRequest );
   
           assertEquals( "atom", parser.astring( request ) );
  @@ -144,17 +154,42 @@
       }
   
       /**
  -     * Builds and ImapRequestLineReader with the specified string, using {@link #writer}
  +     * Tests for reading "date-time" arguments.
  +     * TODO this test fails, as timezones aren't handled properly - need to investigate.
  +     */
  +    public void xtestDateTime() throws Exception
  +    {
  +        String testRequest = "\"20-Mar-1971 00:23:02 +0000\"";
  +        ImapRequestLineReader request = getRequest( testRequest );
  +
  +        SimpleDateFormat formatter
  +            = new SimpleDateFormat ("yyyyMMdd hh:mm:ss");
  +        formatter.setTimeZone( TimeZone.getTimeZone( "UTC" ));
  +        String actual = formatter.format( parser.dateTime( request ) );
  +        assertEquals( "19710320 00:23:02", actual );
  +    }
  +
  +    /**
  +     * Builds and ImapRequestLineReader with the specified string, using {@link #output}
        * as the server writer for command continuation requests
        * @param testRequest A string containing client requests.
        * @return An initialised ImapRequestLineReader
        */
  -    private ImapRequestLineReader getRequest( String testRequest )
  +    private ImapRequestLineReader getRequest( String testRequest ) throws Exception
       {
  -        BufferedReader reader = new BufferedReader( new StringReader( testRequest ) );
  +        InputStream input = new ByteArrayInputStream( testRequest.getBytes( "US-ASCII" ) );
           // Clear the writer.
  -        writer.getBuffer().setLength(0);
  -        ImapRequestLineReader request = new ImapRequestLineReader( reader, writer );
  +        output.reset();
  +        ImapRequestLineReader request = new ImapRequestLineReader( input, output );
           return request;
       }
  +
  +    private String getServerResponse() throws UnsupportedEncodingException
  +    {
  +        byte[] bytesOut = output.toByteArray();
  +        output.reset();
  +        return new String( bytesOut, "US-ASCII" );
  +    }
  +
  +
   }
  
  
  
  1.2       +11 -1     jakarta-james/proposals/imap2/test/org/apache/james/imapserver/Create.test
  
  Index: Create.test
  ===================================================================
  RCS file: /home/cvs/jakarta-james/proposals/imap2/test/org/apache/james/imapserver/Create.test,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- Create.test	22 Nov 2002 02:09:52 -0000	1.1
  +++ Create.test	3 Dec 2002 14:00:13 -0000	1.2
  @@ -9,4 +9,14 @@
   S: 12 OK CREATE completed
   # Create a fully qualified sub folder
   C: 13 CREATE test1.subfolder1
  -S: 13 OK CREATE completed
  \ No newline at end of file
  +S: 13 OK CREATE completed
  +
  +# Cleanup
  +C: a1 DELETE test1.subfolder1
  +S: a1 OK DELETE completed
  +C: a1 DELETE test1
  +S: a1 OK DELETE completed
  +C: a1 DELETE test.subfolder
  +S: a1 OK DELETE completed
  +C: a1 DELETE test
  +S: a1 OK DELETE completed
  \ No newline at end of file
  
  
  
  1.2       +10 -0     jakarta-james/proposals/imap2/test/org/apache/james/imapserver/Delete.test
  
  Index: Delete.test
  ===================================================================
  RCS file: /home/cvs/jakarta-james/proposals/imap2/test/org/apache/james/imapserver/Delete.test,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- Delete.test	22 Nov 2002 02:09:52 -0000	1.1
  +++ Delete.test	3 Dec 2002 14:00:13 -0000	1.2
  @@ -1,3 +1,13 @@
  +# Create a few folders
  +C: 10 CREATE test
  +S: 10 OK CREATE completed
  +C: 11 CREATE test.subfolder
  +S: 11 OK CREATE completed
  +C: 12 CREATE test1
  +S: 12 OK CREATE completed
  +C: 13 CREATE test1.subfolder1
  +S: 13 OK CREATE completed
  +
   # Delete subfolder, then folder
   C: 10 DELETE test.subfolder
   S: 10 OK DELETE completed
  
  
  
  1.2       +20 -0     jakarta-james/proposals/imap2/test/org/apache/james/imapserver/ExamineEmpty.test
  
  Index: ExamineEmpty.test
  ===================================================================
  RCS file: /home/cvs/jakarta-james/proposals/imap2/test/org/apache/james/imapserver/ExamineEmpty.test,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- ExamineEmpty.test	22 Nov 2002 02:09:52 -0000	1.1
  +++ ExamineEmpty.test	3 Dec 2002 14:00:13 -0000	1.2
  @@ -1,3 +1,13 @@
  +# Create a few folders
  +C: 10 CREATE test
  +S: 10 OK CREATE completed
  +C: 11 CREATE test.subfolder
  +S: 11 OK CREATE completed
  +C: 12 CREATE test1
  +S: 12 OK CREATE completed
  +C: 13 CREATE test1.subfolder1
  +S: 13 OK CREATE completed
  +
   C: abcd EXAMINE test
   S: \* FLAGS \(\\Answered \\Deleted \\Draft \\Flagged \\Seen\)
   S: \* 0 EXISTS
  @@ -27,3 +37,13 @@
   
   C: abcd EXAMINE subfolder1
   S: abcd NO EXAMINE failed. No such mailbox.
  +
  +# Cleanup
  +C: a1 DELETE test1.subfolder1
  +S: a1 OK DELETE completed
  +C: a1 DELETE test1
  +S: a1 OK DELETE completed
  +C: a1 DELETE test.subfolder
  +S: a1 OK DELETE completed
  +C: a1 DELETE test
  +S: a1 OK DELETE completed
  \ No newline at end of file
  
  
  
  1.2       +112 -159  jakarta-james/proposals/imap2/test/org/apache/james/imapserver/FetchSingleMessage.test
  
  Index: FetchSingleMessage.test
  ===================================================================
  RCS file: /home/cvs/jakarta-james/proposals/imap2/test/org/apache/james/imapserver/FetchSingleMessage.test,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- FetchSingleMessage.test	22 Nov 2002 02:09:52 -0000	1.1
  +++ FetchSingleMessage.test	3 Dec 2002 14:00:13 -0000	1.2
  @@ -1,210 +1,163 @@
  -// BODY
  +# BODY
   C: f1 FETCH 1 (BODY)
  -S: * 1 FETCH (BODY ("text" "plain" NIL NIL NIL "7bit" 28 1))
  +# TODO this is wrong - line number incorrect.
  +S: \* 1 FETCH \(BODY \(\"TEXT\" \"PLAIN\" \(\"CHARSET\" \"US-ASCII\"\) NIL NIL \"7BIT\" 9 -1\)\)
   S: f1 OK FETCH completed
   
  -// BODY[]
  +# BODY[]
   C: f1 FETCH 1 (BODY[])
  -S: * 1 FETCH (BODY[] {476}
  -S: Received: from ${ignore} [127.0.0.1] by localhost with ESMTP
  -S:   (SMTPD32-7.05 EVAL) id ${ignore}; ${rfcDate}
  -S: Message-ID: ${ignore}
  -S: From: sender@somewhere
  -S: To: imapuser@localhost
  -S: Subject: Message 1
  -S: Mime-Version: 1.0
  -S: Content-Type: text/plain
  -S: Content-Transfer-Encoding: 7bit
  -S: Date: ${ignore-12}
  -S: X-RCPT-TO: <imapuser@localhost>
  -S: Status: R
  -S: X-UIDL: ${ignore}
  -S: 
  -S: This is the first message.
  -S: )
  +S: \* 1 FETCH \(BODY\[\] \{255\}
  +S: Date: Mon, 7 Feb 1994 21:52:25 -0800 \(PST\)
  +S: From: Fred Foobar <foobar@Blurdybloop\.COM>
  +S: Subject: Test 01
  +S: To: mooch@owatagu\.siam\.edu
  +S: Message-Id: <B27397-0100000@Blurdybloop\.COM>
  +S: MIME-Version: 1\.0
  +S: Content-Type: TEXT/PLAIN; CHARSET=US-ASCII
  +S:
  +S: Test 01
  +S: \)
   S: f1 OK FETCH completed
   
  -// BODY[HEADER]
  +# BODY[HEADER]
   C: f1 FETCH 1 (BODY[HEADER])
  -S: * 1 FETCH (BODY[HEADER] {448}
  -S: Received: from ${ignore} [127.0.0.1] by localhost with ESMTP
  -S:   (SMTPD32-7.05 EVAL) id ${ignore}; ${rfcDate}
  -S: Message-ID: ${ignore}
  -S: From: sender@somewhere
  -S: To: imapuser@localhost
  -S: Subject: Message 1
  -S: Mime-Version: 1.0
  -S: Content-Type: text/plain
  -S: Content-Transfer-Encoding: 7bit
  -S: Date: ${ignore-12}
  -S: X-RCPT-TO: <imapuser@localhost>
  -S: Status: R
  -S: X-UIDL: ${ignore}
  -S: 
  -S: )
  -S: f1 OK FETCH completed
  -
  -// BODY[HEADER.FIELDS]
  -C: f1 FETCH 1 (BODY[HEADER.FIELDS (From To Subject Date Status)])
  -S: * 1 FETCH (BODY[HEADER.FIELDS (From To Subject Date Status)] {120}
  -S: From: sender@somewhere
  -S: To: imapuser@localhost
  -S: Subject: Message 1
  -S: Date: ${ignore-12}
  -S: Status: R
  -S: 
  -S: )
  -S: f1 OK FETCH completed
  -
  -// BODY[HEADER.FIELDS.NOT]
  -C: f1 FETCH 1 (BODY[HEADER.FIELDS.NOT (From To Subject Date Status)])
  -S: * 1 FETCH (BODY[HEADER.FIELDS.NOT (From To Subject Date Status)] {332}
  -S: Received: from ${ignore} [127.0.0.1] by localhost with ESMTP
  -S:   (SMTPD32-7.05 EVAL) id ${ignore}; ${rfcDate}
  -S: Message-ID: ${ignore}
  -S: Mime-Version: 1.0
  -S: Content-Type: text/plain
  -S: Content-Transfer-Encoding: 7bit
  -S: X-RCPT-TO: <imapuser@localhost>
  -S: X-UIDL: ${ignore}
  -S: 
  -S: 
  -S: )
  -S: f1 OK FETCH completed
  -
  -// BODY[MIME]
  -C: f1 FETCH 1 (BODY[MIME])
  -S: * 1 FETCH (BODY[MIME] {448}
  -S: Received: from ${ignore} [127.0.0.1] by localhost with ESMTP
  -S:   (SMTPD32-7.05 EVAL) id ${ignore}; ${rfcDate}
  -S: Message-ID: ${ignore}
  -S: From: sender@somewhere
  -S: To: imapuser@localhost
  -S: Subject: Message 1
  -S: Mime-Version: 1.0
  -S: Content-Type: text/plain
  -S: Content-Transfer-Encoding: 7bit
  -S: Date: ${ignore-12}
  -S: X-RCPT-TO: <imapuser@localhost>
  -S: Status: R
  -S: X-UIDL: ${ignore}
  -S: 
  -S: )
  +S: \* 1 FETCH \(BODY\[HEADER\] \{245\}
  +S: Date: Mon, 7 Feb 1994 21:52:25 -0800 \(PST\)
  +S: From: Fred Foobar <foobar@Blurdybloop\.COM>
  +S: Subject: Test 01
  +S: To: mooch@owatagu\.siam\.edu
  +S: Message-Id: <B27397-0100000@Blurdybloop\.COM>
  +S: MIME-Version: 1\.0
  +S: Content-Type: TEXT/PLAIN; CHARSET=US-ASCII
  +S:
  +S: \)
  +S: f1 OK FETCH completed
  +
  +# BODY[HEADER.FIELDS]
  +C: f1 FETCH 1 (BODY[HEADER.FIELDS (From To)])
  +S: \* 1 FETCH \(BODY\[HEADER\.FIELDS \(From To\)\] \{74\}
  +S: From: Fred Foobar <foobar@Blurdybloop\.COM>
  +S: To: mooch@owatagu\.siam\.edu
  +S:
  +S: \)
  +S: f1 OK FETCH completed
  +
  +# BODY[HEADER.FIELDS.NOT]
  +C: f1 FETCH 1 (BODY[HEADER.FIELDS.NOT (From To)])
  +S: \* 1 FETCH \(BODY\[HEADER\.FIELDS\.NOT \(From To\)\] \{173\}
  +S: Date: Mon, 7 Feb 1994 21:52:25 -0800 \(PST\)
  +S: Subject: Test 01
  +S: Message-Id: <B27397-0100000@Blurdybloop\.COM>
  +S: MIME-Version: 1\.0
  +S: Content-Type: TEXT/PLAIN; CHARSET=US-ASCII
  +S:
  +S: \)
   S: f1 OK FETCH completed
   
  -// BODY[TEXT]
  +# BODY[MIME]
  +# NOT implemented
  +
  +# BODY[TEXT]
   C: f1 FETCH 1 (BODY[TEXT])
  -S: * 1 FETCH (BODY[TEXT] {28}
  -S: This is the first message.
  -S: )
  +S: \* 1 FETCH \(BODY\[TEXT\] \{10\}
  +S: Test 01
  +S: \)
   S: f1 OK FETCH completed
   
  -// todo - multipart messages.
  -// todo - partial fetch BODY[]<0.100>
  -
  -// BODY.PEEK[]
  -// BODY.PEEK[HEADER]
  -// BODY.PEEK[HEADER.FIELDS]
  -// BODY.PEEK[HEADER.FIELDS.NOT]
  -// BODY.PEEK[MIME]
  -// BODY.PEEK[TEXT]
  +# todo - multipart messages.
  +# todo - partial fetch BODY[]<0.100>
  +
  +# BODY.PEEK[]
  +# BODY.PEEK[HEADER]
  +# BODY.PEEK[HEADER.FIELDS]
  +# BODY.PEEK[HEADER.FIELDS.NOT]
  +# BODY.PEEK[MIME]
  +# BODY.PEEK[TEXT]
   C: f1 FETCH 1 (BODY.PEEK[TEXT])
  -S: * 1 FETCH (BODY[TEXT] {28}
  -S: This is the first message.
  -S: )
  +S: \* 1 FETCH \(BODY\[TEXT\] \{10\}
  +S: Test 01
  +S: \)
   S: f1 OK FETCH completed
   
  -// BODYSTRUCTURE
  +# BODYSTRUCTURE - TODO should be returning extension data - language, md5 etc.
   C: f1 FETCH 1 (BODYSTRUCTURE)
  -S: * 1 FETCH (BODYSTRUCTURE ("text" "plain" NIL NIL NIL "7bit" 28 1 NIL NIL NIL))
  +S: \* 1 FETCH \(BODYSTRUCTURE \(\"TEXT\" \"PLAIN\" \(\"CHARSET\" \"US-ASCII\"\) NIL NIL \"7BIT\" 9 -1\)\)
   S: f1 OK FETCH completed
   
  -// ENVELOPE
  +# ENVELOPE - TODO need to study the rfc and write more tests: this format could be wrong.
   C: f1 FETCH 1 (ENVELOPE)
  -S: * 1 FETCH (ENVELOPE ("${ignore-12}" "Message 1" ((NIL NIL "sender" "somewhere")) ((NIL NIL "sender" "somewhere")) ((NIL NIL "sender" "somewhere")) ((NIL NIL "imapuser" "localhost")) NIL NIL NIL "${ignore}"))
  +S: \* 1 FETCH \(ENVELOPE \(\".*\" \"Test 01\" \(\(\"Fred Foobar\" NIL \"foobar\" \"Blurdybloop\.COM\"\)\) \(\(\"Fred Foobar\" NIL \"foobar\" \"Blurdybloop\.COM\"\)\) \(\(\"Fred Foobar\" NIL \"foobar\" \"Blurdybloop\.COM\"\)\) \(\(NIL NIL \"mooch\" \"owatagu\.siam\.edu\"\)\) NIL NIL NIL \"<B27397-0100000@Blurdybloop\.COM>\"\)\)
   S: f1 OK FETCH completed
   
  -// FLAGS
  +# FLAGS
   C: f1 FETCH 1 (FLAGS)
  -S: * 1 FETCH (FLAGS (\SEEN))
  +S: \* 1 FETCH \(FLAGS \(\\Seen\)\)
   S: f1 OK FETCH completed
   
  -// INTERNALDATE
  +# INTERNALDATE
   C: f1 FETCH 1 (INTERNALDATE)
  -S: * 1 FETCH (INTERNALDATE "${ignore-5}")
  +S: \* 1 FETCH \(INTERNALDATE \".*\"\)
   S: f1 OK FETCH completed
   
  -// RFC822 ( === BODY[])
  +# RFC822 ( === BODY[])
   C: f1 FETCH 1 (RFC822)
  -S: * 1 FETCH (RFC822 {476}
  -S: Received: from ${ignore} [127.0.0.1] by localhost with ESMTP
  -S:   (SMTPD32-7.05 EVAL) id ${ignore}; ${rfcDate}
  -S: Message-ID: ${ignore}
  -S: From: sender@somewhere
  -S: To: imapuser@localhost
  -S: Subject: Message 1
  -S: Mime-Version: 1.0
  -S: Content-Type: text/plain
  -S: Content-Transfer-Encoding: 7bit
  -S: Date: ${ignore-12}
  -S: X-RCPT-TO: <imapuser@localhost>
  -S: Status: R
  -S: X-UIDL: ${ignore}
  -S: 
  -S: This is the first message.
  -S: )
  +S: \* 1 FETCH \(RFC822 \{255\}
  +S: Date: Mon, 7 Feb 1994 21:52:25 -0800 \(PST\)
  +S: From: Fred Foobar <foobar@Blurdybloop\.COM>
  +S: Subject: Test 01
  +S: To: mooch@owatagu\.siam\.edu
  +S: Message-Id: <B27397-0100000@Blurdybloop\.COM>
  +S: MIME-Version: 1\.0
  +S: Content-Type: TEXT/PLAIN; CHARSET=US-ASCII
  +S:
  +S: Test 01
  +S: \)
   S: f1 OK FETCH completed
   
  -// RFC822.HEADER ( === BODY.PEEK[HEADER])
  +# RFC822.HEADER ( === BODY.PEEK[HEADER])
   C: f1 FETCH 1 (RFC822.HEADER)
  -S: * 1 FETCH (RFC822.HEADER {448}
  -S: Received: from ${ignore} [127.0.0.1] by localhost with ESMTP
  -S:   (SMTPD32-7.05 EVAL) id ${ignore}; ${rfcDate}
  -S: Message-ID: ${ignore}
  -S: From: sender@somewhere
  -S: To: imapuser@localhost
  -S: Subject: Message 1
  -S: Mime-Version: 1.0
  -S: Content-Type: text/plain
  -S: Content-Transfer-Encoding: 7bit
  -S: Date: ${ignore-12}
  -S: X-RCPT-TO: <imapuser@localhost>
  -S: Status: R
  -S: X-UIDL: ${ignore}
  -S: 
  -S: )
  +S: \* 1 FETCH \(RFC822\.HEADER \{245\}
  +S: Date: Mon, 7 Feb 1994 21:52:25 -0800 \(PST\)
  +S: From: Fred Foobar <foobar@Blurdybloop\.COM>
  +S: Subject: Test 01
  +S: To: mooch@owatagu\.siam\.edu
  +S: Message-Id: <B27397-0100000@Blurdybloop\.COM>
  +S: MIME-Version: 1\.0
  +S: Content-Type: TEXT/PLAIN; CHARSET=US-ASCII
  +S:
  +S: \)
   S: f1 OK FETCH completed
   
  -// RFC822.SIZE
  +# RFC822.SIZE
   C: f1 FETCH 1 (RFC822.SIZE)
  -S: * 1 FETCH (RFC822.SIZE 476)
  +S: \* 1 FETCH \(RFC822\.SIZE 9\)
   S: f1 OK FETCH completed
   
  -// RFC822.TEXT ( === BODY[TEXT])
  +# RFC822.TEXT ( === BODY[TEXT])
   C: f1 FETCH 1 (RFC822.TEXT)
  -S: * 1 FETCH (RFC822.TEXT {28}
  -S: This is the first message.
  -S: )
  +S: \* 1 FETCH \(RFC822\.TEXT \{10\}
  +S: Test 01
  +S: \)
   S: f1 OK FETCH completed
   
  -// UID
  +# UID
   C: f1 FETCH 1 (UID)
  -S: * 1 FETCH (UID ${ignore})
  +S: \* 1 FETCH \(UID \d+\)
   S: f1 OK FETCH completed
   
   
  -// Macro Commands
  -// ALL ( === FLAGS INTERNALDATE RFC822.SIZE ENVELOPE)
  +# Macro Commands
  +# ALL ( === FLAGS INTERNALDATE RFC822.SIZE ENVELOPE)
   C: f1 FETCH 1 (ALL)
  -S: * 1 FETCH (FLAGS (\SEEN) INTERNALDATE "${ignore-5}" RFC822.SIZE 476 ENVELOPE ("${ignore-12}" "Message 1" ((NIL NIL "sender" "somewhere")) ((NIL NIL "sender" "somewhere")) ((NIL NIL "sender" "somewhere")) ((NIL NIL "imapuser" "localhost")) NIL NIL NIL "${ignore}"))
  +S: \* 1 FETCH \(FLAGS \(\\Seen\) INTERNALDATE \".*\" RFC822.SIZE 9 ENVELOPE \(\".*\" \"Test 01\" \(\(\"Fred Foobar\" NIL \"foobar\" \"Blurdybloop\.COM\"\)\) \(\(\"Fred Foobar\" NIL \"foobar\" \"Blurdybloop\.COM\"\)\) \(\(\"Fred Foobar\" NIL \"foobar\" \"Blurdybloop\.COM\"\)\) \(\(NIL NIL \"mooch\" \"owatagu\.siam\.edu\"\)\) NIL NIL NIL \"<B27397-0100000@Blurdybloop\.COM>\"\)\)
   S: f1 OK FETCH completed
   
  -// FULL ( === FLAGS INTERNALDATE RFC822.SIZE ENVELOPE BODY )
  +# FULL ( === FLAGS INTERNALDATE RFC822.SIZE ENVELOPE BODY )
   C: f1 FETCH 1 (FULL)
  -S: * 1 FETCH (FLAGS (\SEEN) INTERNALDATE "${ignore-5}" RFC822.SIZE 476 ENVELOPE ("${ignore-12}" "Message 1" ((NIL NIL "sender" "somewhere")) ((NIL NIL "sender" "somewhere")) ((NIL NIL "sender" "somewhere")) ((NIL NIL "imapuser" "localhost")) NIL NIL NIL "${ignore}") BODY RFC822.SIZE 476("text" "plain" NIL NIL NIL "7bit" 28 1))
  +S: \* 1 FETCH \(FLAGS \(\\Seen\) INTERNALDATE \".*\" RFC822\.SIZE 9 ENVELOPE \(\".*\" \"Test 01\" \(\(\"Fred Foobar\" NIL \"foobar\" \"Blurdybloop\.COM\"\)\) \(\(\"Fred Foobar\" NIL \"foobar\" \"Blurdybloop\.COM\"\)\) \(\(\"Fred Foobar\" NIL \"foobar\" \"Blurdybloop\.COM\"\)\) \(\(NIL NIL \"mooch\" \"owatagu\.siam\.edu\"\)\) NIL NIL NIL \"<B27397-0100000@Blurdybloop\.COM>\"\) BODY \(\"TEXT\" \"PLAIN\" \(\"CHARSET\" \"US-ASCII\"\) NIL NIL \"7BIT\" 9 -1\)\)
   S: f1 OK FETCH completed
   
  -// FAST ( === FLAGS INTERNALDATE RFC822.SIZE )
  +# FAST ( === FLAGS INTERNALDATE RFC822.SIZE )
   C: f1 FETCH 1 (FAST)
  -S: * 1 FETCH (FLAGS (\SEEN) INTERNALDATE "${ignore-5}" RFC822.SIZE 476)
  +S: \* 1 FETCH \(FLAGS \(\\Seen\) INTERNALDATE \".*\" RFC822.SIZE 9\)
   S: f1 OK FETCH completed
  
  
  
  1.2       +4 -4      jakarta-james/proposals/imap2/test/org/apache/james/imapserver/ImapMailboxTest.java
  
  Index: ImapMailboxTest.java
  ===================================================================
  RCS file: /home/cvs/jakarta-james/proposals/imap2/test/org/apache/james/imapserver/ImapMailboxTest.java,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- ImapMailboxTest.java	27 Nov 2002 12:26:46 -0000	1.1
  +++ ImapMailboxTest.java	3 Dec 2002 14:00:13 -0000	1.2
  @@ -12,7 +12,7 @@
   import org.apache.james.imapserver.store.ImapMailbox;
   import org.apache.james.imapserver.store.MailboxException;
   import org.apache.james.imapserver.store.MessageFlags;
  -import org.apache.james.imapserver.store.ImapMessage;
  +import org.apache.james.imapserver.store.SimpleImapMessage;
   import org.apache.james.core.MimeMessageSource;
   import org.apache.james.core.MimeMessageWrapper;
   import org.apache.james.core.MailImpl;
  @@ -62,7 +62,7 @@
           "\r\n";
           long uid = appendMessage( message, flags, datetime, mailbox );
   
  -        ImapMessage imapMessage = mailbox.getMessage( uid );
  +        SimpleImapMessage imapMessage = mailbox.getMessage( uid );
   
           assertEquals( 1, mailbox.getMessageCount() );
           assertTrue( imapMessage.getFlags().isFlagged() );
  @@ -83,7 +83,7 @@
                   new MimeMessageByteArraySource( "messageContent:" + System.currentTimeMillis(),
                                                   messageContent.getBytes());
           MimeMessage message = new MimeMessageWrapper( source );
  -        ImapMessage imapMessage = mailbox.createMessage( message, flags, datetime );
  +        SimpleImapMessage imapMessage = mailbox.createMessage( message, flags, datetime );
           return imapMessage.getUid();
       }
   
  
  
  
  1.2       +26 -0     jakarta-james/proposals/imap2/test/org/apache/james/imapserver/ListMailboxes.test
  
  Index: ListMailboxes.test
  ===================================================================
  RCS file: /home/cvs/jakarta-james/proposals/imap2/test/org/apache/james/imapserver/ListMailboxes.test,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- ListMailboxes.test	22 Nov 2002 02:09:52 -0000	1.1
  +++ ListMailboxes.test	3 Dec 2002 14:00:13 -0000	1.2
  @@ -1,3 +1,13 @@
  +# Create a few folders
  +C: 10 CREATE test
  +S: 10 OK CREATE completed
  +C: 11 CREATE test.subfolder
  +S: 11 OK CREATE completed
  +C: 12 CREATE test1
  +S: 12 OK CREATE completed
  +C: 13 CREATE test1.subfolder1
  +S: 13 OK CREATE completed
  +
   # Empty 1st arg searches default context (#mail)
   C: 10 LIST "" test
   S: \* LIST \(\) \"\.\" test
  @@ -49,3 +59,19 @@
   S: \* LIST \(\) \"\.\" test1.subfolder1
   }
   S: b4 OK LIST completed
  +
  +# List a nonexistent group
  +C: a1 LIST "nosuchmailbox" "*"
  +S: a1 OK LIST completed
  +C: a1 LIST "" "nosuch*"
  +S: a1 OK LIST completed
  +
  +# Cleanup
  +C: a1 DELETE test1.subfolder1
  +S: a1 OK DELETE completed
  +C: a1 DELETE test1
  +S: a1 OK DELETE completed
  +C: a1 DELETE test.subfolder
  +S: a1 OK DELETE completed
  +C: a1 DELETE test
  +S: a1 OK DELETE completed
  \ No newline at end of file
  
  
  
  1.2       +20 -0     jakarta-james/proposals/imap2/test/org/apache/james/imapserver/SelectEmpty.test
  
  Index: SelectEmpty.test
  ===================================================================
  RCS file: /home/cvs/jakarta-james/proposals/imap2/test/org/apache/james/imapserver/SelectEmpty.test,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- SelectEmpty.test	22 Nov 2002 02:09:52 -0000	1.1
  +++ SelectEmpty.test	3 Dec 2002 14:00:13 -0000	1.2
  @@ -1,3 +1,13 @@
  +# Create a few folders
  +C: 10 CREATE test
  +S: 10 OK CREATE completed
  +C: 11 CREATE test.subfolder
  +S: 11 OK CREATE completed
  +C: 12 CREATE test1
  +S: 12 OK CREATE completed
  +C: 13 CREATE test1.subfolder1
  +S: 13 OK CREATE completed
  +
   C: abcd SELECT test
   S: \* FLAGS \(\\Answered \\Deleted \\Draft \\Flagged \\Seen\)
   S: \* 0 EXISTS
  @@ -29,3 +39,13 @@
   
   C: abcd SELECT subfolder1
   S: abcd NO SELECT failed. No such mailbox.
  +
  +# Cleanup
  +C: a1 DELETE test1.subfolder1
  +S: a1 OK DELETE completed
  +C: a1 DELETE test1
  +S: a1 OK DELETE completed
  +C: a1 DELETE test.subfolder
  +S: a1 OK DELETE completed
  +C: a1 DELETE test
  +S: a1 OK DELETE completed
  \ No newline at end of file
  
  
  
  1.2       +6 -1      jakarta-james/proposals/imap2/test/org/apache/james/imapserver/Status.test
  
  Index: Status.test
  ===================================================================
  RCS file: /home/cvs/jakarta-james/proposals/imap2/test/org/apache/james/imapserver/Status.test,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- Status.test	26 Nov 2002 14:53:48 -0000	1.1
  +++ Status.test	3 Dec 2002 14:00:13 -0000	1.2
  @@ -1,3 +1,6 @@
  +C: a1 CREATE test
  +S: a1 OK CREATE completed
  +
   # Tests for the STATUS command
   C: a001 STATUS test (MESSAGES)
   S: \* STATUS test \(MESSAGES \d+\)
  @@ -15,4 +18,6 @@
   S: \* STATUS test \(RECENT \d+ UNSEEN \d+\)
   S: a001 OK STATUS completed
   
  -
  +# Cleanup
  +C: a1 DELETE test
  +S: a1 OK DELETE completed
  
  
  
  1.3       +17 -11    jakarta-james/proposals/imap2/test/org/apache/james/imapserver/StringArgs.test
  
  Index: StringArgs.test
  ===================================================================
  RCS file: /home/cvs/jakarta-james/proposals/imap2/test/org/apache/james/imapserver/StringArgs.test,v
  retrieving revision 1.2
  retrieving revision 1.3
  diff -u -r1.2 -r1.3
  --- StringArgs.test	27 Nov 2002 02:24:10 -0000	1.2
  +++ StringArgs.test	3 Dec 2002 14:00:13 -0000	1.3
  @@ -15,27 +15,33 @@
   S: \* STATUS INBOX \(MESSAGES \d+\)
   S: a001 OK STATUS completed
   
  +# Tests with an atomic mailbox name.
  +C: a1 CREATE atomMailbox
  +S: a1 OK CREATE completed
  +
   # atom
  -C: a001 STATUS test (MESSAGES)
  -S: \* STATUS test \(MESSAGES \d+\)
  +C: a001 STATUS atomMailbox (MESSAGES)
  +S: \* STATUS atomMailbox \(MESSAGES \d+\)
   S: a001 OK STATUS completed
   
   # quoted
  -C: a001 STATUS "test" (MESSAGES)
  -S: \* STATUS test \(MESSAGES \d+\)
  +C: a001 STATUS "atomMailbox" (MESSAGES)
  +S: \* STATUS atomMailbox \(MESSAGES \d+\)
   S: a001 OK STATUS completed
   
   # non-synchronized literal
  -C: a001 STATUS {4}
  +C: a001 STATUS {11}
   S: \+
  -C: test (MESSAGES)
  -S: \* STATUS test \(MESSAGES \d+\)
  +C: atomMailbox (MESSAGES)
  +S: \* STATUS atomMailbox \(MESSAGES \d+\)
   S: a001 OK STATUS completed
   
   # synchronized literal
  -C: a001 STATUS {4+}
  -C: test (MESSAGES)
  -S: \* STATUS test \(MESSAGES \d+\)
  +C: a001 STATUS {11+}
  +C: atomMailbox (MESSAGES)
  +S: \* STATUS atomMailbox \(MESSAGES \d+\)
   S: a001 OK STATUS completed
   
  -
  +# Cleanup
  +C: a1 DELETE atomMailbox
  +S: a1 OK DELETE completed
  
  
  
  1.3       +21 -4     jakarta-james/proposals/imap2/test/org/apache/james/imapserver/Subscribe.test
  
  Index: Subscribe.test
  ===================================================================
  RCS file: /home/cvs/jakarta-james/proposals/imap2/test/org/apache/james/imapserver/Subscribe.test,v
  retrieving revision 1.2
  retrieving revision 1.3
  diff -u -r1.2 -r1.3
  --- Subscribe.test	27 Nov 2002 03:24:20 -0000	1.2
  +++ Subscribe.test	3 Dec 2002 14:00:13 -0000	1.3
  @@ -1,3 +1,13 @@
  +# Create a few folders
  +C: 10 CREATE test
  +S: 10 OK CREATE completed
  +C: 11 CREATE test.subfolder
  +S: 11 OK CREATE completed
  +C: 12 CREATE test1
  +S: 12 OK CREATE completed
  +C: 13 CREATE test1.subfolder1
  +S: 13 OK CREATE completed
  +
   C: a01 LSUB "" "*"
   S: a01 OK LSUB completed
   
  @@ -44,13 +54,20 @@
   C: a01 UNSUBSCRIBE test.subfolder
   S: a01 OK UNSUBSCRIBE completed
   
  -# Leave test1.subfolder1 subscribed for next test
   # LIST All subscribed
   C: a01 LSUB "" "*"
   S: \* LSUB \(\) \"\.\" test1\.subfolder1
   S: a01 OK LSUB completed
   
  +# Cleanup
  +C: a01 UNSUBSCRIBE test1.subfolder1
  +S: a01 OK UNSUBSCRIBE completed
   
  -
  -
  -
  +C: a1 DELETE test1.subfolder1
  +S: a1 OK DELETE completed
  +C: a1 DELETE test1
  +S: a1 OK DELETE completed
  +C: a1 DELETE test.subfolder
  +S: a1 OK DELETE completed
  +C: a1 DELETE test
  +S: a1 OK DELETE completed
  \ No newline at end of file
  
  
  
  1.1                  jakarta-james/proposals/imap2/test/org/apache/james/imapserver/AppendExpunge.test
  
  Index: AppendExpunge.test
  ===================================================================
  C: 10 CREATE appendexpunge
  S: 10 OK CREATE completed
  
  C: a001 STATUS appendexpunge (MESSAGES)
  S: \* STATUS appendexpunge \(MESSAGES 0\)
  S: a001 OK STATUS completed
  
  C: A003 APPEND appendexpunge (\Deleted) {310+}
  C: Date: Mon, 7 Feb 1994 21:52:25 -0800 (PST)
  C: From: Fred Foobar <foobar@Blurdybloop.COM>
  C: Subject: afternoon meeting
  C: To: mooch@owatagu.siam.edu
  C: Message-Id: <B27397-0100000@Blurdybloop.COM>
  C: MIME-Version: 1.0
  C: Content-Type: TEXT/PLAIN; CHARSET=US-ASCII
  C:
  C: Hello Joe, do you think we can meet at 3:30 tomorrow?
  C:
  S: A003 OK APPEND completed
  
  C: A003 APPEND appendexpunge (\Deleted) {310+}
  C: Date: Mon, 7 Feb 1994 21:52:25 -0800 (PST)
  C: From: Fred Foobar <foobar@Blurdybloop.COM>
  C: Subject: afternoon meeting 2
  C: To: mooch@owatagu.siam.edu
  C: Message-Id: <B27397-0100000@Blurdybloop.COM>
  C: MIME-Version: 1.0
  C: Content-Type: TEXT/PLAIN; CHARSET=US-ASCII
  C:
  C: Hello Joe, could we change that to 4:00pm tomorrow?
  C:
  S: A003 OK APPEND completed
  
  C: a1 STATUS appendexpunge (MESSAGES)
  S: \* STATUS appendexpunge \(MESSAGES 2\)
  S: a1 OK STATUS completed
  
  C: a1 SELECT appendexpunge
  S: \* FLAGS \(\\Answered \\Deleted \\Draft \\Flagged \\Seen\)
  S: \* 2 EXISTS
  S: \* \d+ RECENT
  S: \* OK \[UIDVALIDITY \d+\]
  S: \* OK No messages unseen
  #S: \* OK \[PERMANENTFLAGS \(\)\]
  S: a1 OK \[READ-WRITE\] SELECT completed
  
  C: a1 EXPUNGE
  S: \* 1 EXPUNGE
  S: \* 1 EXPUNGE
  S: a1 OK EXPUNGE completed
  
  C: a1 STATUS appendexpunge (MESSAGES)
  S: \* STATUS appendexpunge \(MESSAGES 0\)
  S: a1 OK STATUS completed
  
  C: a1 DELETE appendexpunge
  S: a1 OK DELETE completed
  
  
  
  
  
  
  
  
  
  
  
  1.1                  jakarta-james/proposals/imap2/test/org/apache/james/imapserver/Check.test
  
  Index: Check.test
  ===================================================================
  C: a01 CHECK
  S: a01 OK CHECK completed
  
  
  1.1                  jakarta-james/proposals/imap2/test/org/apache/james/imapserver/Copy.test
  
  Index: Copy.test
  ===================================================================
  C: a1 CREATE copied
  S: a1 OK CREATE completed
  
  C: a1 STATUS copied (MESSAGES)
  S: \* STATUS copied \(MESSAGES 0\)
  S: a1 OK STATUS completed
  
  C: a1 STORE 3 FLAGS (\Deleted)
  S: \* 3 FETCH \(FLAGS \(\\Deleted\)
  S: a1 OK STORE completed
  
  C: a1 COPY 2:4 copied
  S: a1 OK COPY completed
  
  C: a1 STATUS copied (MESSAGES)
  S: \* STATUS copied \(MESSAGES 3\)
  S: a1 OK STATUS completed
  
  C: a1 SELECT copied
  S: \* FLAGS \(\\Answered \\Deleted \\Draft \\Flagged \\Seen\)
  S: \* 3 EXISTS
  S: \* \d+ RECENT
  S: \* OK \[UIDVALIDITY \d+\]
  S: (\* OK \[UNSEEN \d+\] \d+ is the first unseen)|(\* OK No messages unseen)
  #S: \* OK \[PERMANENTFLAGS \(\)\]
  S: a1 OK \[READ-WRITE\] SELECT completed
  
  C: a1 FETCH 1:4 (FLAGS)
  S: \* 1 FETCH \(FLAGS \(\)
  S: \* 2 FETCH \(FLAGS \(\\Deleted\)
  S: \* 3 FETCH \(FLAGS \(\)
  S: a1 OK FETCH completed
  
  C: a1 DELETE copied
  S: a1 OK DELETE completed
  
  
  1.1                  jakarta-james/proposals/imap2/test/org/apache/james/imapserver/Expunge.test
  
  Index: Expunge.test
  ===================================================================
  C: a1 EXPUNGE
  S: a1 OK EXPUNGE completed
  
  
  
  1.1                  jakarta-james/proposals/imap2/test/org/apache/james/imapserver/Noop.test
  
  Index: Noop.test
  ===================================================================
  # TODO make sure we get unsolicited responses on NOOP
  C: a01 NOOP
  S: a01 OK NOOP completed
  
  
  1.1                  jakarta-james/proposals/imap2/test/org/apache/james/imapserver/Search.test
  
  Index: Search.test
  ===================================================================
  # TODO implement search - currently returns everything regardless
  C: a SEARCH FLAGGED
  S: \* SEARCH 1 2 3 4
  S: a OK SEARCH completed
  
  
  
  1.1                  jakarta-james/proposals/imap2/test/org/apache/james/imapserver/SelectedStateCleanup.test
  
  Index: SelectedStateCleanup.test
  ===================================================================
  C: abcd SELECT inbox
  S: \* FLAGS \(\\Answered \\Deleted \\Draft \\Flagged \\Seen\)
  S: \* \d+ EXISTS
  S: \* \d+ RECENT
  S: \* OK \[UIDVALIDITY \d+\]
  S: (\* OK \[UNSEEN \d+\] \d+ is the first unseen)|(\* OK No messages unseen)
  #S: \* OK \[PERMANENTFLAGS \(\)\]
  S: abcd OK \[READ-WRITE\] SELECT completed
  
  C: a1 DELETE selected
  S: a1 OK DELETE completed
  
  C: a1 SELECT selected
  S: a1 NO SELECT failed. No such mailbox.
  
  
  1.1                  jakarta-james/proposals/imap2/test/org/apache/james/imapserver/SelectedStateSetup.test
  
  Index: SelectedStateSetup.test
  ===================================================================
  C: a1 CREATE selected
  S: a1 OK CREATE completed
  
  C: A003 APPEND selected {254+}
  C: Date: Mon, 7 Feb 1994 21:52:25 -0800 (PST)
  C: From: Fred Foobar <foobar@Blurdybloop.COM>
  C: Subject: Test 01
  C: To: mooch@owatagu.siam.edu
  C: Message-Id: <B27397-0100000@Blurdybloop.COM>
  C: MIME-Version: 1.0
  C: Content-Type: TEXT/PLAIN; CHARSET=US-ASCII
  C:
  C: Test 01
  C:
  S: A003 OK APPEND completed
  
  C: A003 APPEND selected {254+}
  C: Date: Mon, 7 Feb 1994 21:52:25 -0800 (PST)
  C: From: Fred Foobar <foobar@Blurdybloop.COM>
  C: Subject: Test 02
  C: To: mooch@owatagu.siam.edu
  C: Message-Id: <B27397-0100000@Blurdybloop.COM>
  C: MIME-Version: 1.0
  C: Content-Type: TEXT/PLAIN; CHARSET=US-ASCII
  C:
  C: Test 02
  C:
  S: A003 OK APPEND completed
  
  C: A003 APPEND selected {254+}
  C: Date: Mon, 7 Feb 1994 21:52:25 -0800 (PST)
  C: From: Fred Foobar <foobar@Blurdybloop.COM>
  C: Subject: Test 03
  C: To: mooch@owatagu.siam.edu
  C: Message-Id: <B27397-0100000@Blurdybloop.COM>
  C: MIME-Version: 1.0
  C: Content-Type: TEXT/PLAIN; CHARSET=US-ASCII
  C:
  C: Test 03
  C:
  S: A003 OK APPEND completed
  
  C: A003 APPEND selected {254+}
  C: Date: Mon, 7 Feb 1994 21:52:25 -0800 (PST)
  C: From: Fred Foobar <foobar@Blurdybloop.COM>
  C: Subject: Test 04
  C: To: mooch@owatagu.siam.edu
  C: Message-Id: <B27397-0100000@Blurdybloop.COM>
  C: MIME-Version: 1.0
  C: Content-Type: TEXT/PLAIN; CHARSET=US-ASCII
  C:
  C: Test 04
  C:
  S: A003 OK APPEND completed
  
  C: a1 SELECT selected
  S: \* FLAGS \(\\Answered \\Deleted \\Draft \\Flagged \\Seen\)
  S: \* 4 EXISTS
  S: \* \d+ RECENT
  S: \* OK \[UIDVALIDITY \d+\]
  S: (\* OK \[UNSEEN \d+\] \d+ is the first unseen)|(\* OK No messages unseen)
  #S: \* OK \[PERMANENTFLAGS \(\)\]
  S: a1 OK \[READ-WRITE\] SELECT completed
  
  
  
  
  1.1                  jakarta-james/proposals/imap2/test/org/apache/james/imapserver/Store.test
  
  Index: Store.test
  ===================================================================
  # FLAGS
  C: f1 FETCH 1:4 (FLAGS)
  S: \* 1 FETCH \(FLAGS \(\)\)
  S: \* 2 FETCH \(FLAGS \(\)\)
  S: \* 3 FETCH \(FLAGS \(\)\)
  S: \* 4 FETCH \(FLAGS \(\)\)
  S: f1 OK FETCH completed
  
  #Simple store
  C: f1 STORE 1 FLAGS (\Deleted)
  S: \* 1 FETCH \(FLAGS \(\\Deleted\)\)
  S: f1 OK STORE completed
  
  C: f1 FETCH 1 (FLAGS)
  S: \* 1 FETCH \(FLAGS \(\\Deleted\)\)
  S: f1 OK FETCH completed
  
  #Override previous value (silent)
  C: f2 STORE 1 FLAGS.SILENT (\Draft \Flagged)
  S: f2 OK STORE completed
  
  C: f1 FETCH 1 (FLAGS)
  S: \* 1 FETCH \(FLAGS \(\\Draft \\Flagged\)\)
  S: f1 OK FETCH completed
  
  #Add to existing flags
  C: f2 STORE 1 +FLAGS (\Deleted)
  S: \* 1 FETCH \(FLAGS \(\\Deleted \\Draft \\Flagged\)\)
  S: f2 OK STORE completed
  
  C: f1 FETCH 1 (FLAGS)
  S: \* 1 FETCH \(FLAGS \(\\Deleted \\Draft \\Flagged\)\)
  S: f1 OK FETCH completed
  
  #Remove from existing flags (silent)
  C: f2 STORE 1 -FLAGS.SILENT (\Draft)
  S: f2 OK STORE completed
  
  C: f1 FETCH 1 (FLAGS)
  S: \* 1 FETCH \(FLAGS \(\\Deleted \\Flagged\)\)
  S: f1 OK FETCH completed
  
  C: f4 STORE 3:4 FLAGS (\Flagged)
  S: \* 3 FETCH \(FLAGS \(\\Flagged\)
  S: \* 4 FETCH \(FLAGS \(\\Flagged\)
  S: f4 OK STORE completed
  
  C: f1 FETCH 1:4 (FLAGS)
  S: \* 1 FETCH \(FLAGS \(\\Deleted \\Flagged\)\)
  S: \* 2 FETCH \(FLAGS \(\)\)
  S: \* 3 FETCH \(FLAGS \(\\Flagged\)
  S: \* 4 FETCH \(FLAGS \(\\Flagged\)
  S: f1 OK FETCH completed
  
  
  
  1.1                  jakarta-james/proposals/imap2/test/org/apache/james/imapserver/TestCommandsInAuthenticatedState.java
  
  Index: TestCommandsInAuthenticatedState.java
  ===================================================================
  /*
   * Copyright (C) The Apache Software Foundation. All rights reserved.
   *
   * This software is published under the terms of the Apache Software License
   * version 1.1, a copy of which has been included with this distribution in
   * the LICENSE file.
   */
  package org.apache.james.imapserver;
  
  import org.apache.james.test.SimpleFileProtocolTest;
  
  import junit.framework.Test;
  import junit.framework.TestSuite;
  
  /**
   * Runs tests for commands valid in the AUTHENTICATED state. A login session precedes
   * the execution of the test elements.
   */
  public class TestCommandsInAuthenticatedState
          extends SimpleFileProtocolTest implements ImapTest
  {
      public TestCommandsInAuthenticatedState( String name )
      {
          super( name );
      }
  
      /**
       * Sets up {@link #preElements} with a welcome message and login request/response.
       * @throws Exception
       */
      public void setUp() throws Exception
      {
          super.setUp();
          addTestFile( "Welcome.test", preElements );
          addLogin( USER, PASSWORD );
      }
  
      protected void addLogin( String username, String password )
      {
          preElements.CL( "a001 LOGIN " + username + " " + password );
          preElements.SL( "a001 OK LOGIN completed", "TestCommandsInAuthenticatedState.java:33" );
      }
  
      /**
       * Provides all tests which should be run in the authenicated state. Each test name
       * corresponds to a protocol session file.
       */
      public static Test suite() throws Exception
      {
          TestSuite suite = new TestSuite();
          // Not valid in this state
          suite.addTest( new TestCommandsInAuthenticatedState( "ValidSelected" ) );
          suite.addTest( new TestCommandsInAuthenticatedState( "ValidNonAuthenticated" ) );
  
          // Valid in all states
          suite.addTest( new TestCommandsInAuthenticatedState( "Capability" ) );
          suite.addTest( new TestCommandsInAuthenticatedState( "Noop" ) );
          suite.addTest( new TestCommandsInAuthenticatedState( "Logout" ) );
  
          // Valid in authenticated state
          suite.addTest( new TestCommandsInAuthenticatedState( "ExamineInbox" ) );
          suite.addTest( new TestCommandsInAuthenticatedState( "SelectInbox" ) );
          suite.addTest( new TestCommandsInAuthenticatedState( "Create" ) );
          suite.addTest( new TestCommandsInAuthenticatedState( "ExamineEmpty" ) );
          suite.addTest( new TestCommandsInAuthenticatedState( "SelectEmpty" ) );
          suite.addTest( new TestCommandsInAuthenticatedState( "ListNamespace" ) );
          suite.addTest( new TestCommandsInAuthenticatedState( "ListMailboxes" ) );
          suite.addTest( new TestCommandsInAuthenticatedState( "Status" ) );
          suite.addTest( new TestCommandsInAuthenticatedState( "Subscribe" ) );
          suite.addTest( new TestCommandsInAuthenticatedState( "Delete" ) );
          suite.addTest( new TestCommandsInAuthenticatedState( "Append" ) );
  
          return suite;
      }
  
  }
  
  
  
  1.1                  jakarta-james/proposals/imap2/test/org/apache/james/imapserver/TestCommandsInNonAuthenticatedState.java
  
  Index: TestCommandsInNonAuthenticatedState.java
  ===================================================================
  /*
   * Copyright (C) The Apache Software Foundation. All rights reserved.
   *
   * This software is published under the terms of the Apache Software License
   * version 1.1, a copy of which has been included with this distribution in
   * the LICENSE file.
   */
  package org.apache.james.imapserver;
  
  import org.apache.james.test.SimpleFileProtocolTest;
  
  import junit.framework.Test;
  import junit.framework.TestSuite;
  
  /**
   * Runs tests for commands valid in the NON_AUTHENTICATED state.
   * A welcome message precedes the execution of the test elements.
   */
  public class TestCommandsInNonAuthenticatedState
          extends SimpleFileProtocolTest
  {
      public TestCommandsInNonAuthenticatedState( String name )
      {
          super( name );
      }
  
      /**
       * Adds a welcome message to the {@link #preElements}.
       * @throws Exception
       */
      public void setUp() throws Exception
      {
          super.setUp();
          addTestFile( "Welcome.test", preElements );
      }
  
      /**
       * Sets up tests valid in the non-authenticated state.
       */ 
      public static Test suite() throws Exception
      {
          TestSuite suite = new TestSuite();
          // Not valid in this state
          suite.addTest( new TestCommandsInNonAuthenticatedState( "ValidAuthenticated" ) );
          suite.addTest( new TestCommandsInNonAuthenticatedState( "ValidSelected" ) );
  
          // Valid in all states
          suite.addTest( new TestCommandsInNonAuthenticatedState( "Capability" ) );
          suite.addTest( new TestCommandsInNonAuthenticatedState( "Noop" ) );
          suite.addTest( new TestCommandsInNonAuthenticatedState( "Logout" ) );
  
          // Valid only in non-authenticated state.
          suite.addTest( new TestCommandsInNonAuthenticatedState( "Authenticate" ) );
          suite.addTest( new TestCommandsInNonAuthenticatedState( "Login" ) );
  
          return suite;
      }
  
  }
  
  
  
  1.1                  jakarta-james/proposals/imap2/test/org/apache/james/imapserver/TestCompound.java
  
  Index: TestCompound.java
  ===================================================================
  /*
   * Copyright (C) The Apache Software Foundation. All rights reserved.
   *
   * This software is published under the terms of the Apache Software License
   * version 1.1, a copy of which has been included with this distribution in
   * the LICENSE file.
   */
  package org.apache.james.imapserver;
  
  import junit.framework.Test;
  import junit.framework.TestSuite;
  
  /**
   *
   * @author  Darrell DeBoer <darrell@apache.org>
   *
   * @version $Revision: 1.1 $
   */
  public class TestCompound extends TestCommandsInAuthenticatedState
  {
      public TestCompound( String name )
      {
          super( name );
      }
  
      /**
       * Provides all tests which should be run in the authenicated state. Each test name
       * corresponds to a protocol session file.
       */
      public static Test suite() throws Exception
      {
          TestSuite suite = new TestSuite();
          suite.addTest( new TestCommandsInAuthenticatedState( "AppendExpunge" ) );
          suite.addTest( new TestCommandsInAuthenticatedState( "StringArgs" ) );
          // TODO various mailbox names (eg with spaces...)
          return suite;
      }
  
  }
  
  
  
  1.1                  jakarta-james/proposals/imap2/test/org/apache/james/imapserver/TestOtherCommandsInSelectedState.java
  
  Index: TestOtherCommandsInSelectedState.java
  ===================================================================
  /*
   * Copyright (C) The Apache Software Foundation. All rights reserved.
   *
   * This software is published under the terms of the Apache Software License
   * version 1.1, a copy of which has been included with this distribution in
   * the LICENSE file.
   */
  package org.apache.james.imapserver;
  
  import junit.framework.Test;
  import junit.framework.TestSuite;
  
  /**
   * Tests commands which are valid in AUTHENTICATED and NONAUTHENTICATED by running
   * them in the SELECTED state. Many commands function identically, while others
   * are invalid in this state.
   *
   * @author  Darrell DeBoer <darrell@apache.org>
   *
   * @version $Revision: 1.1 $
   */
  public class TestOtherCommandsInSelectedState
          extends TestCommandsInAuthenticatedState
  {
      public TestOtherCommandsInSelectedState( String name )
      {
          super( name );
      }
  
      /**
       * Superclass sets up welcome message and login session in {@link #preElements}.
       * A "SELECT INBOX" session is then added to these elements.
       * @throws Exception
       */
      public void setUp() throws Exception
      {
          super.setUp();
          addTestFile( "SelectInbox.test", preElements );
      }
  
      /**
       * Provides all tests which should be run in the selected state. Each test name
       * corresponds to a protocol session file.
       */
      public static Test suite() throws Exception
      {
          TestSuite suite = new TestSuite();
          // Not valid in this state
          suite.addTest( new TestOtherCommandsInSelectedState( "ValidNonAuthenticated" ) );
  
          // Valid in all states
          suite.addTest( new TestOtherCommandsInSelectedState( "Capability" ) );
          suite.addTest( new TestOtherCommandsInSelectedState( "Noop" ) );
          suite.addTest( new TestOtherCommandsInSelectedState( "Logout" ) );
  
          // Valid in authenticated state
          suite.addTest( new TestOtherCommandsInSelectedState( "Create" ) );
          suite.addTest( new TestOtherCommandsInSelectedState( "ExamineEmpty" ) );
          suite.addTest( new TestOtherCommandsInSelectedState( "SelectEmpty" ) );
          suite.addTest( new TestOtherCommandsInSelectedState( "ListNamespace" ) );
          suite.addTest( new TestOtherCommandsInSelectedState( "ListMailboxes" ) );
          suite.addTest( new TestOtherCommandsInSelectedState( "Status" ) );
          suite.addTest( new TestOtherCommandsInSelectedState( "StringArgs" ) );
          suite.addTest( new TestOtherCommandsInSelectedState( "Subscribe" ) );
          suite.addTest( new TestOtherCommandsInSelectedState( "Append" ) );
          suite.addTest( new TestOtherCommandsInSelectedState( "Delete" ) );
  
          return suite;
      }
  }
  
  
  
  1.1                  jakarta-james/proposals/imap2/test/org/apache/james/imapserver/TestSelectedCommandsInSelectedState.java
  
  Index: TestSelectedCommandsInSelectedState.java
  ===================================================================
  /*
   * Copyright (C) The Apache Software Foundation. All rights reserved.
   *
   * This software is published under the terms of the Apache Software License
   * version 1.1, a copy of which has been included with this distribution in
   * the LICENSE file.
   */
  package org.apache.james.imapserver;
  
  import junit.framework.Test;
  import junit.framework.TestSuite;
  
  /**
   * Runs tests for commands valid only in the SELECTED state. A login session
   * and setup of a "seleted" mailbox precedes the execution of the test elements.
   */
  public class TestSelectedCommandsInSelectedState
          extends TestCommandsInAuthenticatedState
  {
      public TestSelectedCommandsInSelectedState( String name )
      {
          super( name );
      }
  
      /**
       * Superclass sets up welcome message and login session in {@link #preElements}.
       * A "SELECT INBOX" session is then added to these elements.
       * @throws Exception
       */
      public void setUp() throws Exception
      {
          super.setUp();
          addTestFile( "SelectedStateSetup.test", preElements );
          addTestFile( "SelectedStateCleanup.test", postElements );
      }
  
      /**
       * Provides all tests which should be run in the selected state. Each test name
       * corresponds to a protocol session file.
       */
      public static Test suite() throws Exception
      {
          TestSuite suite = new TestSuite();
  
          // Valid in selected state
          suite.addTest( new TestSelectedCommandsInSelectedState( "Check" ) );
          suite.addTest( new TestSelectedCommandsInSelectedState( "Expunge" ) );
          suite.addTest( new TestSelectedCommandsInSelectedState( "Search" ) );
          suite.addTest( new TestSelectedCommandsInSelectedState( "FetchSingleMessage" ) );
  //        suite.addTest( new TestSelectedCommandsInSelectedState( "FetchMultipleMessages" ) );
          suite.addTest( new TestSelectedCommandsInSelectedState( "Store" ) );
          suite.addTest( new TestSelectedCommandsInSelectedState( "Copy" ) );
          suite.addTest( new TestSelectedCommandsInSelectedState( "Uid" ) );
  
          return suite;
      }
  }
  
  
  
  1.1                  jakarta-james/proposals/imap2/test/org/apache/james/imapserver/Uid.test
  
  Index: Uid.test
  ===================================================================
  #Regular fetch
  C: a FETCH 1:10 (UID)
  S: \* 1 FETCH \(UID 10\)
  S: \* 2 FETCH \(UID 20\)
  S: \* 3 FETCH \(UID 30\)
  S: \* 4 FETCH \(UID 40\)
  S: a OK FETCH completed
  
  #UID fetch
  C: a UID FETCH 1:20 (UID)
  S: \* 1 FETCH \(UID 10\)
  S: \* 2 FETCH \(UID 20\)
  S: a OK FETCH completed
  
  #UID store
  C: a UID STORE 15:35 +FLAGS (\Deleted)
  S: \* 2 FETCH \(FLAGS \(\\Deleted\)\)
  S: \* 3 FETCH \(FLAGS \(\\Deleted\)\)
  S: a OK STORE completed
  
  # Regular fetch
  C: a FETCH 1:100 (FLAGS UID)
  S: \* 1 FETCH \(FLAGS \(\) UID 10\)
  S: \* 2 FETCH \(FLAGS \(\\Deleted\) UID 20\)
  S: \* 3 FETCH \(FLAGS \(\\Deleted\) UID 30\)
  S: \* 4 FETCH \(FLAGS \(\) UID 40\)
  S: a OK FETCH completed
  
  # CREATE and do a UID COPY
  C: a CREATE copied
  S: a OK CREATE completed
  
  C: a UID COPY 20 copied
  S: a OK COPY completed
  
  C: a STATUS copied (MESSAGES)
  S: \* STATUS copied \(MESSAGES 1\)
  S: a OK STATUS completed
  
  
  # Do a UID SEARCH
  C: a UID SEARCH FLAGGED
  S: \* SEARCH 10 20 30 40
  S: a OK SEARCH completed
  
  # Cleanup
  C: a1 DELETE copied
  S: a1 OK DELETE completed
  
  
  
  
  
  
  1.1                  jakarta-james/proposals/imap2/test/org/apache/james/imapserver/ValidAuthenticated.test
  
  Index: ValidAuthenticated.test
  ===================================================================
  # Commands not valid in NONAUTHENTICATED state
  # Valid in authenticated or selected only
  C: a001 SELECT
  S: a001 NO SELECT failed. Command not valid in this state
  C: a001 EXAMINE
  S: a001 NO EXAMINE failed. Command not valid in this state
  C: a001 CREATE
  S: a001 NO CREATE failed. Command not valid in this state
  C: a001 DELETE
  S: a001 NO DELETE failed. Command not valid in this state
  C: a001 RENAME
  S: a001 NO RENAME failed. Command not valid in this state
  C: a001 SUBSCRIBE
  S: a001 NO SUBSCRIBE failed. Command not valid in this state
  C: a001 UNSUBSCRIBE
  S: a001 NO UNSUBSCRIBE failed. Command not valid in this state
  C: a001 LIST
  S: a001 NO LIST failed. Command not valid in this state
  C: a001 LSUB
  S: a001 NO LSUB failed. Command not valid in this state
  C: a001 STATUS
  S: a001 NO STATUS failed. Command not valid in this state
  C: a001 APPEND
  S: a001 NO APPEND failed. Command not valid in this state
  
  
  
  1.1                  jakarta-james/proposals/imap2/test/org/apache/james/imapserver/ValidNonAuthenticated.test
  
  Index: ValidNonAuthenticated.test
  ===================================================================
  # Valid in non-authenticated only
  C: a001 LOGIN daz daz
  S: a001 NO LOGIN failed. Command not valid in this state
  C: abcd AUTHENTICATE KERBEROS_V4
  S: abcd NO AUTHENTICATE failed. Command not valid in this state
  
  
  
  1.1                  jakarta-james/proposals/imap2/test/org/apache/james/imapserver/ValidSelected.test
  
  Index: ValidSelected.test
  ===================================================================
  # Valid in selected only
  C: a001 CHECK
  S: a001 NO CHECK failed. Command not valid in this state
  C: a001 CLOSE
  S: a001 NO CLOSE failed. Command not valid in this state
  C: a001 COPY
  S: a001 NO COPY failed. Command not valid in this state
  C: a001 EXPUNGE
  S: a001 NO EXPUNGE failed. Command not valid in this state
  C: a001 SEARCH
  S: a001 NO SEARCH failed. Command not valid in this state
  C: a001 FETCH
  S: a001 NO FETCH failed. Command not valid in this state
  C: a001 STORE
  S: a001 NO STORE failed. Command not valid in this state
  C: a001 UID
  S: a001 NO UID failed. Command not valid in this state
  
  
  
  1.4       +27 -23    jakarta-james/proposals/imap2/test/org/apache/james/test/AbstractProtocolTest.java
  
  Index: AbstractProtocolTest.java
  ===================================================================
  RCS file: /home/cvs/jakarta-james/proposals/imap2/test/org/apache/james/test/AbstractProtocolTest.java,v
  retrieving revision 1.3
  retrieving revision 1.4
  diff -u -r1.3 -r1.4
  --- AbstractProtocolTest.java	27 Nov 2002 01:59:22 -0000	1.3
  +++ AbstractProtocolTest.java	3 Dec 2002 14:00:14 -0000	1.4
  @@ -1,11 +1,11 @@
   package org.apache.james.test;
   
  -import org.apache.james.imapserver.ImapTest;
   import org.apache.james.imapserver.ImapHandler;
   import org.apache.james.imapserver.ImapHost;
   import org.apache.james.imapserver.ImapRequestHandler;
   import org.apache.james.imapserver.ImapSession;
   import org.apache.james.imapserver.ImapSessionImpl;
  +import org.apache.james.imapserver.ImapTest;
   import org.apache.james.imapserver.JamesImapHost;
   import org.apache.james.imapserver.store.MailboxException;
   import org.apache.james.services.User;
  @@ -16,9 +16,10 @@
   import junit.framework.TestCase;
   
   import java.io.BufferedReader;
  +import java.io.ByteArrayInputStream;
  +import java.io.ByteArrayOutputStream;
  +import java.io.InputStream;
   import java.io.InputStreamReader;
  -import java.io.PipedReader;
  -import java.io.PipedWriter;
   import java.io.PrintWriter;
   import java.net.Socket;
   import java.util.HashMap;
  @@ -53,15 +54,21 @@
       protected int timeout = TIMEOUT;
   
       /** A UsersRepository which all tests share. */
  -    private static UsersRepository users;
  +    private UsersRepository users;
       /** An ImapHost instance which all tests share. */
  -    private static ImapHost imapHost;
  +    private ImapHost imapHost;
   
       public AbstractProtocolTest( String s )
       {
           super( s );
       }
   
  +    protected void setUp() throws Exception
  +    {
  +        super.setUp();
  +        setUpEnvironment();
  +    }
  +
       /**
        * Uses a system property to determine whether to run tests locally, or against
        * a remote server.
  @@ -110,36 +117,34 @@
        * This does not require that James be running, and is useful for rapid development and
        * debugging.
        *
  -     * Instead of sending requests to a socket, requests are written to a PipedWriter,
  -     * which then provides a Reader which can be given to the ImapRequestHandler.
  -     * Likewise, server responses are writter to a PipedWriter, and the associate reader
  +     * Instead of sending requests to a socket, requests are written to a CharArrayWriter,
  +     * which then constructs a Reader which can be given to the ImapRequestHandler.
  +     * Likewise, server responses are written to a CHarArrayWriter, and the associate reader
        * is parsed to ensure that the responses match those expected.
        */
       private void runLocalProtocolSessions() throws Exception
       {
  -        // Read the client requests into a piped reader.
  -        PipedReader clientPipeReader = new PipedReader();
  -        PipedWriter clientPipeWriter = new PipedWriter( clientPipeReader );
  -        PrintWriter clientOut = new PrintWriter( clientPipeWriter );
  +        ByteArrayOutputStream clientRequestCollector = new ByteArrayOutputStream();
  +        PrintWriter clientOut = new PrintWriter( clientRequestCollector );
           preElements.writeClient( clientOut );
           testElements.writeClient( clientOut );
           postElements.writeClient( clientOut );
   
  -        clientPipeWriter.close();
  -
  -        BufferedReader clientIn = new BufferedReader( clientPipeReader );
  -
  -        PipedReader serverPipeReader = new PipedReader();
  -        PipedWriter serverPipeWriter = new PipedWriter( serverPipeReader );
  -        PrintWriter serverOut = new PrintWriter( serverPipeWriter );
  +        InputStream clientIn = new ByteArrayInputStream( clientRequestCollector.toByteArray() );
  +        clientRequestCollector.close();
   
  -        serverOut.println( "* OK IMAP4rev1 Server XXX ready" );
  +        ByteArrayOutputStream serverResponseCollector = new ByteArrayOutputStream();
  +        serverResponseCollector.write( "* OK IMAP4rev1 Server XXX ready".getBytes() );
  +        serverResponseCollector.write( '\r' );
  +        serverResponseCollector.write( '\n' );
   
           ImapSession session = getImapSession();
           ImapRequestHandler requestHandler = new ImapRequestHandler();
  -        while( requestHandler.handleRequest( clientIn, serverOut, session ) ) {};
  +        while( requestHandler.handleRequest( clientIn, serverResponseCollector, session ) ) {};
  +
  +        InputStream serverInstream = new ByteArrayInputStream( serverResponseCollector.toByteArray() );
  +        BufferedReader serverIn = new BufferedReader( new InputStreamReader( serverInstream ) );
   
  -        BufferedReader serverIn = new BufferedReader( serverPipeReader );
           try {
               preElements.testResponse(  serverIn );
               testElements.testResponse(  serverIn );
  @@ -156,7 +161,6 @@
        */
       private ImapSession getImapSession() throws MailboxException
       {
  -        setUpEnvironment();
           ImapSession session = new ImapSessionImpl( imapHost, users, new ImapHandler(), null, null );
           return session;
       }
  
  
  
  1.4       +2 -2      jakarta-james/proposals/imap2/test/org/apache/james/test/FileProtocolSessionBuilder.java
  
  Index: FileProtocolSessionBuilder.java
  ===================================================================
  RCS file: /home/cvs/jakarta-james/proposals/imap2/test/org/apache/james/test/FileProtocolSessionBuilder.java,v
  retrieving revision 1.3
  retrieving revision 1.4
  diff -u -r1.3 -r1.4
  --- FileProtocolSessionBuilder.java	27 Nov 2002 12:26:46 -0000	1.3
  +++ FileProtocolSessionBuilder.java	3 Dec 2002 14:00:14 -0000	1.4
  @@ -85,7 +85,7 @@
               }
               else if ( next.startsWith( SERVER_TAG ) ) {
                   String serverMsg = "";
  -                if ( serverMsg.length() > 3 ) {
  +                if ( next.length() > 3 ) {
                       serverMsg = next.substring( 3 );
                   }
                   session.SL( serverMsg, location );
  
  
  
  1.4       +5 -2      jakarta-james/proposals/imap2/test/org/apache/james/test/ProtocolSession.java
  
  Index: ProtocolSession.java
  ===================================================================
  RCS file: /home/cvs/jakarta-james/proposals/imap2/test/org/apache/james/test/ProtocolSession.java,v
  retrieving revision 1.3
  retrieving revision 1.4
  diff -u -r1.3 -r1.4
  --- ProtocolSession.java	27 Nov 2002 12:26:46 -0000	1.3
  +++ ProtocolSession.java	3 Dec 2002 14:00:14 -0000	1.4
  @@ -19,6 +19,8 @@
   /**
    * A protocol session which can be run against a reader and writer, which checks
    * the server response against the expected values.
  + * TODO make ProtocolSession itself be a permissible ProtocolElement,
  + * so that we can nest and reuse sessions.
    * @author  Darrell DeBoer <darrell@apache.org>
    *
    * @version $Revision$
  @@ -134,9 +136,10 @@
            */
           public void testProtocol( PrintWriter out, BufferedReader in )
           {
  -            out.print( message );
  +            out.write( message );
               out.write( '\r' );
               out.write( '\n' );
  +            out.flush();
           }
       }
   
  
  
  

--
To unsubscribe, e-mail:   <mailto:james-dev-unsubscribe@jakarta.apache.org>
For additional commands, e-mail: <mailto:james-dev-help@jakarta.apache.org>


Mime
View raw message