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 JamesTask.java SimpleFileProtocolTest.java
Date Fri, 22 Nov 2002 02:09:55 GMT
darrell     2002/11/21 18:09:54

  Added:       proposals/imap2 build-test.xml build.xml
               proposals/imap2/conf james-assembly.xml james-config.xml
                        james-server.xml
               proposals/imap2/java/org/apache/james James.java James.xinfo
               proposals/imap2/java/org/apache/james/imapserver
                        AuthorizationException.java ImapConstants.java
                        ImapHandler.java ImapHandlerConfigurationData.java
                        ImapHost.java ImapRequestParser.java
                        ImapResponse.java ImapServer.java ImapServer.xinfo
                        ImapSession.java ImapSessionState.java
                        JamesImapHost.java JamesImapHost.xinfo
                        ProtocolException.java
               proposals/imap2/java/org/apache/james/imapserver/commands
                        AuthenticateCommand.java
                        AuthenticatedStateCommand.java
                        CapabilityCommand.java CommandTemplate.java
                        CreateCommand.java DeleteCommand.java
                        ExamineCommand.java ImapCommand.java
                        ImapCommandFactory.java ListCommand.java
                        LoginCommand.java LogoutCommand.java
                        LsubCommand.java NonAuthenticatedStateCommand.java
                        NoopCommand.java RenameCommand.java
                        SelectCommand.java SelectedStateCommand.java
               proposals/imap2/java/org/apache/james/imapserver/store
                        ImapMailbox.java ImapStore.java InMemoryStore.java
                        MailboxException.java MessageFlags.java
               proposals/imap2/java/org/apache/james/util Assert.java
               proposals/imap2/test/org/apache/james/imapserver
                        Authenticate.test AuthenticateAuthenticated.test
                        Capability.test Create.test Delete.test
                        ExamineEmpty.test ExamineInbox.test
                        FetchMultipleMessages.test FetchSingleMessage.test
                        IMAPTest.java ImapHostTest.java
                        ImapRequestParserTest.java ImapStoreTest.java
                        InitialMail.java InitialUsers.java
                        ListMailboxes.test ListNamespace.test Login.test
                        LoginAuthenticated.test Logout.test Lsub.test
                        SelectEmpty.test SelectInbox.test Subscribe.test
                        Subscribe2.test Test.test TestAuthenticated.java
                        TestNonAuthenticated.java TestSelected.java
                        Welcome.test
               proposals/imap2/test/org/apache/james/remotemanager
                        AddUsers.test DeleteUsers.test
                        RemoteManagerLogin.test RemoteManagerLogout.test
                        TestRemoteManager.java UserManagementTest.java
               proposals/imap2/test/org/apache/james/smtpserver Send.test
                        TestSMTP.java
               proposals/imap2/test/org/apache/james/test
                        AbstractProtocolTest.java JamesTask.java
                        SimpleFileProtocolTest.java
  Log:
  A new proposal for the implementation of IMAP4rev1
  for James. Bit's of this have been taken from Charles'
  initial proposal, and my later refactorings. But a large
  chunk has been rebuilt from scratch.
  
  Details in a pending email...
  
  Revision  Changes    Path
  1.1                  jakarta-james/proposals/imap2/build-test.xml
  
  Index: build-test.xml
  ===================================================================
  <?xml version="1.0"?>
  
  <!-- ==========================================================================
  
  James Functional Test build file.
  This file provides targets for building the functional test harness,
  and running a number of functional tests against a running instance of James.
  
  Authors:
      Darrell DeBoer  <darrell@apache.org>
  
  Legal:
    Copyright (c) 1999-2001 The Apache Software Foundation. All Rights Reserved.
  
  
  ==============================================================================
   -->
  
  <project default="main" name="IMAP tests for v2" basedir="../..">
  
      <!--
        Give user a chance to override without editing this file
        (and without typing -D each time he compiles it)
      -->
      <property file=".ant.properties"/>
      <property file="${user.home}/.ant.properties"/>
  
      <!-- There should be no need to override default compiler but need to change
        javac task to run without this
      <property name="build.compiler" value="classic"/>-->
  
      <!--
        these are here only for those who use jikes compiler. For other
        developers this part makes no difference.
      -->
      <property name="build.compiler.emacs" value="on"/>
      <property name="build.compiler.pedantic" value="true"/>
      <property name="build.compiler.depend" value="true"/>
      <property name="build.compiler.fulldepend" value="true"/>
  
      <property name="debug" value="on"/>
      <property name="optimize" value="on"/>
      <property name="deprecation" value="off"/>
  
  
      <!-- Set the properties for source directories -->
      <property name="proposal.base" value="proposals"/>
      <property name="proposal.dir" value="${proposal.base}/imap2"/>
      <property name="java.proposal.dir" value="${proposal.dir}/java"/>
      <property name="conf.proposal.dir" value="${proposal.dir}/conf"/>
      <property name="test.dir" value="${proposal.dir}/test"/>
  
      <property name="lib.dir" value="lib"/>
      <property name="phoenix.dir" value="phoenix-bin"/>
  
      <!-- Set the properties for build directory -->
      <property name="build.dir" value="${proposal.dir}/build"/>
      <property name="build.classes" value="${build.dir}/classes"/>
      <property name="build.test.classes" value="${build.dir}/test/classes"/>
  
      <path id="test.class.path">
          <pathelement location="${build.classes}"/>
          <fileset dir="${lib.dir}">
              <include name="junit-3.7.jar"/>
              <include name="mail_1_2.jar"/>
              <include name="activation.jar"/>
              <include name="jakarta-oro-2.0.1.jar"/>
          </fileset>
          <fileset dir="${phoenix.dir}/lib">
              <include name="avalon-framework-4.1.3.jar"/>
          </fileset>
          <fileset dir="${phoenix.dir}/bin/lib">
              <include name="phoenix-engine.jar"/>
          </fileset>
      </path>
  
      <!--
           ===================================================================
                                      Main target
           ===================================================================
      -->
      <target name="main" depends="compile"/>
  
      <target name="clean">
          <delete dir="${build.test.classes}"/>
      </target>
  
      <target name="clean-james">
          <ant antfile="proposals/imap/build.xml" target="rebuild-dist"/>
      </target>
  
      <!-- Compiles the Functional tests -->
      <target name="compile">
          <echo message="${java.home}"/>
          <mkdir dir="${build.test.classes}"/>
  
          <javac destdir="${build.test.classes}"
              debug="${debug}"
              optimize="${optimize}"
              deprecation="${deprecation}"
              classpathref="test.class.path">
  
              <src path="${test.dir}"/>
              <!--        <exclude name="**/JamesTask.java"/>-->
          </javac>
  
          <!-- Copy extra files (resources) into the classes directory -->
          <copy todir="${build.test.classes}">
              <fileset dir="${test.dir}">
                  <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">
          <junit fork="yes">
              <classpath>
                  <pathelement location="${build.test.classes}"/>
                  <path refid="test.class.path"/>
              </classpath>
              <formatter type="plain" usefile="false"/>
              <test name="org.apache.james.imapserver.ImapHostTest"/>
              <test name="org.apache.james.imapserver.ImapRequestParserTest"/>
              <test name="org.apache.james.imapserver.ImapStoreTest"/>
          </junit>
      </target>
  
      <!-- Executes tests against a running instance of James -->
      <target name="protocol-tests" depends="testimap-init, testimap-nonauthenticated, testimap-authenticated"/>
  
      <!-- Initialises the IMAP server for running tests.
           Namely, creates the "imapuser" user, and sends 4 test messages to that user
           via SMTP
      -->
      <target name="testimap-init" depends="compile">
          <junit fork="yes">
              <classpath>
                  <pathelement location="${build.test.classes}"/>
                  <path refid="test.class.path"/>
              </classpath>
              <formatter type="plain" usefile="false"/>
              <test name="org.apache.james.imapserver.InitialUsers"/>
              <test name="org.apache.james.imapserver.InitialMail"/>
          </junit>
      </target>
  
      <!-- Tests IMAP commands valid in the NonAuthenticated state -->
      <target name="testimap-nonauthenticated" depends="compile">
          <junit fork="yes">
              <classpath>
                  <pathelement location="${build.test.classes}"/>
                  <path refid="test.class.path"/>
              </classpath>
              <formatter type="plain" usefile="false"/>
              <test name="org.apache.james.imapserver.TestNonAuthenticated"/>
          </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>
              <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>
              <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>
              <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.1                  jakarta-james/proposals/imap2/build.xml
  
  Index: build.xml
  ===================================================================
  <?xml version="1.0"?>
  <!-- ==========================================================================
  
   James build file $Revision: 1.1 $  Committed on $Date: 2002/11/22 02:09:50 $ by: $Author: darrell $
  
  Authors:
   Federico Barbieri <scoobie@systemy.it>
   Serge
   Charles Benett    <charlesb@apache.org>
   Peter Donald      <donaldp@apache.org>
   Harmeet Bedi      <harmeet@kodemuse.com>
   Danny Angus       <danny@apache.org>
   Peter M. Goldstein <farsight@alum.mit.edu>
   Darrell DeBoer <darrell@apache.org>
  
  Legal:
    Copyright (c) 1999-2002 The Apache Software Foundation. All Rights Reserved.
  
  
  ==============================================================================
   -->
  <project default="main" name="IMAP proposal v2" basedir="./../..">
      <!--
      Give user a chance to override without editing this file
      (and without typing -D each time he compiles it)
    -->
      <property file=".ant.properties"/>
      <property file="${user.home}/.ant.properties"/>
      <property name="name" value="james"/>
      <property name="Name" value="James"/>
      <property name="version" value="2.1a1-cvs"/>
      <property name="year" value="1999-2002"/>
      <!-- There should be no need to override default compiler but need to change
      javac task to run without this -->
      <property name="build.compiler" value="modern"/>
      <!--
      these are here only for those who use jikes compiler. For other
      developers this part makes no difference.
    -->
      <property name="build.compiler.emacs" value="on"/>
      <property name="build.compiler.pedantic" value="true"/>
      <property name="build.compiler.depend" value="true"/>
      <property name="build.compiler.fulldepend" value="true"/>
      <property name="debug" value="on"/>
      <property name="optimize" value="on"/>
      <property name="deprecation" value="off"/>
  
      <!--
         ===================================================================
         Set the properties for proposal directories
         ===================================================================
    -->
  
      <property name="proposal.name" value="imap2"/>
      <property name="proposal.base" value="proposals"/>
      <property name="proposal.dir" value="${proposal.base}/${proposal.name}"/>
      <property name="java.proposal.dir" value="${proposal.dir}/java"/>
      <property name="conf.proposal.dir" value="${proposal.dir}/conf"/>
  
  
      <!--
         ===================================================================
         Set the properties for intermediate directory
         ===================================================================
    -->
      <property name="build.dir" value="${proposal.dir}/build"/>
      <property name="build.lib" value="${build.dir}/lib"/>
      <property name="build.src" value="${build.dir}/src"/>
      <property name="build.classes" value="${build.dir}/classes"/>
      <property name="build.javadocs" value="${build.dir}/javadocs"/>
      <property name="build.docs" value="${build.dir}/docs"/>
      <property name="build.mailetdocs" value="${build.dir}/mailetdocs"/>
      <property name="build.conf" value="${build.dir}/conf"/>
  
      <!--
         ===================================================================
         Set the properties for source directories and parameters
         ===================================================================
    -->
      <property name="src.dir" value="src"/>
      <property name="java.dir" value="${src.dir}/java"/>
      <property name="conf.dir" value="${src.dir}/conf"/>
      <property name="xdocs.dir" value="${src.dir}/xdocs"/>
      <property name="docs.src" value="${xdocs.dir}"/>
  
      <property name="constants.file" value="org/apache/james/Constants.java"/>
      <property name="poolconn.file" value="org/apache/james/util/mordred/PoolConnEntry.java"/>
  
  
      <!--
         ===================================================================
         Set the properties for the distribution directories
         ===================================================================
    -->
      <property name="dist.base" value="${proposal.dir}/dist"/>
      <property name="dist.name" value="${name}-${version}"/>
      <property name="dist.dir" value="${dist.base}/${dist.name}"/>
  
      <!--
         ===================================================================
         Third party binary directories
         ===================================================================
    -->
      <property name="lib.dir" value="lib"/>
      <property name="phoenix.dir" value="phoenix-bin"/>
      <property name="phoenix.lib.dir" value="${phoenix.dir}/lib"/>
  
      <!--
         ===================================================================
         Set the properties for the website directories
         ===================================================================
    -->
      <property name="www.dir" value="${proposal.dir}/www"/>
  
      <!--
         ===================================================================
         Set the classpath
         ===================================================================
    -->
      <property name="xerces.jar" value="${lib.dir}/xerces-1.4.3.jar"/>
      <!--
      <property name="activation.jar" value="${lib.dir}/activation.jar"/>
    -->
      <path id="project.class.path">
          <pathelement location="${xerces.jar}"/>
          <pathelement path="${java.class.path}"/>
          <fileset dir="${lib.dir}">
              <include name="*.jar"/>
              <exclude name="xerces.jar"/>
          </fileset>
          <fileset dir="${phoenix.lib.dir}">
              <include name="*.jar"/>
          </fileset>
          <pathelement path="${build.classes}"/>
      </path>
      <taskdef name="sar" classname="org.apache.avalon.phoenix.tools.tasks.Sar">
          <classpath refid="project.class.path"/>
      </taskdef>
      <!--
         ===================================================================
                                    Main target
         ===================================================================
    -->
      <target name="main" depends="dist-lite"/>
      <!--
         ===================================================================
                                    Prepare
         ===================================================================
    -->
      <target name="prepare">
      <echo message="preparing code"/>
  
          <tstamp/>
          <property name="dist.bin" value="${dist.dir}/bin"/>
          <property name="dist.apps" value="${dist.dir}/apps"/>
          <property name="dist.lib" value="${dist.dir}/lib"/>
          <property name="dist.docs" value="${dist.dir}/docs"/>
          <property name="dist.javadocs" value="${dist.dir}/docs/api"/>
          <available property="jdbc3.present" classname="java.sql.Savepoint"/>
          <mkdir dir="${dist.dir}"/>
          <copy todir="${dist.dir}">
              <fileset dir="${phoenix.dir}">
                  <exclude name="CVS/**"/>
              </fileset>
              <fileset dir=".">
                  <include name="release-notes.txt"/>
                  <include name="LICENSE.txt"/>
                  <include name="README"/>
              </fileset>
          </copy>
          <fixcrlf srcdir="${java.proposal.dir}" includes="**/*.java" eol="lf" tab="remove" tablength="4" />
          <fixcrlf srcdir="${java.proposal.dir}" includes="**/*.minfo" eol="lf" tab="remove" tablength="4" />
          <fixcrlf srcdir="${java.proposal.dir}" includes="**/*.xinfo" eol="lf" tab="remove" tablength="4" />
          <fixcrlf srcdir="${java.proposal.dir}" includes="**/*.html" eol="lf" tab="remove" tablength="4" />
          <fixcrlf srcdir="${conf.proposal.dir}" includes="**/*.xml" eol="lf" tab="remove" tablength="4" />
          <fixcrlf srcdir="${dist.dir}/bin" includes="run.sh" eol="lf"/>
          <fixcrlf srcdir="${dist.dir}/bin" includes="run.bat" eol="crlf"/>
          <fixcrlf srcdir="${phoenix.dir}/bin" includes="phoenix.sh" eol="lf"/>
          <chmod perm="+x">
              <fileset dir="${dist.dir}/bin">
                  <include name="run.sh"/>
                  <include name="phoenix.sh"/>
              </fileset>
          </chmod>
          <fixcrlf srcdir="." includes="build.sh" eol="lf"/>
          <fixcrlf srcdir="." includes="build.bat" eol="crlf"/>
          <chmod perm="+x">
              <fileset dir=".">
                  <include name="build.sh"/>
              </fileset>
          </chmod>
          <available classname="org.apache.velocity.anakia.AnakiaTask" property="AnakiaTask.present"/>
          <mkdir dir="${build.src}"/>
          <copy todir="${build.src}">
              <fileset dir="${java.dir}">
                  <include name="${constants.file}"/>
                  <include name="${poolconn.file}"/>
              </fileset>
          </copy>
          <replace file="${build.src}/${constants.file}" token="@@VERSION@@" value="${version}"/>
          <replace file="${build.src}/${constants.file}" token="@@NAME@@" value="${Name}"/>
          <replace file="${build.src}/${constants.file}" token="@@DATE@@" value="${TODAY}"/>
      </target>
      <!--
         ===================================================================
                                     jdbc3 
         ===================================================================
    -->
      <target name="prepare-jdbc3" depends="prepare" if="jdbc3.present">
      <echo message="preparing for JDBC3"/>
          <replace file="${build.src}/${poolconn.file}" token="/* JDBC_3_ANT_KEY" value=""/>
          <replace file="${build.src}/${poolconn.file}" token="JDBC_3_ANT_KEY */" value=""/>
      </target>
  
  
      <!--
         ===================================================================
                                    compile
         ===================================================================
    -->
      <target name="compile" depends="prepare,prepare-jdbc3">
          <!-- First compile the main James tree, leaving out any files that
               overlap with the IMAP proposal. -->
          <echo message="compiling James"/>
          <available property="jndi.present" classname="javax.naming.InitialContext"/>
          <mkdir dir="${build.classes}"/>
          <javac destdir="${build.classes}" debug="${debug}" optimize="${optimize}" deprecation="${deprecation}">
              <classpath refid="project.class.path"/>
              <src path="${build.src}"/>
              <src path="${java.dir}"/>
              <exclude name="${constants.file}"/>
              <exclude name="${poolconn.file}"/>
              <exclude name="org/apache/james/userrepository/UsersLDAPRepository.java" unless="jndi.present"/>
  
              <!-- These file(s) are in both the proposal and the main trunk. -->
              <exclude name="org/apache/james/James.java"/>
          </javac>
  
            <!-- Copy .xinfo and .properties files from the core source, once again,
                 exclude overlapping files. -->
            <copy todir="${build.classes}">
              <fileset dir="${java.dir}">
                <include name="**/*.properties"/>
                <include name="**/*.xinfo"/>
                <include name="**/*.mxinfo"/>
              </fileset>
            </copy>
  
           <!-- Now build the proposal source over those classes -->
            <javac destdir="${build.classes}"
                   debug="${debug}"
                   optimize="${optimize}"
                   deprecation="${deprecation}">
              <classpath refid="project.class.path" />
  
              <!-- The proposal source -->
              <src path="${java.proposal.dir}"/>
            </javac>
  
            <!-- Copy .xinfo and .properties files from the proposal source -->
            <copy todir="${build.classes}" overwrite="on">
              <fileset dir="${java.proposal.dir}">
                <include name="**/*.properties"/>
                <include name="**/*.xinfo"/>
              </fileset>
            </copy>
      </target>
      <!--
         ===================================================================
                                    Clean classes, then recompile
         ===================================================================
    -->
      <target name="recompile" depends="clean-classes, compile"/>
      <!--
         ===================================================================
                                    Javadocs, and mailet javadocs
         ===================================================================
    -->
      <target name="javadocs">
      <echo message="Making James javadocs"/>
  
          <delete dir="${build.javadocs}"/>
          <mkdir dir="${build.javadocs}"/>
          <javadoc packagenames="org.apache.*" sourcepath="${build.src}" destdir="${build.javadocs}">
              <classpath refid="project.class.path"/>
              <doclet name="com.sun.tools.doclets.standard.Standard">
                  <param name="-author"/>
                  <param name="-version"/>
                  <param name="-use"/>
                  <param name="-breakiterator"/>
                  <param name="-link" value="http://java.sun.com/j2se/1.4/docs/api"/>
                  <param name="-link" value="http://java.sun.com/j2ee/sdk_1.3/techdocs/api"/>
                  <param name="-link" value="http://jakarta.apache.org/avalon/api"/>
                  <param name="-link" value="http://jakarta.apache.org/avalon/phoenix/api"/>
                  <param name="-link" value="http://jakarta.apache.org/avalon/cornerstone/api"/>
                  <param name="-link" value="http://jakarta.apache.org/avalon/logkit/api"/>
                  <param name="-doctitle" value="${Name} ${version}"/>
                  <param name="-windowtitle" value="${Name} API"/>
                  <param name="-bottom" value="&quot;Copyright &#169; ${year} Apache Jakarta Project. All Rights Reserved.&quot;"/>
              </doclet>
          </javadoc>
          <echo message="Making Mailet javadocs"/>
          <javadoc packagenames="org.apache.mailet.*" sourcepath="${build.src}" destdir="${build.dir}/mailet">
              <classpath refid="project.class.path"/>
              <doclet name="com.sun.tools.doclets.standard.Standard">
                  <param name="-author"/>
                  <param name="-version"/>
                  <param name="-breakiterator"/>
                  <param name="-use"/>
                  <param name="-link" value="http://java.sun.com/j2se/1.4/docs/api"/>
                  <param name="-link" value="http://java.sun.com/j2ee/sdk_1.3/techdocs/api"/>  
                  <param name="-link" value="http://jakarta.apache.org/avalon/api"/>
                  <param name="-link" value="http://jakarta.apache.org/avalon/phoenix/api"/>
                  <param name="-link" value="http://jakarta.apache.org/avalon/cornerstone/api"/>
                  <param name="-link" value="http://jakarta.apache.org/avalon/logkit/api"/>
                  <param name="-doctitle" value="Apache Jakarta Mailet API"/>
                  <param name="-windowtitle" value="Apache Jakarta Mailet API"/>
                  <param name="-bottom" value="&quot;Copyright &#169; ${year} Apache Jakarta Project. All Rights Reserved.&quot;"/>
              </doclet>
          </javadoc>
      </target>
      <!--
         ===================================================================
                                    build xdocs
         ===================================================================
    -->
      <target name="xdocs" depends="prepare,prepare-jdbc3">
          <style style="${docs.src}/stylesheets/site.xsl"
                  basedir="${docs.src}/"
                  destdir="${build.docs}/" 
                  includes="**/*.xml" >
          </style>
          <copy todir="${build.docs}" filtering="no">
              <fileset dir="${docs.src}">
                          <include name="**/*.css"/>
              </fileset>
          </copy>
          <copy todir="${build.docs}/images" filtering="no">
              <fileset dir="${docs.src}/images">
                  <include name="**/*.gif"/>
                  <include name="**/*.jpeg"/>
                  <include name="**/*.jpg"/>
              </fileset>
          </copy>
          <mkdir dir="${dist.dir}/docs"/>
          <copy todir="${dist.dir}/docs" filtering="no">
              <fileset dir="${build.docs}"/>
          </copy>
      </target>
      <!--
         ===================================================================
                                  build   website 
         ===================================================================
    -->
      <target name="website" depends="xdocs,javadocs">
      <echo message="preparing website in ${www.dir}"/>
          <delete>
              <fileset dir="${www.dir}">
                  <exclude name="CVS/**"/>
                  <exclude name="rfclist/**"/>
                  <exclude name="javadocs/**"/>
                  <exclude name="mailet/**"/>
              </fileset>
          </delete>
          <mkdir dir="${www.dir}"/>
          <copy todir="${www.dir}" filtering="no">
              <fileset dir="${build.docs}"/>
          </copy>
          <copy todir="${www.dir}" filtering="no">
              <fileset dir="${build.dir}">
                  <include name="javadocs/**"/>
                  <include name="mailet/**"/>
              </fileset>
          </copy>
      </target>
      <!--
         ===================================================================
                  Build everything
         ===================================================================
    -->
      <target name="everything" depends="clean,website,dist-bin,dist-src,dist-mailet-SDK"/>
      <!--
         ===================================================================
                                    Build all distributions
         ===================================================================
    -->
      <target name="dist" depends="dist-bin,dist-src,dist-mailet-SDK"/>
      <!--
         ===================================================================
                                    Make james.sar
         ===================================================================
    -->
      <target name="sar" depends="prepare,compile">
      <echo message="Making James.sar"/>
  
          <!-- builds james-sar-->
          <mkdir dir="${build.lib}"/>
          <!-- Make james.jar-->
          <jar jarfile="${build.lib}/${name}.jar" basedir="${build.classes}" manifest="${src.dir}/Manifest.mf">
              <include name="org/apache/james/**"/>
              <include name="org/apache/mailet/**"/>
          </jar>
          <!-- Make sar file-->
          <sar sarfile="${build.lib}/${name}.sar" config="${conf.proposal.dir}/james-config.xml" environment="${conf.proposal.dir}/james-server.xml" assembly="${conf.proposal.dir}/james-assembly.xml">
              <lib dir="${build.lib}/">
                  <include name="*.jar"/>
              </lib>
              <lib dir="lib">
                  <include name="dnsjava-1.2.3.jar"/>
                  <include name="jakarta-oro-2.0.1.jar"/>
                  <include name="mm.mysql-2.0.14.jar"/>
                  <include name="mm.mysql.LICENCE"/>
                  <include name="avalon-framework-4.1.3.jar"/>
                  <include name="cornerstone.jar"/>
                  <include name="excalibur-datasource-1.0.jar"/>
                  <include name="activation.jar"/>
                  <include name="mail_1_2.jar"/>
                  <include name="commons-net-1.0.0-dev.jar"/>
              </lib>
              <zipfileset dir="${conf.dir}" fullpath="conf/sqlResources.xml">
                  <include name="sqlResources.xml"/>
              </zipfileset>
          </sar>
      </target>
      <!--
         ===================================================================
                                    Make dist directory with unpacked dist ready to run
         ===================================================================
    -->
      <target name="dist-lite" depends="sar">
      <echo message="Installing James to ${dist.dir}"/>
  
          <!--installs sar into ./dist/ ready to test-->
          <copy file="${build.lib}/${name}.sar" todir="${dist.dir}/apps"/>
          
          <!-- Make mailet.jar-->
          <jar jarfile="${build.lib}/mailet.jar" basedir="${build.classes}" manifest="${src.dir}/Manifest.mf" includes="org/apache/mailet/**"/>
          <!-- copy file="${build.lib}/mailet.jar" todir="${dist.dir}/lib"/ -->
      </target>
      <!--
         ===================================================================
                                    binary distributions
         ===================================================================
    -->
      <target name="dist-bin" depends="dist-lite,xdocs,javadocs">
      <echo message ="building binary distributions"/>
          <mkdir dir="${dist.dir}/downloads/bin"/>
          <!-- binary with phoenix -->
          <zip zipfile="${dist.dir}/downloads/bin/${name}-binary-${version}.zip">
              <fileset dir="dist">
                  <include name="${name}-${version}/**"/>
                  <exclude name="${name}-${version}/downloads/**"/>
                  <include name="release-notes.txt"/>
                  <include name="LICENSE.txt"/>
              </fileset>
          </zip>
          <tar longfile="gnu" tarfile="${dist.dir}/downloads/bin/${name}-binary-${version}.tar">
              <tarfileset dir="dist" username="${name}" group="${name}">
                  <include name="${name}-${version}/**"/>
                  <exclude name="${name}-${version}/downloads/**"/>
                  <include name="release-notes.txt"/>
                  <include name="LICENSE.txt"/>
              </tarfileset>
          </tar>
          <gzip zipfile="${dist.dir}/downloads/bin/${name}-binary-${version}.tar.gz" src="${dist.dir}/downloads/bin/${name}-binary-${version}.tar"/>
          <delete file="${dist.dir}/downloads/bin/${name}-binary-${version}.tar"/>
          <!-- Sar only-->
          <copy file="${dist.dir}/apps/${name}.sar" todir="${dist.dir}/downloads/bin"/>
      </target>
      <!--
         ===================================================================
                                    source distributions
         ===================================================================
    -->
      <target name="dist-src" depends="dist-lite,xdocs,javadocs">
          <echo message ="building source distributions"/>
  
          <mkdir dir="${dist.dir}/downloads/src"/>
          <zip zipfile="${dist.dir}/downloads/src/${dist.name}-src.zip">
              <fileset dir=".">
                  <include name="src/**"/>
                  <include name="lib/**"/>
                  <include name="proposals/**"/>
                  <include name="www/**"/>
                  <include name="tools/**"/>
                  <include name="*.xml"/>
                  <include name="*.txt"/>
                  <include name="README"/>
                  <include name="KEYS"/>
                  <include name="build.*"/>
              </fileset>
          </zip>
          <tar longfile="gnu" tarfile="${dist.dir}/downloads/src/${dist.name}-src.tar">
              <tarfileset dir="." username="${name}" group="${name}">
                  <include name="src/**"/>
                  <include name="lib/**"/>
                  <include name="proposals/**"/>
                  <include name="www/**"/>
                  <include name="tools/**"/>
                  <include name="*.xml"/>
                  <include name="*.txt"/>
                  <include name="README"/>
                  <include name="KEYS"/>
                  <include name="build.*"/>
              </tarfileset>
          </tar>
          <gzip zipfile="${dist.dir}/downloads/src/${dist.name}-src.tar.gz" src="${dist.dir}/downloads/src/${dist.name}-src.tar"/>
          <delete file="${dist.dir}/downloads/src/${dist.name}-src.tar"/>
          <!--Source  With Phoenix -->
          <zip zipfile="${dist.dir}/downloads/src/${name}-with-phoenix-${version}-src.zip">
              <fileset dir=".">
                  <include name="${dist.dir}/**"/>
                  <exclude name="${dist.dir}/downloads/**"/>
                  <exclude name="${dist.dir}/apps/*.sar"/>
                  <exclude name=""/>
                  <include name="*.txt"/>
                  <include name="*.xml"/>
                  <include name="KEYS"/>
                  <include name="README"/>
                  <include name="build.*"/>
                  <include name="lib/**"/>
                  <include name="proposals/**"/>
                  <include name="src/**"/>
                  <include name="tools/**"/>
                  <include name="www/**"/>
              </fileset>
          </zip>
          <tar longfile="gnu" tarfile="${dist.dir}/downloads/src/${name}-with-phoenix-${version}-src.tar">
              <tarfileset dir="." username="${name}" group="${name}">
                  <include name="${dist.dir}/**"/>
                  <exclude name="${dist.dir}/downloads/**"/>
                  <exclude name="${dist.dir}/apps/*.sar"/>
                  <include name="*.txt"/>
                  <include name="*.xml"/>
                  <include name="KEYS"/>
                  <include name="README"/>
                  <include name="build.*"/>
                  <include name="lib/**"/>
                  <include name="proposals/**"/>
                  <include name="src/**"/>
                  <include name="tools/**"/>
                  <include name="www/**"/>
              </tarfileset>
          </tar>
          <gzip zipfile="${dist.dir}/downloads/src/${name}-with-phoenix-${version}-src.tar.gz" src="${dist.dir}/downloads/src/${name}-with-phoenix-${version}-src.tar"/>
          <delete file="${dist.dir}/downloads/src/${name}-with-phoenix-${version}-src.tar"/>
      </target>
      <!--
         ===================================================================
                                    Mailet SDK distribution
         ===================================================================
    -->
      <target name="dist-mailet-SDK" depends="dist-lite,xdocs,javadocs">
          <echo message ="building mailet SDK distributions"/>
  
          <mkdir dir="${dist.dir}/MailetSDK/src"/>
          <mkdir dir="${dist.dir}/MailetSDK/javadocs"/>
          <copy todir="${dist.dir}/MailetSDK/src">
              <fileset dir="src">
                  <include name="**/mailet/**"/>
              </fileset>
          </copy>
          <copy todir="${dist.dir}/MailetSDK/javadocs">
              <fileset dir="${build.dir}/mailet">
                  <include name="**/*"/>
              </fileset>
          </copy>
          <zip zipfile="${dist.dir}/downloads/bin/${name}-MailetSDK-${version}.zip">
              <fileset dir="dist">
                  <include name="${name}-${version}/**"/>
                  <exclude name="${name}-${version}/downloads/**"/>
              </fileset>
          </zip>
          <tar longfile="gnu" tarfile="${dist.dir}/downloads/bin/${name}-MailetSDK-${version}.tar">
              <tarfileset dir="dist" username="${name}" group="${name}">
                  <include name="${name}-${version}/**"/>
                  <exclude name="${name}-${version}/downloads/**"/>
              </tarfileset>
          </tar>
          <gzip zipfile="${dist.dir}/downloads/bin/${name}-MailetSDK-${version}.tar.gz" src="${dist.dir}/downloads/bin/${name}-MailetSDK-${version}.tar"/>
          <delete file="${dist.dir}/downloads/bin/${name}-MailetSDK-${version}.tar"/>
          <delete dir="${dist.dir}/MailetSDK"/>
      </target>
      <!--
         ===================================================================
                                    delete dist/ and build/
         ===================================================================
    -->
      <target name="clean">
          <echo message ="deleting working directories ready for a clean build"/>
  
          <delete dir="${dist.dir}"/>
          <delete dir="${build.dir}"/>
      </target>
  
      <!--
         ===================================================================
                                    delete build/classes
         ===================================================================
    -->
      <target name="clean-classes">
          <echo message="deleting all java class files."/>
          <delete dir="${build.classes}"/>
      </target>
  
      <!-- =================================================================== -->
      <!-- Help on usage                                                       -->
      <!-- =================================================================== -->
      <target name="usage">
          <echo message=""/>
          <echo message=""/>
          <echo message="JAMES Build file"/>
          <echo message="-------------------------------------------------------------"/>
          <echo message=""/>
          <echo message=" available targets are:"/>
          <echo message=""/>
          <echo message="   dist-lite --> generates unpacked distribution (default)"/>
          <echo message="   website   --> makes all the docs and javadocs"/>
          <echo message="   compile   --> compiles the source code"/>
          <echo message="   dist      --> generates all the JAMES distributions, packed"/>
          <echo message="   clean     --> cleans up the directory"/>
          <echo message="   xdocs     --> creates the documentaion for James"/>
          <echo message="   javadocs  --> creates the Javadoc"/>
          <echo message=""/>
          <echo message=" See the comments inside the build.xml file for more details."/>
          <echo message="-------------------------------------------------------------"/>
          <echo message=""/>
          <echo message=""/>
      </target>
      <target name="help" depends="usage"/>
  </project>
  
  
  
  1.1                  jakarta-james/proposals/imap2/conf/james-assembly.xml
  
  Index: james-assembly.xml
  ===================================================================
  <?xml version="1.0"?>
  
  <assembly>
  
    <!-- The list of blocks being run in this Phoenix server. -->
    <!-- -->
    <!-- Each block element has a name attribute that is unique -->
    <!-- among the blocks. It also has a class attribute that -->
    <!-- specifies the class providing that block-->
    <!-- -->
    <!-- The block element may have one or more provide sub-elements. -->
    <!-- Each provide element represents another block on which this -->
    <!-- block depends.  Phoenix will calculate a dependency chain when it -->
    <!-- reads this file, and will load and start the blocks in the order -->
    <!-- specified by that chain.  Each provide element has a name attribute, -->
    <!-- which matches the name of a block defined in this file.  It also -->
    <!-- has a role attribute.  This attribute is the string by which the -->
    <!-- enclosing block will identify the required block. -->
    <!-- -->
  
    <!-- The James block  -->
    <block name="James" class="org.apache.james.James" >
  
      <!-- Specify which components will provide the services required by this
      block. The roles are specified in the code and the .xinfo file. The names
      here must match the names specified for a Block in this xml file.   -->
      <provide name="dnsserver" role="org.apache.james.services.DNSServer"/>
      <provide name="mailstore" role="org.apache.james.services.MailStore"/>
      <provide name="users-store" role="org.apache.james.services.UsersStore"/>
      <provide name="sockets"
               role="org.apache.avalon.cornerstone.services.sockets.SocketManager"/>
      <provide name="connections"
               role="org.apache.avalon.cornerstone.services.connection.ConnectionManager"/>
      <provide name="scheduler"
               role="org.apache.avalon.cornerstone.services.scheduler.TimeScheduler"/>
      <provide name="database-connections"
               role="org.apache.avalon.cornerstone.services.datasource.DataSourceSelector" />
      <provide name="imaphost"
               role="org.apache.james.imapserver.ImapHost"/>
    </block>
  
    <!-- The James Spool Manager block  -->
    <block name="spoolmanager" class="org.apache.james.transport.JamesSpoolManager" >
      <provide name="James" role="org.apache.mailet.MailetContext"/>
      <provide name="mailstore" role="org.apache.james.services.MailStore"/>
      <provide name="thread-manager"
               role="org.apache.avalon.cornerstone.services.threads.ThreadManager" />
    </block>
  
    <block name="dnsserver" class="org.apache.james.dnsserver.DNSServer" />
  
    <block name="remotemanager" class="org.apache.james.remotemanager.RemoteManager" >
      <provide name="mailstore" role="org.apache.james.services.MailStore"/>
      <provide name="users-store" role="org.apache.james.services.UsersStore"/>
      <provide name="sockets"
               role="org.apache.avalon.cornerstone.services.sockets.SocketManager"/>
      <provide name="connections"
               role="org.apache.avalon.cornerstone.services.connection.ConnectionManager"/>
      <provide name="James" role="org.apache.james.services.MailServer"/>
      <provide name="thread-manager"
               role="org.apache.avalon.cornerstone.services.threads.ThreadManager" />
    </block>
  
    <!-- WARNING - The IMAP server is only experimental, ie pre-alpha -->
    <block class="org.apache.james.imapserver.ImapServer" name="imapserver" >
        <provide name="mailstore" role="org.apache.james.services.MailStore"/>
        <provide name="users-store" role="org.apache.james.services.UsersStore"/>
        <provide name="sockets"
                 role="org.apache.avalon.cornerstone.services.sockets.SocketManager"/>
        <provide name="connections"
                 role="org.apache.avalon.cornerstone.services.connection.ConnectionManager"/>
        <provide name="James" role="org.apache.james.services.MailServer"/>
        <provide name="thread-manager"
                 role="org.apache.avalon.cornerstone.services.threads.ThreadManager" />
  
        <provide name="imaphost" role="org.apache.james.imapserver.ImapHost"/>
    </block>
  
    <block class="org.apache.james.imapserver.JamesImapHost" name="imaphost">
    </block>
  
    <!-- POP3 Server -->
    <block name="pop3server" class="org.apache.james.pop3server.POP3Server" >
      <provide name="mailstore" role="org.apache.james.services.MailStore"/>
      <provide name="users-store" role="org.apache.james.services.UsersStore"/>
      <provide name="sockets"
               role="org.apache.avalon.cornerstone.services.sockets.SocketManager"/>
      <provide name="connections"
               role="org.apache.avalon.cornerstone.services.connection.ConnectionManager"/>
      <provide name="James" role="org.apache.james.services.MailServer"/>
      <provide name="thread-manager"
               role="org.apache.avalon.cornerstone.services.threads.ThreadManager" />
    </block>
  
    <!-- SMTP Server -->
    <block name="smtpserver" class="org.apache.james.smtpserver.SMTPServer" >
      <provide name="James" role="org.apache.mailet.MailetContext"/>
      <provide name="mailstore" role="org.apache.james.services.MailStore"/>
      <provide name="users-store" role="org.apache.james.services.UsersStore"/>
      <provide name="sockets"
               role="org.apache.avalon.cornerstone.services.sockets.SocketManager"/>
      <provide name="connections"
               role="org.apache.avalon.cornerstone.services.connection.ConnectionManager"/>
      <provide name="James" role="org.apache.james.services.MailServer"/>
      <provide name="thread-manager"
               role="org.apache.avalon.cornerstone.services.threads.ThreadManager" />
    </block>
  
    <!-- NNTP Server -->
    <block name="nntpserver" class="org.apache.james.nntpserver.NNTPServer" >
      <provide name="users-store" role="org.apache.james.services.UsersStore"/>
      <provide name="sockets"
               role="org.apache.avalon.cornerstone.services.sockets.SocketManager"/>
      <provide name="connections"
               role="org.apache.avalon.cornerstone.services.connection.ConnectionManager"/>
      <provide name="nntp-repository"
               role="org.apache.james.nntpserver.repository.NNTPRepository"/>
      <provide name="thread-manager"
               role="org.apache.avalon.cornerstone.services.threads.ThreadManager" />
    </block>
  
    <!-- NNTP Repository -->
    <block name="nntp-repository" class="org.apache.james.nntpserver.repository.NNTPRepositoryImpl" />
  
    <!-- FetchPOP Service -->
    <block name="fetchpop" class="org.apache.james.fetchpop.FetchScheduler" >
      <provide name="scheduler"
               role="org.apache.avalon.cornerstone.services.scheduler.TimeScheduler"/> 
      <provide name="James" role="org.apache.james.services.MailServer"/>      
    </block>
  
    <!-- The High Level Storage block -->
    <block name="mailstore" class="org.apache.james.core.AvalonMailStore" >
      <provide name="objectstorage"
               role="org.apache.avalon.cornerstone.services.store.Store"/>
      <provide name="database-connections"
               role="org.apache.avalon.cornerstone.services.datasource.DataSourceSelector" />
    </block>
  
    <!-- The User Storage block -->
    <block name="users-store" class="org.apache.james.core.AvalonUsersStore" >
      <!-- Configure file based user store here, defaults should be fine -->
      <provide name="objectstorage"
               role="org.apache.avalon.cornerstone.services.store.Store"/>
      <provide name="database-connections"
               role="org.apache.avalon.cornerstone.services.datasource.DataSourceSelector" />
    </block>
  
  
    <!-- Configuration for Cornerstone Blocks only after here
         NOTHING BELOW THIS SHOULD NEED CHANGING,
         (unless you want secure sockets (TLS)) -->
  
    <!-- The Storage block -->
    <block name="objectstorage"
           class="org.apache.avalon.cornerstone.blocks.masterstore.RepositoryManager" />
  
    <!-- The Connection Manager block -->
    <block name="connections"
           class="org.apache.james.util.connection.SimpleConnectionManager" >
      <provide name="thread-manager"
               role="org.apache.avalon.cornerstone.services.threads.ThreadManager" />
    </block>
  
    <!-- The Socket Manager block -->
    <block name="sockets"
           class="org.apache.avalon.cornerstone.blocks.sockets.DefaultSocketManager"/>
  
    <!-- The Time Scheduler block -->
    <block name="scheduler"
           class="org.apache.avalon.cornerstone.blocks.scheduler.DefaultTimeScheduler" >
      <provide name="thread-manager"
               role="org.apache.avalon.cornerstone.services.threads.ThreadManager" />
    </block>
  
    <!-- The DataSourceSelector block -->
    <block name="database-connections"
           class="org.apache.avalon.cornerstone.blocks.datasource.DefaultDataSourceSelector" />
  
    <!-- The ThreadManager block -->
    <block name="thread-manager"
           class="org.apache.avalon.cornerstone.blocks.threads.DefaultThreadManager" />
  
  </assembly>
  
  
  
  1.1                  jakarta-james/proposals/imap2/conf/james-config.xml
  
  Index: james-config.xml
  ===================================================================
  <?xml version="1.0"?>
  <!--  Configuration file for the Apache Jakarta James server -->
  
  <!--  This file contains important settings that control the behaviour -->
  <!--  of all of the services and repositories. -->
  
  <!--                               README!                            -->
  
  <!-- This configuration file is designed to run without alteration for simple tests. -->
  <!-- It assumes you have a DNS server on localhost and assigns a root password of root. -->
  
  <!-- In case the defaults do not suit you, the items you are most likely to need to change -->
  <!-- are preceded by a CHECKME! or CONFIRM? comment in the left margin. -->
  
  <!-- For production use you will probably need to make more extensive changes, see -->
  <!-- http://jakarta.apache.org/james/configuration_v2_1.html -->
  
  <!-- $Revision: 1.1 $ Committed on $Date: 2002/11/22 02:09:50 $ by: $Author: darrell $ -->
  
  <config>
     <James>
  
  <!-- CHECKME! -->
        <!-- This is the postmaster email address for this mail server. -->
        <!-- Set this to the appropriate email address for error reports -->
        <!-- If this is set to a non-local email address, the mail server -->
        <!-- will still function, but will generate a warning on startup. -->
        <postmaster>Postmaster@localhost</postmaster>
  
        <!-- servernames identifies the DNS namespace served by this instance of James. -->
        <!-- These servernames are used for both matcher/mailet processing and SMTP auth -->
        <!-- to determine when a mail is intended for local delivery. -->
        <!-- -->
        <!-- If autodetect is TRUE, James wil attempt to discover its own host name AND -->
        <!-- use any explicitly specified servernames. -->
        <!-- If autodetect is FALSE, James will use only the specified servernames. -->
        <!-- -->
        <!-- If autodetectIP is not FALSE, James will also allow add the IP address for each servername. -->
        <!-- The automatic IP detection is to support RFC 2821, Sec 4.1.3, address literals. -->
        <!-- -->
        <!-- To override autodetected server names simply add explicit servername elements. -->
        <!-- In most cases this will be necessary. -->
        <!-- By default, the servername 'localhost' is specified. This can be removed, if required. -->
        <!-- -->
        <!-- Warning: If you are using fetchpop it is important to include the -->
        <!-- fetched domains in the server name list to prevent looping.       -->
        <servernames autodetect="true" autodetectIP="true">
  <!-- CONFIRM? -->
           <servername>localhost</servername>
        </servernames>
  
        <!-- Set whether user names are case sensitive or case insensitive -->
        <!-- Set whether to enable local aliases -->
        <!-- Set whether to enable forwarding -->
        <usernames ignoreCase="true" enableAliases="true" enableForwarding="true"/>
  
        <!-- Set the type of permanent mailfolders to be used.      -->
        <!-- If IMAP service is to be provided, the storage type -->
        <!-- must be 'IMAP'; if only POP3 service is being provided -->
        <!-- then use must be 'basic' (default) . At some stage POP3-->
        <!-- will, hopefully, be able to use IMAP storage as well.  -->
        <!-- This choice is irrelevant if the only service provided is SMTP. -->
        <storage>IMAP</storage>
  
        <!-- The inbox repository is the location for users inboxes -->
        <!-- Default setting: file based repository - enter path ( use  "file:///" for absolute) -->
        <inboxRepository>
           <repository destinationURL="file://var/mail/inboxes/" type="MAIL"/>
        </inboxRepository>
  
        <!-- Alternative inbox repository definition for DB use. -->
        <!-- The format for the destinationURL is "db://<data-source>/<table>" -->
        <!-- <data-source> is the datasource name set up in the database-connections block, below -->
        <!-- <table> is the name of the table to store user inboxes in -->
        <!-- The user name is used as <repositoryName> for this repository config. -->
        <!--
        <inboxRepository>
           <repository destinationURL="db://maildb/inbox/" type="MAIL"/>
        </inboxRepository>
        -->
     </James>
  
     <!-- Fetch pop block, fetches mail from POP3 servers and inserts it into the incoming spool -->
     <!-- Warning: It is important to prevent mail from looping by setting the  -->
     <!-- fetched domains in the <servernames> section of the <James> block      -->
     <!-- above. This block is disabled by default.                              -->
      <fetchpop enabled="false">
          <!-- You can have as many fetch tasks as you want, but each must have a -->
          <!-- unique name by which it identified -->
          <fetch name="mydomain.com">
              <!-- Host name or IP address -->
              <host>mail.mydomain.com</host>
              <!-- Account login username -->
              <user>username</user>
              <!-- Account login password -->
              <password>pass</password>
              <!-- How frequently this account is checked - in milliseconds. 600000 is every ten minutes -->
              <interval>600000</interval>
          </fetch>
      </fetchpop>
  
  
     <!-- The James Spool Manager block  -->
     <!-- -->
     <!-- This block is responsible for processing messages on the spool. -->
     <spoolmanager>
        <!-- Number of spool threads -->
        <threads> 10 </threads>
  
        <!-- Set the Java packages from which to load mailets and matchers -->
        <mailetpackages>
           <mailetpackage>org.apache.james.transport.mailets</mailetpackage>
        </mailetpackages>
        <matcherpackages>
           <matcherpackage>org.apache.james.transport.matchers</matcherpackage>
        </matcherpackages>
  
        <!-- The root processor is a required processor - James routes all mail on the spool -->
        <!-- through this processor first. -->
        <!-- -->
        <!-- This configuration is a sample configuration for the root processor. -->
        <processor name="root">
  
           <!-- Checks that the email Sender is associated with a valid domain. -->
           <!-- Useful for detecting and eliminating spam. -->
           <!-- For this block to function, the spam processor must be configured. -->
           <!--
           <mailet match="SenderInFakeDomain" class="ToProcessor">
              <processor> spam </processor>
           </mailet>
           -->
  
           <!-- Important check to avoid looping -->
           <mailet match="RelayLimit=30" class="Null"/>
  
           <!-- Check for delivery from a known spam server -->
           <!-- This set of matchers/mailets redirect all emails from known -->
           <!-- black holes, open relays, and spam servers to the spam processor -->
           <!-- For this set to function properly, the spam processor must be configured. -->
           <mailet match="InSpammerBlacklist=blackholes.mail-abuse.org" class="ToProcessor">
              <processor> spam </processor>
              <notice> Rejected - see  http://www.mail-abuse.org/rbl/ </notice>
           </mailet>
           <mailet match="InSpammerBlacklist=dialups.mail-abuse.org" class="ToProcessor">
              <processor> spam </processor>
              <notice> Dialup - see http://www.mail-abuse.org/dul/ </notice>
           </mailet>
           <mailet match="InSpammerBlacklist=relays.mail-abuse.org" class="ToProcessor">
              <processor> spam </processor>
              <notice> Open spam relay - see http://www.mail-abuse.org/rss/ </notice>
           </mailet>
  
           <!-- Sample matching to kill a message (send to Null) -->
           <!--
           <mailet match="RecipientIs=badboy@badhost" class="Null"/>
           -->
  
           <!-- Send remaining mails to the transport processor for either local or remote delivery -->
           <mailet match="All" class="ToProcessor">
              <processor> transport </processor>
           </mailet>
        </processor>
  
        <!-- The error processor is required.  James may internally set emails to the -->
        <!-- error state.  The error processor is generally invoked when there is an -->
        <!-- unexpected error either in the mailet chain or internal to James. -->
        <!-- -->
        <!-- By default configuration all email that generates an error in placed in -->
        <!-- an error repository. -->
        <processor name="error">
  
           <!-- Logs any messages to the repository specified -->
           <mailet match="All" class="ToRepository">
              <repositoryPath> file://var/mail/error/</repositoryPath>
              <!-- An alternative database repository example follows. -->
              <!--
              <repositoryPath> db://maildb/deadletter/error </repositoryPath>
              -->
              <passThrough> true </passThrough>
           </mailet>
  
           <!-- If you want to notify the sender their message generated an error, uncomment this       -->
           <!--
           <mailet match="All" class="NotifySender"/>
           -->
           <!-- If you want to notify the postmaster that a message generated an error, uncomment this  -->
           <!--
           <mailet match="All" class="NotifyPostmaster"/>
           -->
        </processor>
  
        <!-- Processor CONFIGURATION SAMPLE: transport is a sample custom processor for local or -->
        <!-- remote delivery -->
        <processor name="transport">
  
           <!-- Is the recipient is for a local account, deliver it locally -->
           <mailet match="RecipientIsLocal" class="LocalDelivery"/>
  
           <!-- If the host is handled by this server and it did not get -->
           <!-- locally delivered, this is an invalid recipient -->
           <mailet match="HostIsLocal" class="ToProcessor">
              <processor>error</processor>
           </mailet>
  
  <!-- CHECKME! -->
           <!-- This is an anti-relay matcher/mailet combination -->
           <!-- -->
           <!-- Emails sent from servers not in the network list are  -->
           <!-- rejected as spam.  This is one method of preventing your -->
           <!-- server from being used as an open relay.  Make sure you understand -->
           <!-- how to prevent your server from becoming an open relay before -->
           <!-- changing this configuration. -->
           <!-- -->
           <!-- This matcher/mailet combination must come after local delivery has -->
           <!-- been performed.  Otherwise local users will not be able to receive -->
           <!-- email from senders not in this remote address list. -->
           <!-- -->
           <!-- If you are using this matcher/mailet you will probably want to -->
           <!-- update the configuration to include your own network/addresses.  The -->
           <!-- matcher can be configured with a comma separated list of IP addresses  -->
           <!-- wildcarded IP subnets, and wildcarded hostname subnets. -->
           <!-- e.g. "RemoteAddrNotInNetwork=127.0.0.1, abc.de.*, 192.168.0.*" -->
           <!-- -->
           <!-- If you are using SMTP authentication then you can (and generally -->
           <!-- should) disable this matcher/mailet pair. -->
           <mailet match="RemoteAddrNotInNetwork=127.0.0.1" class="ToProcessor">
              <processor> spam </processor>
           </mailet>
  
           <!-- Attempt remote delivery using the specified repository for the spool, -->
           <!-- using delay time to retry delivery and the maximum number of retries -->
           <mailet match="All" class="RemoteDelivery">
              <outgoing> file://var/mail/outgoing/ </outgoing>
              <!-- alternative database repository example below -->
              <!--
              <outgoing> db://maildb/spool/outgoing </outgoing>
              -->
  
              <!-- Number of milliseconds between delivery attempts -->
              <delayTime> 21600000 </delayTime>
  
              <!-- Number of failed attempts before returning to the sender -->
              <maxRetries> 5 </maxRetries>
  
              <!-- The number of threads that should be trying to deliver outgoing messages -->
              <deliveryThreads> 1 </deliveryThreads>
  
              <!-- A single mail server to deliver all outgoing messages. -->
              <!-- This is useful if this server is a backup or failover machine, -->
              <!-- or if you want all messages to be routed through a particular mail server, -->
              <!-- regardless of the email addresses specified in the message -->
              <!-- -->
              <!-- The gateway element specifies the gateway SMTP server name. -->
              <!-- If your gateway mail server is listening on a port other than 25, -->
              <!-- you can set James to connect to it on that port using the gatewayPort -->
              <!-- element. -->
              <!--
              <gateway> otherserver.mydomain.com </gateway>
              <gatewayPort>25</gatewayPort>
              -->
           </mailet>
  
        </processor>
  
        <!-- Processor CONFIGURATION SAMPLE: spam is a sample custom processor for handling -->
        <!-- spam. -->
        <!-- You can either log these, bounce these, or just ignore them. -->
        <processor name="spam">
           <!-- To destroy all messages, uncomment this matcher/mailet configuration -->
           <!--
           <mailet match="All" class="Null"/>
           -->
  
           <!-- To notify the sender their message was marked as spam, uncomment this matcher/mailet configuration -->
           <!--
           <mailet match="All" class="NotifySender"/>
           -->
  
           <!-- To notify the postmaster that a message was marked as spam, uncomment this matcher/mailet configuration -->
           <!--
           <mailet match="All" class="NotifyPostmaster"/>
           -->
  
           <!-- To log the message to a repository, this matcher/mailet configuration should be uncommented. -->
           <!-- This is the default configuration. -->
           <mailet match="All" class="ToRepository">
              <repositoryPath>file://var/mail/spam/</repositoryPath>
  
              <!-- Changing the repositoryPath, as in this commented out example, will -->
              <!-- cause the mails to be stored in a database repository.  -->
              <!-- Please note that only one repositoryPath element can be present for the mailet -->
              <!-- configuration. -->
              <!--
              <repositoryPath> db://maildb/deadletter/spam </repositoryPath>
              -->
           </mailet>
        </processor>
     </spoolmanager>
  
     <!-- DNS Server Block -->
     <!-- -->
     <!-- Specifies DNS Server information for use by various components inside -->
     <!-- James. -->
     <!-- -->
     <!-- Information includes a list of DNS Servers to be used by James.  These are -->
     <!-- specified by the server elements, each of which is a child element of the -->
     <!-- servers element.  Each server element is the IP address of a single DNS server. -->
     <!-- The servers element can have multiple server children. -->
     <dnsserver>
        <servers>
  <!-- CONFIRM? -->
          <!--Enter ip address of your DNS server, one IP address per server -->
          <!-- element.  The default configuration assumes a DNS server on the localhost. -->
           <server>127.0.0.1</server>
        </servers>
        <authoritative>false</authoritative>
     </dnsserver>
  
     <remotemanager>
        <port>4555</port>
        <!--  Uncomment this if you want to bind to a specific inetaddress -->
        <!--
        <bind> </bind>
        -->
        <!--  Uncomment this if you want to use TLS (SSL) on this port -->
        <!--
        <useTLS>true</useTLS>
        -->
        <handler>
           <!-- This is the name used by the server to identify itself in the RemoteManager -->
           <!-- protocol.  If autodetect is TRUE, the server will discover its -->
           <!-- own host name and use that in the protocol.  If discovery fails, -->
           <!-- the value of 'localhost' is used.  If autodetect is FALSE, James -->
           <!-- will use the specified value. -->
           <helloName autodetect="true">myMailServer</helloName>
           <administrator_accounts>
  <!-- CHECKME! -->
              <!-- Change the default login/password. -->
              <account login="root" password="root"/>
           </administrator_accounts>
           <connectiontimeout> 60000 </connectiontimeout>
        </handler>
     </remotemanager>
  
    <!-- WARNING - The IMAP server is only experimental, ie pre-alpha -->
    <imapserver>
        <port>143</port>
        <!-- <port>995</port> -->
  
        <!--  Uncomment this if you want to bind to a specific inetaddress -->
        <!--
        <bind> </bind>
        -->
        <!--  Uncomment this if you want to use TLS (SSL) on this port -->
        <!--
        <useTLS>true</useTLS>
        -->
        <handler>
           <!-- helloName is the single host name this instance of James will -->
           <!-- use to identify itself  for example, in SMTP and POP3 greetings. -->
           <!-- If autodetect is TRUE, James will attempt to discover its own name -->
           <!-- OR use 'localhost'. -->
           <!-- If autodetect is FALSE, James will use the value provided -->
           <!-- OR use 'localhost'-->
           <helloName autodetect="TRUE">myMailServer</helloName>
           <connectiontimeout>1800000</connectiontimeout>
      </handler>
    </imapserver>
  
    <imaphost>
      <!--
      <namespaces token="#">
        <privateNamespace separator=".">#mail</privateNamespace>
        <otherusersNamespace separator=".">#users</otherusersNamespace>
        <sharedNamespace separator=".">#shared</sharedNamespace>
      </namespaces>
      -->
    </imaphost>
  
      <!-- The POP3 server is enabled by default -->
      <!-- Disabling blocks will stop them from listening, -->
      <!-- but does not free as many resources as removing them would -->
     <pop3server enabled="true">
        <!-- port 995 is the well-known/IANA registered port for POP3S  ie over SSL/TLS -->
        <!-- port 100 is the well-known/IANA registered port for Standard POP3 -->
        <port>110</port>
  
        <!-- Uncomment this if you want to bind to a specific inetaddress -->
        <!--
        <bind> </bind>
        -->
        <!--  Uncomment this if you want to use TLS (SSL) on this port -->
        <!--
        <useTLS>true</useTLS>
        -->
  
        <handler>
           <!-- This is the name used by the server to identify itself in the POP3 -->
           <!-- protocol.  If autodetect is TRUE, the server will discover its -->
           <!-- own host name and use that in the protocol.  If discovery fails, -->
           <!-- the value of 'localhost' is used.  If autodetect is FALSE, James -->
           <!-- will use the specified value. -->
           <helloName autodetect="true">myMailServer</helloName>
           <connectiontimeout>120000</connectiontimeout>
        </handler>
     </pop3server>
  
      <!-- The SMTP server is enabled by default -->
      <!-- Disabling blocks will stop them from listening, -->
      <!-- but does not free as many resources as removing them would -->
     <smtpserver enabled="true">
        <!-- port 25 is the well-known/IANA registered port for SMTP -->
        <port>25</port>
  
        <!-- Uncomment this if you want to bind to a specific inetaddress -->
        <!--
        <bind> </bind>
        -->
        <!-- Uncomment this if you want to use TLS (SSL) on this port -->
        <!--
        <useTLS>true</useTLS>
        -->
  
        <handler>
           <!-- This is the name used by the server to identify itself in the SMTP -->
           <!-- protocol.  If autodetect is TRUE, the server will discover its -->
           <!-- own host name and use that in the protocol.  If discovery fails, -->
           <!-- the value of 'localhost' is used.  If autodetect is FALSE, James -->
           <!-- will use the specified value. -->
           <helloName autodetect="true">myMailServer</helloName>
           <connectiontimeout>360000</connectiontimeout>
  
           <!--  Uncomment this if you want to require SMTP authentication. -->
           <!--
           <authRequired>true</authRequired>
           -->
  
           <!--  Uncomment this if you want to verify sender addresses, ensuring that -->
           <!--  the sender address matches the user who has authenticated. -->
           <!--  This prevents a user of your mail server from acting as someone else -->
           <!--
           <verifyIdentity>true</verifyIdentity>
           -->
  
           <!--  This sets the maximum allowed message size (in kilobytes) for this -->
           <!--  SMTP service. If unspecified, the value defaults to 0, which means no limit. -->
           <maxmessagesize>0</maxmessagesize>
        </handler>
     </smtpserver>
  
      <!-- The NNTP server is enabled by default -->
      <!-- Disabling blocks will stop them from listening, -->
      <!-- but does not free as many resources as removing them would -->
     <nntpserver enabled="true">
        <!-- port 563 is the well-known/IANA registered port for NNTP over SSL/TLS -->
        <!-- port 119 is the well-known/IANA registered port for Standard NNTP -->
        <port>119</port>
  
        <!-- Uncomment this if you want to bind to a specific inetaddress -->
        <!--
        <bind> </bind>
        -->
        <!-- Uncomment this if you want to use TLS (SSL)  on this port -->
        <!--
        <useTLS>true</useTLS>
        -->
  
        <handler>
           <!-- This is the name used by the server to identify itself in the NNTP -->
           <!-- protocol.  If autodetect is TRUE, the server will discover its -->
           <!-- own host name and use that in the protocol.  If discovery fails, -->
           <!-- the value of 'localhost' is used.  If autodetect is FALSE, James -->
           <!-- will use the specified value. -->
           <helloName autodetect="true">myMailServer</helloName>
           <connectiontimeout>120000</connectiontimeout>
            <!-- Set the authRequired value to true to enable authenticated NNTP -->
           <authRequired>false</authRequired>
        </handler>
     </nntpserver>
  
     <nntp-repository>
        <!-- If this is set to true, posting will be disallowed. -->
        <readOnly>false</readOnly>
  
        <rootPath>file://var/nntp/groups</rootPath>
        <tempPath>file://var/nntp/temp</tempPath>
        <articleIDPath>file://var/nntp/articleid</articleIDPath>
        <articleIDDomainSuffix>news.james.apache.org</articleIDDomainSuffix>
  
        <!-- The news groups hosted in this NNTP repository. -->
        <newsgroups>
           <newsgroup>org.apache.james.dev</newsgroup>
           <newsgroup>org.apache.james.user</newsgroup>
           <newsgroup>org.apache.avalon.dev</newsgroup>
           <newsgroup>org.apache.avalon.user</newsgroup>
        </newsgroups>
  
        <spool>
           <configuration>
              <spoolPath>file://var/nntp/spool</spoolPath>
              <!-- The number of threads that process spooler related tasks. -->
              <threadCount>1</threadCount>
              <!-- The spool thread(s) should idle for some time, if it has nothing to do  -->
              <threadIdleTime>1000</threadIdleTime>
           </configuration>
        </spool>
     </nntp-repository>
  
     <!-- The Mailstore block -->
     <mailstore>
        <repositories>
  
           <!-- File based repositories.  These repositories store all message data -->
           <!-- in the file system. -->
           <repository class="org.apache.james.mailrepository.AvalonMailRepository">
              <protocols>
                 <protocol>file</protocol>
              </protocols>
              <types>
                 <type>MAIL</type>
              </types>
           </repository>
           <repository class="org.apache.james.mailrepository.AvalonSpoolRepository">
              <protocols>
                 <protocol>file</protocol>
              </protocols>
              <types>
                 <type>SPOOL</type>
              </types>
           </repository>
  
           <!-- JDBC based repositories.  These repositories store all message data -->
           <!-- in the database. -->
           <repository class="org.apache.james.mailrepository.JDBCMailRepository">
              <protocols>
                 <protocol>db</protocol>
              </protocols>
              <types>
                 <type>MAIL</type>
              </types>
              <config>
                 <sqlFile>file://conf/sqlResources.xml</sqlFile>
              </config>
           </repository>
  
           <repository class="org.apache.james.mailrepository.JDBCSpoolRepository">
              <protocols>
                 <protocol>db</protocol>
              </protocols>
              <types>
                 <type>SPOOL</type>
              </types>
              <config>
                 <sqlFile>file://conf/sqlResources.xml</sqlFile>
              </config>
           </repository>
  
           <!-- These repositories store message delivery and headers in the DB, and the body to the filesystem -->
           <repository class="org.apache.james.mailrepository.JDBCMailRepository">
              <protocols>
                 <protocol>dbfile</protocol>
              </protocols>
              <types>
                 <type>MAIL</type>
              </types>
              <config>
                 <sqlFile>file://conf/sqlResources.xml</sqlFile>
                 <filestore>file://var/dbmail</filestore>
              </config>
           </repository>
  
           <repository class="org.apache.james.mailrepository.JDBCSpoolRepository">
              <protocols>
                 <protocol>dbfile</protocol>
              </protocols>
              <types>
                 <type>SPOOL</type>
              </types>
              <config>
                 <sqlFile>file://conf/sqlResources.xml</sqlFile>
                 <filestore>file://var/dbmail</filestore>
              </config>
           </repository>
        </repositories>
  
        <!-- Spool repository configuration -->
        <!-- The spool repository is the location where incoming mails are temporarily stored -->
        <!-- before being processed. -->
        <spoolRepository>
           <repository destinationURL="file://var/mail/spool/" type="SPOOL"/>
        </spoolRepository>
  
        <!-- Alternative spool repository definition for JDBC use -->
        <!--
        <spoolRepository>
           <repository destinationURL="db://maildb/spool/spool" type="SPOOL"/>
        </spoolRepository>
        -->
     </mailstore>
  
  
     <!-- The User Storage block -->
     <users-store>
        <!-- Configure User Repositories here. -->
        <!-- -->
        <!-- User repositories are required for the following purposes: -->
        <!--    - storing James user information, including forwards, aliases, -->
        <!--      and authentication data. -->
        <!--    - holding lists of users for the listserv mailet -->
        <!-- Currently, two different storage options are available: -->
        <!--    - file-based storage using Java serialization -->
        <!--    - database-backed storage -->
        <!-- (Use of database or file-system is defined on a "per-repository" basis) -->
        <!-- -->
        <!-- Note: One user repository is required for James: -->
        <!--   LocalUsers - the users for whom you are providing POP3, NNTP, or SMTP service -->
        <!-- -->
        <!-- Other repositories may be used by matchers or mailets. -->
  
        <!-- Default: File-based user repositories  Use these configurations to store user info in the filesystem  -->
        <!-- The LocalUsers repository, for storing James' User info. -->
        <repository name="LocalUsers" class="org.apache.james.userrepository.UsersFileRepository">
           <destination URL="file://var/users/"/>
        </repository>
  
        <!-- Database backed user repositories -->
        <!-- -->
        <!-- Use these configurations to store user info in a database. -->
        <!-- Note: The <data-source> element must refer to a connection configured -->
        <!--       in the <database-connections> configuration section. -->
  
        <!-- The LocalUsers repository, for storing James' User info. -->
        <!--
        <repository name="LocalUsers" class="org.apache.james.userrepository.JamesUsersJdbcRepository" destinationURL="db://maildb/users">
           <sqlFile>file://conf/sqlResources.xml</sqlFile>
        </repository>
        -->
  
     </users-store>
  
     <!-- The database-connections block -->
     <database-connections>
        <!-- These connections are referred to by name elsewhere in the config file -->
  <!-- CHECKME! -->
        <!-- To allow James to use a database you must configure the database connection here. -->
        <!-- If you are not using a database, you can leave this section unchanged. -->
        <!-- These connections are referred to by name in URLs elsewhere in the config file. -->
        <data-sources>
  
           <!-- James is distributed with a built in relevant copy of the mm.mysql JDBC    -->
           <!-- driver.  No additional driver is needed for mysql. Read the mm.mysql LGPL  -->
           <!-- license at apps\james\SAR-INF\lib\mm.mysql.LICENCE                       -->
  
           <!-- JDBC driver .jar libraries for other RDBMS can be placed in ~james/lib/  -->
  
           <!-- Example, connecting to a MySQL database called "mail" on localhost-->
           <!-- -->
           <!-- The max value is the maximum number of concurrent connections James will -->
           <!-- open to this database-->
           <!-- If you see "SQLException: Giving up... no connections available." in your -->
           <!-- log files or bounced mail you should increase this value -->
           <!--
           <data-source name="maildb" class="org.apache.james.util.mordred.JdbcDataSource">
              <driver>org.gjt.mm.mysql.Driver</driver>
              <dburl>jdbc:mysql://127.0.0.1/mail</dburl>
              <user>username</user>
              <password>password</password>
              <max>20</max>
           </data-source>
           -->
  
           <!-- Example, connecting to a Microsoft MSSQL database called "mail" on localhost-->
           <!-- -->
           <!-- The max value is the maximum number of concurrent connections James will -->
           <!-- open to this database-->
           <!-- If you see "SQLException: Giving up... no connections available." in your -->
           <!-- log files or bounced mail you should increase this value -->
           <!--
           <data-source name="maildb" class="org.apache.james.util.mordred.JdbcDataSource">
              <driver>com.inet.tds.TdsDriver</driver>
              <dburl>jdbc:inetdae7:127.0.0.1?database=James</dburl>
              <user>sa_james</user>
              <password>blahblah</password>
              <max>20</max>
           </data-source>
           -->
  
        </data-sources>
     </database-connections>
  
     <!-- Configuration for Cornerstone Services -->
     <!-- -->
     <!-- For a simple configuration, nothing beneath this line should require -->
     <!-- alteration. -->
     <!-- -->
     <!-- You will need to adjust the Socket Manager service configuration if you want -->
     <!-- to enable secure sockets (TLS) for any James service.                        -->
     <!-- -->
     <!-- Complex or high volume configurations may require changes to the parameters -->
     <!-- in this section.  Please read the James and Avalon documentation before -->
     <!-- attempting to adjust this section. -->
     <!-- -->
  
     <!-- The Object Storage block -->
     <!-- -->
     <!-- Defines file storage details that are used for file-based repositories. -->
     <objectstorage>
        <repositories>
           <repository class="org.apache.avalon.cornerstone.blocks.masterstore.File_Persistent_Object_Repository">
              <protocols>
                 <protocol>file</protocol>
              </protocols>
              <types>
                 <type>OBJECT</type>
              </types>
              <models>
                 <model>SYNCHRONOUS</model>
                 <model>ASYNCHRONOUS</model>
                 <model>CACHE</model>
              </models>
           </repository>
  
           <repository class="org.apache.avalon.cornerstone.blocks.masterstore.File_Persistent_Stream_Repository">
              <protocols>
                 <protocol>file</protocol>
              </protocols>
              <types>
                 <type>STREAM</type>
              </types>
              <models>
                 <model>SYNCHRONOUS</model>
                 <model>ASYNCHRONOUS</model>
                 <model>CACHE</model>
              </models>
           </repository>
        </repositories>
     </objectstorage>
  
     <!-- The Connection Manager block -->
     <!-- -->
     <!-- The idle-timeout is the number of milliseconds that it will take for idle -->
     <!-- client connections managed by this connection manager to be marked at timed out. -->
     <!-- If no value is specified, the value defaults to 5 minutes, 300000 milliseconds -->
     <!-- A value of 0 means that client sockets will not timeout. -->
     <!-- -->
     <!-- The max-connections parameter specifies the default maximum number of client -->
     <!-- connections that this connection manager will allow per managed server socket. -->
     <!-- This value can be overridden by each individual service. -->
     <!-- If no value is specified, the value defaults to 30. -->
     <!-- A value of 0 means that there is no limit imposed by the connection manager, although -->
     <!-- resource limitations imposed by other components (i.e. max # of threads) may -->
     <!-- serve to limit the number of open connections. -->
     <!-- -->
     <connections>
        <idle-timeout>300000</idle-timeout>
        <max-connections>30</max-connections>
     </connections>
  
     <!-- The Socket Manager block -->
     <!-- -->
     <!-- The server-sockets element has a number of factory sub-elements. -->
     <!-- Each of the factory elements has a name and class attribute -->
     <!-- The name attribute for each factory element must be unique.  -->
     <!-- The class attribute is the name of a class that implements the -->
     <!-- interface org.apache.avalon.cornerstone.services.ServerSocketFactory -->
     <!-- Specific factory elements may require some sub-elements.  This is -->
     <!-- factory class dependent. -->
     <!-- -->
     <!-- The client-sockets element has a number of factory sub-elements. -->
     <!-- Each of the factory elements has a name and class attribute -->
     <!-- The name attribute for each factory element must be unique.  -->
     <!-- The class attribute is the name of a class that implements the -->
     <!-- interface org.apache.avalon.cornerstone.services.SocketFactory -->
     <!-- Specific factory elements may require some sub-elements.  This is -->
     <!-- factory class dependent. -->
     <!-- -->
     <sockets>
        <server-sockets>
           <factory name="plain" class="org.apache.avalon.cornerstone.blocks.sockets.DefaultServerSocketFactory"/>
           <!--
           <factory name="ssl" class="org.apache.avalon.cornerstone.blocks.sockets.TLSServerSocketFactory">
              <keystore>
                 <file>conf/keystore</file>
                 <password>secret</password>
                 <type>JKS</type>
                 <protocol>TLS</protocol>
                 <algorithm>SunX509</algorithm>
                 <authenticate-client>false</authenticate-client>
              </keystore>
           </factory>
           -->
        </server-sockets>
        <client-sockets>
           <factory name="plain" class="org.apache.avalon.cornerstone.blocks.sockets.DefaultSocketFactory"/>
        </client-sockets>
     </sockets>
  
     <!-- The Thread Manager block -->
     <!-- -->
     <!-- The thread manager provides thread pools for use throughout the server. -->
     <!-- -->
     <!-- A thread pool with the name "default" must be defined in this thread manager -->
     <!-- configuration. -->
     <!-- -->
     <!-- Each thread pool is defined with a "thread-group" element. -->
     <!-- Each of these elements has the following required sub-elements: -->
     <!--   name - the name of the thread pool, used by other components to -->
     <!--          lookup the thread pool -->
     <!--   priority - the thread priority for threads in the pool.  This is -->
     <!--              a value between 0 and 10, with 5 being the normal -->
     <!--              priority and 10 being the maximum. -->
     <!--   is-daemon - whether the threads in the pool are daemon threads. -->
     <!--   max-threads - the maximum number of threads allowed in the pool. -->
     <!--   min-threads - the minimum number of threads allowed in the pool. (not implemented) -->
     <!--   min-spare-threads - (not implemented) -->
     <thread-manager>
        <thread-group>
           <name>default</name>
           <priority>5</priority>
           <is-daemon>false</is-daemon>
           <max-threads>100</max-threads>
           <min-threads>20</min-threads>
           <min-spare-threads>20</min-spare-threads>
        </thread-group>
     </thread-manager>
  </config>
  
  
  
  1.1                  jakarta-james/proposals/imap2/conf/james-server.xml
  
  Index: james-server.xml
  ===================================================================
  <?xml version="1.0"?>
  
  <server>
    <logs version="1.1">
      <!-- see http://jakarta.apache.org/avalon/excalibur/logger/index.html -->
      <factories>
        <factory type="file" class="org.apache.avalon.excalibur.logger.factory.FileTargetFactory"/>
      </factories>
  
      <categories>
        <category name="" log-level="DEBUG">
          <log-target id-ref="default"/>
        </category>
        <category name="James.Mailet" log-level="DEBUG">
          <log-target id-ref="James-Mailet-target"/>
        </category>
        <category name="James" log-level="DEBUG">
          <log-target id-ref="James-target"/>
        </category>
        <category name="spoolmanager" log-level="DEBUG">
          <log-target id-ref="spoolmanager-target"/>
        </category>
        <category name="dnsserver" log-level="DEBUG">
          <log-target id-ref="dnsserver-target"/>
        </category>
        <category name="remotemanager" log-level="DEBUG">
          <log-target id-ref="remotemanager-target"/>
        </category>
        <category name="imapserver" log-level="DEBUG">
          <log-target id-ref="imapserver-target"/>
        </category>
        <category name="pop3server" log-level="DEBUG">
          <log-target id-ref="pop3server-target"/>
        </category>
        <category name="smtpserver" log-level="DEBUG">
          <log-target id-ref="smtpserver-target"/>
        </category>
        <category name="nntpserver" log-level="DEBUG">
          <log-target id-ref="nntpserver-target"/>
        </category>
        <category name="nntp-repository" log-level="DEBUG">
          <log-target id-ref="nntp-repository-target"/>
        </category>
        <category name="mailstore" log-level="DEBUG">
          <log-target id-ref="mailstore-target"/>
        </category>
        <category name="users-store" log-level="DEBUG">
          <log-target id-ref="users-store-target"/>
        </category>
        <category name="objectstorage" log-level="DEBUG">
          <log-target id-ref="objectstorage-target"/>
        </category>
        <category name="connections" log-level="DEBUG">
          <log-target id-ref="connections-target"/>
        </category>
        <category name="sockets" log-level="DEBUG">
          <log-target id-ref="sockets-target"/>
        </category>
        <category name="scheduler" log-level="DEBUG">
          <log-target id-ref="scheduler-target"/>
        </category>
        <category name="fetchpop" log-level="INFO">
          <log-target id-ref="fetchpop-target"/>
        </category>
      </categories>
  
      <targets>
        <file id="default">
          <filename>${app.home}/logs/default.log</filename>
          <format>%{time:dd/MM/yy hh:mm:ss} %5.5{priority} %{category}: %{message}\n%{throwable}</format>
          <append>true</append>
        </file>
        <file id="James-Mailet-target">
          <filename>${app.home}/logs/mailet.log</filename>
          <format>%{time:dd/MM/yy hh:mm:ss} %5.5{priority} %{category}: %{message}\n%{throwable}</format>
          <append>true</append>
        </file>
        <file id="James-target">
          <filename>${app.home}/logs/james.log</filename>
          <format>%{time:dd/MM/yy hh:mm:ss} %5.5{priority} %{category}: %{message}\n%{throwable}</format>
          <append>true</append>
        </file>
        <file id="spoolmanager-target">
          <filename>${app.home}/logs/spoolmanager.log</filename>
          <format>%{time:dd/MM/yy hh:mm:ss} %5.5{priority} %{category}: %{message}\n%{throwable}</format>
          <append>true</append>
        </file>
        <file id="dnsserver-target">
          <filename>${app.home}/logs/dnsserver.log</filename>
          <format>%{time:dd/MM/yy hh:mm:ss} %5.5{priority} %{category}: %{message}\n%{throwable}</format>
          <append>true</append>
        </file>
        <file id="remotemanager-target">
          <filename>${app.home}/logs/remotemanager.log</filename>
          <format>%{time:dd/MM/yy hh:mm:ss} %5.5{priority} %{category}: %{message}\n%{throwable}</format>
          <append>true</append>
        </file>
        <file id="imapserver-target">
          <filename>${app.home}/logs/imapserver.log</filename>
          <format>%{time:dd/MM/yy hh:mm:ss} %5.5{priority} %{category}: %{message}\n%{throwable}</format>
          <append>true</append>
        </file>
        <file id="pop3server-target">
          <filename>${app.home}/logs/pop3server.log</filename>
          <format>%{time:dd/MM/yy hh:mm:ss} %5.5{priority} %{category}: %{message}\n%{throwable}</format>
          <append>true</append>
        </file>
        <file id="smtpserver-target">
          <filename>${app.home}/logs/smtpserver.log</filename>
          <format>%{time:dd/MM/yy hh:mm:ss} %5.5{priority} %{category}: %{message}\n%{throwable}</format>
          <append>true</append>
        </file>
        <file id="nntpserver-target">
          <filename>${app.home}/logs/nntpserver.log</filename>
          <format>%{time:dd/MM/yy hh:mm:ss} %5.5{priority} %{category}: %{message}\n%{throwable}</format>
          <append>true</append>
        </file>
        <file id="nntp-repository-target">
          <filename>${app.home}/logs/nntpstore.log</filename>
          <format>%{time:dd/MM/yy hh:mm:ss} %5.5{priority} %{category}: %{message}\n%{throwable}</format>
          <append>true</append>
        </file>
        <file id="mailstore-target">
          <filename>${app.home}/logs/mailstore.log</filename>
          <format>%{time:dd/MM/yy hh:mm:ss} %5.5{priority} %{category}: %{message}\n%{throwable}</format>
          <append>true</append>
        </file>
        <file id="users-store-target">
          <filename>${app.home}/logs/usersstore.log</filename>
          <format>%{time:dd/MM/yy hh:mm:ss} %5.5{priority} %{category}: %{message}\n%{throwable}</format>
          <append>true</append>
        </file>
        <file id="objectstorage-target">
          <filename>${app.home}/logs/objectstore.log</filename>
          <format>%{time:dd/MM/yy hh:mm:ss} %5.5{priority} %{category}: %{message}\n%{throwable}</format>
          <append>true</append>
        </file>
        <file id="connections-target">
          <filename>${app.home}/logs/connections.log</filename>
          <format>%{time:dd/MM/yy hh:mm:ss} %5.5{priority} %{category}: %{message}\n%{throwable}</format>
          <append>true</append>
        </file>
        <file id="sockets-target">
          <filename>${app.home}/logs/sockets.log</filename>
          <format>%{time:dd/MM/yy hh:mm:ss} %5.5{priority} %{category}: %{message}\n%{throwable}</format>
          <append>true</append>
        </file>
        <file id="scheduler-target">
          <filename>${app.home}/logs/scheduler.log</filename>
          <format>%{time:dd/MM/yy hh:mm:ss} %5.5{priority} %{category}: %{message}\n%{throwable}</format>
          <append>true</append>
        </file>
        <file id="fetchpop-target">
          <filename>${app.home}/logs/fetchpop.log</filename>
          <format>%{time:dd/MM/yy HH:mm:ss} %5.5{priority} %{category}: %{message}\n%{throwable}</format>
          <append>true</append>
        </file>
      </targets>
    </logs>
  
  </server>
  
  
  
  1.1                  jakarta-james/proposals/imap2/java/org/apache/james/James.java
  
  Index: James.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;
  
  import org.apache.avalon.framework.activity.Initializable;
  import org.apache.avalon.framework.component.Component;
  import org.apache.avalon.framework.component.ComponentException;
  import org.apache.avalon.framework.component.ComponentManager;
  import org.apache.avalon.framework.component.Composable;
  import org.apache.avalon.framework.component.DefaultComponentManager;
  import org.apache.avalon.framework.configuration.Configurable;
  import org.apache.avalon.framework.configuration.Configuration;
  import org.apache.avalon.framework.configuration.ConfigurationException;
  import org.apache.avalon.framework.configuration.DefaultConfiguration;
  import org.apache.avalon.framework.context.Context;
  import org.apache.avalon.framework.context.Contextualizable;
  import org.apache.avalon.framework.context.DefaultContext;
  import org.apache.avalon.framework.logger.AbstractLogEnabled;
  import org.apache.avalon.framework.logger.Logger;
  import org.apache.james.core.MailHeaders;
  import org.apache.james.core.MailImpl;
  import org.apache.james.imapserver.ImapHost;
  import org.apache.james.imapserver.store.ImapMailbox;
  import org.apache.james.imapserver.store.MailboxException;
  import org.apache.james.services.DNSServer;
  import org.apache.james.services.JamesUser;
  import org.apache.james.services.MailRepository;
  import org.apache.james.services.MailServer;
  import org.apache.james.services.MailStore;
  import org.apache.james.services.SpoolRepository;
  import org.apache.james.services.UsersRepository;
  import org.apache.james.services.UsersStore;
  import org.apache.james.userrepository.DefaultJamesUser;
  import org.apache.james.util.RFC2822Headers;
  import org.apache.james.util.RFC822DateFormat;
  import org.apache.mailet.Mail;
  import org.apache.mailet.MailAddress;
  import org.apache.mailet.MailetContext;
  
  import javax.mail.Address;
  import javax.mail.MessagingException;
  import javax.mail.internet.InternetAddress;
  import javax.mail.internet.MimeBodyPart;
  import javax.mail.internet.MimeMessage;
  import javax.mail.internet.MimeMultipart;
  import java.io.ByteArrayInputStream;
  import java.io.IOException;
  import java.io.InputStream;
  import java.io.SequenceInputStream;
  import java.net.InetAddress;
  import java.net.UnknownHostException;
  import java.util.Collection;
  import java.util.Date;
  import java.util.Enumeration;
  import java.util.HashMap;
  import java.util.HashSet;
  import java.util.Hashtable;
  import java.util.Iterator;
  import java.util.Locale;
  import java.util.Map;
  import java.util.Vector;
  
  /**
   * Core class for JAMES. Provides three primary services:
   * <br> 1) Instantiates resources, such as user repository, and protocol
   * handlers
   * <br> 2) Handles interactions between components
   * <br> 3) Provides container services for Mailets
   *
   * @author  Federico Barbieri <scoobie@pop.systemy.it>
   * @author Serge
   * @author <a href="mailto:charles@benett1.demon.co.uk">Charles Benett</a>
   *
   * @version This is $Revision: 1.1 $
  
   */
  public class James
          extends AbstractLogEnabled
          implements Contextualizable, Composable, Configurable, JamesMBean,
          Initializable, MailServer, MailetContext, Component
  {
  
      /**
       * The software name and version
       */
      private final static String SOFTWARE_NAME_VERSION = Constants.SOFTWARE_NAME + " " + Constants.SOFTWARE_VERSION;
  
      /**
       * The component manager used both internally by James and by Mailets.
       */
      private DefaultComponentManager compMgr; //Components shared
  
      /**
       * TODO: Investigate what this is supposed to do.  Looks like it
       *       was supposed to be the Mailet context.
       */
      private DefaultContext context;
  
      /**
       * The top level configuration object for this server.
       */
      private Configuration conf;
  
      /**
       * The logger used by the Mailet API.
       */
      private Logger mailetLogger = null;
  
      /**
       * The mail store containing the inbox repository and the spool.
       */
      private MailStore mailstore;
  
      /**
       * The store containing the local user repository.
       */
      private UsersStore usersStore;
  
      /**
       * The spool used for processing mail handled by this server.
       */
      private SpoolRepository spool;
  
      /**
       * The repository that stores the user inboxes.
       */
      private MailRepository localInbox;
  
      /**
       * The root URL used to get mailboxes from the repository
       */
      private String inboxRootURL;
  
      /**
       * The user repository for this mail server.  Contains all the users with inboxes
       * on this server.
       */
      private UsersRepository localusers;
  
      /**
       * The collection of domain/server names for which this instance of James
       * will receive and process mail.
       */
      private Collection serverNames;
  
      /**
       * Whether to ignore case when looking up user names on this server
       */
      private boolean ignoreCase;
  
      /**
       * Whether to enable aliasing for users on this server
       */
      private boolean enableAliases;
  
      /**
       * Whether to enable forwarding for users on this server
       */
      private boolean enableForwarding;
  
      /**
       * The number of mails generated.  Access needs to be synchronized for
       * thread safety and to ensure that all threads see the latest value.
       */
      private static long count;
  
      /**
       * The address of the postmaster for this server
       */
      private MailAddress postmaster;
  
      /**
       * A map used to store mailboxes and reduce the cost of lookup of individual
       * mailboxes.
       */
      private Map mailboxes; //Not to be shared!
  
      /**
       * A hash table of server attributes
       * These are the MailetContext attributes
       */
      private Hashtable attributes = new Hashtable();
  
      /**
       * The Avalon context used by the instance
       */
      protected Context myContext;
  
      /**
       * An RFC822 date formatter used to format dates in mail headers
       */
      private RFC822DateFormat rfc822DateFormat = new RFC822DateFormat();
  
      /**
       * Whether James should use IMAP storage
       */
      private boolean useIMAPstorage = false;
  
      /**
       * The host to be used for IMAP storage
       */
      private ImapHost imapHost;
  
      /**
       * @see Contextualizable#contextualize(Context)
       */
      public void contextualize( final Context context )
      {
          this.myContext = context;
      }
  
      /**
       * @see Composable#compose(ComponentManager)
       */
      public void compose( ComponentManager comp )
      {
          compMgr = new DefaultComponentManager( comp );
          mailboxes = new HashMap( 31 );
      }
  
      /**
       * @see Configurable#configure(Configuration)
       */
      public void configure( Configuration conf )
      {
          this.conf = conf;
      }
  
      /**
       * @see Initializable#initialize()
       */
      public void initialize() throws Exception
      {
  
          getLogger().info( "JAMES init..." );
  
          // TODO: This should retrieve a more specific named thread pool from
          // Context that is set up in server.xml
          try {
              mailstore = ( MailStore ) compMgr.lookup( MailStore.ROLE );
          }
          catch ( Exception e ) {
              if ( getLogger().isWarnEnabled() ) {
                  getLogger().warn( "Can't get Store: " + e );
              }
          }
          if ( getLogger().isDebugEnabled() ) {
              getLogger().debug( "Using MailStore: " + mailstore.toString() );
          }
          try {
              usersStore = ( UsersStore ) compMgr.lookup( UsersStore.ROLE );
          }
          catch ( Exception e ) {
              if ( getLogger().isWarnEnabled() ) {
                  getLogger().warn( "Can't get Store: " + e );
              }
          }
          if ( getLogger().isDebugEnabled() ) {
              getLogger().debug( "Using UsersStore: " + usersStore.toString() );
          }
  
          String hostName = null;
          try {
              hostName = InetAddress.getLocalHost().getHostName();
          }
          catch ( UnknownHostException ue ) {
              hostName = "localhost";
          }
  
          context = new DefaultContext();
          context.put( "HostName", hostName );
          getLogger().info( "Local host is: " + hostName );
  
          // Get the domains and hosts served by this instance
          serverNames = new HashSet();
          Configuration serverConf = conf.getChild( "servernames" );
          if ( serverConf.getAttributeAsBoolean( "autodetect" ) && ( !hostName.equals( "localhost" ) ) ) {
              serverNames.add( hostName.toLowerCase( Locale.US ) );
          }
  
          final Configuration[] serverNameConfs =
                  conf.getChild( "servernames" ).getChildren( "servername" );
          for ( int i = 0; i < serverNameConfs.length; i++ ) {
              serverNames.add( serverNameConfs[i].getValue().toLowerCase( Locale.US ) );
  
              if ( serverConf.getAttributeAsBoolean( "autodetectIP", true ) ) {
                  try {
                      /* This adds the IP address(es) for each host to support
                       * support <user@address-literal> - RFC 2821, sec 4.1.3.
                       * It might be proper to use the actual IP addresses
                       * available on this server, but we can't do that
                       * without NetworkInterface from JDK 1.4.  Because of
                       * Virtual Hosting considerations, we may need to modify
                       * this to keep hostname and IP associated, rather than
                       * just both in the set.
                       */
                      InetAddress[] addrs = InetAddress.getAllByName( serverNameConfs[i].getValue() );
                      for ( int j = 0; j < addrs.length; j++ ) {
                          serverNames.add( addrs[j].getHostAddress() );
                      }
                  }
                  catch ( Exception genericException ) {
                      getLogger().error( "Cannot get IP address(es) for " + serverNameConfs[i].getValue() );
                  }
              }
          }
          if ( serverNames.isEmpty() ) {
              throw new ConfigurationException( "Fatal configuration error: no servernames specified!" );
          }
  
          if ( getLogger().isInfoEnabled() ) {
              for ( Iterator i = serverNames.iterator(); i.hasNext(); ) {
                  getLogger().info( "Handling mail for: " + i.next() );
              }
          }
          context.put( Constants.SERVER_NAMES, this.serverNames );
          attributes.put( Constants.SERVER_NAMES, this.serverNames );
  
  
          // Get postmaster
          String postMasterAddress = conf.getChild( "postmaster" ).getValue( "postmaster" );
          // if there is no @domain part, then add the first one from the
          // list of supported domains that isn't localhost.  If that
          // doesn't work, use the hostname, even if it is localhost.
          if ( postMasterAddress.indexOf( '@' ) < 0 ) {
              String domainName = null;    // the domain to use
              // loop through candidate domains until we find one or exhaust the list
              for ( int i = 0; domainName == null && i < serverNameConfs.length; i++ ) {
                  String serverName = serverNameConfs[i].getValue().toLowerCase( Locale.US );
                  if ( !( "localhost".equals( serverName ) ) ) {
                      domainName = serverName;    // ok, not localhost, so use it
                  }
              }
              // if we found a suitable domain, use it.  Otherwise fallback to the host name.
              postMasterAddress = postMasterAddress + "@" + ( domainName != null ? domainName : hostName );
          }
          this.postmaster = new MailAddress( postMasterAddress );
          context.put( Constants.POSTMASTER, postmaster );
  
          if ( !isLocalServer( postmaster.getHost() ) ) {
              StringBuffer warnBuffer
                      = new StringBuffer( 320 )
                      .append( "The specified postmaster address ( " )
                      .append( postmaster )
                      .append( " ) is not a local address.  This is not necessarily a problem, but it does mean that emails addressed to the postmaster will be routed to another server.  For some configurations this may cause problems." );
              getLogger().warn( warnBuffer.toString() );
          }
  
          Configuration userNamesConf = conf.getChild( "usernames" );
          ignoreCase = userNamesConf.getAttributeAsBoolean( "ignoreCase", false );
          enableAliases = userNamesConf.getAttributeAsBoolean( "enableAliases", false );
          enableForwarding = userNamesConf.getAttributeAsBoolean( "enableForwarding", false );
  
          //Get localusers
          try {
              localusers = ( UsersRepository ) usersStore.getRepository( "LocalUsers" );
          }
          catch ( Exception e ) {
              getLogger().error( "Cannot open private UserRepository" );
              throw e;
          }
          //}
          compMgr.put( UsersRepository.ROLE, ( Component ) localusers );
          getLogger().info( "Local users repository opened" );
  
          try {
              // Get storage config param
              if ( conf.getChild( "storage" ).getValue().equals( "IMAP" ) ) {
                  useIMAPstorage = true;
                  getLogger().info( "Using IMAP Store-System" );
              }
          }
          catch ( Exception e ) {
              // No storage entry found in config file
          }
  
          // Get the LocalInbox repository
          if ( useIMAPstorage ) {
              try {
                  // We will need to use a no-args constructor for flexibility
                  imapHost = ( ImapHost ) compMgr.lookup( ImapHost.ROLE );
              }
              catch ( Exception e ) {
                  getLogger().error( "Exception in IMAP Storage init: " + e.getMessage() );
                  throw e;
              }
          }
          else {
              Configuration inboxConf = conf.getChild( "inboxRepository" );
              Configuration inboxRepConf = inboxConf.getChild( "repository" );
              try {
                  localInbox = ( MailRepository ) mailstore.select( inboxRepConf );
              }
              catch ( Exception e ) {
                  getLogger().error( "Cannot open private MailRepository" );
                  throw e;
              }
              inboxRootURL = inboxRepConf.getAttribute( "destinationURL" );
          }
          getLogger().info( "Private Repository LocalInbox opened" );
  
          // Add this to comp
          compMgr.put( MailServer.ROLE, this );
  
          spool = mailstore.getInboundSpool();
          if ( getLogger().isDebugEnabled() ) {
              getLogger().debug( "Got spool" );
          }
  
          // For mailet engine provide MailetContext
          //compMgr.put("org.apache.mailet.MailetContext", this);
          // For AVALON aware mailets and matchers, we put the Component object as
          // an attribute
          attributes.put( Constants.AVALON_COMPONENT_MANAGER, compMgr );
  
          System.out.println( SOFTWARE_NAME_VERSION );
          getLogger().info( "JAMES ...init end" );
      }
  
      /**
       * Place a mail on the spool for processing
       *
       * @param message the message to send
       *
       * @throws MessagingException if an exception is caught while placing the mail
       *                            on the spool
       */
      public void sendMail( MimeMessage message ) throws MessagingException
      {
          MailAddress sender = new MailAddress( ( InternetAddress ) message.getFrom()[0] );
          Collection recipients = new HashSet();
          Address addresses[] = message.getAllRecipients();
          for ( int i = 0; i < addresses.length; i++ ) {
              recipients.add( new MailAddress( ( InternetAddress ) addresses[i] ) );
          }
          sendMail( sender, recipients, message );
      }
  
      /**
       * Place a mail on the spool for processing
       *
       * @param sender the sender of the mail
       * @param recipients the recipients of the mail
       * @param message the message to send
       *
       * @throws MessagingException if an exception is caught while placing the mail
       *                            on the spool
       */
      public void sendMail( MailAddress sender, Collection recipients, MimeMessage message )
              throws MessagingException
      {
          sendMail( sender, recipients, message, Mail.DEFAULT );
      }
  
      /**
       * Place a mail on the spool for processing
       *
       * @param sender the sender of the mail
       * @param recipients the recipients of the mail
       * @param message the message to send
       * @param state the state of the message
       *
       * @throws MessagingException if an exception is caught while placing the mail
       *                            on the spool
       */
      public void sendMail( MailAddress sender, Collection recipients, MimeMessage message, String state )
              throws MessagingException
      {
          MailImpl mail = new MailImpl( getId(), sender, recipients, message );
          mail.setState( state );
          sendMail( mail );
      }
  
      /**
       * Place a mail on the spool for processing
       *
       * @param sender the sender of the mail
       * @param recipients the recipients of the mail
       * @param msg an <code>InputStream</code> containing the message
       *
       * @throws MessagingException if an exception is caught while placing the mail
       *                            on the spool
       */
      public void sendMail( MailAddress sender, Collection recipients, InputStream msg )
              throws MessagingException
      {
          // parse headers
          MailHeaders headers = new MailHeaders( msg );
  
          // if headers do not contains minimum REQUIRED headers fields throw Exception
          if ( !headers.isValid() ) {
              throw new MessagingException( "Some REQURED header field is missing. Invalid Message" );
          }
          ByteArrayInputStream headersIn = new ByteArrayInputStream( headers.toByteArray() );
          sendMail( new MailImpl( getId(), sender, recipients, new SequenceInputStream( headersIn, msg ) ) );
      }
  
      /**
       * Place a mail on the spool for processing
       *
       * @param mail the mail to place on the spool
       *
       * @throws MessagingException if an exception is caught while placing the mail
       *                            on the spool
       */
      public void sendMail( Mail mail ) throws MessagingException
      {
          MailImpl mailimpl = ( MailImpl ) mail;
          try {
              spool.store( mailimpl );
          }
          catch ( Exception e ) {
              try {
                  spool.remove( mailimpl );
              }
              catch ( Exception ignored ) {
              }
              throw new MessagingException( "Exception spooling message: " + e.getMessage(), e );
          }
          if ( getLogger().isDebugEnabled() ) {
              StringBuffer logBuffer =
                      new StringBuffer( 64 )
                      .append( "Mail " )
                      .append( mailimpl.getName() )
                      .append( " pushed in spool" );
              getLogger().debug( logBuffer.toString() );
          }
      }
  
      /**
       * <p>Retrieve the mail repository for a user</p>
       *
       * <p>For POP3 server only - at the moment.</p>
       *
       * @param userName the name of the user whose inbox is to be retrieved
       *
       * @return the POP3 inbox for the user
       */
      public synchronized MailRepository getUserInbox( String userName )
      {
          MailRepository userInbox = ( MailRepository ) null;
  
          userInbox = ( MailRepository ) mailboxes.get( userName );
  
          if ( userInbox != null ) {
              return userInbox;
          }
          else if ( mailboxes.containsKey( userName ) ) {
              // we have a problem
              getLogger().error( "Null mailbox for non-null key" );
              throw new RuntimeException( "Error in getUserInbox." );
          }
          else {
              // need mailbox object
              if ( getLogger().isDebugEnabled() ) {
                  getLogger().debug( "Retrieving and caching inbox for " + userName );
              }
              StringBuffer destinationBuffer =
                      new StringBuffer( 192 )
                      .append( inboxRootURL )
                      .append( userName )
                      .append( "/" );
              String destination = destinationBuffer.toString();
              DefaultConfiguration mboxConf
                      = new DefaultConfiguration( "repository", "generated:AvalonFileRepository.compose()" );
              mboxConf.setAttribute( "destinationURL", destination );
              mboxConf.setAttribute( "type", "MAIL" );
              try {
                  userInbox = ( MailRepository ) mailstore.select( mboxConf );
                  mailboxes.put( userName, userInbox );
              }
              catch ( Exception e ) {
                  e.printStackTrace();
                  if ( getLogger().isErrorEnabled() ) {
                      getLogger().error( "Cannot open user Mailbox" + e );
                  }
                  throw new RuntimeException( "Error in getUserInbox." + e );
              }
              return userInbox;
          }
      }
  
      /**
       * Return a new mail id.
       *
       * @return a new mail id
       */
      public String getId()
      {
          long localCount = -1;
          synchronized ( James.class ) {
              localCount = count++;
          }
          StringBuffer idBuffer =
                  new StringBuffer( 64 )
                  .append( "Mail" )
                  .append( System.currentTimeMillis() )
                  .append( "-" )
                  .append( count++ );
          return idBuffer.toString();
      }
  
      /**
       * The main method.  Should never be invoked, as James must be called
       * from within an Avalon framework container.
       *
       * @param args the command line arguments
       */
      public static void main( String[] args )
      {
          System.out.println( "ERROR!" );
          System.out.println( "Cannot execute James as a stand alone application." );
          System.out.println( "To run James, you need to have the Avalon framework installed." );
          System.out.println( "Please refer to the Readme file to know how to run James." );
      }
  
      //Methods for MailetContext
  
      /**
       * <p>Get the prioritized list of mail servers for a given host.</p>
       *
       * <p>TODO: This needs to be made a more specific ordered subtype of Collection.</p>
       *
       * @param host
       */
      public Collection getMailServers( String host )
      {
          DNSServer dnsServer = null;
          try {
              dnsServer = ( DNSServer ) compMgr.lookup( DNSServer.ROLE );
          }
          catch ( final ComponentException cme ) {
              getLogger().error( "Fatal configuration error - DNS Servers lost!", cme );
              throw new RuntimeException( "Fatal configuration error - DNS Servers lost!" );
          }
          return dnsServer.findMXRecords( host );
      }
  
      public Object getAttribute( String key )
      {
          return attributes.get( key );
      }
  
      public void setAttribute( String key, Object object )
      {
          attributes.put( key, object );
      }
  
      public void removeAttribute( String key )
      {
          attributes.remove( key );
      }
  
      public Iterator getAttributeNames()
      {
          Vector names = new Vector();
          for ( Enumeration e = attributes.keys(); e.hasMoreElements(); ) {
              names.add( e.nextElement() );
          }
          return names.iterator();
      }
  
      /**
       * This generates a response to the Return-Path address, or the address of
       * the message's sender if the Return-Path is not available.  Note that
       * this is different than a mail-client's reply, which would use the
       * Reply-To or From header. This will send the bounce with the server's
       * postmaster as the sender.
       */
      public void bounce( Mail mail, String message ) throws MessagingException
      {
          bounce( mail, message, getPostmaster() );
      }
  
      /**
       * This generates a response to the Return-Path address, or the address of
       * the message's sender if the Return-Path is not available.  Note that
       * this is different than a mail-client's reply, which would use the
       * Reply-To or From header.
       */
      public void bounce( Mail mail, String message, MailAddress bouncer ) throws MessagingException
      {
          MimeMessage orig = mail.getMessage();
          //Create the reply message
          MimeMessage reply = ( MimeMessage ) orig.reply( false );
          //If there is a Return-Path header,
          if ( orig.getHeader( RFC2822Headers.RETURN_PATH ) != null ) {
              //Return the message to that address, not to the Reply-To address
              reply.setRecipient( MimeMessage.RecipientType.TO, new InternetAddress( orig.getHeader( RFC2822Headers.RETURN_PATH )[0] ) );
          }
          //Create the list of recipients in our MailAddress format
          Collection recipients = new HashSet();
          Address addresses[] = reply.getAllRecipients();
          for ( int i = 0; i < addresses.length; i++ ) {
              recipients.add( new MailAddress( ( InternetAddress ) addresses[i] ) );
          }
          //Change the sender...
          reply.setFrom( bouncer.toInternetAddress() );
          try {
              //Create the message body
              MimeMultipart multipart = new MimeMultipart();
              //Add message as the first mime body part
              MimeBodyPart part = new MimeBodyPart();
              part.setContent( message, "text/plain" );
              part.setHeader( RFC2822Headers.CONTENT_TYPE, "text/plain" );
              multipart.addBodyPart( part );
  
              //Add the original message as the second mime body part
              part = new MimeBodyPart();
              part.setContent( orig.getContent(), orig.getContentType() );
              part.setHeader( RFC2822Headers.CONTENT_TYPE, orig.getContentType() );
              multipart.addBodyPart( part );
              reply.setHeader( RFC2822Headers.DATE, rfc822DateFormat.format( new Date() ) );
              reply.setContent( multipart );
              reply.setHeader( RFC2822Headers.CONTENT_TYPE, multipart.getContentType() );
          }
          catch ( IOException ioe ) {
              throw new MessagingException( "Unable to create multipart body", ioe );
          }
          //Send it off...
          sendMail( bouncer, recipients, reply );
      }
  
      /**
       * Returns whether that account has a local inbox on this server
       *
       * @param name the name to be checked
       *
       * @return whether the account has a local inbox
       */
      public boolean isLocalUser( String name )
      {
          if ( ignoreCase ) {
              return localusers.containsCaseInsensitive( name );
          }
          else {
              return localusers.contains( name );
          }
      }
  
      /**
       * Returns the address of the postmaster for this server.
       *
       * @return the <code>MailAddress</code> for the postmaster
       */
      public MailAddress getPostmaster()
      {
          return postmaster;
      }
  
      public void storeMail( MailAddress sender, MailAddress recipient, MimeMessage message )
              throws MessagingException
      {
          String username;
          if ( recipient == null ) {
              throw new IllegalArgumentException( "Recipient for mail to be spooled cannot be null." );
          }
          if ( message == null ) {
              throw new IllegalArgumentException( "Mail message to be spooled cannot be null." );
          }
          if ( ignoreCase ) {
              String originalUsername = recipient.getUser();
              username = localusers.getRealName( originalUsername );
              if ( username == null ) {
                  StringBuffer errorBuffer =
                          new StringBuffer( 128 )
                          .append( "The inbox for user " )
                          .append( originalUsername )
                          .append( " was not found on this server." );
                  throw new MessagingException( errorBuffer.toString() );
              }
          }
          else {
              username = recipient.getUser();
          }
          JamesUser user;
          if ( enableAliases || enableForwarding ) {
              user = ( JamesUser ) localusers.getUserByName( username );
              if ( enableAliases && user.getAliasing() ) {
                  username = user.getAlias();
              }
              // Forwarding takes precedence over local aliases
              if ( enableForwarding && user.getForwarding() ) {
                  MailAddress forwardTo = user.getForwardingDestination();
                  if ( forwardTo == null ) {
                      StringBuffer errorBuffer =
                              new StringBuffer( 128 )
                              .append( "Forwarding was enabled for " )
                              .append( username )
                              .append( " but no forwarding address was set for this account." );
                      throw new MessagingException( errorBuffer.toString() );
                  }
                  Collection recipients = new HashSet();
                  recipients.add( forwardTo );
                  try {
                      sendMail( sender, recipients, message );
                      if ( getLogger().isInfoEnabled() ) {
                          StringBuffer logBuffer =
                                  new StringBuffer( 128 )
                                  .append( "Mail for " )
                                  .append( username )
                                  .append( " forwarded to " )
                                  .append( forwardTo.toString() );
                          getLogger().info( logBuffer.toString() );
                      }
                      return;
                  }
                  catch ( MessagingException me ) {
                      if ( getLogger().isErrorEnabled() ) {
                          StringBuffer logBuffer =
                                  new StringBuffer( 128 )
                                  .append( "Error forwarding mail to " )
                                  .append( forwardTo.toString() )
                                  .append( "attempting local delivery" );
                          getLogger().error( logBuffer.toString() );
                      }
                      throw me;
                  }
              }
          }
  
          if ( useIMAPstorage ) {
              ImapMailbox mbox = null;
              try {
                  user = ( JamesUser ) localusers.getUserByName( username );
                  mbox = imapHost.getInbox( user );
                  MailImpl mail = new MailImpl( message );
                  mbox.store( mail );
                  getLogger().info( "Message " + message.getMessageID() +
                                    " stored in " +
                                    mbox.getFullName() );
                  mbox = null;
              }
              catch ( Exception e ) {
                  getLogger().error( "Exception storing mail: " + e );
                  e.printStackTrace();
                  if ( mbox != null ) {
                      mbox = null;
                  }
                  throw new RuntimeException( "Exception storing mail: " + e );
              }
          }
          else {
              Collection recipients = new HashSet();
              recipients.add( recipient );
              MailImpl mailImpl = new MailImpl( getId(), sender, recipients, message );
              MailRepository userInbox = getUserInbox( username );
              if ( userInbox == null ) {
                  StringBuffer errorBuffer =
                          new StringBuffer( 128 )
                          .append( "The inbox for user " )
                          .append( username )
                          .append( " was not found on this server." );
                  throw new MessagingException( errorBuffer.toString() );
              }
              userInbox.store( mailImpl );
          }
      }
  
      /**
       * Return the major version number for the server
       *
       * @return the major vesion number for the server
       */
      public int getMajorVersion()
      {
          return 2;
      }
  
      /**
       * Return the minor version number for the server
       *
       * @return the minor vesion number for the server
       */
      public int getMinorVersion()
      {
          return 1;
      }
  
      /**
       * Check whether the mail domain in question is to be
       * handled by this server.
       *
       * @param serverName the name of the server to check
       * @return whether the server is local
       */
      public boolean isLocalServer( final String serverName )
      {
          return serverNames.contains( serverName.toLowerCase( Locale.US ) );
      }
  
      /**
       * Return the type of the server
       *
       * @return the type of the server
       */
      public String getServerInfo()
      {
          return "Apache Jakarta JAMES";
      }
  
      /**
       * Return the logger for the Mailet API
       *
       * @return the logger for the Mailet API
       */
      private Logger getMailetLogger()
      {
          if ( mailetLogger == null ) {
              mailetLogger = getLogger().getChildLogger( "Mailet" );
          }
          return mailetLogger;
      }
  
      /**
       * Log a message to the Mailet logger
       *
       * @param message the message to pass to the Mailet logger
       */
      public void log( String message )
      {
          getMailetLogger().info( message );
      }
  
      /**
       * Log a message and a Throwable to the Mailet logger
       *
       * @param message the message to pass to the Mailet logger
       * @param t the <code>Throwable</code> to be logged
       */
      public void log( String message, Throwable t )
      {
          getMailetLogger().info( message, t );
      }
  
      /**
       * Adds a user to this mail server. Currently just adds user to a
       * UsersRepository.
       * <p> As we move to IMAP support this will also create mailboxes and
       * access control lists.
       *
       * @param userName String representing user name, that is the portion of
       * an email address before the '@<domain>'.
       * @param password String plaintext password
       * @return boolean true if user added succesfully, else false.
       */
      public boolean addUser( String userName, String password )
      {
          boolean success;
          DefaultJamesUser user = new DefaultJamesUser( userName, "SHA" );
          user.setPassword( password );
          user.initialize();
          success = localusers.addUser( user );
          if ( useIMAPstorage && success ) {
              try {
                  imapHost.createPrivateMailAccount( user );
                  getLogger().info( "New MailAccount created for" + userName );
              }
              catch ( MailboxException e ) {
                  return false;
              }
          }
          return success;
      }
  }
  
  
  
  1.1                  jakarta-james/proposals/imap2/java/org/apache/james/James.xinfo
  
  Index: James.xinfo
  ===================================================================
  <?xml version="1.0"?>
  
  <blockinfo>
  
    <!-- section to describe block -->
    <block>
      <version>1.0</version>
    </block>
  
    <!-- services that are offered by this block -->
    <services>
      <service name="org.apache.james.services.MailServer" version="1.0" />
      <service name="org.apache.mailet.MailetContext" version="1.0" />
    </services>
  
    <dependencies>
  
      <dependency>
        <service name="org.apache.james.services.DNSServer" version="1.0"/>
      </dependency>
      <dependency>
        <service name="org.apache.james.services.MailStore" version="1.0"/>
      </dependency>
      <dependency>
        <service name="org.apache.james.services.UsersStore" version="1.0"/>
      </dependency>
      <dependency>
        <service name="org.apache.avalon.cornerstone.services.connection.ConnectionManager"
                 version="1.0"/>
      </dependency>
      <dependency>
        <service name="org.apache.avalon.cornerstone.services.sockets.SocketManager" version="1.0"/>
      </dependency>
      <dependency>
        <service name="org.apache.avalon.cornerstone.services.scheduler.TimeScheduler" version="1.0"/>
      </dependency>
      <dependency>
        <service name="org.apache.avalon.cornerstone.services.datasource.DataSourceSelector" version="1.0"/>
      </dependency>
  
      <dependency>
          <service name="org.apache.james.imapserver.ImapHost" version="1.0"/>
      </dependency>
  
    </dependencies>
  
  
  </blockinfo>
  
  
  
  1.1                  jakarta-james/proposals/imap2/java/org/apache/james/imapserver/AuthorizationException.java
  
  Index: AuthorizationException.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;
  
  /**
   * Thrown when a user attempts to do something (e.g. alter mailbox) for which
   * they do not have appropriate rights.
   *
   * @author <a href="mailto:charles@benett1.demon.co.uk">Charles Benett</a>
   * @version  0.1 on 14 Dec 2000
   */
  public class AuthorizationException
          extends Exception
  {
  
      /**
       * Construct a new <code>AuthorizationException</code> instance.
       *
       * @param message The detail message for this exception (mandatory).
       */
      public AuthorizationException( final String message )
      {
          super( message );
      }
  }
  
  
  
  1.1                  jakarta-james/proposals/imap2/java/org/apache/james/imapserver/ImapConstants.java
  
  Index: ImapConstants.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;
  
  public interface ImapConstants
  {
      // Basic response types
      String OK = "OK";
      String NO = "NO";
      String BAD = "BAD";
      String BYE = "BYE";
      String UNTAGGED = "*";
  
      String SP = " ";
      String VERSION = "IMAP4rev1";
  
      String USER_NAMESPACE = "#mail";
  
      char HIERARCHY_DELIMITER_CHAR = '.';
      char NAMESPACE_PREFIX_CHAR = '#';
      String HIERARCHY_DELIMITER = String.valueOf( HIERARCHY_DELIMITER_CHAR );
      String NAMESPACE_PREFIX = String.valueOf( NAMESPACE_PREFIX_CHAR );
  
      String INBOX_NAME = "INBOX";
  }
  
  
  
  1.1                  jakarta-james/proposals/imap2/java/org/apache/james/imapserver/ImapHandler.java
  
  Index: ImapHandler.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.avalon.cornerstone.services.connection.ConnectionHandler;
  import org.apache.avalon.excalibur.pool.Poolable;
  import org.apache.avalon.framework.activity.Disposable;
  import org.apache.avalon.framework.logger.AbstractLogEnabled;
  import org.apache.avalon.framework.logger.Logger;
  import org.apache.james.Constants;
  import org.apache.james.imapserver.commands.ImapCommand;
  import org.apache.james.imapserver.commands.ImapCommandFactory;
  import org.apache.james.imapserver.store.ImapMailbox;
  import org.apache.james.services.MailRepository;
  import org.apache.james.services.User;
  import org.apache.james.services.UsersRepository;
  import org.apache.james.util.InternetPrintWriter;
  import org.apache.james.util.watchdog.Watchdog;
  import org.apache.james.util.watchdog.WatchdogTarget;
  
  import java.io.BufferedOutputStream;
  import java.io.BufferedReader;
  import java.io.IOException;
  import java.io.InputStreamReader;
  import java.io.OutputStream;
  import java.io.PrintWriter;
  import java.net.Socket;
  
  /**
   * The handler class for IMAP connections.
   * TODO: This is a quick cut-and-paste hack from POP3Handler. This, and the ImapServer
   * should probably be rewritten from scratch.
   *
   * @author Federico Barbieri <scoobie@systemy.it>
   * @author Peter M. Goldstein <farsight@alum.mit.edu>
   */
  public class ImapHandler
          extends AbstractLogEnabled
          implements ConnectionHandler, Poolable, ImapConstants
  {
  
      private String softwaretype = "JAMES IMAP4rev1 Server " + Constants.SOFTWARE_VERSION;
      private ImapResponse untaggedResponse;
      private ImapRequestParser request;
      private ImapSession session;
      private ImapCommandFactory imapCommands = new ImapCommandFactory();
  
      /**
       * The per-service configuration data that applies to all handlers
       */
      private ImapHandlerConfigurationData theConfigData;
  
      /**
       * The mail server's copy of the user's inbox
       */
      private MailRepository userInbox;
  
      /**
       * The thread executing this handler
       */
      private Thread handlerThread;
  
      /**
       * The TCP/IP socket over which the IMAP interaction
       * is occurring
       */
      private Socket socket;
  
      /**
       * The reader associated with incoming characters.
       */
      private BufferedReader in;
  
      /**
       * The writer to which outgoing messages are written.
       */
      private PrintWriter out;
  
      /**
       * The socket's output stream
       */
      private OutputStream outs;
  
      /**
       * The watchdog being used by this handler to deal with idle timeouts.
       */
      private Watchdog theWatchdog;
  
      /**
       * The watchdog target that idles out this handler.
       */
      private WatchdogTarget theWatchdogTarget = new IMAPWatchdogTarget();
      private static final String REQUEST_SYNTAX = "Protocol Error: Was expecting <tag SPACE command [arguments]>";
  
      /**
       * Set the configuration data for the handler.
       *
       * @param theData the configuration data
       */
      void setConfigurationData( ImapHandlerConfigurationData theData )
      {
          theConfigData = theData;
      }
  
      /**
       * Set the Watchdog for use by this handler.
       *
       * @param theWatchdog the watchdog
       */
      void setWatchdog( Watchdog theWatchdog )
      {
          this.theWatchdog = theWatchdog;
      }
  
      /**
       * Gets the Watchdog Target that should be used by Watchdogs managing
       * this connection.
       *
       * @return the WatchdogTarget
       */
      WatchdogTarget getWatchdogTarget()
      {
          return theWatchdogTarget;
      }
  
      /**
       * Idle out this connection
       */
      void idleClose()
      {
          // TODO: Send BYE message before closing.
          if ( getLogger() != null ) {
              getLogger().error( "IMAP Connection has idled out." );
          }
          try {
              if ( socket != null ) {
                  socket.close();
              }
          }
          catch ( Exception e ) {
              // ignored
          }
          finally {
              socket = null;
          }
  
          synchronized ( this ) {
              // Interrupt the thread to recover from internal hangs
              if ( handlerThread != null ) {
                  handlerThread.interrupt();
                  handlerThread = null;
              }
          }
  
      }
  
      /**
       * @see ConnectionHandler#handleConnection(Socket)
       */
      public void handleConnection( Socket connection )
              throws IOException
      {
  
          String remoteHost = "";
          String remoteIP = "";
  
          try {
              this.socket = connection;
              synchronized ( this ) {
                  handlerThread = Thread.currentThread();
              }
              in = new BufferedReader( new InputStreamReader( socket.getInputStream(), "ASCII" ), 512 );
              remoteIP = socket.getInetAddress().getHostAddress();
              remoteHost = socket.getInetAddress().getHostName();
          }
          catch ( IOException e ) {
              if ( getLogger().isErrorEnabled() ) {
                  StringBuffer exceptionBuffer =
                          new StringBuffer( 256 )
                          .append( "Cannot open connection from " )
                          .append( remoteHost )
                          .append( " (" )
                          .append( remoteIP )
                          .append( "): " )
                          .append( e.getMessage() );
                  getLogger().error( exceptionBuffer.toString(), e );
              }
              throw e;
          }
  
          if ( getLogger().isInfoEnabled() ) {
              StringBuffer logBuffer =
                      new StringBuffer( 128 )
                      .append( "Connection from " )
                      .append( remoteHost )
                      .append( " (" )
                      .append( remoteIP )
                      .append( ") " );
              getLogger().info( logBuffer.toString() );
          }
  
          try {
              outs = new BufferedOutputStream( socket.getOutputStream(), 1024 );
              out = new InternetPrintWriter( outs, true );
              untaggedResponse = new ImapResponse( out );
  
              // Write welcome message
              StringBuffer responseBuffer =
                      new StringBuffer( 256 )
                      .append( VERSION )
                      .append( " Server " )
                      .append( theConfigData.getHelloName() )
                      .append( " ready" );
              untaggedResponse.okResponse( null, responseBuffer.toString() );
  
              request = new ImapRequestParser( in );
              session = new ImapSessionImpl();
  
              theWatchdog.start();
              while ( parseCommand() ) {
                  theWatchdog.reset();
              }
              theWatchdog.stop();
  
              //Write BYE message.
              if ( getLogger().isInfoEnabled() ) {
                  StringBuffer logBuffer =
                          new StringBuffer( 128 )
                          .append( "Connection for " )
                          .append( session.getUser().getUserName() )
                          .append( " from " )
                          .append( remoteHost )
                          .append( " (" )
                          .append( remoteIP )
                          .append( ") closed." );
                  getLogger().info( logBuffer.toString() );
              }
  
          }
          catch ( Exception e ) {
              out.println( "Error closing connection." );
              out.flush();
              StringBuffer exceptionBuffer =
                      new StringBuffer( 128 )
                      .append( "Exception on connection from " )
                      .append( remoteHost )
                      .append( " (" )
                      .append( remoteIP )
                      .append( ") : " )
                      .append( e.getMessage() );
              getLogger().error( exceptionBuffer.toString(), e );
          }
          finally {
              resetHandler();
          }
      }
  
      /**
       * Resets the handler data to a basic state.
       */
      private void resetHandler()
      {
  
          if ( theWatchdog != null ) {
              if ( theWatchdog instanceof Disposable ) {
                  ( ( Disposable ) theWatchdog ).dispose();
              }
              theWatchdog = null;
          }
  
          // Close and clear streams, sockets
  
          try {
              if ( socket != null ) {
                  socket.close();
                  socket = null;
              }
          }
          catch ( IOException ioe ) {
              // Ignoring exception on close
          }
          finally {
              socket = null;
          }
  
          try {
              if ( in != null ) {
                  in.close();
              }
          }
          catch ( Exception e ) {
              // Ignored
          }
          finally {
              in = null;
          }
  
          try {
              if ( out != null ) {
                  out.close();
              }
          }
          catch ( Exception e ) {
              // Ignored
          }
          finally {
              out = null;
          }
  
          try {
              if ( outs != null ) {
                  outs.close();
              }
          }
          catch ( Exception e ) {
              // Ignored
          }
          finally {
              outs = null;
          }
  
          synchronized ( this ) {
              handlerThread = null;
          }
  
          // Clear user data
          session = null;
  
          // Clear config data
          theConfigData = null;
      }
  
      /**
       * Implements a "stat".  If the handler is currently in
       * a transaction state, this amounts to a rollback of the
       * mailbox contents to the beginning of the transaction.
       * This method is also called when first entering the
       * transaction state to initialize the handler copies of the
       * user inbox.
       *
       */
      private void stat()
      {
  //        userMailbox = new Vector();
  //        userMailbox.addElement(DELETED);
  //        for (Iterator it = userInbox.list(); it.hasNext(); ) {
  //            String key = (String) it.next();
  //            MailImpl mc = userInbox.retrieve(key);
  //            // Retrieve can return null if the mail is no longer in the store.
  //            // In this case we simply continue to the next key
  //            if (mc == null) {
  //                continue;
  //            }
  //            userMailbox.addElement(mc);
  //        }
  //        backupUserMailbox = (Vector) userMailbox.clone();
      }
  
      /**
       * This method parses POP3 commands read off the wire in handleConnection.
       * Actual processing of the command (possibly including additional back and
       * forth communication with the client) is delegated to one of a number of
       * command specific handler methods.  The primary purpose of this method is
       * to parse the raw command string to determine exactly which handler should
       * be called.  It returns true if expecting additional commands, false otherwise.
       *
       * @return whether additional commands are expected.
       */
      private boolean parseCommand()
      {
          if ( !request.nextRequest() ) {
              return false;
          }
          ImapResponse response = new ImapResponse( out );
          String tag = null;
          String commandName = null;
          try {
              tag = request.tag();
          }
          catch ( ProtocolException e ) {
              response.badResponse( REQUEST_SYNTAX );
              return true;
          }
  
          response.setTag( tag );
          try {
              commandName = request.atom();
          }
          catch ( ProtocolException e ) {
              response.commandError( REQUEST_SYNTAX );
              return true;
          }
  
          ImapCommand command = imapCommands.getCommand( commandName );
          if ( command == null )
          {
              response.commandError( "Invalid command.");
              return true;
          }
  
          if ( !command.validForState( session.getState() ) ) {
              response.commandFailed( command, "Command not valid in this state" );
              return true;
          }
          
          command.process( request, response, session );
          return true;
      }
  
      /**
       * This method logs at a "DEBUG" level the response string that
       * was sent to the POP3 client.  The method is provided largely
       * as syntactic sugar to neaten up the code base.  It is declared
       * private and final to encourage compiler inlining.
       *
       * @param responseString the response string sent to the client
       */
      private final void logResponseString( String responseString )
      {
          if ( getLogger().isDebugEnabled() ) {
              getLogger().debug( "Sent: " + responseString );
          }
      }
  
      /**
       * Write and flush a response string.  The response is also logged.
       * Should be used for the last line of a multi-line response or
       * for a single line response.
       *
       * @param responseString the response string sent to the client
       */
      final void writeLoggedFlushedResponse( String responseString )
      {
          out.println( responseString );
          out.flush();
          logResponseString( responseString );
      }
  
      /**
       * Write a response string.  The response is also logged.
       * Used for multi-line responses.
       *
       * @param responseString the response string sent to the client
       */
      final void writeLoggedResponse( String responseString )
      {
          out.println( responseString );
          logResponseString( responseString );
      }
  
      /**
       * A private inner class which serves as an adaptor
       * between the WatchdogTarget interface and this
       * handler class.
       */
      private class IMAPWatchdogTarget
              implements WatchdogTarget
      {
  
          /**
           * @see WatchdogTarget#execute()
           */
          public void execute()
          {
              ImapHandler.this.idleClose();
          }
  
      }
  
      private final class ImapSessionImpl implements ImapSession
      {
          private ImapSessionState state = ImapSessionState.NON_AUTHENTICATED;
          private User user = null;
          private ImapMailbox selectedMailbox = null;
          // TODO: Use a session-specific wrapper for user's view of mailbox
          // this wrapper would provide access control and track if the mailbox
          // is opened read-only.
          private boolean selectedIsReadOnly = false;
  
          public ImapHost getHost()
          {
              return theConfigData.getImapHost();
          }
  
          public void unsolicitedResponses( ImapResponse request )
          {
          }
  
          public void closeConnection()
          {
              resetHandler();
          }
  
          public UsersRepository getUsers()
          {
              return theConfigData.getUsersRepository();
          }
  
          public Logger getSecurityLogger()
          {
              return getLogger();
          }
  
          public String getClientHostname()
          {
              return socket.getInetAddress().getHostName();
          }
  
          public String getClientIP()
          {
              return socket.getInetAddress().getHostAddress();
          }
  
          public void setAuthenticated( User user )
          {
              this.state = ImapSessionState.AUTHENTICATED;
              this.user = user;
          }
  
          public User getUser()
          {
              return this.user;
          }
  
          public void deselect()
          {
              this.state = ImapSessionState.AUTHENTICATED;
          }
  
          public void setSelected( ImapMailbox mailbox, boolean readOnly )
          {
              this.state = ImapSessionState.SELECTED;
              this.selectedMailbox = mailbox;
              this.selectedIsReadOnly = readOnly;
          }
  
          public ImapMailbox getSelected()
          {
              return this.selectedMailbox;
          }
  
          public boolean selectedIsReadOnly()
          {
              return this.selectedIsReadOnly;
          }
  
          public ImapSessionState getState()
          {
              return this.state;
          }
      }
  }
  
  
  
  
  1.1                  jakarta-james/proposals/imap2/java/org/apache/james/imapserver/ImapHandlerConfigurationData.java
  
  Index: ImapHandlerConfigurationData.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.services.MailServer;
  import org.apache.james.services.UsersRepository;
  
  /**
   * Provides a number of server-wide constant values to the
   * POP3Handlers
   *
   * @author Peter M. Goldstein <farsight@alum.mit.edu>
   */
  public interface ImapHandlerConfigurationData
  {
  
      /**
       * Returns the service wide hello name
       *
       * @return the hello name
       */
      String getHelloName();
  
      /**
       * Returns the service wide reset length in bytes.
       *
       * @return the reset length
       */
      int getResetLength();
  
      /**
       * Returns the MailServer interface for this service.
       *
       * @return the MailServer interface for this service
       */
      MailServer getMailServer();
  
      /**
       * Returns the UsersRepository for this service.
       *
       * @return the local users repository
       */
      UsersRepository getUsersRepository();
  
      /**
       * @return the ImapHost for this server.
       */
      ImapHost getImapHost();
  
  }
  
  
  
  1.1                  jakarta-james/proposals/imap2/java/org/apache/james/imapserver/ImapHost.java
  
  Index: ImapHost.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.services.User;
  import org.apache.james.imapserver.store.ImapMailbox;
  import org.apache.james.imapserver.store.MailboxException;
  
  import java.util.Collection;
  
  /**
   * A host machine that has an IMAP4rev1 messaging server. There should be one
   * instance of this class per instance of James.
   * An IMAP messaging system may span more than one host.
   * <p><code>String</code> parameters representing mailbox names must be the
   * full hierarchical name of the target, with namespace, as used by the
   * specified user. Examples:
   * '#mail.Inbox' or '#shared.finance.Q2Earnings'.
   * <p>An imap Host must keep track of existing and deleted mailboxes.
   *
   * References: rfc 2060, rfc 2193, rfc 2221
   * @author <a href="mailto:charles@benett1.demon.co.uk">Charles Benett</a>
   * @author Darrell DeBoer <darrell@apache.org>
   * @version $Revision: 1.1 $
   */
  public interface ImapHost
  {
  
      String ROLE = "org.apache.james.imapserver.ImapHost";
  
      String IMAP_HOST = "IMAP_HOST";
  
      /**
       * Returns the hierarchy delimiter for mailboxes on this host.
       * @return The hierarchy delimiter character.
       */
      char getHierarchyDelimiter();
  
      /**
       * Returns a reference to an existing Mailbox. The requested mailbox
       * must already exists on this server and the requesting user must have at
       * least lookup rights.
       *
       * TODO: should default behaviour be to return null?
       *
       * @param user User making the request.
       * @param mailboxName String name of the target.
       * @return an Mailbox reference.
       */
      ImapMailbox getMailbox( User user, String mailboxName );
  
      /**
       * Returns a reference to an existing Mailbox.
       * If mustExist == true, an exception is thrown if the requested mailbox
       * doesn't exists on this server or the requesting user doesn't have at
       * least lookup rights. If mustExist == false, simply returns <code>null</code> in
       * this case.
       *
       * @param user User making the request.
       * @param mailboxName String name of the target.
       * @param mustExist Specified behaviour where a mailbox is missing or non-viewable.
       * @return an Mailbox reference.
       * @throws org.apache.james.imapserver.store.MailboxException if mailbox does not exist locally, and mustExist is true.
       */
      ImapMailbox getMailbox( User user, String mailboxName, boolean mustExist )
              throws MailboxException;
  
      /**
       * Returns a reference to the user's INBOX. The user must have been
       * registered on the server by the {@link #createPrivateMailAccount} method.
       * @param user The user making the request.
       * @return The user's Inbox.
       * @throws org.apache.james.imapserver.store.MailboxException if the user doesn't have an inbox on this server.
       */
      ImapMailbox getInbox( User user ) throws MailboxException;
  
      /**
       * Registers a user with the ImapHost, creating a personal mail space and
       * INBOX for that user.
       * @param user The user to register with the Host.
       * @throws org.apache.james.imapserver.store.MailboxException if an error occurred creating the user account.
       */
      void createPrivateMailAccount( User user ) throws MailboxException;
  
      /**
       * Returns a reference to a newly created Mailbox. The request should
       * specify a mailbox that does not already exist on this server, that
       * could exist on this server and that the user has rights to create.
       * If a system allocates different namespaces to different hosts then a
       * request to create a mailbox in a namespace not served by this host would
       * be an error.
       * It is an error to create a mailbox with the name of a mailbox that has
       * been deleted, if that name is still in use.
       *
       * @param user User making the request.
       * @param mailboxName String name of the target
       * @return an Mailbox reference.
       * @throws org.apache.james.imapserver.store.MailboxException if mailbox already exists, locally or remotely,
       * or if mailbox cannot be created locally.
       * @throws AuthorizationException if mailbox could be created locally but
       * user does not have create rights.
       */
      ImapMailbox createMailbox( User user, String mailboxName )
              throws AuthorizationException, MailboxException;
  
  
      /**
       * Deletes an existing MailBox. Specified mailbox must already exist on
       * this server, and the user must have rights to delete it. (Mailbox delete
       * rights are implementation defined, one way is if the user would have the
       * right to create it).
       *
       * @param user User making the request.
       * @param mailboxName String name of the target
       * @throws org.apache.james.imapserver.store.MailboxException if mailbox does not exist locally or is any
       * identities INBOX.
       * @throws AuthorizationException if mailbox exists locally but user does
       * not have rights to delete it.
       */
      void deleteMailbox( User user, String mailboxName )
              throws MailboxException, AuthorizationException;
  
  
      /**
       * Renames an existing MailBox. The specified mailbox must already
       * exist locally, the requested name must not exist locally already but
       * must be able to be created locally and the user must have rights to
       * delete the existing mailbox and create a mailbox with the new name.
       * Any inferior hierarchical names must also be renamed.
       * If INBOX is renamed, the contents of INBOX are transferred to a new
       * folder with the new name, but INBOX is not deleted. If INBOX has
       * inferior mailboxes these are not renamed.
       * It is an error to create a mailbox with the name of a mailbox that has
       * been deleted, if that name is still in use.
       *
       * @param user User making the request.
       * @param oldMailboxName String name of the existing mailbox
       * @param newMailboxName String target new name
       * @throws org.apache.james.imapserver.store.MailboxException if mailbox does not exist locally, or there
       * is an existing mailbox with the new name.
       * @throws AuthorizationException if user does not have rights to delete
       * the existing mailbox or create the new mailbox.
       */
      void renameMailbox( User user,
                          String oldMailboxName,
                          String newMailboxName )
              throws MailboxException, AuthorizationException;
  
      /**
       * Returns an collection of mailboxes on this host. The specified
       * user must have at least lookup rights for each mailbox returned.
       * If the subscribedOnly flag is set, only mailboxes to which the
       * specified user is currently subscribed should be returned.
       * Implementations that may export circular hierarchies SHOULD restrict the
       * levels of hierarchy returned. The depth suggested by rfc 2683 is 20
       * hierarchy levels.
       * <p>The reference name must be non-empty. If the mailbox name is empty,
       * implementations must not throw either exception but must return a single
       * String (described below) if the reference name specifies a local mailbox
       * accessible to the user and a one-character String containing the
       * hierarchy delimiter of the referenced namespace, otherwise.
       * <p>Each String returned should be a space seperated triple of name
       * attributes, hierarchy delimiter and full mailbox name.   The mailbox
       * name should include the namespace and be relative to the specified user.
       * <p> RFC comments: Implementations SHOULD return quickly. They SHOULD
       * NOT go to excess trouble to calculate\Marked or \Unmarked status.
       * <p>JAMES comment: By elimination, implementations should usually include
       * \Noinferiors or \Noselect, if appropriate. Also, if the reference name
       * and mailbox name resolve to a single local mailbox, implementations
       * should establish all attributes.
       *
       * @param user User making the request
       * @param mailboxPattern String name of a mailbox possible including a
       * wildcard.
       * @return Collection of mailboxes matching the pattern.
       * @throws org.apache.james.imapserver.store.MailboxException if the referenceName is not local or if
       * referenceName and mailbox name resolve to a single mailbox which does
       * not exist locally.
       */
      Collection listMailboxes( User user,
                                String mailboxPattern )
              throws MailboxException;
  
      /**
       * Returns an collection of mailboxes on this host. The specified
       * user must have at least lookup rights for each mailbox returned.
       * If the subscribedOnly flag is set, only mailboxes to which the
       * specified user is currently subscribed should be returned.
       * Implementations that may export circular hierarchies SHOULD restrict the
       * levels of hierarchy returned. The depth suggested by rfc 2683 is 20
       * hierarchy levels.
       * <p>The reference name must be non-empty. If the mailbox name is empty,
       * implementations must not throw either exception but must return a single
       * String (described below) if the reference name specifies a local mailbox
       * accessible to the user and a one-character String containing the
       * hierarchy delimiter of the referenced namespace, otherwise.
       * <p>Each String returned should be a space seperated triple of name
       * attributes, hierarchy delimiter and full mailbox name.   The mailbox
       * name should include the namespace and be relative to the specified user.
       * <p> RFC comments: Implementations SHOULD return quickly. They SHOULD
       * NOT go to excess trouble to calculate\Marked or \Unmarked status.
       * <p>JAMES comment: By elimination, implementations should usually include
       * \Noinferiors or \Noselect, if appropriate. Also, if the reference name
       * and mailbox name resolve to a single local mailbox, implementations
       * should establish all attributes.
       * <p> Note that servers cannot unilaterally remove mailboxes from the
       * subscribed list. A request that attempts to list a deleted, but subscribed,
       * mailbox must return that mailbox with the \Noselect attribute.
       *
       * @param user User making the request
       * @param mailboxPattern String name of a mailbox possible including a
       * wildcard.
       * @return Collection of mailboxes matching the pattern.
       * @throws org.apache.james.imapserver.store.MailboxException if the referenceName is not local or if
       * referenceName and mailbox name resolve to a single mailbox which does
       * not exist locally.
       */
      Collection listSubscribedMailboxes( User user,
                                          String mailboxPattern)
              throws MailboxException;
  
      /**
       * Subscribes a user to a mailbox. The mailbox must exist locally and the
       * user must have at least lookup rights to it.
       *
       * @param user User making the request
       * @param mailbox String representation of a mailbox name.
       * @throws org.apache.james.imapserver.store.MailboxException if the mailbox does not exist locally (for the user).
       */
      void subscribe( User user, String mailbox )
              throws MailboxException;
  
      /**
       * Unsubscribes from a given mailbox.
       *
       * @param username String representation of an email address
       * @param mailbox String representation of a mailbox name.
       */
      void unsubscribe( String username, String mailbox )
              throws MailboxException;
  
  }
  
  
  
  
  1.1                  jakarta-james/proposals/imap2/java/org/apache/james/imapserver/ImapRequestParser.java
  
  Index: ImapRequestParser.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 java.io.BufferedReader;
  import java.io.IOException;
  import java.util.StringTokenizer;
  
  /**
   * TODO: Study up parser design techniques and rewrite this
   * (I don't think it warrants a generated parser.)
   *
   * Encapsulates a single client request to the Imap server, and the server
   * response.
   *
   * @author  Darrell DeBoer <darrell@apache.org>
   *
   * @version $Revision: 1.1 $
   */
  public class ImapRequestParser
  {
      private BufferedReader reader;
  
      // TODO: implement this without string tokenizer. I need to learn more about parser design...
      private StringTokenizer tokens;
      private String peek = null;
      private static final char[] EMPTY_CHAR_ARRAY = new char[0];
      private static final char[] WILDCARD_CHARS = new char[]{'*', '%'};
  
      private StringBuffer buffer = new StringBuffer();
  
      ImapRequestParser( BufferedReader reader )
      {
          this.reader = reader;
      }
  
  
      /**
       * @return An argument of type "atom"
       */
      public String atom() throws ProtocolException
      {
          return extendedAtom( EMPTY_CHAR_ARRAY );
      }
  
      private String extendedAtom( char[] extraChars ) throws ProtocolException
      {
          // TODO implement this.
          return nextWord();
      }
  
      /**
       * @return The "tag" from the client request.
       */
      public String tag() throws ProtocolException
      {
          String atom = atom();
          if ( atom.indexOf( '+' ) != -1 ) {
              throw new ProtocolException( "Tag values must not contain the '+' character." );
          }
          return atom;
      }
  
      /**
       * @return An argument of type "astring"
       */
      public String astring() throws ProtocolException
      {
          // TODO: do this properly - need to check for disallowed characters
          // in unquoted strings.
  
          String token = peek();
  
          if ( token.charAt( 0 ) == '\"' ) {
              return quoted();
          }
  
          if ( token.charAt( 0 ) == '{' ) {
              return literal();
          }
  
          return atom();
      }
  
      /**
       * @return An argument of type "nstring"
       */
      public String nstring() throws ProtocolException
      {
          String token = peek();
  
          if ( "NIL".equals( token ) ) {
              // Consume the "NIL".
              token = nextWord();
              return null;
          }
  
          if ( token.charAt( 0 ) == '\"' ) {
              return quoted();
          }
  
          if ( token.charAt( 0 ) == '{' ) {
              return literal();
          }
  
          throw new ProtocolException( "Invalid <nstring> argument: expected NIL, <literal>, or <quoted>" );
      }
  
      public String quoted() throws ProtocolException
      {
          // TODO: Handle escaped quotes.
          String firstToken = nextWord();
          StringBuffer astring = new StringBuffer( firstToken );
          while ( astring.length() == 1 ||
                  astring.charAt( astring.length() - 1 ) != '\"' ) {
              if ( tokens.hasMoreTokens() ) {
                  astring.append( " " );
                  astring.append( tokens.nextToken() );
              }
              else {
                  throw new ProtocolException( "Missing closing quote on <astring> argument." );
              }
          }
          astring.deleteCharAt( 0 );
          astring.deleteCharAt( astring.length() - 1 );
          return astring.toString();
      }
  
      private String literal()
      {
          // TODO - implement this.
          return null;
      }
  
      /**
       * Reads an argument of type "list_mailbox" from the request, which is
       * the second argument for a LIST or LSUB command. Valid values are a "string"
       * argument, an "atom" with wildcard characters.
       * @return An argument of type "list_mailbox"
       */
      public String listMailbox() throws ProtocolException
      {
          String token = peek();
          if ( token.charAt( 0 ) == '"' ) {
              return quoted();
          }
          if ( token.charAt( 0 ) == '{' ) {
              return literal();
          }
  
          return extendedAtom( WILDCARD_CHARS );
      }
  
      private String peek() throws ProtocolException
      {
          if ( peek == null ) {
              peek = nextWord();
          }
  
          return peek;
      }
  
      private String nextWord() throws ProtocolException
      {
          if ( peek != null ) {
              String tmp = peek;
              peek = null;
              return tmp;
          }
  
          if ( tokens.hasMoreTokens() ) {
              return tokens.nextToken();
          }
  
          // Word not found.
          throw new ProtocolException( "Missing argument." );
      }
  
      private boolean readNextLine() throws ProtocolException
      {
          String nextLine = null;
          try {
              peek = null;
              nextLine = reader.readLine();
          }
          catch ( IOException e ) {
              throw new ProtocolException( "Unexpected protocol exception: " +
                                           e.getMessage() );
          }
  
          if ( nextLine == null ) {
              tokens = null;
              return false;
          }
          else {
              tokens = new StringTokenizer( nextLine );
              return true;
          }
      }
  
      /**
       * Read a CRLF from the request, finishing the line.
       */
      public void endLine() throws ProtocolException
      {
          if ( peek != null || tokens.hasMoreTokens() ) {
              peek = null;
              tokens = null;
              throw new ProtocolException( "Extra argument found." );
          }
          peek = null;
          tokens = null;
      }
  
      public boolean nextRequest()
      {
          if ( tokens != null && tokens.hasMoreTokens() ) {
              return true;
          }
          try {
              if ( readNextLine() ) {
                  return true;
              }
              else {
                  return false;
              }
          }
          catch ( ProtocolException e ) {
              return false;
          }
      }
  
      private boolean isCHAR( char chr )
      {
          return ( chr >= 0x01 && chr <= 0x7f );
      }
  
      private boolean isCHAR8( char chr )
      {
          return ( chr >= 0x01 && chr <= 0xff );
      }
  
      private boolean isATOM_CHAR( char chr )
      {
          return ( isCHAR( chr ) && !isAtomSpecial( chr ) &&
                  !isListWildcard( chr ) && !isQuotedSpecial( chr ) );
      }
  
      private boolean isAtomSpecial( char chr )
      {
          return ( chr == '(' ||
                  chr == ')' ||
                  chr == '{' ||
                  chr == ' ' ||
                  chr == Character.CONTROL );
      }
  
      private boolean isListWildcard( char chr )
      {
          return ( chr == '*' || chr == '%' );
      }
  
      private boolean isQuotedSpecial( char chr )
      {
          return ( chr == '"' || chr == '\\' );
      }
  
  }
  
  
  
  1.1                  jakarta-james/proposals/imap2/java/org/apache/james/imapserver/ImapResponse.java
  
  Index: ImapResponse.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.imapserver.commands.ImapCommand;
  import org.apache.james.imapserver.store.MessageFlags;
  
  import java.io.PrintWriter;
  
  /**
   * Class providing methods to send response messages from the server
   * to the client.
   */
  public class ImapResponse implements ImapConstants
  {
      private PrintWriter writer;
      private String tag = UNTAGGED;
  
      public ImapResponse( PrintWriter writer )
      {
          this.writer = writer;
      }
  
      public void setTag( String tag )
      {
          this.tag = tag;
      }
  
      /**
       * Writes a standard tagged OK response on completion of a command.
       * Response is writen as:
       * <pre>     a01 OK COMMAND_NAME completed.</pre>
       *
       * @param command The ImapCommand which was completed.
       */
      public void commandComplete( ImapCommand command )
      {
          commandComplete( command, null );
      }
  
      /**
       * Writes a standard tagged OK response on completion of a command,
       * with a response code (eg READ-WRITE)
       * Response is writen as:
       * <pre>     a01 OK [responseCode] COMMAND_NAME completed.</pre>
       *
       * @param command The ImapCommand which was completed.
       * @param responseCode A string response code to send to the client.
       */
      public void commandComplete( ImapCommand command, String responseCode )
      {
          tag();
          message( OK );
          responseCode( responseCode );
          commandName( command );
          message( "completed." );
          end();
      }
  
      /**
       * Writes a standard NO response on command failure, together with a
       * descriptive message.
       * Response is writen as:
       * <pre>     a01 NO COMMAND_NAME failed. <reason></pre>
       *
       * @param command The ImapCommand which failed.
       * @param reason A message describing why the command failed.
       */
      public void commandFailed( ImapCommand command, String reason )
      {
          commandFailed( command, null, reason );
      }
  
      /**
       * Writes a standard NO response on command failure, together with a
       * descriptive message.
       * Response is writen as:
       * <pre>     a01 NO [responseCode] COMMAND_NAME failed. <reason></pre>
       *
       * @param command The ImapCommand which failed.
       * @param responseCode The Imap response code to send.
       * @param reason A message describing why the command failed.
       */
      public void commandFailed( ImapCommand command,
                                 String responseCode,
                                 String reason )
      {
          tag();
          message( NO );
          responseCode( responseCode );
          commandName( command );
          message( "failed." );
          message( reason );
          end();
      }
  
      /**
       * Writes a standard BAD response on command error, together with a
       * descriptive message.
       * Response is writen as:
       * <pre>     a01 BAD <message></pre>
       *
       * @param message The descriptive error message.
       */
      public void commandError( String message )
      {
          tag();
          message( BAD );
          message( message );
          end();
      }
  
      /**
       * Writes a standard untagged BAD response, together with a descriptive message.
       */
      public void badResponse( String message )
      {
          untagged();
          message( BAD );
          message( message );
          end();
      }
  
      /**
       * Writes an untagged OK response, with the supplied response code,
       * and an optional message.
       * @param responseCode The response code, included in [].
       * @param message The message to follow the []
       */
      public void okResponse( String responseCode, String message )
      {
          untagged();
          message( OK );
          responseCode( responseCode );
          message( message );
          end();
      }
  
      public void flagsResponse( MessageFlags flags )
      {
          untagged();
          message( "FLAGS" );
          message( flags.format() );
          end();
      }
  
      public void existsResponse( int count )
      {
          untagged();
          message( count );
          message( "EXISTS" );
          end();
      }
  
      public void recentResponse( int count )
      {
          untagged();
          message( count );
          message( "RECENT" );
          end();
      }
  
      public void commandResponse( ImapCommand command, String message )
      {
          untagged();
          commandName( command );
          message( message );
          end();
      }
  
      /**
       * Writes the message provided to the client, prepended with the
       * request tag.
       *
       * @param message The message to write to the client.
       */
      public void taggedResponse( String message )
      {
          tag();
          message( message );
          end();
      }
  
      /**
       * Writes the message provided to the client, prepended with the
       * untagged marker "*".
       *
       * @param message The message to write to the client.
       */
      public void untaggedResponse( String message )
      {
          untagged();
          message( message );
          end();
      }
  
      private void untagged()
      {
          writer.print( UNTAGGED );
      }
  
      private void tag()
      {
          writer.print( tag );
      }
  
      private void commandName( ImapCommand command )
      {
          String name = command.getName();
          writer.print( SP );
          writer.print( name );
      }
  
      private void message( String message )
      {
          if ( message != null ) {
              writer.print( SP );
              writer.print( message );
          }
      }
  
      private void message( int number )
      {
          writer.print( SP );
          writer.print( number );
      }
  
      private void responseCode( String responseCode )
      {
          if ( responseCode != null ) {
              writer.print( " [" );
              writer.print( responseCode );
              writer.print( "]" );
          }
      }
  
      private void end()
      {
          writer.println();
      }
  
  }
  
  
  
  1.1                  jakarta-james/proposals/imap2/java/org/apache/james/imapserver/ImapServer.java
  
  Index: ImapServer.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.avalon.cornerstone.services.connection.ConnectionHandler;
  import org.apache.avalon.excalibur.pool.DefaultPool;
  import org.apache.avalon.excalibur.pool.HardResourceLimitingPool;
  import org.apache.avalon.excalibur.pool.ObjectFactory;
  import org.apache.avalon.excalibur.pool.Pool;
  import org.apache.avalon.excalibur.pool.Poolable;
  import org.apache.avalon.framework.activity.Initializable;
  import org.apache.avalon.framework.component.ComponentException;
  import org.apache.avalon.framework.component.ComponentManager;
  import org.apache.avalon.framework.component.Composable;
  import org.apache.avalon.framework.configuration.Configuration;
  import org.apache.avalon.framework.configuration.ConfigurationException;
  import org.apache.avalon.framework.logger.LogEnabled;
  import org.apache.james.core.AbstractJamesService;
  import org.apache.james.services.MailServer;
  import org.apache.james.services.UsersRepository;
  import org.apache.james.services.UsersStore;
  import org.apache.james.util.watchdog.Watchdog;
  import org.apache.james.util.watchdog.WatchdogFactory;
  
  /**
   * TODO: this is a quick cut-and-paste hack from POP3Server. Should probably be
   * rewritten from scratch, together with ImapHandler.
   *
   * <p>Accepts IMAP connections on a server socket and dispatches them to IMAPHandlers.</p>
   *
   * <p>Also responsible for loading and parsing IMAP specific configuration.</p>
   *
   * @version 1.0.0, 24/04/1999
   * @author  Federico Barbieri <scoobie@pop.systemy.it>
   * @author  <a href="mailto:danny@apache.org">Danny Angus</a>
   * @author Peter M. Goldstein <farsight@alum.mit.edu>
   * @author Darrell DeBoer <darrell@apache.org>
   */
  public class ImapServer extends AbstractJamesService
  {
  
      /**
       * The internal mail server service
       */
      private MailServer mailServer;
  
      /**
       * The user repository for this server - used to authenticate users.
       */
      private UsersRepository users;
  
      /**
       * The ImapHost for this server - used for all mail storage.
       */
      private ImapHost imapHost;
  
      /**
       * The number of bytes to read before resetting
       * the connection timeout timer.  Defaults to
       * 20 KB.
       */
      private int lengthReset = 20 * 1024;
  
      /**
       * The pool used to provide IMAP Handler objects
       */
      private Pool theHandlerPool = null;
  
      /**
       * The factory used to provide IMAP Handler objects
       */
      private ObjectFactory theHandlerFactory = new IMAPHandlerFactory();
  
      /**
       * The factory used to generate Watchdog objects
       */
      private WatchdogFactory theWatchdogFactory;
  
      /**
       * The configuration data to be passed to the handler
       */
      private ImapHandlerConfigurationData theConfigData
              = new IMAPHandlerConfigurationDataImpl();
  
      /**
       * @see Composable#compose(ComponentManager)
       */
      public void compose( final ComponentManager componentManager )
              throws ComponentException
      {
          super.compose( componentManager );
          mailServer = ( MailServer ) componentManager.
                  lookup( "org.apache.james.services.MailServer" );
          UsersStore usersStore = ( UsersStore ) componentManager.
                  lookup( "org.apache.james.services.UsersStore" );
          users = usersStore.getRepository( "LocalUsers" );
          imapHost = ( ImapHost ) componentManager.
                  lookup( "org.apache.james.imapserver.ImapHost" );
          if ( users == null ) {
              throw new ComponentException( "The user repository could not be found." );
          }
      }
  
      /**
       * @see org.apache.avalon.framework.configuration.Configurable#configure(Configuration)
       */
      public void configure( final Configuration configuration ) throws ConfigurationException
      {
          super.configure( configuration );
          if ( isEnabled() ) {
              Configuration handlerConfiguration = configuration.getChild( "handler" );
              lengthReset = handlerConfiguration.getChild( "lengthReset" ).getValueAsInteger( lengthReset );
              if ( getLogger().isInfoEnabled() ) {
                  getLogger().info( "The idle timeout will be reset every " + lengthReset + " bytes." );
              }
          }
      }
  
      /**
       * @see Initializable#initialize()
       */
      public void initialize() throws Exception
      {
          super.initialize();
          if ( !isEnabled() ) {
              return;
          }
  
          if ( connectionLimit != null ) {
              theHandlerPool = new HardResourceLimitingPool( theHandlerFactory, 5, connectionLimit.intValue() );
              getLogger().debug( "Using a bounded pool for IMAP handlers with upper limit " + connectionLimit.intValue() );
          }
          else {
              // NOTE: The maximum here is not a real maximum.  The handler pool will continue to
              //       provide handlers beyond this value.
              theHandlerPool = new DefaultPool( theHandlerFactory, null, 5, 30 );
              getLogger().debug( "Using an unbounded pool for IMAP handlers." );
          }
          if ( theHandlerPool instanceof LogEnabled ) {
              ( ( LogEnabled ) theHandlerPool ).enableLogging( getLogger() );
          }
          if ( theHandlerPool instanceof Initializable ) {
              ( ( Initializable ) theHandlerPool ).initialize();
          }
  
          theWatchdogFactory = getWatchdogFactory();
      }
  
      /**
       * @see AbstractJamesService#getDefaultPort()
       */
      protected int getDefaultPort()
      {
          return 110;
      }
  
      /**
       * @see AbstractJamesService#getServiceType()
       */
      public String getServiceType()
      {
          return "IMAP Service";
      }
  
      /**
       * @see org.apache.avalon.cornerstone.services.connection.AbstractHandlerFactory#newHandler()
       */
      protected ConnectionHandler newHandler()
              throws Exception
      {
          ImapHandler theHandler = ( ImapHandler ) theHandlerPool.get();
  
          Watchdog theWatchdog = theWatchdogFactory.getWatchdog( theHandler.getWatchdogTarget() );
  
          theHandler.setConfigurationData( theConfigData );
  
          theHandler.setWatchdog( theWatchdog );
  
          return theHandler;
      }
  
      /**
       * @see org.apache.avalon.cornerstone.services.connection.ConnectionHandlerFactory#releaseConnectionHandler(ConnectionHandler)
       */
      public void releaseConnectionHandler( ConnectionHandler connectionHandler )
      {
          if ( !( connectionHandler instanceof ImapHandler ) ) {
              throw new IllegalArgumentException( "Attempted to return non-ImapHandler to pool." );
          }
          theHandlerPool.put( ( Poolable ) connectionHandler );
      }
  
      /**
       * The factory for producing handlers.
       */
      private static class IMAPHandlerFactory
              implements ObjectFactory
      {
  
          /**
           * @see ObjectFactory#newInstance()
           */
          public Object newInstance() throws Exception
          {
              return new ImapHandler();
          }
  
          /**
           * @see ObjectFactory#getCreatedClass()
           */
          public Class getCreatedClass()
          {
              return ImapHandler.class;
          }
  
          /**
           * @see ObjectFactory#decommission(Object)
           */
          public void decommission( Object object ) throws Exception
          {
              return;
          }
      }
  
      /**
       * A class to provide POP3 handler configuration to the handlers
       */
      private class IMAPHandlerConfigurationDataImpl
              implements ImapHandlerConfigurationData
      {
  
          /**
           * @see ImapHandlerConfigurationData#getHelloName()
           */
          public String getHelloName()
          {
              return ImapServer.this.helloName;
          }
  
          /**
           * @see ImapHandlerConfigurationData#getResetLength()
           */
          public int getResetLength()
          {
              return ImapServer.this.lengthReset;
          }
  
          /**
           * @see ImapHandlerConfigurationData#getMailServer()
           */
          public MailServer getMailServer()
          {
              return ImapServer.this.mailServer;
          }
  
          /**
           * @see ImapHandlerConfigurationData#getUsersRepository()
           */
          public UsersRepository getUsersRepository()
          {
              return ImapServer.this.users;
          }
  
          /** @see ImapHandlerConfigurationData#getImapHost */
          public ImapHost getImapHost()
          {
              return ImapServer.this.imapHost;
          }
      }
  }
  
  
  
  1.1                  jakarta-james/proposals/imap2/java/org/apache/james/imapserver/ImapServer.xinfo
  
  Index: ImapServer.xinfo
  ===================================================================
  <?xml version="1.0"?>
  
  <blockinfo>
  
    <!-- section to describe block -->
    <block>
      <version>1.0</version>
    </block>
  
    <!-- services that are offered by this block -->
    <services>
      <service name="org.apache.avalon.framework.component.Component" version="1.0"/>
    </services>
  
    <dependencies>
      <dependency>
        <service name="org.apache.james.services.MailStore" version="1.0"/>
      </dependency>
      <dependency>
        <service name="org.apache.james.services.UsersStore" version="1.0"/>
      </dependency>
      <dependency>
        <service name="org.apache.avalon.cornerstone.services.connection.ConnectionManager" 
                 version="1.0"/>
      </dependency>
      <dependency>
        <service name="org.apache.avalon.cornerstone.services.sockets.SocketManager" version="1.0"/>
      </dependency>
      <dependency>
        <service name="org.apache.james.services.MailServer" version="1.0"/>
      </dependency>
      <dependency>
        <service name="org.apache.avalon.cornerstone.services.threads.ThreadManager" version="1.0"/>
      </dependency>
  
        <dependency>
            <service name="org.apache.james.imapserver.ImapHost" version="1.0"/>
        </dependency>
    </dependencies>
  
  </blockinfo>
  
  
  
  1.1                  jakarta-james/proposals/imap2/java/org/apache/james/imapserver/ImapSession.java
  
  Index: ImapSession.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.avalon.framework.logger.Logger;
  import org.apache.james.services.User;
  import org.apache.james.services.UsersRepository;
  import org.apache.james.imapserver.store.ImapMailbox;
  
  /**
   * Encapsulates all state held for an ongoing Imap session,
   * which commences when a client first establishes a connection to the Imap
   * server, and continues until that connection is closed.
   *
   * @author  Darrell DeBoer <darrell@apache.org>
   *
   * @version $Revision: 1.1 $
   */
  public interface ImapSession
  {
      /**
       * Sends any unsolicited responses to the client, such as EXISTS and FLAGS
       * responses when the selected mailbox is modified by another user.
       * @param response The response to write to
       */
      public void unsolicitedResponses( ImapResponse response );
  
      /**
       * Closes the connection for this session.
       */
      public void closeConnection();
  
      /**
       * Provides the Imap host for this server, which is used for all access to mail
       * storage and subscriptions.
       * @return The ImapHost for this server.
       */
      ImapHost getHost();
  
      /**
       * Provides the UsersRepository for this session, to allow session
       * to validate logins.
       *
       * @return The UsersRepository for this session.
       */
      UsersRepository getUsers();
  
      /**
       * Provides a security logger for auditing logins.
       * @return The security logger.
       */
      Logger getSecurityLogger();
  
      /**
       * @return The hostname of the connected client.
       */
      String getClientHostname();
  
      /**
       * @return The IP address of the connected client.
       */
      String getClientIP();
  
      /**
       * @return Returns the current state of this session.
       */
      ImapSessionState getState();
  
      /**
       * Moves the session into {@link ImapSessionState#AUTHENTICATED} state with
       * the supplied user.
       * @param user The user who is authenticated for this session.
       */
      void setAuthenticated( User user );
  
      /**
       * Provides the authenticated user for this session, or <code>null</code> if this
       * session is not in {@link ImapSessionState#AUTHENTICATED} or
       * {@link ImapSessionState#SELECTED} state.
       * @return The user authenticated for this session
       */
      User getUser();
  
      /**
       * Moves this session into {@link ImapSessionState#SELECTED} state and sets the
       * supplied mailbox to be the currently selected mailbox.
       * @param mailbox The selected mailbox.
       * @param readOnly If <code>true</code>, the selection is set to be read only.
       */
      void setSelected( ImapMailbox mailbox, boolean readOnly );
  
      /**
       * Moves the session out of {@link ImapSessionState#SELECTED} state and back into
       * {@link ImapSessionState#AUTHENTICATED} state. The selected mailbox is cleared.
       */
      void deselect();
  
      /**
       * Provides the selected mailbox for this session, or <code>null</code> if this
       * session is not in {@link ImapSessionState#SELECTED} state.
       * @return the currently selected mailbox.
       */
      ImapMailbox getSelected();
  
      /**
       * TODO? return a read-only wrapper for read-only selections, and put the
       * isReadOnly() on the mailbox itself?
       * @return if the currently selected mailbox is open read only.
       */
      boolean selectedIsReadOnly();
  
  }
  
  
  
  1.1                  jakarta-james/proposals/imap2/java/org/apache/james/imapserver/ImapSessionState.java
  
  Index: ImapSessionState.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;
  
  /**
   * Enumerated type representing an IMAP session state.
   */
  public class ImapSessionState
  {
      public static final ImapSessionState NON_AUTHENTICATED = new ImapSessionState( "NON_AUTHENTICATED" );
      public static final ImapSessionState AUTHENTICATED = new ImapSessionState( "AUTHENTICATED" );
      public static final ImapSessionState SELECTED = new ImapSessionState( "SELECTED" );
      public static final ImapSessionState LOGOUT = new ImapSessionState( "LOGOUT" );
  
      private final String myName; // for debug only
  
      private ImapSessionState( String name )
      {
          myName = name;
      }
  
      public String toString()
      {
          return myName;
      }
  }
  
  
  
  1.1                  jakarta-james/proposals/imap2/java/org/apache/james/imapserver/JamesImapHost.java
  
  Index: JamesImapHost.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.services.User;
  import org.apache.james.imapserver.store.ImapStore;
  import org.apache.james.imapserver.store.InMemoryStore;
  import org.apache.james.imapserver.store.ImapMailbox;
  import org.apache.james.imapserver.store.MailboxException;
  
  import java.util.ArrayList;
  import java.util.Collection;
  import java.util.Iterator;
  import java.util.StringTokenizer;
  
  /**
   * An initial implementation of an ImapHost. By default, uses,
   * the {@link org.apache.james.imapserver.store.InMemoryStore} implementation of {@link org.apache.james.imapserver.store.ImapStore}.
   * TODO: Make the underlying store configurable with Phoenix.
   *
   * @author  Darrell DeBoer <darrell@apache.org>
   *
   * @version $Revision: 1.1 $
   */
  public class JamesImapHost
          implements ImapHost, ImapConstants
  {
      private ImapStore store;
  
      public JamesImapHost()
      {
          store = new InMemoryStore();
      }
  
      public JamesImapHost( ImapStore store )
      {
          this.store = store;
      }
  
      public char getHierarchyDelimiter()
      {
          return HIERARCHY_DELIMITER_CHAR;
      }
  
      /** @see ImapHost#getMailbox */
      public ImapMailbox getMailbox( User user, String mailboxName )
      {
          String name = getQualifiedMailboxName( user, mailboxName );
          ImapMailbox mailbox = store.getMailbox( name );
          return ( checkViewable( mailbox ) );
      }
  
      public ImapMailbox getMailbox( User user, String mailboxName, boolean mustExist )
              throws MailboxException
      {
          ImapMailbox mailbox = getMailbox( user, mailboxName );
          if ( mustExist && mailbox == null )
          {
              throw new MailboxException( "No such mailbox." );
          }
          return mailbox;
      }
  
      private ImapMailbox checkViewable( ImapMailbox mailbox )
      {
          // TODO implement this.
          return mailbox;
      }
  
      /** @see ImapHost#getInbox */
      public ImapMailbox getInbox( User user ) throws MailboxException
      {
          return getMailbox( user, INBOX_NAME );
      }
  
      /** @see ImapHost#createPrivateMailAccount */
      public void createPrivateMailAccount( User user ) throws MailboxException
      {
          ImapMailbox root = store.getMailbox( USER_NAMESPACE );
          ImapMailbox userRoot = store.createMailbox( root, user.getUserName(), false );
          store.createMailbox( userRoot, INBOX_NAME, true );
      }
  
      /** @see ImapHost#createMailbox */
      public ImapMailbox createMailbox( User user, String mailboxName )
              throws AuthorizationException, MailboxException
      {
          String qualifiedName = getQualifiedMailboxName( user, mailboxName );
          if ( store.getMailbox( qualifiedName ) != null )
          {
              throw new MailboxException( "Mailbox already exists." );
          }
  
          StringTokenizer tokens = new StringTokenizer( qualifiedName,
                                                        HIERARCHY_DELIMITER );
  
          if ( tokens.countTokens() < 2 ) {
              throw new MailboxException( "Cannot create mailbox at namespace level." );
          }
  
          String namespaceRoot = tokens.nextToken();
          ImapMailbox mailbox = store.getMailbox( namespaceRoot );
          if ( mailbox == null ) {
              throw new MailboxException( "Invalid namespace." );
          }
  
          while ( tokens.hasMoreTokens() ) {
              // Get the next name from the list, and find the child
              String childName = tokens.nextToken();
              ImapMailbox child = store.getMailbox( mailbox, childName );
              // Create if neccessary
              if ( child == null ) {
                  // TODO check permissions.
                  boolean makeSelectable = ( !tokens.hasMoreTokens() );
                  child = store.createMailbox( mailbox, childName, makeSelectable );
              }
              mailbox = child;
          }
  
          return mailbox;
      }
  
      /** @see ImapHost#deleteMailbox */
      public void deleteMailbox( User user, String mailboxName )
              throws MailboxException, AuthorizationException
      {
          ImapMailbox toDelete = getMailbox( user, mailboxName, true );
  
          if ( store.getChildren( toDelete ).isEmpty() ) {
              // Does this delete all messages?
              store.deleteMailbox( toDelete );
          }
          else {
              if ( toDelete.isSelectable() ) {
                  // TODO delete all messages.
                  store.setSelectable( toDelete, false );
              }
              else {
                  throw new MailboxException( "Can't delete a non-selectable mailbox with children." );
              }
          }
      }
  
      /** @see ImapHost#renameMailbox */
      public void renameMailbox( User user,
                                 String oldMailboxName,
                                 String newMailboxName )
              throws MailboxException, AuthorizationException
      {
  
          ImapMailbox existingMailbox = getMailbox( user, oldMailboxName, true );
  
          // TODO: check permissions.
  
          // Handle case where existing is INBOX
          //          - just create new folder, move all messages,
          //            and leave INBOX (with children) intact.
          String userInboxName = getQualifiedMailboxName( user, INBOX_NAME );
          if ( userInboxName.equals( existingMailbox.getFullName() ) ) {
              ImapMailbox newBox = createMailbox( user, newMailboxName );
              // TODO copy all messages from INBOX.
              return;
          }
  
          store.renameMailbox( existingMailbox, newMailboxName );
      }
  
      public Collection listSubscribedMailboxes( User user,
                                                 String mailboxPattern )
              throws MailboxException
      {
          throw new MailboxException( "Subscriptions not implemented" );
  //        return listMailboxes( user, mailboxPattern, true );
      }
  
      public Collection listMailboxes( User user,
                                       String mailboxPattern )
              throws MailboxException
      {
          return listMailboxes( user, mailboxPattern, false );
      }
  
      /**
       * Partial implementation of list functionality.
       * TODO: Handle subscriptions (currently ignored)
       * TODO: Handle wildcards anywhere in mailbox pattern
       *       (currently only supported as last character of pattern)
       * @see org.apache.james.imapserver.ImapHost#listMailboxes
       */
      private Collection listMailboxes( User user,
                                       String mailboxPattern,
                                       boolean subscribedOnly )
              throws MailboxException
      {
          System.out.println( "Listing for user: '" + user.getUserName() + "'" +
                              " pattern:'" + mailboxPattern + "'" );
  
          ArrayList mailboxes = new ArrayList();
          String qualifiedPattern = getQualifiedMailboxName( user, mailboxPattern );
  
          Iterator iter = store.listMailboxes( qualifiedPattern ).iterator();
          while ( iter.hasNext() ) {
              ImapMailbox mailbox = ( ImapMailbox ) iter.next();
  
              // Sets the mailbox to null if it's not viewable.
              mailbox = checkViewable( mailbox );
  
              // TODO check subscriptions.
              if ( subscribedOnly ) {
                  // if not subscribed
                  mailbox = null;
              }
  
              if ( mailbox != null ) {
                  mailboxes.add( mailbox );
              }
          }
  
          return mailboxes;
      }
  
      public void subscribe( User user, String mailbox )
              throws MailboxException
      {
          // TODO implement
      }
  
      public void unsubscribe( String username, String mailbox )
              throws MailboxException
      {
          // TODO implement
      }
  
      /**
       * Convert a user specified mailbox name into a server absolute name.
       * If the mailboxName begins with the namespace token,
       * return as-is.
       * If not, need to resolve the Mailbox name for this user.
       * Example:
       * <br> Convert "INBOX" for user "Fred.Flinstone" into
       * absolute name: "#user.Fred.Flintstone.INBOX"
       *
       * @return String of absoluteName, null if not valid selection
       */
      private String getQualifiedMailboxName( User user, String mailboxName )
      {
          String userName = user.getUserName();
  
          if ( "INBOX".equalsIgnoreCase( mailboxName ) ) {
              return USER_NAMESPACE + HIERARCHY_DELIMITER + userName +
                      HIERARCHY_DELIMITER + INBOX_NAME;
          }
  
          if ( mailboxName.startsWith( NAMESPACE_PREFIX ) ) {
              return mailboxName;
          }
          else {
              if ( mailboxName.length() == 0 ) {
                  return USER_NAMESPACE + HIERARCHY_DELIMITER + userName;
              }
              else {
                  return USER_NAMESPACE + HIERARCHY_DELIMITER + userName +
                          HIERARCHY_DELIMITER + mailboxName;
              }
          }
      }
  
  
  }
  
  
  
  1.1                  jakarta-james/proposals/imap2/java/org/apache/james/imapserver/JamesImapHost.xinfo
  
  Index: JamesImapHost.xinfo
  ===================================================================
  <?xml version="1.0"?>
  
  <blockinfo>
  
    <!-- section to describe block -->
    <block>
      <version>1.0</version>
    </block>
  
    <!-- services that are offered by this block -->
    <services>
      <service name="org.apache.james.imapserver.ImapHost" version="1.0"/>
    </services>
  
    <dependencies>
        <!-- TODO: Make ImapStore a service, and add as a dependency -->
    </dependencies>
  
  </blockinfo>
  
  
  
  1.1                  jakarta-james/proposals/imap2/java/org/apache/james/imapserver/ProtocolException.java
  
  Index: ProtocolException.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;
  
  /**
   *
   * @author  Darrell DeBoer <darrell@apache.org>
   *
   * @version $Revision: 1.1 $
   */
  public class ProtocolException extends Exception
  {
      public ProtocolException( String s )
      {
          super( s );
      }
  }
  
  
  
  1.1                  jakarta-james/proposals/imap2/java/org/apache/james/imapserver/commands/AuthenticateCommand.java
  
  Index: AuthenticateCommand.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.ImapRequestParser;
  import org.apache.james.imapserver.ImapResponse;
  import org.apache.james.imapserver.ImapSession;
  import org.apache.james.imapserver.ProtocolException;
  
  /**
   * Handles processeing for the AUTHENTICATE imap command.
   *
   * @author  Darrell DeBoer <darrell@apache.org>
   *
   * @version $Revision: 1.1 $
   */
  class AuthenticateCommand extends NonAuthenticatedStateCommand
  {
      public static final String NAME = "AUTHENTICATE";
      public static final String ARGS = "<auth_type> *(CRLF base64)";
  
      /** @see CommandTemplate#doProcess */
      protected void doProcess( ImapRequestParser request,
                                ImapResponse response,
                                ImapSession session
                                ) throws ProtocolException
      {
          String authType = request.astring();
          request.endLine();
  
          response.commandFailed( this, "Unsupported authentication mechanism '" +
                                        authType + "'" );
      }
  
      /** @see ImapCommand#getName */
      public String getName()
      {
          return NAME;
      }
  
      /** @see CommandTemplate#getArgSyntax */
      public String getArgSyntax()
      {
          return ARGS;
      }
  }
  
  /*
  6.2.1.  AUTHENTICATE Command
  
     Arguments:  authentication mechanism name
  
     Responses:  continuation data can be requested
  
     Result:     OK - authenticate completed, now in authenticated state
                 NO - authenticate failure: unsupported authentication
                      mechanism, credentials rejected
                BAD - command unknown or arguments invalid,
                      authentication exchange cancelled
  
        The AUTHENTICATE command indicates an authentication mechanism,
        such as described in [IMAP-AUTH], to the server.  If the server
        supports the requested authentication mechanism, it performs an
        authentication protocol exchange to authenticate and identify the
        client.  It MAY also negotiate an OPTIONAL protection mechanism
        for subsequent protocol interactions.  If the requested
        authentication mechanism is not supported, the server SHOULD
        reject the AUTHENTICATE command by sending a tagged NO response.
  
        The authentication protocol exchange consists of a series of
        server challenges and client answers that are specific to the
        authentication mechanism.  A server challenge consists of a
        command continuation request response with the "+" token followed
        by a BASE64 encoded string.  The client answer consists of a line
        consisting of a BASE64 encoded string.  If the client wishes to
        cancel an authentication exchange, it issues a line with a single
        "*".  If the server receives such an answer, it MUST reject the
        AUTHENTICATE command by sending a tagged BAD response.
  
        A protection mechanism provides integrity and privacy protection
        to the connection.  If a protection mechanism is negotiated, it is
        applied to all subsequent data sent over the connection.  The
        protection mechanism takes effect immediately following the CRLF
        that concludes the authentication exchange for the client, and the
        CRLF of the tagged OK response for the server.  Once the
        protection mechanism is in effect, the stream of command and
        response octets is processed into buffers of ciphertext.  Each
        buffer is transferred over the connection as a stream of octets
        prepended with a four octet field in network byte order that
        represents the length of the following data.  The maximum
        ciphertext buffer length is defined by the protection mechanism.
  
        Authentication mechanisms are OPTIONAL.  Protection mechanisms are
        also OPTIONAL; an authentication mechanism MAY be implemented
        without any protection mechanism.  If an AUTHENTICATE command
        fails with a NO response, the client MAY try another
        authentication mechanism by issuing another AUTHENTICATE command,
        or MAY attempt to authenticate by using the LOGIN command.  In
        other words, the client MAY request authentication types in
        decreasing order of preference, with the LOGIN command as a last
        resort.
  
     Example:    S: * OK KerberosV4 IMAP4rev1 Server
                 C: A001 AUTHENTICATE KERBEROS_V4
                 S: + AmFYig==
                 C: BAcAQU5EUkVXLkNNVS5FRFUAOCAsho84kLN3/IJmrMG+25a4DT
                    +nZImJjnTNHJUtxAA+o0KPKfHEcAFs9a3CL5Oebe/ydHJUwYFd
                    WwuQ1MWiy6IesKvjL5rL9WjXUb9MwT9bpObYLGOKi1Qh
                 S: + or//EoAADZI=
                 C: DiAF5A4gA+oOIALuBkAAmw==
                 S: A001 OK Kerberos V4 authentication successful
  
        Note: the line breaks in the first client answer are for editorial
        clarity and are not in real authenticators.
  */
  
  
  
  1.1                  jakarta-james/proposals/imap2/java/org/apache/james/imapserver/commands/AuthenticatedStateCommand.java
  
  Index: AuthenticatedStateCommand.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.ImapSessionState;
  
  /**
   * A base class for ImapCommands only valid in AUTHENTICATED and SELECTED states.
   *
   * @author  Darrell DeBoer <darrell@apache.org>
   *
   * @version $Revision: 1.1 $
   */
  abstract class AuthenticatedStateCommand extends CommandTemplate
  {
      /**
       * Check that the state is {@link ImapSessionState#AUTHENTICATED } or
       * {@link ImapSessionState#SELECTED}
       */
      public boolean validForState( ImapSessionState state )
      {
          return ( state == ImapSessionState.AUTHENTICATED
                  || state == ImapSessionState.SELECTED );
      }
  }
  
  
  
  1.1                  jakarta-james/proposals/imap2/java/org/apache/james/imapserver/commands/CapabilityCommand.java
  
  Index: CapabilityCommand.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.ImapRequestParser;
  import org.apache.james.imapserver.ImapResponse;
  import org.apache.james.imapserver.ImapSession;
  import org.apache.james.imapserver.ProtocolException;
  
  /**
   * Handles processeing for the CAPABILITY imap command.
   *
   * @author  Darrell DeBoer <darrell@apache.org>
   *
   * @version $Revision: 1.1 $
   */
  class CapabilityCommand extends CommandTemplate
  {
      public static final String NAME = "CAPABILITY";
      public static final String ARGS = null;
  
      public static final String CAPABILITY_RESPONSE = NAME + SP + VERSION;
  
      /** @see CommandTemplate#doProcess */
      protected void doProcess( ImapRequestParser request,
                                ImapResponse response,
                                ImapSession session )
              throws ProtocolException
      {
          request.endLine();
          response.untaggedResponse( CAPABILITY_RESPONSE );
          session.unsolicitedResponses( response );
          response.commandComplete( this );
      }
  
      /** @see ImapCommand#getName */
      public String getName()
      {
          return NAME;
      }
  
      /** @see CommandTemplate#getArgSyntax */
      public String getArgSyntax()
      {
          return ARGS;
      }
  }
  
  /*
  6.1.1.  CAPABILITY Command
  
     Arguments:  none
  
     Responses:  REQUIRED untagged response: CAPABILITY
  
     Result:     OK - capability completed
                 BAD - command unknown or arguments invalid
  
        The CAPABILITY command requests a listing of capabilities that the
        server supports.  The server MUST send a single untagged
        CAPABILITY response with "IMAP4rev1" as one of the listed
        capabilities before the (tagged) OK response.  This listing of
        capabilities is not dependent upon connection state or user.  It
        is therefore not necessary to issue a CAPABILITY command more than
        once in a connection.
  
        A capability name which begins with "AUTH=" indicates that the
        server supports that particular authentication mechanism.  All
        such names are, by definition, part of this specification.  For
        example, the authorization capability for an experimental
        "blurdybloop" authenticator would be "AUTH=XBLURDYBLOOP" and not
        "XAUTH=BLURDYBLOOP" or "XAUTH=XBLURDYBLOOP".
  
        Other capability names refer to extensions, revisions, or
        amendments to this specification.  See the documentation of the
        CAPABILITY response for additional information.  No capabilities,
        beyond the base IMAP4rev1 set defined in this specification, are
        enabled without explicit client action to invoke the capability.
  
        See the section entitled "Client Commands -
        Experimental/Expansion" for information about the form of site or
        implementation-specific capabilities.
  
     Example:    C: abcd CAPABILITY
                 S: * CAPABILITY IMAP4rev1 AUTH=KERBEROS_V4
                 S: abcd OK CAPABILITY completed
  
  
  7.2.1.  CAPABILITY Response
  
     Contents:   capability listing
  
        The CAPABILITY response occurs as a result of a CAPABILITY
        command.  The capability listing contains a space-separated
        listing of capability names that the server supports.  The
        capability listing MUST include the atom "IMAP4rev1".
  
        A capability name which begins with "AUTH=" indicates that the
        server supports that particular authentication mechanism.
        Other capability names indicate that the server supports an
        extension, revision, or amendment to the IMAP4rev1 protocol.
        Server responses MUST conform to this document until the client
        issues a command that uses the associated capability.
  
        Capability names MUST either begin with "X" or be standard or
        standards-track IMAP4rev1 extensions, revisions, or amendments
        registered with IANA.  A server MUST NOT offer unregistered or
        non-standard capability names, unless such names are prefixed with
        an "X".
  
        Client implementations SHOULD NOT require any capability name
        other than "IMAP4rev1", and MUST ignore any unknown capability
        names.
  
     Example:    S: * CAPABILITY IMAP4rev1 AUTH=KERBEROS_V4 XPIG-LATIN
  
  */
  
  
  
  1.1                  jakarta-james/proposals/imap2/java/org/apache/james/imapserver/commands/CommandTemplate.java
  
  Index: CommandTemplate.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.avalon.framework.logger.AbstractLogEnabled;
  import org.apache.james.imapserver.AuthorizationException;
  import org.apache.james.imapserver.ImapConstants;
  import org.apache.james.imapserver.store.ImapMailbox;
  import org.apache.james.imapserver.ImapRequestParser;
  import org.apache.james.imapserver.ImapResponse;
  import org.apache.james.imapserver.ImapSession;
  import org.apache.james.imapserver.ImapSessionState;
  import org.apache.james.imapserver.store.MailboxException;
  import org.apache.james.imapserver.ProtocolException;
  
  /**
   * Base class for all command implementations. This class provides common
   * core functionality useful for all {@link org.apache.james.imapserver.commands.ImapCommand} implementations.
   *
   * @author  Darrell DeBoer <darrell@apache.org>
   *
   * @version $Revision: 1.1 $
   */
  abstract class CommandTemplate
          extends AbstractLogEnabled
          implements ImapCommand, ImapConstants
  {
      /**
       * By default, valid in any state (unless overridden by subclass.
       * @see org.apache.james.imapserver.commands.ImapCommand#validForState
       */
      public boolean validForState( ImapSessionState state )
      {
          return true;
      }
  
      /**
       * Template methods for handling command processing. This method reads
       * argument values (validating them), and checks the request for correctness.
       * If correct, the command processing is delegated to the specific command
       * implemenation.
       *
       * @see ImapCommand#process
       */
      public void process( ImapRequestParser request,
                           ImapResponse response,
                           ImapSession session )
      {
          try {
              doProcess( request, response, session );
          }
          catch ( MailboxException e ) {
              response.commandFailed( this, e.getMessage() );
          }
          catch ( AuthorizationException e ) {
              String msg = "Authorization error: Lacking permissions to perform requested operation.";
              response.commandFailed( this, msg );
          }
          catch ( ProtocolException e ) {
              String msg = e.getMessage() + " Command should be '" +
                      getExpectedMessage() + "'";
              response.commandError( msg );
          }
      }
  
      /**
       * This is the method overridden by specific command implementations to
       * perform commend-specific processing.
       *
       * @param request The client request
       * @param response The server response
       * @param session The current client session
       */
      protected abstract void doProcess( ImapRequestParser request,
                                         ImapResponse response,
                                         ImapSession session )
              throws ProtocolException, MailboxException, AuthorizationException;
  
      /**
       * Provides a message which describes the expected format and arguments
       * for this command. This is used to provide user feedback when a command
       * request is malformed.
       *
       * @return A message describing the command protocol format.
       */
      protected String getExpectedMessage()
      {
          StringBuffer syntax = new StringBuffer( "<tag> " );
          syntax.append( getName() );
  
          String args = getArgSyntax();
          if ( args != null && args.length() > 0 ) {
              syntax.append( " " );
              syntax.append( args );
          }
  
          return syntax.toString();
      }
  
      /**
       * Provides the syntax for the command arguments if any. This value is used
       * to provide user feedback in the case of a malformed request.
       *
       * For commands which do not allow any arguments, <code>null</code> should
       * be returned.
       *
       * @return The syntax for the command arguments, or <code>null</code> for
       *         commands without arguments.
       */
      protected abstract String getArgSyntax();
  
      protected ImapMailbox getMailbox( String mailboxName,
                                        ImapSession session,
                                        boolean mustExist )
              throws MailboxException
      {
          return session.getHost().getMailbox( session.getUser(), mailboxName, mustExist );
      }
  
  //    /**
  //     * Logs the command details.
  //     */
  //    protected void logCommand( ImapRequestParser request, ImapSession session )
  //    {
  //        getLogger().debug( request.getCommand() + " command completed for " +
  //                           session.getRemoteHost() + "(" +
  //                           session.getRemoteIP() + ")" );
  //    }
  //
  //    protected ACLMailbox getMailbox( ImapSession session, String mailboxName, String command )
  //    {
  //        if ( session.getState() == ImapSessionState.SELECTED && session.currentMailbox().equals( mailboxName ) ) {
  //            return session.getCurrentMailbox();
  //        }
  //        else {
  //            try {
  //                return session.getImapHost().getMailbox( session.getCurrentUser(), mailboxName );
  //            } catch ( MailboxException me ) {
  //                if ( me.isRemote() ) {
  //                    session.noResponse( "[REFERRAL " + me.getRemoteServer() + "]" + SP + "Remote mailbox" );
  //                } else {
  //                    session.noResponse( command, "Unknown mailbox" );
  //                    getLogger().info( "MailboxException in method getBox for user: "
  //                                      + session.getCurrentUser() + " mailboxName: " + mailboxName + " was "
  //                                      + me.getMessage() );
  //                }
  //                return null;
  //            }
  //            catch ( AccessControlException e ) {
  //                session.noResponse( command, "Unknown mailbox" );
  //                return null;
  //            }
  //        }
  //    }
  
  //    public static String readAstring( StringTokenizer tokens )
  //    {
  //        if ( ! tokens.hasMoreTokens() ) {
  //            throw new RuntimeException( "Not enough tokens" );
  //        }
  //        String token = tokens.nextToken();
  //        Assert.isTrue( token.length() > 0 );
  //
  //        StringBuffer astring = new StringBuffer( token );
  //
  //        if ( astring.charAt(0) == '\"' ) {
  //            while ( astring.length() == 1 ||
  //                    astring.charAt( astring.length() - 1 ) != '\"' ) {
  //                if ( tokens.hasMoreTokens() ) {
  //                    astring.append( tokens.nextToken() );
  //                }
  //                else {
  //                    throw new RuntimeException( "Missing closing quote" );
  //                }
  //            }
  //            astring.deleteCharAt( 0 );
  //            astring.deleteCharAt( astring.length() - 1 );
  //        }
  //
  //        return astring.toString();
  //    }
  //
  //    public String decodeAstring( String rawAstring )
  //    {
  //
  //        if ( rawAstring.length() == 0 ) {
  //            return rawAstring;
  //        }
  //
  //        if ( rawAstring.startsWith( "\"" ) ) {
  //            //quoted string
  //            if ( rawAstring.endsWith( "\"" ) ) {
  //                if ( rawAstring.length() == 2 ) {
  //                    return new String(); //ie blank
  //                }
  //                else {
  //                    return rawAstring.substring( 1, rawAstring.length() - 1 );
  //                }
  //            }
  //            else {
  //                getLogger().error( "Quoted string with no closing quote." );
  //                return null;
  //            }
  //        }
  //        else {
  //            //atom
  //            return rawAstring;
  //        }
  //    }
  //
  //    public void setArgs( List args )
  //    {
  //        this.args = args;
  //    }
  }
  
  
  
  1.1                  jakarta-james/proposals/imap2/java/org/apache/james/imapserver/commands/CreateCommand.java
  
  Index: CreateCommand.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.AuthorizationException;
  import org.apache.james.imapserver.ImapRequestParser;
  import org.apache.james.imapserver.ImapResponse;
  import org.apache.james.imapserver.ImapSession;
  import org.apache.james.imapserver.store.MailboxException;
  import org.apache.james.imapserver.ProtocolException;
  
  /**
   * Handles processeing for the CREATE imap command.
   *
   * @author  Darrell DeBoer <darrell@apache.org>
   *
   * @version $Revision: 1.1 $
   */
  class CreateCommand extends AuthenticatedStateCommand
  {
      public static final String NAME = "CREATE";
      public static final String ARGS = "<mailbox>";
  
      /** @see CommandTemplate#doProcess */
      protected void doProcess( ImapRequestParser request,
                                ImapResponse response,
                                ImapSession session )
              throws ProtocolException, MailboxException, AuthorizationException
      {
          String mailboxName = request.astring();
          request.endLine();
  
          session.getHost().createMailbox( session.getUser(), mailboxName );
          session.unsolicitedResponses( response );
          response.commandComplete( this );
      }
  
      /** @see ImapCommand#getName */
      public String getName()
      {
          return NAME;
      }
  
      /** @see CommandTemplate#getArgSyntax */
      public String getArgSyntax()
      {
          return ARGS;
      }
  
  }
  
  /*
  6.3.3.  CREATE Command
  
     Arguments:  mailbox name
  
     Responses:  no specific responses for this command
  
     Result:     OK - create completed
                 NO - create failure: can't create mailbox with that name
                 BAD - command unknown or arguments invalid
  
        The CREATE command creates a mailbox with the given name.  An OK
        response is returned only if a new mailbox with that name has been
        created.  It is an error to attempt to create INBOX or a mailbox
        with a name that refers to an extant mailbox.  Any error in
        creation will return a tagged NO response.
  
        If the mailbox name is suffixed with the server's hierarchy
        separator character (as returned from the server by a LIST
        command), this is a declaration that the client intends to create
        mailbox names under this name in the hierarchy.  Server
        implementations that do not require this declaration MUST ignore
        it.
  
        If the server's hierarchy separator character appears elsewhere in
        the name, the server SHOULD create any superior hierarchical names
        that are needed for the CREATE command to complete successfully.
        In other words, an attempt to create "foo/bar/zap" on a server in
        which "/" is the hierarchy separator character SHOULD create foo/
        and foo/bar/ if they do not already exist.
  
        If a new mailbox is created with the same name as a mailbox which
        was deleted, its unique identifiers MUST be greater than any
        unique identifiers used in the previous incarnation of the mailbox
        UNLESS the new incarnation has a different unique identifier
        validity value.  See the description of the UID command for more
        detail.
  
     Example:    C: A003 CREATE owatagusiam/
                 S: A003 OK CREATE completed
                 C: A004 CREATE owatagusiam/blurdybloop
                 S: A004 OK CREATE completed
  
        Note: the interpretation of this example depends on whether "/"
        was returned as the hierarchy separator from LIST.  If "/" is the
        hierarchy separator, a new level of hierarchy named "owatagusiam"
        with a member called "blurdybloop" is created.  Otherwise, two
        mailboxes at the same hierarchy level are created.
  */
  
  
  
  1.1                  jakarta-james/proposals/imap2/java/org/apache/james/imapserver/commands/DeleteCommand.java
  
  Index: DeleteCommand.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.AuthorizationException;
  import org.apache.james.imapserver.ImapRequestParser;
  import org.apache.james.imapserver.ImapResponse;
  import org.apache.james.imapserver.ImapSession;
  import org.apache.james.imapserver.store.MailboxException;
  import org.apache.james.imapserver.ProtocolException;
  
  /**
   * Handles processeing for the DELETE imap command.
   *
   * @author  Darrell DeBoer <darrell@apache.org>
   *
   * @version $Revision: 1.1 $
   */
  class DeleteCommand extends AuthenticatedStateCommand
  {
      public static final String NAME = "DELETE";
      public static final String ARGS = "<mailbox>";
  
      /** @see CommandTemplate#doProcess */
      protected void doProcess( ImapRequestParser request,
                                ImapResponse response,
                                ImapSession session )
              throws ProtocolException, MailboxException, AuthorizationException
      {
  
          String mailboxName = request.astring();
          request.endLine();
  
          session.getHost().deleteMailbox( session.getUser(), mailboxName );
  
          session.unsolicitedResponses( response );
          response.commandComplete( this );
      }
  
      /** @see ImapCommand#getName */
      public String getName()
      {
          return NAME;
      }
  
      /** @see CommandTemplate#getArgSyntax */
      public String getArgSyntax()
      {
          return ARGS;
      }
  }
  
  /*
  6.3.4.  DELETE Command
  
     Arguments:  mailbox name
  
     Responses:  no specific responses for this command
  
     Result:     OK - delete completed
                 NO - delete failure: can't delete mailbox with that name
                 BAD - command unknown or arguments invalid
  
        The DELETE command permanently removes the mailbox with the given
        name.  A tagged OK response is returned only if the mailbox has
        been deleted.  It is an error to attempt to delete INBOX or a
        mailbox name that does not exist.
  
        The DELETE command MUST NOT remove inferior hierarchical names.
        For example, if a mailbox "foo" has an inferior "foo.bar"
        (assuming "." is the hierarchy delimiter character), removing
        "foo" MUST NOT remove "foo.bar".  It is an error to attempt to
        delete a name that has inferior hierarchical names and also has
        the \Noselect mailbox name attribute (see the description of the
        LIST response for more details).
  
        It is permitted to delete a name that has inferior hierarchical
        names and does not have the \Noselect mailbox name attribute.  In
        this case, all messages in that mailbox are removed, and the name
        will acquire the \Noselect mailbox name attribute.
  
        The value of the highest-used unique identifier of the deleted
        mailbox MUST be preserved so that a new mailbox created with the
        same name will not reuse the identifiers of the former
        incarnation, UNLESS the new incarnation has a different unique
        identifier validity value.  See the description of the UID command
        for more detail.
  
  
     Examples:   C: A682 LIST "" *
                 S: * LIST () "/" blurdybloop
                 S: * LIST (\Noselect) "/" foo
                 S: * LIST () "/" foo/bar
                 S: A682 OK LIST completed
                 C: A683 DELETE blurdybloop
                 S: A683 OK DELETE completed
                 C: A684 DELETE foo
                 S: A684 NO Name "foo" has inferior hierarchical names
                 C: A685 DELETE foo/bar
                 S: A685 OK DELETE Completed
                 C: A686 LIST "" *
                 S: * LIST (\Noselect) "/" foo
                 S: A686 OK LIST completed
                 C: A687 DELETE foo
                 S: A687 OK DELETE Completed
  
  
                 C: A82 LIST "" *
                 S: * LIST () "." blurdybloop
                 S: * LIST () "." foo
                 S: * LIST () "." foo.bar
                 S: A82 OK LIST completed
                 C: A83 DELETE blurdybloop
                 S: A83 OK DELETE completed
                 C: A84 DELETE foo
                 S: A84 OK DELETE Completed
                 C: A85 LIST "" *
                 S: * LIST () "." foo.bar
                 S: A85 OK LIST completed
                 C: A86 LIST "" %
                 S: * LIST (\Noselect) "." foo
                 S: A86 OK LIST completed
  */
  
  
  
  1.1                  jakarta-james/proposals/imap2/java/org/apache/james/imapserver/commands/ExamineCommand.java
  
  Index: ExamineCommand.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 $
   */
  class ExamineCommand extends SelectCommand
  {
      public static final String NAME = "EXAMINE";
  
      public String getName()
      {
          return NAME;
      }
  }
  
  
  
  1.1                  jakarta-james/proposals/imap2/java/org/apache/james/imapserver/commands/ImapCommand.java
  
  Index: ImapCommand.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.ImapRequestParser;
  import org.apache.james.imapserver.ImapResponse;
  import org.apache.james.imapserver.ImapSession;
  import org.apache.james.imapserver.ImapSessionState;
  
  /**
   * Represents a processor for a particular Imap command. Implementations of this
   * interface should encpasulate all command specific processing.
   *
   * @author  Darrell DeBoer <darrell@apache.org>
   *
   * @version $Revision: 1.1 $
   */
  public interface ImapCommand
  {
      /**
       * @return the name of the command, as specified in rfc2060.
       */
      String getName();
  
      /**
       * Specifies if this command is valid for the given session state.
       * @param state The current {@link org.apache.james.imapserver.ImapSessionState state} of the {@link org.apache.james.imapserver.ImapSession}
       * @return <code>true</code> if the command is valid in this state.
       */
      boolean validForState( ImapSessionState state );
  
      /**
       * Performs all processing of the current Imap request. Reads command
       * arguments from the request, performs processing, and writes responses
       * back to the request object, which are sent to the client.
       * @param request The current client request
       * @param response The current server response
       * @param session The current session
       */
      void process( ImapRequestParser request,
                    ImapResponse response,
                    ImapSession session );
  }
  
  
  
  1.1                  jakarta-james/proposals/imap2/java/org/apache/james/imapserver/commands/ImapCommandFactory.java
  
  Index: ImapCommandFactory.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.avalon.framework.CascadingRuntimeException;
  import org.apache.avalon.framework.logger.AbstractLogEnabled;
  import org.apache.avalon.framework.logger.LogEnabled;
  
  import java.util.HashMap;
  import java.util.Map;
  
  /**
   * A factory for ImapCommand instances, provided based on the command name.
   * Command instances are created on demand, when first accessed.
   *
   * @author  Darrell DeBoer <darrell@apache.org>
   *
   * @version $Revision: 1.1 $
   */
  public class ImapCommandFactory
          extends AbstractLogEnabled
  {
      private Map _imapCommands;
  
      public ImapCommandFactory()
      {
          _imapCommands = new HashMap();
  
          // Commands valid in any state
          // CAPABILITY, NOOP, and LOGOUT
          _imapCommands.put( CapabilityCommand.NAME, CapabilityCommand.class );
          _imapCommands.put( NoopCommand.NAME, NoopCommand.class );
          _imapCommands.put( LogoutCommand.NAME, LogoutCommand.class );
  
          // Commands valid in NON_AUTHENTICATED state.
          // AUTHENTICATE and LOGIN
          _imapCommands.put( AuthenticateCommand.NAME, AuthenticateCommand.class );
          _imapCommands.put( LoginCommand.NAME, LoginCommand.class );
  
          // Commands valid in AUTHENTICATED or SELECTED state.
          // RFC2060: SELECT, EXAMINE, CREATE, DELETE, RENAME, SUBSCRIBE, UNSUBSCRIBE, LIST, LSUB, STATUS, and APPEND
          _imapCommands.put( SelectCommand.NAME, SelectCommand.class );
          _imapCommands.put( ExamineCommand.NAME, ExamineCommand.class );
          _imapCommands.put( CreateCommand.NAME, CreateCommand.class );
          _imapCommands.put( DeleteCommand.NAME, DeleteCommand.class );
          _imapCommands.put( RenameCommand.NAME, RenameCommand.class );
  //        _imapCommands.put( "SUBSCRIBE", SubscribeCommand.class );
  //        _imapCommands.put( "UNSUBSCRIBE", UnsubscribeCommand.class );
          _imapCommands.put( ListCommand.NAME, ListCommand.class );
          _imapCommands.put( LsubCommand.NAME, LsubCommand.class );
  //        _imapCommands.put( "STATUS", StatusCommand.class );
  //        _imapCommands.put( "APPEND", AppendCommand.class );
  //        // RFC2342 NAMESPACE
  //        _imapCommands.put( "NAMESPACE", NamespaceCommand.class );
  //        // 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 );
      }
  
      public ImapCommand getCommand( String commandName )
      {
          Class cmdClass = ( Class ) _imapCommands.get( commandName.toUpperCase() );
  
          if ( cmdClass == null ) {
              return null;
          }
          else {
              return createCommand( cmdClass );
          }
      }
  
      private ImapCommand createCommand( Class commandClass )
      {
          try {
              ImapCommand cmd = ( ImapCommand ) commandClass.newInstance();
              if ( cmd instanceof LogEnabled ) {
                  ( ( LogEnabled ) cmd ).enableLogging( getLogger() );
              }
              return cmd;
          }
          catch ( Exception e ) {
              throw new CascadingRuntimeException( "Could not create command instance: " + commandClass.getName(), e );
          }
      }
  
  }
  
  
  
  1.1                  jakarta-james/proposals/imap2/java/org/apache/james/imapserver/commands/ListCommand.java
  
  Index: ListCommand.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.store.ImapMailbox;
  import org.apache.james.imapserver.ImapRequestParser;
  import org.apache.james.imapserver.ImapResponse;
  import org.apache.james.imapserver.ImapSession;
  import org.apache.james.imapserver.store.MailboxException;
  import org.apache.james.imapserver.ProtocolException;
  import org.apache.james.util.Assert;
  
  import java.util.ArrayList;
  import java.util.Collection;
  import java.util.Iterator;
  
  /**
   * Handles processeing for the LIST imap command.
   *
   * @author  Darrell DeBoer <darrell@apache.org>
   *
   * @version $Revision: 1.1 $
   */
  class ListCommand extends AuthenticatedStateCommand
  {
      public static final String NAME = "LIST";
      public static final String ARGS = "<reference-name> <mailbox-name-with-wildcards>";
  
      /** @see CommandTemplate#doProcess */
      protected void doProcess( ImapRequestParser request,
                                ImapResponse response,
                                ImapSession session )
              throws ProtocolException, MailboxException
      {
          String referenceName = request.astring();
          String mailboxPattern = request.listMailbox();
          request.endLine();
  
          // Should the #user.userName section be removed from names returned?
          boolean removeUserPrefix;
  
          Collection mailboxes;
          if ( mailboxPattern.length() == 0 ) {
              // An empty mailboxPattern signifies a request for the hierarchy delimiter
              // and root name of the referenceName argument
  
              String referenceRoot;
              if ( referenceName.startsWith( NAMESPACE_PREFIX ) )
              {
                  // A qualified reference name - get the first element,
                  // and don't remove the user prefix
                  removeUserPrefix = false;
                  int firstDelimiter = referenceName.indexOf( HIERARCHY_DELIMITER_CHAR );
                  if ( firstDelimiter == -1 ) {
                      referenceRoot = referenceName;
                  }
                  else {
                      referenceRoot = referenceName.substring(0, firstDelimiter );
                  }
              }
              else {
                  // A relative reference name - need to remove user prefix from results.
                  referenceRoot = "";
                  removeUserPrefix = true;
              }
  
              // Get the mailbox for the reference name.
              ImapMailbox referenceMailbox = getMailbox( referenceRoot, session, false );
  
              // If it doesn't exist, act as though "" was passed for reference name.
              if ( referenceMailbox == null )
              {
                  referenceMailbox = getMailbox( "", session, true );
                  removeUserPrefix = true;
              }
  
              mailboxes = new ArrayList( 1 );
              mailboxes.add( referenceMailbox );
          }
          else {
              String searchPattern;
  
              // If the mailboxPattern is fully qualified, ignore the
              // reference name.
              if ( mailboxPattern.charAt( 0 ) == NAMESPACE_PREFIX_CHAR ) {
                  searchPattern = mailboxPattern;
              }
              else {
                  searchPattern = combineSearchTerms( referenceName, mailboxPattern );
              }
  
              // If the search pattern is relative, need to remove user prefix from results.
              removeUserPrefix = ( searchPattern.charAt(0) != NAMESPACE_PREFIX_CHAR );
  
              mailboxes = doList( session, searchPattern );
          }
  
          String personalNamespace = USER_NAMESPACE + HIERARCHY_DELIMITER_CHAR +
                  session.getUser().getUserName();
          int prefixLength = personalNamespace.length();
  
          Iterator iterator = mailboxes.iterator();
          while ( iterator.hasNext() ) {
              ImapMailbox mailbox = ( ImapMailbox ) iterator.next();
              StringBuffer message = new StringBuffer( "(" );
              if ( !mailbox.isSelectable() ) {
                  message.append( "\\Noselect" );
              }
              message.append( ") \"" );
              message.append( HIERARCHY_DELIMITER_CHAR );
              message.append( "\" " );
  
              String mailboxName = mailbox.getFullName();
              if ( removeUserPrefix ) {
                  if ( mailboxName.length() <= prefixLength ) {
                      mailboxName = "";
                  }
                  else {
                      mailboxName = mailboxName.substring( prefixLength + 1 );
                  }
              }
  
              // TODO: need to check if the mailbox name needs quoting.
              if ( mailboxName.length() == 0 ) {
                  message.append("\"\"");
              }
              else {
                  message.append( mailboxName );
              }
  
              response.commandResponse( this, message.toString() );
          }
  
          session.unsolicitedResponses( response );
          response.commandComplete( this );
      }
  
      protected Collection doList( ImapSession session, String searchPattern ) throws MailboxException
      {
          return session.getHost().listMailboxes( session.getUser(), searchPattern );
      }
  
      private String combineSearchTerms( String referenceName, String mailboxMatch )
      {
          Assert.isTrue( mailboxMatch.length() > 0 );
  
          // Otherwise, combine the referenceName and mailbox name.
          StringBuffer buffer = new StringBuffer( mailboxMatch );
  
          // Make sure the 2 strings are joined by only one HIERARCHY_DELIMITER_CHAR
          if ( referenceName.endsWith( HIERARCHY_DELIMITER ) ) {
              if ( buffer.charAt(0) == HIERARCHY_DELIMITER_CHAR ) {
                  buffer.deleteCharAt( 0 );
              }
          }
          else {
              if ( buffer.charAt(0) != HIERARCHY_DELIMITER_CHAR ) {
                  buffer.insert( 0, HIERARCHY_DELIMITER_CHAR );
              }
          }
  
          buffer.insert( 0, referenceName );
          return buffer.toString();
      }
  
      /** @see ImapCommand#getName */
      public String getName()
      {
          return NAME;
      }
  
      /** @see CommandTemplate#getArgSyntax */
      public String getArgSyntax()
      {
          return ARGS;
      }
  
  }
  
  /*
  6.3..8.  LIST Command
  
     Arguments:  reference name
                 mailbox name with possible wildcards
  
     Responses:  untagged responses: LIST
  
     Result:     OK - list completed
                 NO - list failure: can't list that reference or name
                 BAD - command unknown or arguments invalid
  
        The LIST command returns a subset of names from the complete set
        of all names available to the client.  Zero or more untagged LIST
        replies are returned, containing the name attributes, hierarchy
        delimiter, and name; see the description of the LIST reply for
        more detail.
  
        The LIST command SHOULD return its data quickly, without undue
        delay.  For example, it SHOULD NOT go to excess trouble to
        calculate \Marked or \Unmarked status or perform other processing;
        if each name requires 1 second of processing, then a list of 1200
        names would take 20 minutes!
  
        An empty ("" string) reference name argument indicates that the
        mailbox name is interpreted as by SELECT. The returned mailbox
        names MUST match the supplied mailbox name pattern.  A non-empty
        reference name argument is the name of a mailbox or a level of
        mailbox hierarchy, and indicates a context in which the mailbox
        name is interpreted in an implementation-defined manner.
  
        An empty ("" string) mailbox name argument is a special request to
        return the hierarchy delimiter and the root name of the name given
        in the reference.  The value returned as the root MAY be null if
        the reference is non-rooted or is null.  In all cases, the
        hierarchy delimiter is returned.  This permits a client to get the
        hierarchy delimiter even when no mailboxes by that name currently
        exist.
  
        The reference and mailbox name arguments are interpreted, in an
        implementation-dependent fashion, into a canonical form that
        represents an unambiguous left-to-right hierarchy.  The returned
        mailbox names will be in the interpreted form.
  
        Any part of the reference argument that is included in the
        interpreted form SHOULD prefix the interpreted form.  It SHOULD
        also be in the same form as the reference name argument.  This
        rule permits the client to determine if the returned mailbox name
        is in the context of the reference argument, or if something about
        the mailbox argument overrode the reference argument.  Without
        this rule, the client would have to have knowledge of the server's
        naming semantics including what characters are "breakouts" that
        override a naming context.
  
        For example, here are some examples of how references and mailbox
        names might be interpreted on a UNIX-based server:
  
                 Reference     Mailbox Name  Interpretation
                 ------------  ------------  --------------
                 ~smith/Mail/  foo.*         ~smith/Mail/foo.*
                 archive/      %             archive/%
                 #news.        comp.mail.*   #news.comp.mail.*
                 ~smith/Mail/  /usr/doc/foo  /usr/doc/foo
                 archive/      ~fred/Mail/*  ~fred/Mail/*
  
        The first three examples demonstrate interpretations in the
        context of the reference argument.  Note that "~smith/Mail" SHOULD
        NOT be transformed into something like "/u2/users/smith/Mail", or
        it would be impossible for the client to determine that the
        interpretation was in the context of the reference.
  
        The character "*" is a wildcard, and matches zero or more
        characters at this position.  The character "%" is similar to "*",
        but it does not match a hierarchy delimiter.  If the "%" wildcard
        is the last character of a mailbox name argument, matching levels
        of hierarchy are also returned.  If these levels of hierarchy are
        not also selectable mailboxes, they are returned with the
        \Noselect mailbox name attribute (see the description of the LIST
        response for more details).
  
        Server implementations are permitted to "hide" otherwise
        accessible mailboxes from the wildcard characters, by preventing
        certain characters or names from matching a wildcard in certain
        situations.  For example, a UNIX-based server might restrict the
        interpretation of "*" so that an initial "/" character does not
        match.
  
        The special name INBOX is included in the output from LIST, if
        INBOX is supported by this server for this user and if the
        uppercase string "INBOX" matches the interpreted reference and
        mailbox name arguments with wildcards as described above.  The
        criteria for omitting INBOX is whether SELECT INBOX will return
        failure; it is not relevant whether the user's real INBOX resides
        on this or some other server.
  
     Example:    C: A101 LIST "" ""
                 S: * LIST (\Noselect) "/" ""
                 S: A101 OK LIST Completed
                 C: A102 LIST #news.comp.mail.misc ""
                 S: * LIST (\Noselect) "." #news.
                 S: A102 OK LIST Completed
                 C: A103 LIST /usr/staff/jones ""
                 S: * LIST (\Noselect) "/" /
                 S: A103 OK LIST Completed
                 C: A202 LIST ~/Mail/ %
                 S: * LIST (\Noselect) "/" ~/Mail/foo
                 S: * LIST () "/" ~/Mail/meetings
                 S: A202 OK LIST completed
  
  7.2.2.  LIST Response
  
     Contents:   name attributes
                 hierarchy delimiter
                 name
  
        The LIST response occurs as a result of a LIST command.  It
        returns a single name that matches the LIST specification.  There
        can be multiple LIST responses for a single LIST command.
  
        Four name attributes are defined:
  
        \Noinferiors   It is not possible for any child levels of
                       hierarchy to exist under this name; no child levels
                       exist now and none can be created in the future.
  
        \Noselect      It is not possible to use this name as a selectable
                       mailbox.
  
        \Marked        The mailbox has been marked "interesting" by the
                       server; the mailbox probably contains messages that
                       have been added since the last time the mailbox was
                       selected.
  
        \Unmarked      The mailbox does not contain any additional
                       messages since the last time the mailbox was
                       selected.
  
        If it is not feasible for the server to determine whether the
        mailbox is "interesting" or not, or if the name is a \Noselect
        name, the server SHOULD NOT send either \Marked or \Unmarked.
  
        The hierarchy delimiter is a character used to delimit levels of
        hierarchy in a mailbox name.  A client can use it to create child
        mailboxes, and to search higher or lower levels of naming
        hierarchy.  All children of a top-level hierarchy node MUST use
        the same separator character.  A NIL hierarchy delimiter means
        that no hierarchy exists; the name is a "flat" name.
  
        The name represents an unambiguous left-to-right hierarchy, and
        MUST be valid for use as a reference in LIST and LSUB commands.
        Unless \Noselect is indicated, the name MUST also be valid as an
              argument for commands, such as SELECT, that accept mailbox
        names.
  
     Example:    S: * LIST (\Noselect) "/" ~/Mail/foo
  */
  
  
  
  1.1                  jakarta-james/proposals/imap2/java/org/apache/james/imapserver/commands/LoginCommand.java
  
  Index: LoginCommand.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.ImapRequestParser;
  import org.apache.james.imapserver.ImapResponse;
  import org.apache.james.imapserver.ImapSession;
  import org.apache.james.imapserver.ProtocolException;
  import org.apache.james.services.User;
  
  /**
   * Handles processeing for the LOGIN imap command.
   *
   * @author  Darrell DeBoer <darrell@apache.org>
   *
   * @version $Revision: 1.1 $
   */
  class LoginCommand extends NonAuthenticatedStateCommand
  {
      public static final String NAME = "LOGIN";
      public static final String ARGS = "<userid> <password>";
  
      /** @see CommandTemplate#doProcess */
      protected void doProcess( ImapRequestParser request,
                                ImapResponse response,
                                ImapSession session )
              throws ProtocolException
      {
          String userid = request.astring();
          String password = request.astring();
          request.endLine();
  
          if ( session.getUsers().test( userid, password ) ) {
              User user = session.getUsers().getUserByName( userid );
              session.setAuthenticated( user );
              response.commandComplete( this );
  
              // Log the login.
              session.getSecurityLogger().info( "Login successful for " + user.getUserName() +
                                                " from  " + session.getClientHostname() +
                                                "(" + session.getClientIP() + ")" );
          }
          else {
              response.commandFailed( this, "Invalid login/password" );
              session.getSecurityLogger().error( "Login failed for " + userid +
                                                 " from " + session.getClientHostname() +
                                                 "(" + session.getClientIP() + ")" );
          }
      }
  
      /** @see ImapCommand#getName */
      public String getName()
      {
          return NAME;
      }
  
      /** @see CommandTemplate#getArgSyntax */
      public String getArgSyntax()
      {
          return ARGS;
      }
  }
  
  /*
  6.2.2.  LOGIN Command
  
     Arguments:  user name
                 password
  
     Responses:  no specific responses for this command
  
     Result:     OK - login completed, now in authenticated state
                 NO - login failure: user name or password rejected
                 BAD - command unknown or arguments invalid
  
        The LOGIN command identifies the client to the server and carries
        the plaintext password authenticating this user.
  
     Example:    C: a001 LOGIN SMITH SESAME
                 S: a001 OK LOGIN completed
  */
  
  
  
  1.1                  jakarta-james/proposals/imap2/java/org/apache/james/imapserver/commands/LogoutCommand.java
  
  Index: LogoutCommand.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.ImapRequestParser;
  import org.apache.james.imapserver.ImapResponse;
  import org.apache.james.imapserver.ImapSession;
  import org.apache.james.imapserver.ProtocolException;
  
  /**
   * Handles processeing for the LOGOUT imap command.
   *
   * @author  Darrell DeBoer <darrell@apache.org>
   *
   * @version $Revision: 1.1 $
   */
  class LogoutCommand extends CommandTemplate
  {
      public static final String NAME = "LOGOUT";
      public static final String ARGS = null;
      public static final String BYE_MESSAGE = BYE + SP + VERSION + SP +
              "Server logging out";
  
      /** @see CommandTemplate#doProcess */
      protected void doProcess( ImapRequestParser request,
                                ImapResponse response,
                                ImapSession session ) throws ProtocolException
      {
          request.endLine();
  
          response.untaggedResponse( BYE_MESSAGE );
          response.commandComplete( this );
  
          session.closeConnection();
      }
  
      /** @see ImapCommand#getName */
      public String getName()
      {
          return NAME;
      }
  
      /** @see CommandTemplate#getArgSyntax */
      public String getArgSyntax()
      {
          return ARGS;
      }
  }
  
  /*
  6.1.3.  LOGOUT Command
  
     Arguments:  none
  
     Responses:  REQUIRED untagged response: BYE
  
     Result:     OK - logout completed
                 BAD - command unknown or arguments invalid
  
        The LOGOUT command informs the server that the client is done with
        the connection.  The server MUST send a BYE untagged response
        before the (tagged) OK response, and then close the network
        connection.
  
     Example:    C: A023 LOGOUT
                 S: * BYE IMAP4rev1 Server logging out
                 S: A023 OK LOGOUT completed
                 (Server and client then close the connection)
  */
  
  
  
  1.1                  jakarta-james/proposals/imap2/java/org/apache/james/imapserver/commands/LsubCommand.java
  
  Index: LsubCommand.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.ImapSession;
  import org.apache.james.imapserver.store.MailboxException;
  
  import java.util.Collection;
  
  /**
   *
   * @author  Darrell DeBoer <darrell@apache.org>
   *
   * @version $Revision: 1.1 $
   */
  public class LsubCommand extends ListCommand
  {
      public static final String NAME = "LSUB";
  
      protected Collection doList( ImapSession session, String searchPattern )
              throws MailboxException
      {
          return session.getHost().listSubscribedMailboxes( session.getUser(), searchPattern );
      }
  
      /** @see ImapCommand#getName */
      public String getName()
      {
          return NAME;
      }
  }
  
  
  
  1.1                  jakarta-james/proposals/imap2/java/org/apache/james/imapserver/commands/NonAuthenticatedStateCommand.java
  
  Index: NonAuthenticatedStateCommand.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.ImapSessionState;
  
  /**
   * A base class for ImapCommands only valid in the NON_AUTHENTICATED state.
   *
   * @author  Darrell DeBoer <darrell@apache.org>
   *
   * @version $Revision: 1.1 $
   */
  abstract class NonAuthenticatedStateCommand extends CommandTemplate
  {
  
      /**
       * Ensure that state is {@link ImapSessionState#NON_AUTHENTICATED}.
       */
      public boolean validForState( ImapSessionState state )
      {
          return ( state == ImapSessionState.NON_AUTHENTICATED );
      }
  }
  
  
  
  1.1                  jakarta-james/proposals/imap2/java/org/apache/james/imapserver/commands/NoopCommand.java
  
  Index: NoopCommand.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.ImapRequestParser;
  import org.apache.james.imapserver.ImapResponse;
  import org.apache.james.imapserver.ImapSession;
  import org.apache.james.imapserver.ProtocolException;
  
  /**
   * Handles processeing for the NOOP imap command.
   *
   * @author  Darrell DeBoer <darrell@apache.org>
   *
   * @version $Revision: 1.1 $
   */
  class NoopCommand extends CommandTemplate
  {
      public static final String NAME = "NOOP";
      public static final String ARGS = null;
  
      /** @see org.apache.james.imapserver.commands.CommandTemplate#doProcess */
      protected void doProcess( ImapRequestParser request,
                                ImapResponse response,
                                ImapSession session ) throws ProtocolException
      {
          request.endLine();
          session.unsolicitedResponses( response );
          response.commandComplete( this );
      }
  
      /** @see ImapCommand#getName */
      public String getName()
      {
          return NAME;
      }
  
      /** @see CommandTemplate#getArgSyntax */
      public String getArgSyntax()
      {
          return ARGS;
      }
  }
  
  /*
  6.1.2.  NOOP Command
  
     Arguments:  none
  
     Responses:  no specific responses for this command (but see below)
  
     Result:     OK - noop completed
                 BAD - command unknown or arguments invalid
  
        The NOOP command always succeeds.  It does nothing.
  
        Since any command can return a status update as untagged data, the
        NOOP command can be used as a periodic poll for new messages or
        message status updates during a period of inactivity.  The NOOP
        command can also be used to reset any inactivity autologout timer
        on the server.
  
     Example:    C: a002 NOOP
                 S: a002 OK NOOP completed
                    . . .
                 C: a047 NOOP
                 S: * 22 EXPUNGE
                 S: * 23 EXISTS
                 S: * 3 RECENT
                 S: * 14 FETCH (FLAGS (\Seen \Deleted))
                 S: a047 OK NOOP completed
  */
  
  
  
  1.1                  jakarta-james/proposals/imap2/java/org/apache/james/imapserver/commands/RenameCommand.java
  
  Index: RenameCommand.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.AuthorizationException;
  import org.apache.james.imapserver.ImapRequestParser;
  import org.apache.james.imapserver.ImapResponse;
  import org.apache.james.imapserver.ImapSession;
  import org.apache.james.imapserver.store.MailboxException;
  import org.apache.james.imapserver.ProtocolException;
  
  /**
   * Handles processeing for the RENAME imap command.
   *
   * @author  Darrell DeBoer <darrell@apache.org>
   *
   * @version $Revision: 1.1 $
   */
  class RenameCommand extends AuthenticatedStateCommand
  {
      public static final String NAME = "RENAME";
      public static final String ARGS = "existing-mailbox-name SPACE new-mailbox-name";
  
      /** @see CommandTemplate#doProcess */
      protected void doProcess( ImapRequestParser request,
                                ImapResponse response,
                                ImapSession session )
              throws ProtocolException, MailboxException, AuthorizationException
      {
          String existingName = request.astring();
          String newName = request.astring();
          request.endLine();
  
          session.getHost().renameMailbox( session.getUser(), existingName, newName );
  
          session.unsolicitedResponses( response );
          response.commandComplete( this );
      }
  
      /** @see ImapCommand#getName */
      public String getName()
      {
          return NAME;
      }
  
      /** @see CommandTemplate#getArgSyntax */
      public String getArgSyntax()
      {
          return ARGS;
      }
  
  }
  
  /*
  6.3.5.  RENAME Command
  
     Arguments:  existing mailbox name
                 new mailbox name
  
     Responses:  no specific responses for this command
  
     Result:     OK - rename completed
                 NO - rename failure: can't rename mailbox with that name,
                      can't rename to mailbox with that name
                 BAD - command unknown or arguments invalid
  
        The RENAME command changes the name of a mailbox.  A tagged OK
        response is returned only if the mailbox has been renamed.  It is
        an error to attempt to rename from a mailbox name that does not
        exist or to a mailbox name that already exists.  Any error in
        renaming will return a tagged NO response.
  
        If the name has inferior hierarchical names, then the inferior
        hierarchical names MUST also be renamed.  For example, a rename of
        "foo" to "zap" will rename "foo/bar" (assuming "/" is the
        hierarchy delimiter character) to "zap/bar".
  
        The value of the highest-used unique identifier of the old mailbox
        name MUST be preserved so that a new mailbox created with the same
        name will not reuse the identifiers of the former incarnation,
        UNLESS the new incarnation has a different unique identifier
        validity value.  See the description of the UID command for more
        detail.
  
        Renaming INBOX is permitted, and has special behavior.  It moves
        all messages in INBOX to a new mailbox with the given name,
        leaving INBOX empty.  If the server implementation supports
        inferior hierarchical names of INBOX, these are unaffected by a
        rename of INBOX.
  
     Examples:   C: A682 LIST "" *
                 S: * LIST () "/" blurdybloop
                 S: * LIST (\Noselect) "/" foo
                 S: * LIST () "/" foo/bar
                 S: A682 OK LIST completed
                 C: A683 RENAME blurdybloop sarasoop
                 S: A683 OK RENAME completed
                 C: A684 RENAME foo zowie
                 S: A684 OK RENAME Completed
                 C: A685 LIST "" *
                 S: * LIST () "/" sarasoop
                 S: * LIST (\Noselect) "/" zowie
                 S: * LIST () "/" zowie/bar
                 S: A685 OK LIST completed
  
                 C: Z432 LIST "" *
                 S: * LIST () "." INBOX
                 S: * LIST () "." INBOX.bar
                 S: Z432 OK LIST completed
                 C: Z433 RENAME INBOX old-mail
                 S: Z433 OK RENAME completed
                 C: Z434 LIST "" *
                 S: * LIST () "." INBOX
                 S: * LIST () "." INBOX.bar
                 S: * LIST () "." old-mail
                 S: Z434 OK LIST completed
  */
  
  
  
  1.1                  jakarta-james/proposals/imap2/java/org/apache/james/imapserver/commands/SelectCommand.java
  
  Index: SelectCommand.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.store.ImapMailbox;
  import org.apache.james.imapserver.ImapRequestParser;
  import org.apache.james.imapserver.ImapResponse;
  import org.apache.james.imapserver.ImapSession;
  import org.apache.james.imapserver.store.MailboxException;
  import org.apache.james.imapserver.ProtocolException;
  
  /**
   * Handles processeing for the SELECT imap command.
   *
   * @author  Darrell DeBoer <darrell@apache.org>
   *
   * @version $Revision: 1.1 $
   */
  class SelectCommand extends AuthenticatedStateCommand
  {
      public static final String NAME = "SELECT";
      public static final String ARGS = "mailbox";
  
      /** @see org.apache.james.imapserver.commands.CommandTemplate#doProcess */
      protected void doProcess( ImapRequestParser request,
                                ImapResponse response,
                                ImapSession session )
              throws ProtocolException, MailboxException
      {
          String mailboxName = request.astring();
          request.endLine();
  
          session.deselect();
  
          ImapMailbox mailbox = getMailbox( mailboxName, session, true );
  
          if ( !mailbox.isSelectable() ) {
              throw new MailboxException( "Nonselectable mailbox." );
          }
  
          boolean readOnly = ( this instanceof ExamineCommand );
          session.setSelected( mailbox, readOnly );
  
          response.flagsResponse( mailbox.getAllowedFlags() );
          response.existsResponse( mailbox.getMessageCount() );
          response.recentResponse( mailbox.getRecentCount() );
          response.okResponse( "UIDVALIDITY " + mailbox.getUidValidity(), null );
  
          int firstUnseen = mailbox.getFirstUnseen();
          if ( firstUnseen != 0 ) {
              int msnUnseen = mailbox.getIndex( firstUnseen );
              response.okResponse( "UNSEEN " + msnUnseen,
                                   "Message " + msnUnseen + " is the first unseen" );
          }
          else {
              response.okResponse( null, "No messages unseen" );
          }
  
  
          if ( readOnly ) {
              response.commandComplete( this, "READ-ONLY" );
          }
          else {
              response.commandComplete( this, "READ-WRITE" );
          }
      }
  
      /** @see ImapCommand#getName */
      public String getName()
      {
          return NAME;
      }
  
      /** @see CommandTemplate#getArgSyntax */
      public String getArgSyntax()
      {
          return ARGS;
      }
  }
  
  /*
  6.3.1.  SELECT Command
  
     Arguments:  mailbox name
  
     Responses:  REQUIRED untagged responses: FLAGS, EXISTS, RECENT
                 OPTIONAL OK untagged responses: UNSEEN, PERMANENTFLAGS
  
     Result:     OK - select completed, now in selected state
                 NO - select failure, now in authenticated state: no
                      such mailbox, can't access mailbox
                 BAD - command unknown or arguments invalid
  
     The SELECT command selects a mailbox so that messages in the
     mailbox can be accessed.  Before returning an OK to the client,
     the server MUST send the following untagged data to the client:
  
        FLAGS       Defined flags in the mailbox.  See the description
                    of the FLAGS response for more detail.
  
        <n> EXISTS  The number of messages in the mailbox.  See the
                    description of the EXISTS response for more detail.
  
        <n> RECENT  The number of messages with the \Recent flag set.
                    See the description of the RECENT response for more
                    detail.
  
        OK [UIDVALIDITY <n>]
                    The unique identifier validity value.  See the
                    description of the UID command for more detail.
  
     to define the initial state of the mailbox at the client.
  
     The server SHOULD also send an UNSEEN response code in an OK
     untagged response, indicating the message sequence number of the
     first unseen message in the mailbox.
  
     If the client can not change the permanent state of one or more of
     the flags listed in the FLAGS untagged response, the server SHOULD
     send a PERMANENTFLAGS response code in an OK untagged response,
     listing the flags that the client can change permanently.
  
     Only one mailbox can be selected at a time in a connection;
     simultaneous access to multiple mailboxes requires multiple
     connections.  The SELECT command automatically deselects any
     currently selected mailbox before attempting the new selection.
     Consequently, if a mailbox is selected and a SELECT command that
     fails is attempted, no mailbox is selected.
  
  
  
  
  Crispin                     Standards Track                    [Page 23]
  
  RFC 2060                       IMAP4rev1                   December 1996
  
  
     If the client is permitted to modify the mailbox, the server
     SHOULD prefix the text of the tagged OK response with the
           "[READ-WRITE]" response code.
  
        If the client is not permitted to modify the mailbox but is
        permitted read access, the mailbox is selected as read-only, and
        the server MUST prefix the text of the tagged OK response to
        SELECT with the "[READ-ONLY]" response code.  Read-only access
        through SELECT differs from the EXAMINE command in that certain
        read-only mailboxes MAY permit the change of permanent state on a
        per-user (as opposed to global) basis.  Netnews messages marked in
        a server-based .newsrc file are an example of such per-user
        permanent state that can be modified with read-only mailboxes.
  
     Example:    C: A142 SELECT INBOX
                 S: * 172 EXISTS
                 S: * 1 RECENT
                 S: * OK [UNSEEN 12] Message 12 is first unseen
                 S: * OK [UIDVALIDITY 3857529045] UIDs valid
                 S: * FLAGS (\Answered \Flagged \Deleted \Seen \Draft)
                 S: * OK [PERMANENTFLAGS (\Deleted \Seen \*)] Limited
                 S: A142 OK [READ-WRITE] SELECT completed
  */
  
  
  
  1.1                  jakarta-james/proposals/imap2/java/org/apache/james/imapserver/commands/SelectedStateCommand.java
  
  Index: SelectedStateCommand.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.ImapSessionState;
  
  /**
   * A base class for ImapCommands only valid in the SELECTED state.
   *
   * @author  Darrell DeBoer <darrell@apache.org>
   *
   * @version $Revision: 1.1 $
   */
  abstract class SelectedStateCommand extends CommandTemplate
  {
      /**
       * Subclasses of this command are only valid in the
       * {@link ImapSessionState#SELECTED} state.
       */
      public boolean validForState( ImapSessionState state )
      {
          return ( state == ImapSessionState.SELECTED );
      }
  }
  
  
  
  1.1                  jakarta-james/proposals/imap2/java/org/apache/james/imapserver/store/ImapMailbox.java
  
  Index: ImapMailbox.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.james.services.MailRepository;
  
  /**
   * Represents a mailbox within an {@link org.apache.james.imapserver.store.ImapStore}.
   * May provide storage for MailImpl objects, or be a non-selectable placeholder in the
   * Mailbox hierarchy.
   * TODO this is a "grown" interface, which needs some more design and thought re:
   * how it will fit in with the other mail storage in James.
   *
   * @author  Darrell DeBoer <darrell@apache.org>
   *
   * @version $Revision: 1.1 $
   */
  public interface ImapMailbox extends MailRepository
  {
      String getName();
  
      String getFullName();
  
      MessageFlags getAllowedFlags();
  
      int getMessageCount();
  
      int getRecentCount();
  
      int getUidValidity();
  
      int getFirstUnseen();
  
      int getIndex( int uid );
  
      boolean isSelectable();
  }
  
  
  
  1.1                  jakarta-james/proposals/imap2/java/org/apache/james/imapserver/store/ImapStore.java
  
  Index: ImapStore.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.Collection;
  
  /**
   * Represents the complete mail store for an IMAP server, providing access to
   * and manipulation of all {@link org.apache.james.imapserver.store.ImapMailbox Mailboxes} stored on this server.
   *
   * @author  Darrell DeBoer <darrell@apache.org>
   *
   * @version $Revision: 1.1 $
   */
  public interface ImapStore
  {
      /**
       * Retrieves a mailbox based on a fully qualified name.
       * @param qualifiedMailboxName
       * @return The mailbox if present, or <code>null</code> if not.
       */
      ImapMailbox getMailbox( String qualifiedMailboxName );
  
      /**
       * Looks up a child mailbox of the supplied parent with the name given.
       * @param parent The parent mailbox
       * @param mailboxName The name of the child to lookup
       * @return The child mailbox, or <code>null</code> if not found.
       */
      ImapMailbox getMailbox( ImapMailbox parent, String mailboxName );
  
      /**
       * @param parent A mailbox from this store.
       * @return A read-only collection of {@link ImapMailbox} instances, which
       *         are the children of the supplied parent.
       */
      Collection getChildren( ImapMailbox parent );
  
      /**
       * Creates a mailbox under the supplied parent with the given name.
       * If specified, the mailbox created will be made selectable (able to store messages).
       * @param parent A mailbox from this store.
       * @param mailboxName The name of the mailbox to create.
       * @param selectable If <code>true</code>, the mailbox will be created to store messages.
       * @return The created mailbox
       * @throws MailboxException If the mailbox couldn't be created.
       */
      ImapMailbox createMailbox( ImapMailbox parent,
                                 String mailboxName,
                                 boolean selectable )
              throws MailboxException;
  
      /**
       * Tells the store to make the supplied mailbox selectable or not (able to store
       * messages). The returned mailbox may be a new instance, and the supplied mailbox
       * may no longer be valid.
       * @param mailbox The mailbox to modify.
       * @param selectable Whether this mailbox should be able to store messages.
       * @return The modified mailbox
       */
      ImapMailbox setSelectable( ImapMailbox mailbox, boolean selectable );
  
      /**
       * Deletes the supplied mailbox from the store. To be deleted, mailboxes
       * must be empty of messages, and not have any children.
       * @param mailbox A mailbox from this store.
       * @throws MailboxException If the mailbox couldn't be deleted.
       */
      void deleteMailbox( ImapMailbox mailbox ) throws MailboxException;
  
      /**
       * Renames the mailbox with the new name.
       * @param existingMailbox A mailbox from this store.
       * @param newName The new name for the mailbox.
       * @throws MailboxException If the mailbox couldn't be renamed
       */
      void renameMailbox( ImapMailbox existingMailbox, String newName )
              throws MailboxException;
  
      /**
       * Lists all of the mailboxes in the store which have a name
       *  matching the supplied search pattern.
       * <pre>
       * Valid wildcards are:
       *          '*' - matches any number of characters, including the hierarchy delimiter
       *          '%' - matches any number of characters, but not the hierarchy delimiter
       *
       * @param searchPattern The pattern to match mailboxes
       * @return A read-only collection of mailboxes which match this pattern
       * @throws MailboxException If the list operation failed
       */
      Collection listMailboxes( String searchPattern ) throws MailboxException;
  
  }
  
  
  
  1.1                  jakarta-james/proposals/imap2/java/org/apache/james/imapserver/store/InMemoryStore.java
  
  Index: InMemoryStore.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.james.core.MailImpl;
  import org.apache.james.imapserver.store.ImapStore;
  import org.apache.james.imapserver.ImapConstants;
  
  import java.util.ArrayList;
  import java.util.Collection;
  import java.util.Collections;
  import java.util.Iterator;
  import java.util.StringTokenizer;
  
  /**
   * 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: 1.1 $
   */
  public class InMemoryStore implements ImapStore, ImapConstants
  {
      private RootMailbox rootMailbox = new RootMailbox();
      private static MessageFlags mailboxFlags = new MessageFlags();
      static {
          mailboxFlags.setAnswered( true );
          mailboxFlags.setDeleted( true );
          mailboxFlags.setDraft( true );
          mailboxFlags.setFlagged( true );
          mailboxFlags.setSeen( true );
      }
  
      public ImapMailbox getMailbox( String absoluteMailboxName )
      {
          StringTokenizer tokens = new StringTokenizer( absoluteMailboxName, HIERARCHY_DELIMITER );
  
          // The first token must be "#mail"
          if ( !tokens.hasMoreTokens() ||
                  !tokens.nextToken().equalsIgnoreCase( USER_NAMESPACE ) ) {
              return null;
          }
  
          HierarchicalMailbox parent = rootMailbox;
          while ( parent != null && tokens.hasMoreTokens() ) {
              String childName = tokens.nextToken();
              parent = parent.getChild( childName );
          }
          return parent;
      }
  
      public ImapMailbox getMailbox( ImapMailbox parent, String name )
      {
          return ( ( HierarchicalMailbox ) parent ).getChild( name );
      }
  
      public ImapMailbox createMailbox( ImapMailbox parent,
                                        String mailboxName,
                                        boolean selectable )
              throws MailboxException
      {
          if ( mailboxName.indexOf( HIERARCHY_DELIMITER_CHAR ) != -1 ) {
              throw new MailboxException( "Invalid mailbox name." );
          }
          HierarchicalMailbox castParent = ( HierarchicalMailbox ) parent;
          HierarchicalMailbox child = new HierarchicalMailbox( castParent, mailboxName );
          castParent.getChildren().add( child );
          child.setSelectable( selectable );
          return child;
      }
  
      public void deleteMailbox( ImapMailbox mailbox ) throws MailboxException
      {
          HierarchicalMailbox toDelete = ( HierarchicalMailbox ) mailbox;
  
          if ( !toDelete.getChildren().isEmpty() ) {
              throw new MailboxException( "Cannot delete mailbox with children." );
          }
  
          if ( toDelete.getMessageCount() != 0 ) {
              throw new MailboxException( "Cannot delete non-empty mailbox" );
          }
  
          HierarchicalMailbox parent = toDelete.getParent();
          parent.getChildren().remove( toDelete );
      }
  
      public void renameMailbox( ImapMailbox existingMailbox, String newName ) throws MailboxException
      {
          HierarchicalMailbox toRename = ( HierarchicalMailbox ) existingMailbox;
          toRename.setName( newName );
      }
  
      public Collection getChildren( ImapMailbox parent )
      {
          Collection children = ( ( HierarchicalMailbox ) parent ).getChildren();
          return Collections.unmodifiableCollection( children );
      }
  
      public ImapMailbox setSelectable( ImapMailbox mailbox, boolean selectable )
      {
          ( ( HierarchicalMailbox ) mailbox ).setSelectable( selectable );
          return mailbox;
      }
  
      /** @see org.apache.james.imapserver.store.ImapStore#listMailboxes */
      public Collection listMailboxes( String searchPattern )
              throws MailboxException
      {
          int starIndex = searchPattern.indexOf( '*' );
          int percentIndex = searchPattern.indexOf( '%' );
  
          // We only handle wildcard at the end of the search pattern.
          if ( ( starIndex > -1 && starIndex < searchPattern.length() - 1 ) ||
                  ( percentIndex > -1 && percentIndex < searchPattern.length() - 1 ) ) {
              throw new MailboxException( "WIldcard characters are only handled as the last character of a list argument." );
          }
  
          ArrayList mailboxes = new ArrayList();
          if ( starIndex != -1 || percentIndex != -1 ) {
              int lastDot = searchPattern.lastIndexOf( HIERARCHY_DELIMITER );
              String parentName = searchPattern.substring( 0, lastDot );
              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 );
                      }
                  }
              }
          }
          else {
              ImapMailbox mailbox = getMailbox( searchPattern );
              if ( mailbox != null ) {
                  mailboxes.add( mailbox );
              }
          }
  
          return mailboxes;
      }
  
      private void addAllChildren( HierarchicalMailbox mailbox, Collection mailboxes )
      {
          Collection children = mailbox.getChildren();
          Iterator iterator = children.iterator();
          while ( iterator.hasNext() ) {
              HierarchicalMailbox child = ( HierarchicalMailbox ) iterator.next();
              mailboxes.add( child );
              addAllChildren( child, mailboxes );
          }
      }
  
      private class RootMailbox extends HierarchicalMailbox
      {
          public RootMailbox()
          {
              super( null, ImapConstants.USER_NAMESPACE );
          }
  
          public String getFullName()
          {
              return name;
          }
      }
  
      private class HierarchicalMailbox implements ImapMailbox
      {
          private Collection children;
          private HierarchicalMailbox parent;
  
          protected String name;
          private boolean isSelectable = false;
  
          public HierarchicalMailbox( HierarchicalMailbox parent, String name )
          {
              this.name = name;
              this.children = new ArrayList();
              this.parent = parent;
          }
  
          public Collection getChildren()
          {
              return children;
          }
  
          public HierarchicalMailbox getParent()
          {
              return parent;
          }
  
          public HierarchicalMailbox getChild( String name )
          {
              Iterator iterator = children.iterator();
              while ( iterator.hasNext() ) {
                  HierarchicalMailbox child = ( HierarchicalMailbox ) iterator.next();
                  if ( child.getName().equalsIgnoreCase( name ) ) {
                      return child;
                  }
              }
              return null;
          }
  
          public void setName( String name )
          {
              this.name = name;
          }
  
          public String getName()
          {
              return name;
          }
  
          public String getFullName()
          {
              return parent.getFullName() + HIERARCHY_DELIMITER_CHAR + name;
          }
  
          public MessageFlags getAllowedFlags()
          {
              return mailboxFlags;
          }
  
          public int getMessageCount()
          {
              return 0;
          }
  
          public int getRecentCount()
          {
              return 0;
          }
  
          public int getUidValidity()
          {
              return 0;
          }
  
          public int getFirstUnseen()
          {
              return 0;
          }
  
          public int getIndex( int uid )
          {
              return 0;
          }
  
          public boolean isSelectable()
          {
              return isSelectable;
          }
  
          public void setSelectable( boolean selectable )
          {
              isSelectable = selectable;
          }
  
          // TODO implement these methods.
          public void store( MailImpl mc )
          {
          }
  
          public Iterator list()
          {
              return null;
          }
  
          public MailImpl retrieve( String key )
          {
              return null;
          }
  
          public void remove( MailImpl mail )
          {
          }
  
          public void remove( String key )
          {
          }
  
          public boolean lock( String key )
          {
              return false;
          }
  
          public boolean unlock( String key )
          {
              return false;
          }
      }
  
  }
  
  
  
  1.1                  jakarta-james/proposals/imap2/java/org/apache/james/imapserver/store/MailboxException.java
  
  Index: MailboxException.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;
  
  /**
   * Thrown on an inappropriate attempt to reference a mailbox.
   * Includes attempting to create a mailbox that already exists and attempting
   * to open a mailbox that does not exist.
   * If status is ALREADY_EXISTS_REMOTELY or IF_CREATED_REMOTE then field
   * remoteServer should be set to the url of the remote server, formatted for
   * Mailbox Referral.
   *
   * @author <a href="mailto:charles@benett1.demon.co.uk">Charles Benett</a>
   * @version 0.1 on 14 Dec 2000
   */
  public class MailboxException extends Exception
  {
  
      public final static String ALREADY_EXISTS_LOCALLY
              = "Already exists locally";
      public final static String ALREADY_EXISTS_REMOTELY
              = "Already exists remotely";
      public final static String IF_CREATED_LOCAL
              = "If created, mailbox would be local";
      public final static String IF_CREATED_REMOTE
              = "If created, mailbox would be remote";
      public final static String NOT_LOCAL
              = "Does not exist locally, no further information available";
      public final static String LOCAL_BUT_DELETED
              = "Was local but has been deleted.";
  
      protected String status = null;
      protected String remoteServer = null;
  
      /**
       * Construct a new <code>MailboxException</code> instance.
       *
       * @param message The detail message for this exception (mandatory).
       */
      public MailboxException( String message )
      {
          super( message );
      }
  
      /**
       * Construct a new <code>MailBoxException</code> instance.
       *
       * @param message The detail message for this exception (mandatory).
       * @param aStatus String constant indicating condition
       */
      public MailboxException( String message, String aStatus )
      {
          super( message );
          this.status = aStatus;
      }
  
      /**
       * Construct a new <code>MailBoxException</code> instance.
       *
       * @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.
       */
      public MailboxException( String message, String aStatus, String aServer )
      {
          super( message );
          this.status = aStatus;
          this.remoteServer = aServer;
      }
  
      public String getStatus()
      {
          return status;
      }
  
      public String getRemoteServer()
      {
          return remoteServer;
      }
  
      public boolean isRemote()
      {
          return ( status.equals( ALREADY_EXISTS_REMOTELY )
                  || status.equals( IF_CREATED_REMOTE ) );
      }
  }
  
  
  
  1.1                  jakarta-james/proposals/imap2/java/org/apache/james/imapserver/store/MessageFlags.java
  
  Index: MessageFlags.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;
  
  
  /**
   * The set of flags associated with a message.
   * TODO - should store SEEN flag on a peruser basis (not required, but nice)
   *
   * <p>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 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;
  
      // Array does not include seen flag
      private boolean[] flags = new boolean[6];
  
      public MessageFlags()
      {
          resetAll();
      }
  
      private void resetAll()
      {
          setAll( false );
      }
  
      /**
       * Returns IMAP formatted String of MessageFlags for named user
       */
      public String format()
      {
          StringBuffer buf = new StringBuffer();
          buf.append( "(" );
          if ( flags[ANSWERED] ) {
              buf.append( "\\Answered " );
          }
          if ( flags[DELETED] ) {
              buf.append( "\\Deleted " );
          }
          if ( flags[DRAFT] ) {
              buf.append( "\\Draft " );
          }
          if ( flags[FLAGGED] ) {
              buf.append( "\\Flagged " );
          }
          if ( flags[RECENT] ) {
              buf.append( "\\Recent " );
          }
          if ( flags[SEEN] ) {
              buf.append( "\\Seen " );
          }
          // Remove the trailing space, if necessary.
          if ( buf.length() > 1 )
          {
              buf.setLength( buf.length() - 1 );
          }
          buf.append( ")" );
          return buf.toString();
      }
  
      /**
       * Sets MessageFlags for message from IMAP-forammted string parameter.
       * <BR> The FLAGS<list> form overwrites existing flags, ie sets all other
       * flags to false.
       * <BR> The +FLAGS<list> form adds the flags in list to the existing flags
       * <BR> The -FLAGS<list> form removes the flags in list from the existing
       * flags
       * <BR> Note that the Recent flag cannot be set by user and is ignored by
       * this method.
       *
       * @param flagString a string formatted according to
       * RFC2060 store_att_flags
       * @return true if successful, false if not (including uninterpretable
       * argument)
       */
      public boolean setFlags( String flagString )
      {
          flagString = flagString.toUpperCase();
  
          boolean modValue;
  
          if ( flagString.startsWith( "FLAGS" ) ) {
              modValue = true;
              resetAll();
          }
          else if ( flagString.startsWith( "+FLAGS" ) ) {
              modValue = true;
          }
          else if ( flagString.startsWith( "-FLAGS" ) ) {
              modValue = false;
          }
          else {
              // Invalid flag string.
              return false;
          }
  
          if ( flagString.indexOf( "\\ANSWERED" ) != -1 ) {
              flags[ANSWERED] = modValue;
          }
          if ( flagString.indexOf( "\\DELETED" ) != -1 ) {
              flags[DELETED] = modValue;
          }
          if ( flagString.indexOf( "\\DRAFT" ) != -1 ) {
              flags[DRAFT] = modValue;
          }
          if ( flagString.indexOf( "\\FLAGGED" ) != -1 ) {
              flags[FLAGGED] = modValue;
          }
          if ( flagString.indexOf( "\\SEEN" ) != -1 ) {
              flags[SEEN] = modValue;
          }
          return true;
      }
  
      public void setAnswered( boolean newState )
      {
          flags[ANSWERED] = newState;
      }
  
      public boolean isAnswered()
      {
          return flags[ANSWERED];
      }
  
      public void setDeleted( boolean newState )
      {
          flags[DELETED] = newState;
      }
  
      public boolean isDeleted()
      {
          return flags[DELETED];
      }
  
      public void setDraft( boolean newState )
      {
          flags[DRAFT] = newState;
      }
  
      public boolean isDraft()
      {
          return flags[DRAFT];
      }
  
      public void setFlagged( boolean newState )
      {
          flags[FLAGGED] = newState;
      }
  
      public boolean isFlagged()
      {
          return flags[FLAGGED];
      }
  
      public void setRecent( boolean newState )
      {
          flags[RECENT] = newState;
      }
  
      public boolean isRecent()
      {
          return flags[RECENT];
      }
  
      public void setSeen( boolean newState )
      {
          flags[SEEN] = newState;
      }
  
      public boolean isSeen()
      {
          return flags[SEEN];
      }
  
      public void setAll( boolean newState )
      {
          for ( int i = ANSWERED; i <= SEEN; i++ )
          {
              flags[i] = newState;
          }
      }
  }
  
  
  
  
  1.1                  jakarta-james/proposals/imap2/java/org/apache/james/util/Assert.java
  
  Index: Assert.java
  ===================================================================
  package org.apache.james.util;
  
  
  /**
   * A set of debugging utilities.
   */
  public final class Assert
  {
      public static final boolean ON = true;
  
      // Can't instantiate.
      private Assert()
      {
      };
  
      /**
       * Checks the supplied boolean expression, throwing an AssertionException if false;
       */
      public static void isTrue( boolean expression )
      {
          if ( !expression ) {
              throw new RuntimeException( "Assertion Failed." );
          }
      }
  
      /**
       * Fails with an assertion exception.
       */
      public static void fail()
      {
          throw new RuntimeException( "Assertion error - should not reach here." );
      }
  
      /**
       * Fails, indicating not-yet-implemented features.
       */
      public static void notImplemented()
      {
          throw new RuntimeException( "Not implemented" );
      }
  
  }
  
  
  
  1.1                  jakarta-james/proposals/imap2/test/org/apache/james/imapserver/Authenticate.test
  
  Index: Authenticate.test
  ===================================================================
  # Unsupported Authentication mechanism
  C: abcd AUTHENTICATE KERBEROS_V4
  S: abcd NO AUTHENTICATE failed. Unsupported authentication mechanism 'KERBEROS_V4'
  
  C: abcd AUTHENTICATE
  S: abcd BAD Missing argument. Command should be '<tag> AUTHENTICATE <auth_type> \*\(CRLF base64\)'
  
  C: abcd AUTHENTICATE KERBEROS_V4 extra
  S: abcd BAD Extra argument found. Command should be '<tag> AUTHENTICATE <auth_type> \*\(CRLF base64\)'
  
  
  
  1.1                  jakarta-james/proposals/imap2/test/org/apache/james/imapserver/AuthenticateAuthenticated.test
  
  Index: AuthenticateAuthenticated.test
  ===================================================================
  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/Capability.test
  
  Index: Capability.test
  ===================================================================
  C: abcd CAPABILITY
  S: \* CAPABILITY IMAP4rev1
  S: abcd OK CAPABILITY completed
  
  C: abcd CAPABILITY extra stuff
  S: abcd BAD Extra argument found. Command should be '<tag> CAPABILITY'
  
  
  1.1                  jakarta-james/proposals/imap2/test/org/apache/james/imapserver/Create.test
  
  Index: Create.test
  ===================================================================
  # Create a simple name
  C: 10 CREATE test
  S: 10 OK CREATE completed
  # Create a subfolder with simple name
  C: 11 CREATE test.subfolder
  S: 11 OK CREATE completed
  # Create a fully qualified folder
  C: 12 CREATE test1
  S: 12 OK CREATE completed
  # Create a fully qualified sub folder
  C: 13 CREATE test1.subfolder1
  S: 13 OK CREATE completed
  
  
  1.1                  jakarta-james/proposals/imap2/test/org/apache/james/imapserver/Delete.test
  
  Index: Delete.test
  ===================================================================
  # Delete subfolder, then folder
  C: 10 DELETE test.subfolder
  S: 10 OK DELETE completed
  C: 11 DELETE test
  S: 11 OK DELETE completed
  
  # Delete folder first, then subfolder.
  C: 14 DELETE test1.subfolder1
  S: 14 OK DELETE completed
  C: 13 DELETE test1
  S: 13 OK DELETE completed
  
  
  1.1                  jakarta-james/proposals/imap2/test/org/apache/james/imapserver/ExamineEmpty.test
  
  Index: ExamineEmpty.test
  ===================================================================
  C: abcd EXAMINE test
  S: \* FLAGS \(\\Answered \\Deleted \\Draft \\Flagged \\Seen\)
  S: \* 0 EXISTS
  S: \* 0 RECENT
  S: \* OK \[UIDVALIDITY \d+\]
  S: \* OK No messages unseen
  #S: \* OK \[PERMANENTFLAGS \(\)\]
  S: abcd OK \[READ-ONLY\] EXAMINE completed
  
  C: abcd EXAMINE test.subfolder
  S: \* FLAGS \(\\Answered \\Deleted \\Draft \\Flagged \\Seen\)
  S: \* 0 EXISTS
  S: \* 0 RECENT
  S: \* OK \[UIDVALIDITY \d+\]
  S: \* OK No messages unseen
  #S: * OK \[PERMANENTFLAGS \(\)\]
  S: abcd OK \[READ-ONLY\] EXAMINE completed
  
  C: abcd EXAMINE test1
  S: \* FLAGS \(\\Answered \\Deleted \\Draft \\Flagged \\Seen\)
  S: \* 0 EXISTS
  S: \* 0 RECENT
  S: \* OK \[UIDVALIDITY \d+\]
  S: \* OK No messages unseen
  #S: \* OK \[PERMANENTFLAGS \(\)\]
  S: abcd OK \[READ-ONLY\] EXAMINE completed
  
  C: abcd EXAMINE subfolder1
  S: abcd NO EXAMINE failed. No such mailbox.
  
  
  
  1.1                  jakarta-james/proposals/imap2/test/org/apache/james/imapserver/ExamineInbox.test
  
  Index: ExamineInbox.test
  ===================================================================
  C: abcd EXAMINE 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-ONLY\] EXAMINE completed
  
  # Try again to ensure that no changes to flags were made.
  C: abcd EXAMINE 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-ONLY\] EXAMINE completed
  
  
  1.1                  jakarta-james/proposals/imap2/test/org/apache/james/imapserver/FetchMultipleMessages.test
  
  Index: FetchMultipleMessages.test
  ===================================================================
  // Fetch 3 messages with UID
  C: f1 FETCH 1:3 (UID)
  S: * 1 FETCH (UID ${ignore})
  S: * 2 FETCH (UID ${ignore})
  S: * 3 FETCH (UID ${ignore})
  S: f1 OK FETCH completed
  
  // Fetch 3 messages with BODY
  C: f1 FETCH 1:3 (BODY[HEADER.FIELDS (Subject)])
  S: * 1 FETCH (BODY[HEADER.FIELDS (Subject)] {22}
  S: Subject: Message 1
  S: 
  S: )
  S: * 2 FETCH (BODY[HEADER.FIELDS (Subject)] {22}
  S: Subject: Message 2
  S: 
  S: )
  S: * 3 FETCH (BODY[HEADER.FIELDS (Subject)] {22}
  S: Subject: Message 3
  S: 
  S: )
  S: f1 OK FETCH completed
  
  
  
  1.1                  jakarta-james/proposals/imap2/test/org/apache/james/imapserver/FetchSingleMessage.test
  
  Index: FetchSingleMessage.test
  ===================================================================
  // BODY
  C: f1 FETCH 1 (BODY)
  S: * 1 FETCH (BODY ("text" "plain" NIL NIL NIL "7bit" 28 1))
  S: f1 OK FETCH completed
  
  // 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: f1 OK FETCH completed
  
  // 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: f1 OK FETCH completed
  
  // BODY[TEXT]
  C: f1 FETCH 1 (BODY[TEXT])
  S: * 1 FETCH (BODY[TEXT] {28}
  S: This is the first message.
  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]
  C: f1 FETCH 1 (BODY.PEEK[TEXT])
  S: * 1 FETCH (BODY[TEXT] {28}
  S: This is the first message.
  S: )
  S: f1 OK FETCH completed
  
  // BODYSTRUCTURE
  C: f1 FETCH 1 (BODYSTRUCTURE)
  S: * 1 FETCH (BODYSTRUCTURE ("text" "plain" NIL NIL NIL "7bit" 28 1 NIL NIL NIL))
  S: f1 OK FETCH completed
  
  // ENVELOPE
  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: f1 OK FETCH completed
  
  // FLAGS
  C: f1 FETCH 1 (FLAGS)
  S: * 1 FETCH (FLAGS (\SEEN))
  S: f1 OK FETCH completed
  
  // INTERNALDATE
  C: f1 FETCH 1 (INTERNALDATE)
  S: * 1 FETCH (INTERNALDATE "${ignore-5}")
  S: f1 OK FETCH completed
  
  // 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: f1 OK FETCH completed
  
  // 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: f1 OK FETCH completed
  
  // RFC822.SIZE
  C: f1 FETCH 1 (RFC822.SIZE)
  S: * 1 FETCH (RFC822.SIZE 476)
  S: f1 OK FETCH completed
  
  // RFC822.TEXT ( === BODY[TEXT])
  C: f1 FETCH 1 (RFC822.TEXT)
  S: * 1 FETCH (RFC822.TEXT {28}
  S: This is the first message.
  S: )
  S: f1 OK FETCH completed
  
  // UID
  C: f1 FETCH 1 (UID)
  S: * 1 FETCH (UID ${ignore})
  S: f1 OK FETCH completed
  
  
  // 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: f1 OK FETCH completed
  
  // 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: f1 OK FETCH completed
  
  // FAST ( === FLAGS INTERNALDATE RFC822.SIZE )
  C: f1 FETCH 1 (FAST)
  S: * 1 FETCH (FLAGS (\SEEN) INTERNALDATE "${ignore-5}" RFC822.SIZE 476)
  S: f1 OK FETCH completed
  
  
  
  1.1                  jakarta-james/proposals/imap2/test/org/apache/james/imapserver/IMAPTest.java
  
  Index: IMAPTest.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 javax.mail.internet.InternetAddress;
  
  public interface IMAPTest
  {
      public int PORT = 143;
      public String HOST = "localhost";
  
      public String USER = "imapuser";
      public String PASSWORD = "password";
      public String FROM_ADDRESS = "sender@localhost";
      public String TO_ADDRESS = USER + "@" + HOST;
      
  }
  
  
  
  1.1                  jakarta-james/proposals/imap2/test/org/apache/james/imapserver/ImapHostTest.java
  
  Index: ImapHostTest.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.imapserver.store.ImapStore;
  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.ImapConstants;
  import org.apache.james.services.User;
  import org.apache.james.userrepository.DefaultUser;
  import junit.framework.TestCase;
  
  import java.util.Collection;
  
  /**
   * A test for implementations of the {@link ImapHost} interface.
   *
   * TODO Tests to write:
   *   - Creating and accessing mailboxes with qualified names
   *   - Create existing mailbox
   *   - Delete Inbox
   *   - Rename
   *   - Rename Inbox
   *   - ListMailboxes
   *  
   * @author  Darrell DeBoer <darrell@apache.org>
   *
   * @version $Revision: 1.1 $
   */
  public class ImapHostTest extends TestCase
          implements ImapConstants
  {
      private ImapHost imapHost;
      private User user;
  
      public ImapHostTest( String s )
      {
          super( s );
      }
  
      protected void setUp() throws Exception
      {
          super.setUp();
          user = new DefaultUser( "user", null );
  
          imapHost = getHostImplementation();
          imapHost.createPrivateMailAccount( user );
      }
  
      protected ImapHost getHostImplementation()
      {
          return new JamesImapHost( new InMemoryStore() );
      }
  
      public void testCreatePersonal() throws Exception
      {
          // Create a single mailbox.
          create( "test" );
          assertMailbox( "test", true );
  
          // Create a child of an existing mailbox.
          create("test.another" );
          assertMailbox( "test", true );
          assertMailbox( "test.another", true );
  
          // A multi-level create, which creates intervening mailboxes,
          // with the \NoSelect attribute set.
          create( "this.is.another.mailbox");
          assertMailbox( "this", false );
          assertMailbox( "this.is", false );
          assertMailbox( "this.is.another", false );
          assertMailbox( "this.is.another.mailbox", true );
  
          // Create a child of an existing, no-select mailbox.
          create( "this.is.yet.another.mailbox");
          assertMailbox( "this", false );
          assertMailbox( "this.is", false );
          assertMailbox( "this.is.yet", false );
          assertMailbox( "this.is.yet.another", false );
          assertMailbox( "this.is.yet.another.mailbox", true );
      }
  
      public void testDelete() throws Exception
      {
          // Simple create/delete
          create( "test" );
          assertMailbox( "test", true );
          delete( "test" );
          assertNoMailbox( "test");
  
          // Create a chain and delete the parent.
          // Child should remain, and parent be switched to NoSelect.
          create( "one" );
          create( "one.two" );
          assertMailbox( "one", true );
          assertMailbox( "one.two", true );
          delete( "one");
          assertMailbox( "one", false);
          assertMailbox( "one.two", true );
  
          // Can't delete mailbox with NoSelect attribute and children.
          try
          {
              delete( "one" );
              fail( "Should not be able to delete a non-selectabl mailbox which has children." );
          }
          catch( MailboxException e )
          {
              // TODO check for correct exception.
          }
  
          // Delete the child, then the non-selectable parent
          delete( "one.two");
          delete( "one");
          assertNoMailbox( "one.two" );
          assertNoMailbox( "one" );
      }
  
      private void assertMailbox( String name, boolean selectable ) throws MailboxException
      {
          ImapMailbox mailbox = imapHost.getMailbox( user, name );
          assertNotNull( "Mailbox <" + name + "> expected to exist in store.",
                         mailbox );
          if ( selectable )
          {
              assertTrue( "Mailbox <" + name + "> not selectable.",
                          mailbox.isSelectable() );
          }
          else
          {
              assertTrue( "Mailbox <" + name + "> should not be selectable.",
                          ! mailbox.isSelectable() );
          }
      }
  
      private void assertNoMailbox( String name ) throws Exception
      {
          ImapMailbox mailbox = imapHost.getMailbox( user, name );
          assertNull( "Mailbox <" + name + "> should not exist.",
                      mailbox );
      }
  
      private ImapMailbox create( String name ) throws Exception
      {
          return imapHost.createMailbox( user, name );
      }
  
      private void delete( String name ) throws Exception
      {
          imapHost.deleteMailbox( user, name );
      }
  }
  
  
  
  1.1                  jakarta-james/proposals/imap2/test/org/apache/james/imapserver/ImapRequestParserTest.java
  
  Index: ImapRequestParserTest.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.TestCase;
  
  import java.io.StringReader;
  import java.io.BufferedReader;
  
  /**
   * Tests for the {@link ImapRequestParser}.
   * TODO: atom, literal, other (not yet implemented) arguments
   * @author  Darrell DeBoer <darrell@apache.org>
   *
   * @version $Revision: 1.1 $
   */
  public class ImapRequestParserTest
          extends TestCase
  {
      public ImapRequestParserTest( String s )
      {
          super( s );
      }
  
      public void testTag() throws Exception
      {
          String testRequest = "a01 a.not.her not+ok";
          ImapRequestParser parser = parse( testRequest );
  
          assertEquals( "a01", parser.tag() );
          assertEquals( "a.not.her", parser.tag() );
  
          try {
              String test = parser.tag();
              fail( "Tags may not contain the '+' character." );
          }
          catch ( ProtocolException e ) {}
      }
  
      /**
       * Tests handling of quoted strings.
       * TODO: special characters, escaped quotes
       */
      public void testQuoted() throws Exception
      {
          String testRequest = "\"word\" \"words with spaces\" \"\"";
          ImapRequestParser parser = parse( testRequest );
  
          assertEquals( "word", parser.quoted() );
          assertEquals( "words with spaces", parser.quoted() );
          assertEquals( "", parser.quoted() );
      }
  
      /**
       * Test handling of astring arguments. More detailed tests for atom,
       * quoted and literal should be in specific argument tests.
       * TODO: add literal
       */
      public void testAstring() throws Exception
      {
          String testRequest = "atom at.om \"quoted\" \"\"";
          ImapRequestParser parser = parse( testRequest );
  
          assertEquals( "atom", parser.astring() );
          assertEquals( "at.om", parser.astring() );
          assertEquals( "quoted", parser.astring() );
          assertEquals( "", parser.astring() );
      }
  
      private ImapRequestParser parse( String testRequest )
      {
          BufferedReader reader = new BufferedReader( new StringReader( testRequest ) );
          ImapRequestParser parser = new ImapRequestParser( reader );
          assertTrue( parser.nextRequest() );
          return parser;
      }
  }
  
  
  
  1.1                  jakarta-james/proposals/imap2/test/org/apache/james/imapserver/ImapStoreTest.java
  
  Index: ImapStoreTest.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.imapserver.store.ImapStore;
  import org.apache.james.imapserver.store.InMemoryStore;
  import org.apache.james.imapserver.store.ImapMailbox;
  import org.apache.james.imapserver.store.MailboxException;
  
  import junit.framework.TestCase;
  
  import java.util.Collection;
  
  /**
   * A test for implementations of the {@link org.apache.james.imapserver.store.ImapStore} interface.
   *
   * TODO tests to write
   *   - Rename
   *   - List (various combinations)
   *   - 
   * @author  Darrell DeBoer <darrell@apache.org>
   *
   * @version $Revision: 1.1 $
   */
  public class ImapStoreTest extends TestCase
          implements ImapConstants
  {
      private ImapStore imapStore;
  
      public ImapStoreTest( String s )
      {
          super( s );
      }
  
      protected void setUp() throws Exception
      {
          super.setUp();
          imapStore = getStoreImplementation();
      }
  
      protected ImapStore getStoreImplementation()
      {
          return new InMemoryStore();
      }
  
      public void testCreate() throws Exception
      {
          // Get the user namespace.
          ImapMailbox root = imapStore.getMailbox( ImapConstants.USER_NAMESPACE );
  
          // Create a single mailbox.
          ImapMailbox test = create( root, "test" );
          assertMailbox( "test", true );
  
          // Create a single mailbox as noselect.
          ImapMailbox noSelect = imapStore.createMailbox( root, "noSelect", false );
          assertMailbox( "noSelect", false );
  
          // Create children for these mailboxes.
          create( test, "child" );
          create( noSelect, "child" );
  
          assertMailbox( "test", true );
          assertMailbox( "test.child", true );
          assertMailbox( "noSelect", false );
          assertMailbox( "noSelect.child", true );
  
          try {
              imapStore.createMailbox( root, "bad.name", true );
              fail( "Shouldn't create mailboxes with compound names." );
          }
          catch ( MailboxException e ) {}
      }
  
      public void testDelete() throws Exception
      {
          // Simple create/delete
          create("test" );
          assertMailbox( "test", true );
  
          delete( "test" );
          assertNoMailbox( "test");
  
          // Create a chain and attempt to delete the parent.
          ImapMailbox one = create( "one" );
          create( one, "two" );
          assertMailbox( "one", true );
          assertMailbox( "one.two", true );
  
          try {
              delete( "one");
              fail( "Delete of mailbox with children should fail." );
          }
          catch ( MailboxException e ) {
          }
          assertMailbox( "one", true );
          assertMailbox( "one.two", true );
  
          // Delete the child, then the parent
          delete( "one.two");
          delete( "one");
          assertNoMailbox( "one" );
          assertNoMailbox( "one.two" );
      }
  
      public void testListMailboxes() throws Exception
      {
          Collection coll;
          coll = list("*");
          assertTrue( coll.isEmpty() );
          coll = list("%");
          assertTrue( coll.isEmpty() );
  
          ImapMailbox test = create( "test" );
          ImapMailbox testOne = create( test, "one" );
          ImapMailbox testTwo = create( test, "two" );
          ImapMailbox testTwoAaa = create( testTwo, "aaa" );
          ImapMailbox different = create( "different" );
          ImapMailbox differentOne = create( different, "one" );
          ImapMailbox differentTwo = create( different, "two" );
  
          coll = list("*");
          assertContents( coll, new ImapMailbox[]{test, testOne, testTwo, testTwoAaa, different, differentOne, differentTwo});
  
          coll = list("%");
          assertContents( coll, new ImapMailbox[]{test, different});
  
          coll = list("te*");
          assertContents( coll, new ImapMailbox[]{test, testOne, testTwo, testTwoAaa});
  
          coll = list("te%");
          assertContents( coll, new ImapMailbox[]{test});
  
          coll = list("test*");
          assertContents( coll, new ImapMailbox[]{test, testOne, testTwo, testTwoAaa});
  
          // TODO - should this return children?
          coll = list("test%");
          assertContents( coll, new ImapMailbox[]{test});
  
          coll = list("test.*");
          assertContents( coll, new ImapMailbox[]{testOne, testTwo, testTwoAaa});
  
          coll = list( "test.%" );
          assertContents( coll, new ImapMailbox[]{testOne, testTwo});
  
      }
  
      private void assertContents( Collection coll, ImapMailbox[] imapMailboxes )
              throws Exception
      {
          assertEquals( coll.size(), imapMailboxes.length );
          for ( int i = 0; i < imapMailboxes.length; i++ )
          {
              assertTrue( coll.contains( imapMailboxes[i] ) );
          }
      }
  
      private void assertMailbox( String name, boolean selectable )
      {
          ImapMailbox mailbox = imapStore.getMailbox( prefixUserNamespace( name ) );
          assertNotNull( "Mailbox <" + name + "> expected to exist in store.",
                         mailbox );
          if ( selectable )
          {
              assertTrue( "Mailbox <" + name + "> not selectable.",
                          mailbox.isSelectable() );
          }
          else
          {
              assertTrue( "Mailbox <" + name + "> should not be selectable.",
                          ! mailbox.isSelectable() );
          }
      }
  
      private void assertNoMailbox( String name )
      {
          ImapMailbox mailbox = imapStore.getMailbox( prefixUserNamespace( name ));
          assertNull( "Mailbox <" + name + "> should not exist.",
                      mailbox );
      }
  
      private ImapMailbox create( String name ) throws Exception
      {
          ImapMailbox root = imapStore.getMailbox( USER_NAMESPACE );
          return create( root, name );
      }
  
      private ImapMailbox create( ImapMailbox parent, String name )
              throws MailboxException
      {
          return imapStore.createMailbox( parent, name, true );
      }
  
      private void delete( String name ) throws MailboxException
      {
          ImapMailbox mailbox = imapStore.getMailbox( prefixUserNamespace( name ) );
          imapStore.deleteMailbox( mailbox );
      }
  
      private Collection list( String pattern ) throws MailboxException
      {
          return imapStore.listMailboxes( prefixUserNamespace( pattern ) );
      }
  
      private String prefixUserNamespace( String name )
      {
          return USER_NAMESPACE + HIERARCHY_DELIMITER + name;
      }
  }
  
  
  
  1.1                  jakarta-james/proposals/imap2/test/org/apache/james/imapserver/InitialMail.java
  
  Index: InitialMail.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.TestCase;
  
  import javax.mail.internet.InternetAddress;
  import javax.mail.internet.MimeMessage;
  import javax.mail.Message;
  import javax.mail.Session;
  import javax.mail.Transport;
  import java.util.Properties;
  
  public final class InitialMail extends TestCase
          implements IMAPTest
  {
      private Session _session;
      private InternetAddress _fromAddress;
      private InternetAddress _toAddress;
  
      public InitialMail( String name )
      {
          super( name );
      }
  
      protected void setUp() throws Exception
      {
          super.setUp();
          Properties props = new Properties();
          props.setProperty("mail.debug","true");
          _session = Session.getDefaultInstance( props );
  
          _fromAddress = new InternetAddress( FROM_ADDRESS );
          _toAddress = new InternetAddress( TO_ADDRESS );
      }
  
      public void testSendInitialMessages() throws Exception
      {
          sendMessage( "Message 1", "This is the first message." );
          sendMessage( "Message 2", "This is the second message." );
          sendMessage( "Message 3", "This is the third message." );
          sendMessage( "Message 4", "This is the fourth message." );
      }
  
      private void sendMessage( String subject, String body )
              throws Exception
      {
          MimeMessage msg = new MimeMessage(_session);
          msg.setFrom( _fromAddress );
          msg.addRecipient(Message.RecipientType.TO, _toAddress );
          msg.setSubject( subject );
          msg.setContent( body, "text/plain" );
  
          Transport.send( msg );
          System.out.println( "Sending message: " + subject );
          
          Thread.sleep( 1000 );
      }
  }
  
  
  
  1.1                  jakarta-james/proposals/imap2/test/org/apache/james/imapserver/InitialUsers.java
  
  Index: InitialUsers.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;
  import junit.framework.TestCase;
  import org.apache.james.remotemanager.UserManagementTest;
  
  public final class InitialUsers
          extends TestCase implements IMAPTest
  {
      public InitialUsers( String s )
      {
          super( s );
      }
  
      public static Test suite() throws Exception
      {
          TestSuite suite = new TestSuite();
          suite.addTest( new UserManagementTest( "addUser", USER, PASSWORD ) );
          return suite;
      }
  }
  
  
  
  1.1                  jakarta-james/proposals/imap2/test/org/apache/james/imapserver/ListMailboxes.test
  
  Index: ListMailboxes.test
  ===================================================================
  # Empty 1st arg searches default context (#mail)
  C: 10 LIST "" test
  S: \* LIST \(\) \"\.\" test
  S: 10 OK LIST completed
  
  # % returns all mailboxes matching
  C: a1 LIST "" %
  SUB {
  S: \* LIST \(\) \"\.\" test
  S: \* LIST \(\) \"\.\" test1
  S: \* LIST \(\) \"\.\" INBOX
  }
  S: a1 OK LIST completed
  C: a2 LIST "" INBOX%
  S: \* LIST \(\) \"\.\" INBOX
  S: a2 OK LIST completed
  C: a3 LIST "" I%
  S: \* LIST \(\) \"\.\" INBOX
  S: a3 OK LIST completed
  C: a4 LIST "" t%
  SUB {
  S: \* LIST \(\) \"\.\" test
  S: \* LIST \(\) \"\.\" test1
  }
  S: a4 OK LIST completed
  
  
  # * returns all folders and subfolders
  C: b1 LIST "" *
  SUB {
  S: \* LIST \(\) \"\.\" test
  S: \* LIST \(\) \"\.\" test1
  S: \* LIST \(\) \"\.\" test.subfolder
  S: \* LIST \(\) \"\.\" test1.subfolder1
  S: \* LIST \(\) \"\.\" INBOX
  }
  S: b1 OK LIST completed
  C: b2 LIST "" INBOX*
  S: \* LIST \(\) \"\.\" INBOX
  S: b2 OK LIST completed
  C: b3 LIST "" I*
  S: \* LIST \(\) \"\.\" INBOX
  S: b3 OK LIST completed
  C: b4 LIST "" t*
  SUB {
  S: \* LIST \(\) \"\.\" test
  S: \* LIST \(\) \"\.\" test1
  S: \* LIST \(\) \"\.\" test.subfolder
  S: \* LIST \(\) \"\.\" test1.subfolder1
  }
  S: b4 OK LIST completed
  
  
  
  1.1                  jakarta-james/proposals/imap2/test/org/apache/james/imapserver/ListNamespace.test
  
  Index: ListNamespace.test
  ===================================================================
  # Empty 2nd arg retrieves hierarchy delimiter and root name of 1st argument
  # Rooted in private namespace
  C: e1 LIST "" ""
  S: \* LIST \(\\Noselect\) \"\.\" \"\"
  S: e1 OK LIST completed
  C: e1 LIST "anything" ""
  S: \* LIST \(\\Noselect\) \"\.\" \"\"
  S: e1 OK LIST completed
  C: e1 LIST test ""
  S: \* LIST \(\\Noselect\) \"\.\" \"\"
  S: e1 OK LIST completed
  
  # Rooted in users namespace
  C: e2 LIST "#mail" ""
  S: \* LIST \(\\Noselect\) \"\.\" \#mail
  S: e2 OK LIST completed
  C: e3 LIST "#mail.someone" ""
  S: \* LIST \(\\Noselect\) \"\.\" \#mail
  S: e3 OK LIST completed
  
  # Rooted in shared namespace
  # TODO: shared namespace doesn't currently exist.
  #C: e2 LIST "#share" ""
  #S: * LIST (\Noselect) "." #share
  #S: e2 OK LIST completed
  #C: e3 LIST "#share.something" ""
  #S: * LIST (\Noselect) "." #share
  #S: e3 OK LIST completed
  
  # Rooted in unknown namespace
  C: e2 LIST "#unknown" ""
  S: \* LIST \(\\Noselect\) \"\.\" \"\"
  S: e2 OK LIST completed
  C: e3 LIST "#unknown.something" ""
  S: \* LIST \(\\Noselect\) \"\.\" \"\"
  S: e3 OK LIST completed
  
  
  
  
  1.1                  jakarta-james/proposals/imap2/test/org/apache/james/imapserver/Login.test
  
  Index: Login.test
  ===================================================================
  # Empty file to use for simple login test.
  C: a001 LOGIN
  S: a001 BAD Missing argument. Command should be '<tag> LOGIN <userid> <password>'
  
  C: a002 LOGIN invaliduser
  S: a002 BAD Missing argument. Command should be '<tag> LOGIN <userid> <password>'
  
  C: a002a LOGIN imapuser password extra
  S: a002a BAD Extra argument found. Command should be '<tag> LOGIN <userid> <password>'
  
  C: a003 LOGIN invaliduser password
  S: a003 NO LOGIN failed
  
  C: a004 LOGIN imapuser invalid
  S: a004 NO LOGIN failed
  
  C: a005 LOGIN imapuser password
  S: a005 OK LOGIN completed
  
  
  1.1                  jakarta-james/proposals/imap2/test/org/apache/james/imapserver/LoginAuthenticated.test
  
  Index: LoginAuthenticated.test
  ===================================================================
  C: a001 LOGIN daz daz
  S: a001 NO LOGIN failed. Command not valid in this state
  
  
  1.1                  jakarta-james/proposals/imap2/test/org/apache/james/imapserver/Logout.test
  
  Index: Logout.test
  ===================================================================
  C: a023 LOGOUT extra stuff
  S: a023 BAD Extra argument found. Command should be '<tag> LOGOUT'
  
  C: A023 LOGOUT
  S: \* BYE IMAP4rev1 Server logging out
  S: A023 OK LOGOUT completed
  
  
  1.1                  jakarta-james/proposals/imap2/test/org/apache/james/imapserver/Lsub.test
  
  	<<Binary file>>
  
  
  1.1                  jakarta-james/proposals/imap2/test/org/apache/james/imapserver/SelectEmpty.test
  
  Index: SelectEmpty.test
  ===================================================================
  C: abcd SELECT test
  S: \* FLAGS \(\\Answered \\Deleted \\Draft \\Flagged \\Seen\)
  S: \* 0 EXISTS
  S: \* 0 RECENT
  S: \* OK \[UIDVALIDITY \d+\]
  S: \* OK No messages unseen
  #S: \* OK \[PERMANENTFLAGS \(\)\]
  S: abcd OK \[READ-WRITE\] SELECT completed
  
  C: abcd SELECT test.subfolder
  SUB {
  S: \* FLAGS \(\\Answered \\Deleted \\Draft \\Flagged \\Seen\)
  S: \* 0 EXISTS
  S: \* 0 RECENT
  S: \* OK \[UIDVALIDITY \d+\]
  S: \* OK No messages unseen
  }
  #S: \* OK \[PERMANENTFLAGS \(\)\]
  S: abcd OK \[READ-WRITE\] SELECT completed
  
  C: abcd SELECT test1
  S: \* FLAGS \(\\Answered \\Deleted \\Draft \\Flagged \\Seen\)
  S: \* 0 EXISTS
  S: \* 0 RECENT
  S: \* OK \[UIDVALIDITY \d+\]
  S: \* OK No messages unseen
  #S: \* OK \[PERMANENTFLAGS \(\)\]
  S: abcd OK \[READ-WRITE\] SELECT completed
  
  C: abcd SELECT subfolder1
  S: abcd NO SELECT failed. No such mailbox.
  
  
  
  1.1                  jakarta-james/proposals/imap2/test/org/apache/james/imapserver/SelectInbox.test
  
  Index: SelectInbox.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
  
  # Try again to ensure that no changes to flags were made.
  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
  
  
  
  1.1                  jakarta-james/proposals/imap2/test/org/apache/james/imapserver/Subscribe.test
  
  Index: Subscribe.test
  ===================================================================
  C: a01 LSUB "" "*"
  S: a01 OK LSUB completed
  
  C: a01 SUBSCRIBE test
  S: a01 OK SUBSCRIBE completed
  
  C: a01 LSUB "" "*"
  S: * LSUB (\Unmarked) "." test
  S: a01 OK LSUB completed
  
  C: a01 SUBSCRIBE test.subfolder
  S: a01 OK SUBSCRIBE completed
  
  C: a01 SUBSCRIBE test1.subfolder1
  S: a01 OK SUBSCRIBE completed
  
  C: a01 LSUB "" "*"
  SUB: 1
  S: * LSUB (\Unmarked) "." test
  S: * LSUB (\Unmarked) "." test.subfolder
  S: * LSUB (\Unmarked) "." test1.subfolder1
  SUB: 1
  S: a01 OK LSUB completed
  
  // LIST All subscribed
  C: a01 LSUB "" "*"
  SUB: 1
  S: * LSUB (\Unmarked) "." test
  S: * LSUB (\Unmarked) "." test.subfolder
  S: * LSUB (\Unmarked) "." test1.subfolder1
  SUB: 1
  S: a01 OK LSUB completed
  
  // LIST A subset of subscribed
  C: a01 LSUB "test" "sub*"
  S: * LSUB (\Unmarked) "." test.subfolder
  S: a01 OK LSUB completed
  
  // Unsubscribe from a parent mailbox, make sure that test.subfolder is still subscribed.
  C: a01 UNSUBSCRIBE test
  S: a01 OK UNSUBSCRIBE completed
  
  C: a01 LSUB "test" "sub*"
  S: * LSUB (\Unmarked) "." test.subfolder
  S: a01 OK LSUB completed
  
  
  // Attempt to unsubscribe from a mailbox that isn't subscribed
  C: a01 UNSUBSCRIBE test1
  S: a01 OK UNSUBSCRIBE completed
  
  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 (\Unmarked) "." test1.subfolder1
  S: a01 OK LSUB completed
  
  
  
  
  
  
  
  
  1.1                  jakarta-james/proposals/imap2/test/org/apache/james/imapserver/Subscribe2.test
  
  Index: Subscribe2.test
  ===================================================================
  // Make sure subscriptions are carried into new login sessions.
  // Subscribed to "test1.subfolder1" after test file Subscribe.test
  C: a01 LSUB "" "*"
  S: * LSUB (\Unmarked) "." test1.subfolder1
  S: a01 OK LSUB completed
  
  C: a01 UNSUBSCRIBE test1.subfolder1
  S: a01 OK UNSUBSCRIBE completed
  
  C: a01 LSUB "" "*"
  S: a01 OK LSUB completed
  
  
  
  
  1.1                  jakarta-james/proposals/imap2/test/org/apache/james/imapserver/Test.test
  
  Index: Test.test
  ===================================================================
  C: abcd SELECT inbox
  S: * FLAGS (\Answered \Deleted \Draft \Flagged \Seen)
  S: * 1 EXISTS
  S: * 1 RECENT
  S: * OK [UIDVALIDITY 1 ]
  S: * OK No messages unseen
  S: abcd OK [READ-WRITE] Select completed
  
  C: f1 FETCH 1 (ALL)
  S: * 1 FETCH (FLAGS (\RECENT ) RFC822.SIZE 0 ENVELOPE ("Mon, 31 Dec 2001 13:24:55 +1000" NIL ((NIL NIL "darrell.deboer" "mathiasco.co.uk")) ((NIL NIL "darrell.deboer" "mathiasco.co.uk")) ((NIL NIL "darrell.deboer" "mathiasco.co.uk")) NIL NIL NIL NIL NIL) INTERNALDATE "Mon, 31 Dec 2001 13:24:55 +1000"))
  S: f1 OK
  
  * 1 FETCH (FLAGS () RFC822.SIZE 0 ENVELOPE ("Mon, 31 Dec 2001 13:24:55 +1000" NIL ((NIL NIL "darrell.deboer" "mathiasco.co.uk")) ((NIL NIL "darrell.deboer" "mathiasco.co.uk")) ((NIL NIL "darrell.deboer" "mathiasco.co.uk")) NIL NIL NIL NIL NIL) BODY ("Text" "plain" NIL NIL NIL NIL 0 0) INTERNALDATE "Mon, 31 Dec 2001 13:24:55 +1000"))> but was:<
  * 1 FETCH (FLAGS () INTERNALDATE "Mon, 31 Dec 2001 14:06:49 +1000") RFC822.SIZE 0 ENVELOPE ("Mon, 31 Dec 2001 14:06:49 +1000" NIL ((NIL NIL "darrell.deboer" "mathiasco.co.uk")) ((NIL NIL "darrell.deboer" "mathiasco.co.uk")) ((NIL NIL "darrell.deboer" "mathiasco.co.uk")) NIL NIL NIL NIL NIL) BODY ("Text" "plain" NIL NIL NIL NIL 0 0))>
  
  
  1.1                  jakarta-james/proposals/imap2/test/org/apache/james/imapserver/TestAuthenticated.java
  
  Index: TestAuthenticated.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.TestCase;
  import junit.framework.Test;
  import junit.framework.TestSuite;
  
  import java.net.Socket;
  import java.io.*;
  import java.util.List;
  import java.util.Iterator;
  import java.util.ArrayList;
  import java.util.Date;
  
  import org.apache.james.test.SimpleFileProtocolTest;
  import org.apache.james.remotemanager.UserManagementTest;
  
  public class TestAuthenticated
          extends SimpleFileProtocolTest implements IMAPTest
  {
      public TestAuthenticated( String name )
      {
          super( name );
          _port = 143;
      }
  
      public void setUp() throws Exception
      {
          super.setUp();
          addTestFile( "Welcome.test", _preElements );
          addLogin( USER, PASSWORD );
      }
  
      protected void addLogin( String username, String password )
      {
          _testElements.add( new ClientRequest( "a001 LOGIN " + username + " " + password ) );
          _testElements.add( new ServerResponse( "a001 OK LOGIN completed" ));
      }
  
      public static Test suite() throws Exception
      {
          TestSuite suite = new TestSuite();
          suite.addTest( new TestAuthenticated( "Capability" ) );
          suite.addTest( new TestAuthenticated( "AuthenticateAuthenticated" ) );
          suite.addTest( new TestAuthenticated( "LoginAuthenticated" ) );
          suite.addTest( new TestAuthenticated( "Logout" ) );
          suite.addTest( new TestAuthenticated( "ExamineInbox" ) );
          suite.addTest( new TestAuthenticated( "SelectInbox" ) );
          suite.addTest( new TestAuthenticated( "Create" ) );
          suite.addTest( new TestAuthenticated( "ExamineEmpty" ) );
          suite.addTest( new TestAuthenticated( "SelectEmpty" ) );
          suite.addTest( new TestAuthenticated( "ListNamespace" ) );
          suite.addTest( new TestAuthenticated( "ListMailboxes" ) );
  
  //        suite.addTest( new TestAuthenticated( "Subscribe" ) );
  //        suite.addTest( new TestAuthenticated( "Subscribe2" ) );
  
          suite.addTest( new TestAuthenticated( "Delete" ) );
  
          return suite;
      }
  
  }
  
  
  
  1.1                  jakarta-james/proposals/imap2/test/org/apache/james/imapserver/TestNonAuthenticated.java
  
  Index: TestNonAuthenticated.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.TestCase;
  import junit.framework.TestResult;
  import junit.framework.TestSuite;
  import junit.framework.Test;
  
  import java.net.Socket;
  import java.io.*;
  import java.util.List;
  import java.util.Iterator;
  import java.util.ArrayList;
  import java.util.Date;
  
  import org.apache.james.test.SimpleFileProtocolTest;
  import org.apache.james.remotemanager.UserManagementTest;
  
  public class TestNonAuthenticated
          extends SimpleFileProtocolTest
  {
      public TestNonAuthenticated( String name )
      {
          super( name );
          _port = 143;
      }
  
      public void setUp() throws Exception
      {
          super.setUp();
          addTestFile( "Welcome.test", _preElements );
      }
  
      public static Test suite() throws Exception
      {
          TestSuite suite = new TestSuite();
          suite.addTest( new TestNonAuthenticated( "Capability" ) );
          suite.addTest( new TestNonAuthenticated( "Authenticate" ) );
          suite.addTest( new TestNonAuthenticated( "Login" ) );
          suite.addTest( new TestNonAuthenticated( "Logout" ) );
  
          return suite;
      }
  
  }
  
  
  
  1.1                  jakarta-james/proposals/imap2/test/org/apache/james/imapserver/TestSelected.java
  
  Index: TestSelected.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;
  
  public class TestSelected
          extends TestAuthenticated
  {
      public TestSelected( String name )
      {
          super( name );
      }
  
      public void setUp() throws Exception
      {
          super.setUp();
          addTestFile( "SelectInbox.test" );
      }
  
      public static Test suite() throws Exception
      {
          TestSuite suite = new TestSuite();
          suite.addTest( new TestSelected( "FetchSingleMessage" ) );
          suite.addTest( new TestSelected( "FetchMultipleMessages" ) );
          return suite;
      }
  }
  
  
  
  1.1                  jakarta-james/proposals/imap2/test/org/apache/james/imapserver/Welcome.test
  
  Index: Welcome.test
  ===================================================================
  S: \* OK IMAP4rev1 Server .* ready
  
  
  
  1.1                  jakarta-james/proposals/imap2/test/org/apache/james/remotemanager/AddUsers.test
  
  Index: AddUsers.test
  ===================================================================
  # This is a comment line
  C: adduser user1 password
  S: User user1 added
  C: adduser user2 password
  S: User user2 added
  C: listusers
  S: Existing accounts 2
  S: user: user1
  S: user: user2
  
  
  1.1                  jakarta-james/proposals/imap2/test/org/apache/james/remotemanager/DeleteUsers.test
  
  Index: DeleteUsers.test
  ===================================================================
  C: deluser user2
  S: User user2 deleted
  C: listusers
  S: Existing accounts 1
  S: user: user1
  C: deluser user1
  S: User user1 deleted
  
  
  
  1.1                  jakarta-james/proposals/imap2/test/org/apache/james/remotemanager/RemoteManagerLogin.test
  
  Index: RemoteManagerLogin.test
  ===================================================================
  S: JAMES Remote Administration Tool 2.1a1-cvs
  S: Please enter your login and password
  S: Login id:
  C: root
  S: Password:
  C: root
  S: Welcome root. HELP for a list of commands
  
  
  
  1.1                  jakarta-james/proposals/imap2/test/org/apache/james/remotemanager/RemoteManagerLogout.test
  
  Index: RemoteManagerLogout.test
  ===================================================================
  C: quit
  S: Bye
  
  
  1.1                  jakarta-james/proposals/imap2/test/org/apache/james/remotemanager/TestRemoteManager.java
  
  Index: TestRemoteManager.java
  ===================================================================
  package org.apache.james.remotemanager;
  
  import junit.framework.TestCase;
  import junit.framework.Test;
  import junit.framework.TestSuite;
  
  import java.net.Socket;
  import java.io.*;
  import java.util.List;
  import java.util.Iterator;
  import java.util.ArrayList;
  import java.util.Date;
  
  import org.apache.james.test.SimpleFileProtocolTest;
  
  public class TestRemoteManager
          extends SimpleFileProtocolTest
  {
  
      public TestRemoteManager( String testFileName ) throws Exception
      {
          super( testFileName );
          _port = 4555;
          addTestFile( "RemoteManagerLogin.test", _preElements );
          addTestFile( "RemoteManagerLogout.test", _postElements );
      }
  
      public static Test suite() throws Exception
      {
          TestSuite suite = new TestSuite();
          suite.addTest( new TestRemoteManager( "AddUsers" ) );
          suite.addTest( new TestRemoteManager( "DeleteUsers" ) );
          return suite;
      }
  }
  
  
  
  1.1                  jakarta-james/proposals/imap2/test/org/apache/james/remotemanager/UserManagementTest.java
  
  Index: UserManagementTest.java
  ===================================================================
  package org.apache.james.remotemanager;
  
  import org.apache.james.test.AbstractProtocolTest;
  
  public class UserManagementTest
          extends AbstractProtocolTest
  {
      private String _userName;
      private String _password;
  
      public UserManagementTest( String action, String userName )
      {
          this( action, userName, "password" );
      }
  
      public UserManagementTest( String action, String userName, String password )
      {
          super( action );
          _port = 4555;
          _userName = userName;
          _password = password;
      }
  
      public void setUp() throws Exception
      {
          super.setUp();
          addTestFile( "RemoteManagerLogin.test", _preElements );
          addTestFile( "RemoteManagerLogout.test", _postElements );
      }
  
      public void addUser() throws Exception
      {
            addUser( _userName, _password );
      }
  
      protected void addUser( String userName, String password )
              throws Exception
      {
          CL( "adduser " + userName + " " + password );
          SL( "User " + userName + " added" );
          executeTests();
      }
  
      /*protected void addExistingUser( String userName, String password )  
          throws Exception{
          CL( "adduser " + userName + " " + password );
          SL( "user " + userName + " already exist" );
          executeTests();
      }*/
  
      public void deleteUser() throws Exception
      {
          deleteUser( _userName );
      }
  
      protected void deleteUser( String userName ) throws Exception
      {
          CL( "deluser " + userName );
          SL( "User " + userName + " deleted" );
          executeTests();
      }
  }
  
  
  
  1.1                  jakarta-james/proposals/imap2/test/org/apache/james/smtpserver/Send.test
  
  Index: Send.test
  ===================================================================
  // Comment line - ignored.
  
  // The ${ignore} macro accepts any single word element.
  
  S: 220 ${ignore} SMTP Server (JAMES SMTP Server 2.0a3-cvs) ready ${rfcDate}
  C: HELO localhost
  S: 250 ${ignore} Hello localhost (127.0.0.1 [127.0.0.1])
  // The ${from-address} and ${to-address} macros are NYI, but you get the idea.
  C: MAIL FROM: <${from-address}>
  S: 250 Sender <${from-address}> OK
  C: RCPT TO: <${to-address}>
  S: 250 Recipient <${to-address}> OK
  C: DATA
  S: 354 Ok Send data ending with <CRLF>.<CRLF>
  C: test
  C: .
  S: 250 Message received
  C: QUIT
  S: 221 ${ignore} Service closing transmission channel
  
  
  
  1.1                  jakarta-james/proposals/imap2/test/org/apache/james/smtpserver/TestSMTP.java
  
  Index: TestSMTP.java
  ===================================================================
  package org.apache.james.smtpserver;
  
  import junit.framework.TestCase;
  import junit.framework.Test;
  import junit.framework.TestSuite;
  
  import java.net.Socket;
  import java.io.*;
  import java.util.List;
  import java.util.Iterator;
  import java.util.ArrayList;
  import java.util.Date;
  
  import org.apache.james.test.SimpleFileProtocolTest;
  
  
  public class TestSMTP
          extends SimpleFileProtocolTest
  {
      public TestSMTP( String name )
      {
          super( name );
          _port = 25;
          _timeout = 0;
      }
  
      public static Test suite() throws Exception
      {
          TestSuite suite = new TestSuite();
          suite.addTest( new TestSMTP( "Send" ) );
          return suite;
      }
  }
  
  
  
  1.1                  jakarta-james/proposals/imap2/test/org/apache/james/test/AbstractProtocolTest.java
  
  Index: AbstractProtocolTest.java
  ===================================================================
  package org.apache.james.test;
  
  import org.apache.oro.text.perl.Perl5Util;
  
  import junit.framework.TestCase;
  
  import java.io.*;
  import java.util.*;
  import java.net.Socket;
  
  /**
   * Abstract Protocol Test is the root of all of the James Imap Server test
   * cases.  It provides functionality to create text files for matching
   * client requests and server responses.  In order to use it however you
   * must create a sub class and set all the file names, etc up yourself.
   * All Comments are written by Andy Oliver who is still trying to figure out
   * some of it himself so don't take this as gospel
   *
   * @author Unattributed Original Authors
   * @author Andrew C. Oliver
   */
  public abstract class AbstractProtocolTest extends TestCase
  {
      private Socket _socket;
      private PrintWriter _out;
      private BufferedReader _in;
      protected String _host = "127.0.0.1";
  
      protected int _port;
      protected int _timeout = 0;
  
      protected List _preElements = new ArrayList();
      protected List _testElements = new ArrayList();
      protected List _postElements = new ArrayList();
  
      private static final Perl5Util perl = new Perl5Util();
      private static final String CLIENT_TAG = "C: ";
      private static final String SERVER_TAG = "S: ";
      private static final String OPEN_UNORDERED_BLOCK_TAG = "SUB {";
      private static final String CLOSE_UNORDERED_BLOCK_TAG = "}";
      private static final String COMMENT_TAG = "#";
  
      public AbstractProtocolTest( String s )
      {
          super( s );
      }
  
      // comment in TestCase
      public void setUp() throws Exception
      {
          super.setUp();
          _testElements.clear();
  
          _socket = new Socket( _host, _port );
          _socket.setSoTimeout( _timeout );
          _out = new PrintWriter( _socket.getOutputStream(), true );
          _in = new BufferedReader( new InputStreamReader( _socket.getInputStream() ) );
      }
  
      // comment in TestCase
      protected void tearDown() throws Exception
      {
          _out.close();
          _in.close();
          _socket.close();
          super.tearDown();
      }
  
      // comment in TestCase
      protected void executeTests() throws Exception
      {
          executeTest( _preElements );
          executeTest( _testElements );
          executeTest( _postElements );
      }
  
      /**
       * executes the test case as specified in the file.  Commands in
       * CL: elements are sent to the server, and the SL: lines are verified
       * against those returning from the server.  The order is important
       * unless in a "SUB:" block in which case the order is not important and
       * the test will pass if any line in the SUB: block matches.
       */
      protected void executeTest( List protocolLines ) throws Exception
      {
          for ( Iterator iter = protocolLines.iterator(); iter.hasNext(); ) {
              Object obj = iter.next();
              if ( obj instanceof ProtocolElement ) {
                  ProtocolElement test = ( ProtocolElement ) obj;
                  test.testProtocol( _out, _in );
              }
          }
      }
  
      /**
       * adds a new Client request line to the test elements
       */
      protected void CL( String clientLine )
      {
          _testElements.add( new ClientRequest( clientLine ) );
      }
  
      /**
       * adds a new Server Response line to the test elements
       */
      protected void SL( String serverLine )
      {
          _testElements.add( new ServerResponse( serverLine ) );
      }
  
      /**
       * This Line is sent to the server (everything after "CL: ") in expectation
       * that the server will respond.
       */
      protected class ClientRequest implements ProtocolElement
      {
          private String _msg;
  
          public ClientRequest( String msg )
          {
              _msg = msg;
          }
  
          /**
           * Sends the request to the server
           */
          public void testProtocol( PrintWriter out, BufferedReader in ) throws Exception
          {
              out.println( _msg );
          }
  
          /**
           * This should NOT be called, CL is not blockable!  Runtime exception
           * will be thrown.  Implemented because of "ProtocolElement"
           */
          public void testProtocolBlock( PrintWriter out, BufferedReader in, List list )
                  throws Exception
          {
              //out.println( _msg );
              throw new RuntimeException( "Syntax error in test case, CL is not " +
                                          "able to be used in a SUB: block" );
          }
      }
  
      protected class ServerResponse implements ProtocolElement
      {
          private String expectedLine;
          protected String location;
  
          public ServerResponse( String expectedPattern, String location )
          {
              this.expectedLine = expectedPattern;
              this.location = location;
          }
  
          public ServerResponse( String expectedPattern )
          {
              this( expectedPattern, "" );
          }
  
          public void testProtocol( PrintWriter out, BufferedReader in ) throws Exception
          {
              String testLine = readLine( in );
              if ( ! match( expectedLine, testLine ) ) {
                  String errMsg = "\nLocation: " + location +
                          "\nExcpected: " + expectedLine +
                          "\nActual   : " + testLine;
                  fail( errMsg );
              }
          }
  
          protected boolean match( String expected, String actual )
          {
              String pattern = "m/" + expected + "/";
              return perl.match( pattern, actual );
          }
  
          /**
           * Grabs a line from the server and throws an error message if it
           * doesn't work out
           * @param in BufferedReader for getting the server response
           * @return String of the line from the server
           */
          protected String readLine( BufferedReader in ) throws Exception
          {
              try {
                  return in.readLine();
              }
              catch ( InterruptedIOException e ) {
                  String errMsg = "\nLocation: " + location +
                          "\nExpected: " + expectedLine +
                          "\nReason: Server Timeout.";
                  fail( errMsg );
                  return "";
              }
          }
      }
  
      private class UnorderedBlockResponse extends ServerResponse
      {
          private List expectedLines = new ArrayList();
  
          public UnorderedBlockResponse( List expectedLines, String location )
          {
              super( "<Unordered Block>", location );
              this.expectedLines = expectedLines;
          }
  
          public void testProtocol( PrintWriter out, BufferedReader in ) throws Exception
          {
              List testLines = new ArrayList( expectedLines );
              while ( testLines.size() > 0 )
              {
                  String actualLine = readLine( in );
                  boolean foundMatch = false;
  
                  for ( int i = 0; i < testLines.size(); i++ )
                  {
                      String expected = (String)testLines.get( i );
                      if ( match( expected, actualLine ))
                      {
                          foundMatch = true;
                          testLines.remove( expected );
                          break;
                      }
                  }
  
                  if (! foundMatch )
                  {
                      StringBuffer errMsg = new StringBuffer()
                          .append( "\nLocation: " )
                          .append( location )
                          .append( "\nExpected one of: " );
                      Iterator iter = expectedLines.iterator();
                      while ( iter.hasNext() ) {
                          errMsg.append( "\n    " );
                          errMsg.append( iter.next() );
                      }
                      errMsg.append("\nActual: " )
                            .append( actualLine );
  
                      fail( errMsg.toString() );
                  }
              }
          }
      }
  
  
      protected interface ProtocolElement
      {
          void testProtocol( PrintWriter out, BufferedReader in ) throws Exception;
      }
  
      protected void addTestFile( String fileName ) throws Exception
      {
          addTestFile( fileName, _testElements );
      }
  
      protected void addTestFile( String fileName, List protocolLines ) throws Exception
      {
          // Need to find local resource.
          InputStream is = this.getClass().getResourceAsStream( fileName );
          if ( is == null ) {
              throw new Exception( "Test Resource '" + fileName + "' not found." );
          }
  
          addProtocolLinesFromStream( is, protocolLines, fileName );
      }
  
      private void addProtocolLinesFromStream( InputStream is, List protocolElements, String fileName )
              throws Exception
      {
          BufferedReader reader = new BufferedReader( new InputStreamReader( is ) );
          String next;
          int lineNumber = 1;
          while ( ( next = reader.readLine() ) != null ) {
              String location = fileName + ":" + lineNumber;
              if ( next.startsWith( CLIENT_TAG ) ) {
                  String clientMsg = next.substring( 3 );
                  protocolElements.add( new ClientRequest( clientMsg ) );
              }
              else if ( next.startsWith( SERVER_TAG ) ) {
                  String serverMsg = next.substring( 3 );
                  protocolElements.add( new ServerResponse( serverMsg, location ) );
              }
              else if ( next.startsWith( OPEN_UNORDERED_BLOCK_TAG ) ) {
                  List unorderedLines = new ArrayList( 5 );
                  next = reader.readLine();
  
                  while ( !next.startsWith( CLOSE_UNORDERED_BLOCK_TAG ) ) {
                      if (! next.startsWith( SERVER_TAG ) ) {
                          throw new Exception( "Only 'S: ' lines are permitted inside a 'SUB {' block.");
                      }
                      String serverMsg = next.substring( 3 );
                      unorderedLines.add( serverMsg );
                      next = reader.readLine();
                      lineNumber++;
                  }
  
                  UnorderedBlockResponse blockResponse =
                          new UnorderedBlockResponse( unorderedLines, location );
                  protocolElements.add( blockResponse );
              }
              else if ( next.startsWith( COMMENT_TAG )
                      || next.trim().length() == 0 ) {
                  // ignore these lines.
              }
              else {
                  String prefix = next;
                  if ( next.length() > 3 ) {
                      prefix = next.substring( 0, 3 );
                  }
                  throw new Exception( "Invalid line prefix: " + prefix );
              }
              lineNumber++;
          }
      }
  }
  
  
  
  1.1                  jakarta-james/proposals/imap2/test/org/apache/james/test/JamesTask.java
  
  Index: JamesTask.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.test;
  
  import org.apache.tools.ant.BuildException;
  import org.apache.tools.ant.AntClassLoader;
  import org.apache.tools.ant.types.Path;
  import org.apache.tools.ant.types.Reference;
  import org.apache.tools.ant.types.CommandlineJava;
  import org.apache.avalon.framework.parameters.Parameters;
  import org.apache.avalon.framework.parameters.Parameterizable;
  import org.apache.avalon.framework.ExceptionUtil;
  import org.apache.avalon.framework.CascadingRuntimeException;
  import org.apache.avalon.phoenix.components.embeddor.SingleAppEmbeddor;
  
  /**
   * An attempt at a task which can launch James from Ant, and shut it down again.
   * Doesn't really work, yet.
   */
  public final class JamesTask
         extends org.apache.tools.ant.Task
  {
      private CommandlineJava cmdl = new CommandlineJava();
      private Parameters m_parameters;
      private static SingleAppEmbeddor m_embeddor;
      private String m_action;
  
      public void setAction( String action )
      {
          m_action = action;
      }
  
      /**
       * Set the classpath to be used for this compilation.
       */
      public void setClasspath(Path s) {
          createClasspath().append(s);
      }
  
      /**
       * Creates a nested classpath element
       */
      public Path createClasspath() {
          return cmdl.createClasspath(project).createPath();
      }
  
      /**
       * Adds a reference to a CLASSPATH defined elsewhere.
       */
      public void setClasspathRef(Reference r) {
          createClasspath().setRefid(r);
      }
  
  
      public void execute() throws BuildException
      {
          if ( m_action.equalsIgnoreCase( "start" ) ) {
              startup();
          }
          else if ( m_action.equalsIgnoreCase( "stop" ) ) {
              shutdown();
          }
          else if ( m_action.equalsIgnoreCase( "start-stop" ) ) {
              startup();
              shutdown();
          }
          else if ( m_action.equalsIgnoreCase( "restart" ) ) {
              optionalShutdown();
              startup();
          }
          else {
              throw new BuildException( "Invalid action: '" + m_action + "'" );
          }
      }
  
      private void startup() throws BuildException
      {
          if ( m_embeddor != null ) {
             throw new BuildException( "Already started" );
          }
  
          m_parameters = new Parameters();
  //        m_parameters.setParameter( "log-destination", "." );
  //        m_parameters.setParameter( "log-priority", "DEBUG" );
  //        m_parameters.setParameter( "application-name", "james" );
          m_parameters.setParameter( "application-location", "dist/apps/james.sar");
  
          try
          {
              m_embeddor = new SingleAppEmbeddor();
              if( m_embeddor instanceof Parameterizable )
              {
                  ( (Parameterizable)m_embeddor ).parameterize( m_parameters );
              }
              m_embeddor.initialize();
  
  //            final Thread thread = new Thread( this, "Phoenix" );
  //            thread.start();
          }
          catch( final Throwable throwable )
          {
              System.out.println( "Exception in initiliaze()" );
              throw new BuildException( throwable );
          }
  
          try
          {
              ClassLoader ctxLoader = Thread.currentThread().getContextClassLoader();
              AntClassLoader loader = new AntClassLoader(ctxLoader, project, cmdl.getClasspath(), true);
              loader.setIsolated(false);
              loader.setThreadContextLoader();
              // nothing
              // m_embeddor.execute();
              // Launch the startup thread.
              Thread startThread = new StartupThread();
              startThread.start();
  
              // Hack to make sure that the embeddor has actually started.
              // Need to make a change to Phoenix, so that we can wait til it's running.
              // Yeild processor.
              Thread.sleep( 1000 );
              // m_embeddor will now be in use until applications are deployed.
              synchronized ( m_embeddor ) {
                  System.out.println( "got synch at: " + System.currentTimeMillis() );
              }
          }
          catch( final Throwable throwable )
          {
              System.out.println( "Exception in execute()" );
              throw new BuildException( throwable );
          }
  
      }
  
      private class StartupThread extends Thread
      {
          StartupThread()
          {
              super( "JamesStartup" );
              this.setDaemon( true );
          }
  
          public void run()
          {
              try {
                  m_embeddor.execute();
              }
              catch ( Exception exc ) {
                  exc.printStackTrace();
                  throw new CascadingRuntimeException( "Exception in execute()", exc );
              }
          }
  
      }
  
      private void optionalShutdown() throws BuildException
      {
          if ( m_embeddor != null ) {
              shutdown();
          }
      }
  
      private void shutdown() throws BuildException
      {
          System.out.println( "In shutdown()" );
          if ( m_embeddor == null ) {
              throw new BuildException( "Not running." );
          }
  
          try
          {
              m_embeddor.dispose();
              System.out.println( "Called dispose()" );
              m_embeddor = null;
              m_parameters = null;
          }
          catch( final Throwable throwable )
          {
              System.out.println( "Exception in dispose()" );
              throw new BuildException( throwable );
          }
      }
  }
  
  
  
  1.1                  jakarta-james/proposals/imap2/test/org/apache/james/test/SimpleFileProtocolTest.java
  
  Index: SimpleFileProtocolTest.java
  ===================================================================
  package org.apache.james.test;
  
  public class SimpleFileProtocolTest
          extends AbstractProtocolTest
  {
      public SimpleFileProtocolTest( String fileName )
      {
          super( fileName );
      }
  
      public SimpleFileProtocolTest( String fileName, String host, int port )
      {
          super( fileName );
          _host = host;
          _port = port;
      }
  
      protected void runTest() throws Throwable
      {
          String testFileName = getName() + ".test";
          addTestFile( testFileName );
          executeTests();
      }
  }
  
  
  

--
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