You are viewing a plain text version of this content. The canonical link for it is here.
Posted to server-dev@james.apache.org by da...@apache.org on 2001/08/15 06:41:30 UTC

cvs commit: jakarta-james/proposals/userstore/lib avalon-excalibur.jar

darrell     01/08/14 21:41:30

  Added:       proposals/userstore README build.xml
               proposals/userstore/conf james-assembly.xml james-config.xml
                        sqlResources.xml
               proposals/userstore/java/org/apache/james/core
                        AvalonUsersStore.xinfo
               proposals/userstore/java/org/apache/james/remotemanager
                        RemoteManagerHandler.java
               proposals/userstore/java/org/apache/james/userrepository
                        AbstractJdbcUsersRepository.java
                        AbstractUsersRepository.java
                        DefaultUsersJdbcRepository.java
                        JamesUsersJdbcRepository.java
                        ListUsersJdbcRepository.java
               proposals/userstore/java/org/apache/james/util
                        SqlResources.java
               proposals/userstore/lib avalon-excalibur.jar
  Log:
  Added userstore proposal to James
  ============================
  Features
  - uses Avalon and Cornerstone Jdbc Connection management
  - SqlResources provides detects db-product and provides db-specific SQL automatically
  - New, simpler implemenations of JdbcUsersRepository.
  - See proposals/userstore/README for more details.
  (Note: to build use "./build.sh -buildfile proposals/userstore/build.xml")
  
  Revision  Changes    Path
  1.1                  jakarta-james/proposals/userstore/README
  
  Index: README
  ===================================================================
  PROPOSAL: - Modifications to UserStore.
              
  
  GOALS: 
  - use Avalon and Cornerstone DataSource components for connection 
          serving and pooling (done)
  - Remove hard-coded SQL statements from UsersJdbcRepository (done)
  - 'SqlResources.java' - detect db product from jdbc connection and select appropriate
          SQL statements from SQL definition file for specific product (done)
  - Simpler to create database-backed UserRepository implementations for 
          different User implementations (done)
  - Consolidate existing UserRepository implementations - refactor out common
          functionality
  - Simplify UserRepository specification in config - make it URL:// based, 
          like MailRepository. 
  - Have UserStore serve up repository implementations based on: storage,
          User implementation, and location.
          
  
  OTHER GOALS (reuse development in JdbcMailRepository):
  - use Avalon and Cornerstone DataSource components in JdbcMailRepository
  - Use SqlResources.java to provide db-specific SQL to JdbcMailRepository
  - Automatic table generation for JdbcMailRepository
  
  BUILD INSTRUCTIONS:
  ./build.sh -buildfile proposals/userstore/build.xml
  
  I haven't modified the original buildfile, but instead provided a build.xml within
  the proposals dir. (I think this is a cleaner approach, since we don't pollute the
  "real" buildfile.)
  
  The "javadocs" target builds javadoc from the build/src directory, so it includes
  docs for the proposal.
  
  NOTES:
  I've added an "AbstractJdbcUsersRepository", which takes care of most of the work
  of a JdbcUsersRepository, making it pretty easy to add new ones. The abstract
  implementation doesn't have knowledge of User subclasses, this is restricted to
  overridden methods in concrete implementations.
   
  The AbstractJdbcUsersRepository obtains SQL statements via an "SqlResources" object,
  which reads an sql definition file, finds the appropriate <sqlDefs> element, and
  provides the sql strings contained. In addition, the SqlResources class handles
  2 other things: 
      a) Parameter replacement in SQL (eg replace all occurances of ${table} within
         an sql statement with the parameter value for "table". Currently, all 
         parameters are taken from the configuration <sqlParameters> element. It
         is also possible to define parameters (defaults, if you like) within the 
         sql definition file itself (a <parameters> element).
      b) Examines the Jdbc Connection to determine what database product is being
         used. SQL statements specific to a db product (eg mysql) can then be used
         automatically. (Detection is done by regexp matches on 
         Connection.getMetaData.getDatabaseProductName())
         
  I've added 3 concrete subclasses of AbstractJdbcUserRepository: for DefaultUser,
  DefaultJamesUser, and "ListUser" (which for now is nothing more than a name). These
  give an example of how little work there is to implement a new repository. The
  ListUsersJdbcRepository can store multiple lists of names in a single table.
  
  I've made a simple modification to "RemoteManagerHandler", to allow testing. The
  "use [userRepositoryName]" command will switch the Remote manager to manage the
  named repository. This isn't really intended for production, makes for easier testing.
  The "james-config.xml" included in the proposal sets up 4 jdbc repositories: 
   - "localUsers" - a JamesUsersJdbcRepository.
   - "list-james" - a ListUsersJdbcRepository, used by the ListServ mailet.
   - "list-test" - another ListUsersJdbcRepositor, for testing.
   - "default-users" - a DefaultUsersJdbcRepository, for testing.
  
  Note that in order for the Avalon DataSource components to work, I've included
  an upgraded "avalon-excalibur.jar" in the proposal.
  
         
          
  
  
  
  1.1                  jakarta-james/proposals/userstore/build.xml
  
  Index: build.xml
  ===================================================================
  <?xml version="1.0"?>
  
  <!-- ==========================================================================
  
   James build file
  
  Authors:
   Federico Barbieri <sc...@systemy.it>
   Serge
   Charles Benett (charles@benet1.demon.co.uk>
   Peter Donald <do...@apache.org>
  
  Legal:
    Copyright (c) 1999-2001 The Apache Software Foundation. All Rights Reserved.
  
  ==============================================================================
   -->
  
  <project default="main" basedir="../..">
  
    <property name="with.proposal" value="true"/>
  
    <!--
      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="1.3-dev"/>
    <property name="year" value="1999-2001"/>
  
    <!-- 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.warnings" value="true"/>
    <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="on"/>
  
  
    <!--
         ===================================================================
         Set the properties for intermediate directory
         ===================================================================
    -->
    <property name="build.dir" value="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.xdocs" value="${build.dir}/xdocs"/>
    <property name="build.mailetdocs" value="${build.dir}/mailetdocs"/>
    <property name="build.conf" value="${build.dir}/conf"/>
  
    <!--
         ===================================================================
         Set the properties for source directories
         ===================================================================
    -->
  
    <property name="src.dir" value="src"/>
    <property name="java.dir" value="${src.dir}/java"/>
    <property name="conf.dir" value="${src.dir}/conf"/>
    <property name="scripts.dir" value="${src.dir}/script"/>
    <property name="sql.dir" value="${src.dir}/sql"/>
    <property name="proposal.base" value="proposals"/>
    <property name="proposal.dir" value="${proposal.base}/userstore"/>
    <property name="java.proposal.dir" value="${proposal.dir}/java"/>
    <property name="conf.proposal.dir" value="${proposal.dir}/conf"/>
    <property name="lib.dir" value="lib"/>
    <property name="tools.dir" value="tools"/>
    <property name="xdocs.dir" value="${src.dir}/xdocs"/>
    <property name="docs.src" value="${xdocs.dir}"/>
    <property name="docs.dir" value="docs"/>
    <property name="www.dir" value="www"/>
    <property name="javadocs.dir" value="${docs.dir}/api"/>
    <property name="mailetdocs.dir" value="${docs.dir}/mailet"/>
    <property name="rfcs.dir" value="${docs.dir}/rfclist"/>
    <property name="dist.name" value="${name}-${version}"/>
  
    <property name="constants.file" value="org/apache/james/Constants.java"/>
    <property name="conf.file" value="james.conf.xml"/>
    <property name="server.file" value="james.server.xml"/>
  
    <property name="site.dir" value="../xml-site/sources/james"/>
    <property name="site.docs" value="../xml-site/targets/james"/>
  
  
  
    <!--
         ===================================================================
         Set the classpath
         ===================================================================
    -->
    <property name="xerces.jar" value="${lib.dir}/xerces.jar"/>
    <property name="framework.jar" value="${lib.dir}/avalon-framework.jar"/>
    
    <!-- Use a newer copy of excalibur -->
    <property name="excalibur.jar" value="${proposal.dir}/lib/avalon-excalibur.jar"/>
    
    <property name="scratchpad.jar" value="${lib.dir}/avalon-scratchpad.jar"/>
    <property name="logkit.jar" value="${lib.dir}/logkit.jar"/>
    <property name="cornerstone.bar" value="${lib.dir}/cornerstone.bar"/>
    <property name="phoenix.client.jar" value="${lib.dir}/phoenix-client.jar"/>
    <!--
      <property name="activation.jar" value="${lib.dir}/activation.jar"/>
    -->
    <path id="project.class.path">
      <pathelement location="${xerces.jar}"/>
      <pathelement location="${framework.jar}"/>
      <pathelement location="${excalibur.jar}"/>
      <pathelement location="${scratchpad.jar}"/>
      <pathelement location="${logkit.jar}"/>
      <pathelement location="${phoenix.client.jar}"/>
      <pathelement location="${cornerstone.bar}"/>
      <pathelement path="${java.class.path}" />
      <fileset dir="${lib.dir}">
        <include name="*.jar" />
        <exclude name="xerces.jar"/>
        <exclude name="framework.jar"/>
        <exclude name="excalibur.jar"/>
        <exclude name="scratchpad.jar"/>
        <exclude name="logkit.jar"/>
        <exclude name="phoenix.client.jar"/>
      </fileset>
      <pathelement path="${build.classes}" />
    </path>
  
    <!--
         ===================================================================
                                    Main target
         ===================================================================
    -->
    <target name="main" depends="dist-lite" />
  
    <!-- =================================================================== -->
    <!-- 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="   block-archive  --> generates the James.bar file (default)"/>
      <echo message="   auxiliary-jars --> generates the mailet.jar"/>
      <echo message="   compile        --> compiles the source code"/>
      <echo message="   dist-lite      --> generates the JAMES distribution, unpacked"/>
      <echo message="   dist           --> generates the JAMES distribution, packed"/>
      <echo message="   clean          --> cleans up the directory"/>
      <echo message="   docs           --> created the documentaion for James"/>
      <echo message="   javadoc        --> creates the Javadoc"/>
      <echo message="   javadocmailet  --> creates the Javadoc for the mailet API"/>
      <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"/>
  
    <!--
         ===================================================================
         Set up dist properties
         ===================================================================
    -->
    <target name="setup-properties" >
  
      <tstamp/>
  
      <property name="dist.dir" value="dist"/>
      <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"/>
  
      <property name="src.dist.dir" value="dist-src"/>
      <property name="src.dist.src" value="${src.dist.dir}/src"/>
      <property name="src.dist.docs" value="${src.dist.dir}/docs"/>
      <property name="src.dist.javadocs" value="${src.dist.dir}/docs/api"/>
      <property name="src.dist.lib" value="${src.dist.dir}/lib"/>
      <property name="src.dist.tools" value="${src.dist.dir}/tools"/>
      <property name="src.dist.proposals" value="${src.dist.dir}/proposals"/>
    </target>
  
  
    <!-- =================================================================== -->
    <!-- Prepares the build directory                                        -->
    <!-- =================================================================== -->
    <target name="prepare" depends="setup-properties">
  
      <mkdir dir="${build.src}"/>
      <mkdir dir="${build.classes}"/>
   
      <!-- Copy source, config, and properties files to the build directory -->
      <copy todir="${build.src}">
        <fileset dir="${java.dir}">
          <exclude name="**/*.properties"/>
          <exclude name="**/*.xinfo"/>
          <!-- Exclude the UsersJdbsRepository - will be removed -->
          <exclude name="**/userrepository/UsersJDBCRepository.java"/>
        </fileset>
      </copy>
      <copy todir="${build.classes}">
        <fileset dir="${java.dir}">
          <include name="**/*.properties"/>
          <include name="**/*.xinfo"/>
        </fileset>
      </copy>
      <copy todir="${build.conf}">
        <fileset dir="${conf.dir}">
        </fileset>
      </copy>
      
  
      <available classname="org.apache.velocity.anakia.AnakiaTask"
          property="AnakiaTask.present"/>
  
      <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>
  
    <target name="prepare-proposal" depends="prepare" if="with.proposal">
      <echo>Building with proposal </echo>
      <!-- overcopy source, config and properties files from proposal -->
      <copy todir="${build.src}" overwrite="on">
        <fileset dir="${java.proposal.dir}">
          <exclude name="**/*.properties"/>
          <exclude name="**/*.xinfo"/>
        </fileset>
      </copy>
      <copy todir="${build.classes}" overwrite="on">
        <fileset dir="${java.proposal.dir}">
          <include name="**/*.properties"/>
          <include name="**/*.xinfo"/>
        </fileset>
      </copy>
      <copy todir="${build.conf}" overwrite="on">
        <fileset dir="${conf.proposal.dir}">
        </fileset>
      </copy>
      
    </target>
  
    <target name="prepare-error" depends="prepare" unless="AnakiaTask.present">
      <echo>
          AnakiaTask is not present! Please check to make sure that
          velocity.jar is in your classpath.
      </echo>
    </target>
  
  
    <!-- =================================================================== -->
    <!-- Compiles the source directory                                       -->
    <!-- =================================================================== -->
    <target name="compile" depends="prepare-proposal">
    <!--  <mkdir dir="${build.dir}"/> -->
      <available property="jndi.present" classname="javax.naming.InitialContext" />
  
      <javac srcdir="${build.src}" 
             destdir="${build.classes}" 
             debug="${debug}"
             optimize="${optimize}"
             deprecation="${deprecation}">
        <classpath refid="project.class.path" />
        <src path="${build.src}" />
        <exclude name="${constants.file}" />
        <exclude name="org/apache/james/userrepository/UsersLDAPRepository.java"
                 unless="jndi.present" />
      </javac>
  
    </target>
  
    <!-- =================================================================== -->
    <!-- Creates the JAMES.bar archive                                       -->
    <!-- =================================================================== -->
    <target name="block-archive" depends="compile">
  
      <mkdir dir="${build.lib}"/>
  
      <jar jarfile="${build.lib}/${name}.bar"
           basedir="${build.classes}"
           manifest="${src.dir}/Manifest.mf">
         <include name="org/**"/>
      </jar>
  
    </target>
  
    <!-- =================================================================== -->
    <!-- Creates the mailet.jar                                              -->
    <!-- =================================================================== -->
    <target name="auxiliary-jars" depends="compile">
      <jar jarfile="${build.lib}/mailet.jar" basedir="${build.classes}"
           manifest="${src.dir}/Manifest.mf" includes="org/apache/mailet/**"/>
    </target>
  
  
    <!--
         ===================================================================
                             Creates the .sar file
         ===================================================================
    -->
    <target name="sar" depends="block-archive,auxiliary-jars">
  
      <jar jarfile="${build.lib}/${name}.sar">
  
        <zipfileset dir="${lib.dir}/" prefix="blocks/">
          <include name="*.bar"/>
        </zipfileset>
  
        <zipfileset dir="${build.lib}/" prefix="blocks/">
          <include name="*.bar"/>
        </zipfileset>
  
        <zipfileset dir="${build.lib}" prefix="lib">
          <include name="*.jar"/>
        </zipfileset>
  
        <zipfileset dir="${lib.dir}/" prefix="lib">
          <include name="dnsjava.jar"/>
          <include name="town.jar"/>
          <include name="activation.jar"/>
          <include name="mail_1_2.jar"/>
          <include name="jakarta-oro-2.0.1.jar"/>
          <include name="mysql-2.0.4.jar"/>
        </zipfileset>
  
        <zipfileset dir="${build.conf}" fullpath="conf/config.xml">
          <include name="james-config.xml"/>
        </zipfileset>
  
        <zipfileset dir="${build.conf}" fullpath="conf/assembly.xml">
          <include name="james-assembly.xml"/>
        </zipfileset>
  
        <zipfileset dir="${build.conf}" fullpath="conf/server.xml">
          <include name="james-server.xml"/>
        </zipfileset>
  
      </jar>
  
    </target>
  
    <!-- =================================================================== -->
    <!-- Creates the unpacked binary distribution                            -->
    <!-- =================================================================== -->
  
    <target name="dist-lite" depends="sar">
      <mkdir dir="${dist.dir}"/>
      <mkdir dir="${dist.dir}/bin"/>
  
      <delete dir="${dist.dir}/apps/${name}"/>
      <copy file="${build.lib}/${name}.sar" todir="${dist.dir}/apps"/>
  
      <copy todir="${dist.dir}/bin">
        <fileset dir="lib">
          <include name="phoenix-engine.jar"/>
          <include name="phoenix-loader.jar"/>
        </fileset>
      </copy>
  
      <copy todir="${dist.dir}/lib">
        <fileset dir="lib">
          <include name="avalon-jmx.jar"/>
          <include name="jmxri.jar"/>
          <include name="jmxtools.jar"/>
          <include name="logkit.jar"/>
          <include name="mysql-2.0.4.jar"/>
        </fileset>
      </copy>
      
      <!-- Copy named lib files independently -->
      <copy todir="${dist.dir}/lib" file="${xerces.jar}"/>
      <copy todir="${dist.dir}/lib" file="${framework.jar}"/>
      <copy todir="${dist.dir}/lib" file="${excalibur.jar}"/>
      <copy todir="${dist.dir}/lib" file="${scratchpad.jar}"/>
      <copy todir="${dist.dir}/lib" file="${logkit.jar}"/>
      <copy todir="${dist.dir}/lib" file="${phoenix.client.jar}"/>
      
      <copy file="${scripts.dir}/run.bat" tofile="${dist.dir}/bin/run.bat"/>
      <copy file="${scripts.dir}/run.sh" tofile="${dist.dir}/bin/run.sh"/>
      <copy file="${conf.dir}/avalonTestKeys" tofile="${dist.dir}/conf/avalonTestKeys"/>
      <chmod dir="${dist.dir}" perm="go-rwx" />
      <chmod file="${dist.dir}/bin/run.sh" perm="u+x"/>
      <fixcrlf srcdir="${dist.dir}" cr="add" includes="**/*.bat" />
      <fixcrlf srcdir="${dist.dir}" cr="remove" includes="**/*.sh" />
  
      <copy todir="${dist.dir}/conf">
        <fileset dir="${conf.dir}">
          <include name="inet*"/>
          <include name="maildatabase*"/>
          <include name="*.properties"/>
        </fileset>
        <fileset dir="${conf.proposal.dir}">
          <include name="sqlResources.xml"/>
        </fileset>
      </copy>
      
      <copy file="README" tofile="${dist.dir}/README"/>
      <replace file="${dist.dir}/README" token="@@version@@" value="${version}"/>
      <copy file="LICENSE.txt" tofile="${dist.dir}/LICENSE"/>
      <copy file="build.xml" tofile="${dist.dir}/build.xml"/>
    </target>
  
  
    <!-- =================================================================== -->
    <!-- Cleans everything                                                   -->
    <!-- =================================================================== -->
    <target name="clean" depends="setup-properties">
      <delete dir="${build.dir}"/>
      <delete dir="${dist.dir}"/>
      <delete file="${name}-${version}-${DSTAMP}.zip"/>
      <delete file="${name}-${version}-${DSTAMP}.tar"/>
      <delete file="${name}-${version}-${DSTAMP}.tar.gz"/>
    </target>
  
    <!-- Cleans absolutely everything up -->
    <target name="real-clean" depends="clean">
      <delete dir="${docs.dir}" />
      <delete dir="${src.dist.dir}" />
      <delete file="${name}-${version}-${DSTAMP}-src.zip"/>
      <delete file="${name}-${version}-${DSTAMP}-src.tar"/>
      <delete file="${name}-${version}-${DSTAMP}-src.tar.gz"/>
    </target>
  
  
    <!-- =================================================================== -->
    <!-- Documentation                                                       -->
    <!-- =================================================================== -->
    <!-- Docs are generated from xml sources in src/xdocs, javadocs from     -->
    <!-- source files in src/java.                                           -->
    <!-- Docs for most recent release are on the website and in www dir.     -->
    <!-- Docs for cvs state are generated on demand and can, optionally,     -->
    <!-- be placed in docs dir.                                              -->
    <!-- =================================================================== -->
  
  
  
    <!-- =================================================================== -->
    <!-- Generates the javadoc                                               -->
    <!-- =================================================================== -->
    <target name="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="-doctitle" value="${Name}"/>
         <param name="-windowtitle" value="${Name} API"/>
         <param name="-bottom" 
                value="&quot;Copyright &#169; 2001 Apache Jakarta Project. All Rights Reserved.&quot;"/>
        </doclet>
      </javadoc>
  
    </target>
  
    <!-- =================================================================== -->
    <!-- Generates the javadoc for just the mailet API                       -->
    <!-- =================================================================== -->
    <target name="javadocmailet">
  
      <delete dir="${build.mailetdocs}"/>
      <mkdir dir="${build.mailetdocs}"/>
  
      <javadoc sourcepath="${java.dir}"
          destdir="${build.mailetdocs}"
          packagenames="org.apache.mailet.*">
        <classpath refid="project.class.path" />
        <doclet name="com.sun.tools.doclets.standard.Standard">
         <param name="-author"/>
         <param name="-version"/>
         <param name="-use"/>
         <param name="-doctitle" value="Mailet"/>
         <param name="-windowtitle" value="$Mailet API"/>
         <param name="-bottom" 
                value="&quot;Copyright &#169; 2001 Apache Jakarta Project. All Rights Reserved.&quot;"/>
        </doclet>
      </javadoc>
    </target>
  
    <!-- =================================================================== -->
    <!-- Generates the documentation for James                               -->
    <!-- =================================================================== -->
    <target name="xdocs" depends="prepare-error" if="AnakiaTask.present">
      <taskdef name="anakia" classname="org.apache.velocity.anakia.AnakiaTask"/>
      <anakia basedir="${docs.src}" destdir="${build.docs}/"
           extension=".html" style="./site.vsl"
           projectFile="stylesheets/project.xml"
           excludes="**/stylesheets/** empty.xml"
           includes="**/*.xml"
           lastModifiedCheck="true"
           templatePath="../jakarta-site2/xdocs/stylesheets">
      </anakia>
  
      <copy todir="${build.docs}/images" filtering="no">
          <fileset dir="${docs.src}/images">
              <include name="**/*.gif"/>
              <include name="**/*.jpeg"/>
              <include name="**/*.jpg"/>
          </fileset>
      </copy>
    </target>
  
  
    <target name="docs" depends="javadocs,xdocs"/>
  
    <!--
         ===================================================================
         Create the Local site documentation
         ===================================================================
    -->
    <target name="local-xdocs" depends="xdocs">
  
      <mkdir dir="${docs.dir}"/>
      <delete>
        <fileset dir="${docs.dir}">
          <exclude name="api/**"/>
        </fileset>
      </delete>
  
  
      <copy todir="${docs.dir}">
        <fileset dir="${build.docs}" />
      </copy>
  
    </target>
  
    <!--
         ===================================================================
         Create the Local API documentation
         ===================================================================
    -->
    <target name="local-javadocs" depends="javadocs">
  
      <delete dir="${javadocs.dir}"/>
      <mkdir dir="${javadocs.dir}"/>
  
      <copy todir="${javadocs.dir}">
        <fileset dir="${build.javadocs}" />
      </copy>
  
    </target>
  
    <!--
         ===================================================================
         Create the Local documentation
         ===================================================================
    -->
    <target name="local-docs" depends="local-javadocs,local-xdocs,javadocmailet">
  
     <copy todir="${mailetdocs.dir}">
        <fileset dir="${build.mailetdocs}" />
      </copy>
  
    </target>
  
  
    <!--
         ===================================================================
         Update the www directory
         ===================================================================
    -->
    <target name="site-docs" depends="local-xdocs">
  
      <!-- delete old documents but keep CVS directories, rfclist and mailet
           api -->
      <delete>
        <fileset dir="${www.dir}">
          <exclude name="CVS/**"/>
          <exclude name="rfclist/**"/>
          <exclude name="mailet/**"/>
        </fileset>
      </delete>
  
      <!-- copy new generated docs, but not mailet api or rfclist -->
      <!-- exclude CVS dir until docs dir removed from CVS -->
      <mkdir dir="${www.dir}"/>
      <copy todir="${www.dir}">
        <fileset dir="${docs.dir}">
          <exclude name="api/**"/>
          <exclude name="rfclist/**"/>
          <exclude name="CVS/**"/>
          <exclude name="mailet/**"/>
        </fileset>
      </copy>
  
    </target>
  
  
    <!--
         ===================================================================
                              Create the binary distribution
         ===================================================================
    -->
  
    <target name="dist" depends="dist-lite, docs">
  
      <mkdir dir="${dist.docs}"/>
      <mkdir dir="${dist.javadocs}"/>
  
      <copy todir="${dist.docs}">
        <fileset dir="${build.docs}"/>
      </copy>
  
      <copy todir="${dist.javadocs}">
        <fileset dir="${build.javadocs}"/>
      </copy>
  
      <zip zipfile="${name}-${version}-${DSTAMP}.zip" basedir="${dist.dir}" includes="**"/>
      <tar tarfile="${name}-${version}-${DSTAMP}.tar" basedir="${dist.dir}" includes="**"/>
      <gzip zipfile="${name}-${version}-${DSTAMP}.tar.gz" src="${name}-${version}-${DSTAMP}.tar"/>
    </target>
  
  
    <!--
         ===================================================================
                              Create the source distribution
         ===================================================================
    -->
    <target name="src-dist" depends="javadocs,xdocs,setup-properties">
  
      <mkdir dir="${src.dist.dir}" />
      <mkdir dir="${src.dist.docs}"/>
      <mkdir dir="${src.dist.javadocs}"/>
  
      <copy todir="${src.dist.tools}">
        <fileset dir="${tools.dir}"/>
      </copy>
  
      <copy todir="${src.dist.lib}">
        <fileset dir="${lib.dir}"/>
      </copy>
  
      <copy todir="${src.dist.src}">
        <fileset dir="${src.dir}"/>
      </copy>
  
      <copy todir="${src.dist.proposals}">
        <fileset dir="${proposal.base}"/>
      </copy>
  
      <copy todir="${src.dist.docs}">
        <fileset dir="${build.docs}"/>
      </copy>
  
      <copy todir="${src.dist.javadocs}">
        <fileset dir="${build.javadocs}"/>
      </copy>
  
      <copy todir="${src.dist.dir}">
        <fileset dir=".">
          <include name="build.bat"/>
          <include name="build.sh"/>
          <include name="build.xml"/>
          <include name="README"/>
          <include name="LICENSE.txt"/>
        </fileset>
      </copy>
  
      <fixcrlf srcdir="${src.dist.dir}" includes="build.sh" cr="remove"/>
      <fixcrlf srcdir="${src.dist.dir}" includes="build.bat" cr="add"/>
  
      <chmod perm="+x">
        <fileset dir="${src.dist.dir}">
          <include name="build.sh" />
        </fileset>
      </chmod>
  
      <fixcrlf srcdir="${src.dist.src}/java" includes="**/*.java" cr="remove"/>
  
      <chmod dir="${src.dist.dir}" perm="go-rwx" />
  
      <zip zipfile="${name}-${version}-${DSTAMP}-src.zip" basedir="${src.dist.dir}" includes="**"/>
      <tar tarfile="${name}-${version}-${DSTAMP}-src.tar" basedir="${src.dist.dir}" includes="**"/>
      <gzip zipfile="${name}-${version}-${DSTAMP}-src.tar.gz" src="${name}-${version}-${DSTAMP}.tar"/>
  
    </target>
  
  
    <!--
         ===================================================================
                   Completely build all dists
         ===================================================================
    -->
    <target name="distributions" depends="dist, src-dist">
  
    </target>
  
    <!-- 
         ===================================================================
                   Unit test targets
         ===================================================================
    -->
    <target name="testUserRepos">
      <junit printsummary="yes">
        <classpath refid="project.class.path"/>
        <formatter type="plain" usefile="no"/>
        <test name="org.apache.james.testing.TestUsersJDBCRepository"/>
      </junit>
    </target>
  
  </project>
  
  
  
  
  1.1                  jakarta-james/proposals/userstore/conf/james-assembly.xml
  
  Index: james-assembly.xml
  ===================================================================
  <?xml version="1.0"?>
  
  <assembly>
  
    <!-- The James block  -->
    <block class="org.apache.james.James" name="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"/>
    </block>
  
    <!-- The James Spool Manager block  -->
    <block class="org.apache.james.transport.JamesSpoolManager" 
  	name="spoolmanager" >
      <provide name="James" role="org.apache.mailet.MailetContext"/>
      <provide name="mailstore" role="org.apache.james.services.MailStore"/>
    </block>
  
  
    <block class="org.apache.james.dnsserver.DNSServer" name="dnsserver">
    </block>
  
    <block class="org.apache.james.remotemanager.RemoteManager" name="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="scheduler"
          role="org.apache.avalon.cornerstone.services.scheduler.TimeScheduler"/>
      <provide name="James" role="org.apache.james.services.MailServer"/>
    </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="scheduler"
          role="org.apache.avalon.cornerstone.services.scheduler.TimeScheduler"/>
      <provide name="James" role="org.apache.james.services.MailServer"/>
    </block>
  
    <!-- POP3 Server -->
    <block class="org.apache.james.pop3server.POP3Server" name="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="scheduler"
          role="org.apache.avalon.cornerstone.services.scheduler.TimeScheduler"/>
      <provide name="James" role="org.apache.james.services.MailServer"/>
    </block>
  
    <!-- SMTP Server -->
    <block class="org.apache.james.smtpserver.SMTPServer" name="smtpserver" >
      <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="James" role="org.apache.james.services.MailServer"/>
    </block>
  
    <!-- NNTP Server -->
    <block class="org.apache.james.nntpserver.NNTPServer" name="nntpserver" >
      <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="nntp-repository"
          role="org.apache.james.nntpserver.repository.NNTPRepository"/>
    </block>
  
    <!-- NNTP Repository -->
    <block class="org.apache.james.nntpserver.repository.NNTPRepositoryImpl" name="nntp-repository" >
    </block>
  
    <!-- The High Level Storage block -->
    <block class="org.apache.james.core.AvalonMailStore" name="mailstore" >
      <provide name="objectstorage"
               role="org.apache.avalon.cornerstone.services.store.Store"/> 
    </block>
         
    <!-- The User Storage block -->
    <block class="org.apache.james.core.AvalonUsersStore" name="users-store" >
      <!-- 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 class="org.apache.avalon.cornerstone.blocks.masterstore.RepositoryManager" 
           name="objectstorage">
    </block>
  
    <!-- The Connection Manager block -->
    <block class="org.apache.avalon.cornerstone.blocks.connection.DefaultConnectionManager" 
             name="connections">
    </block>
  
    <!-- The Socket Manager block -->
    <block class="org.apache.avalon.cornerstone.blocks.sockets.DefaultSocketManager"
           name="sockets">
    </block>
  
    <!-- The Time Manager block -->
    <block class="org.apache.avalon.cornerstone.blocks.scheduler.DefaultTimeScheduler" 
           name="scheduler"/>
           
    <!-- The DataSourceSelector block -->
    <block class="org.apache.avalon.cornerstone.blocks.datasource.DefaultDataSourceSelector" 
           name="database-connections"/>
  
  </assembly>
  
  
  
  
  1.1                  jakarta-james/proposals/userstore/conf/james-config.xml
  
  Index: james-config.xml
  ===================================================================
  <?xml version="1.0"?>
  <!--
                                  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 pasword
      of root.
  
      For production use, or in case the defaults do not suit you, the items
      you are most likely to need to change are preceeded by a
      CHECKME! or
      CONFIRM? comment in the left
      margin.
  
      This is $Revision: 1.1 $
      Committed on $Date: 2001/08/15 04:41:30 $ by: $Author: darrell $
  -->
  <config>
  
    <!-- The James block  -->
    <James>
  <!-- CHECKME! Set this to the right email address for error reports -->
        <postmaster> Postmaster@localhost </postmaster>
  
  <!-- CONFIRM? -->
        <!-- servernames identifies the DNS namespace served by this instance
        of James.
        If autodetect is TRUE, James wil attempt to discover its own name AND use
        any specified servernames.  If autodetect is FALSE, James will use only
        the specified servernames.  By default, the servername 'localhost' is
        specified. This can be removed, if required. -->
        <servernames autodetect="TRUE">
          <!--
          <servername>To override autodetected server names
                      uncomment this.  </servername>-->
          <servername>localhost</servername>
        </servernames>
  
        <!-- Set whether user names are case sensitive or insensitive -->
        <!-- Set whether to enable local aliases -->
        <usernames ignoreCase="TRUE" enableAliases="TRUE"
                                     enableForwarding="TRUE"/>
  
  
        <!-- Set the type of permanent mailfolders to be used.
        If IMAP service is to be provided, storage 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>basic</storage>
  
        <!-- If storage is set to IMAP, systemClass and hostClass must point to
        the appropriate classes. -->
        <imapSetup systemClass="org.apache.james.imapserver.SimpleSystem"
                   hostClass="org.apache.james.imapserver.JamesHost"/>
        <imapHost>
          <recordRepository>var/mail/folderRecords/</recordRepository>
          <mailboxRepository>var/mail/mailboxes/</mailboxRepository>
          <namespaces token="#">
            <privateNamespace separator=".">#mail</privateNamespace>
            <otherusersNamespace separator=".">#users</otherusersNamespace>
            <sharedNamespace separator=".">#shared</sharedNamespace>
          </namespaces>
        </imapHost>
  
  
  
        <!-- The inbox repository is the location for users inboxes -->
        <inboxRepository>
          <repository destinationURL="file://var/mail/inboxes/"
                      type="MAIL">
          </repository>
        </inboxRepository>
  
        <!-- Alternative inbox repository definition for DB use. Note you must create the inbox.properties file
        <inboxRepository>
          <repository destinationURL="db://../conf/inbox.properties"
                      type="SPOOL">
          </repository>
        </inboxRepository>
        -->
    </James>
  
    <!-- The James Spool Manager block  -->
    <spoolmanager>
  
  
        <!-- number of spool threads -->
        <threads> 1 </threads>
  
          <!-- Set the packages from which to load mailets and matches -->
          <mailetpackages>
            <mailetpackage>org.apache.james.transport.mailets.</mailetpackage>
          </mailetpackages>
          <matcherpackages>
            <matcherpackage>org.apache.james.transport.matchers.</matcherpackage>
          </matcherpackages>
  
          <!-- Processor CONFIGURATION SAMPLE:
               root is the first processor all mails enter -->
          <processor name="root">
            <!-- Checks that the MAIL FROM command was for a valid domain.
            Important for spam prevention. -->
            <!--
            <mailet match="SenderInFakeDomain" class="ToProcessor">
              <processor> spam </processor>
            </mailet>
            -->
            <!-- Important check to avoid race conditions -->
            <mailet match="RelayLimit=30" class="Null">
            </mailet>
  
            <!-- Check for delivery from a known spam server -->
            <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">
            </mailet>
  
            <!-- Sample listserv wrapping a local avalon list of users. -->
            <mailet match="CommandForListserv=james@localhost"
                    class="AvalonListservManager">
              <repositoryName>list-james</repositoryName>
            </mailet>
  
            <mailet match="RecipientIs=james@localhost" class="AvalonListserv">
              <membersonly> false </membersonly>
              <attachmentsallowed> true </attachmentsallowed>
              <replytolist> true </replytolist>
              <repositoryName>list-james</repositoryName>
              <subjectprefix>JamesList</subjectprefix>
            </mailet>
  
            <!-- Sends remaining mails to the transport processor for either
            local or remote delivery -->
            <mailet match="All" class="ToProcessor">
              <processor> transport </processor>
            </mailet>
          </processor>
  
          <!-- Processor CONFIGURATION SAMPLE: error is the processor mails with
          failure conditions enter -->
          <processor name="error">
            <!-- Logs any messages to the repository specified -->
            <mailet match="All" class="ToRepository">
              <repositoryPath> file://var/mail/error/</repositoryPath>
              <!-- <repositoryPath> db://conf/mail-error.properties </repositoryPath>-->
              <passThrough> true </passThrough>
            </mailet>
  
            <!-- If you want to notify the sender their message was marked as
            spam, uncomment this
            <mailet match="All" class="NotifySender">
            </mailet>
            -->
  
            <!-- If you want to notify the postmaster that a message was marked
            as spam, uncomment this
            <mailet match="All" class="NotifyPostmaster">
            </mailet>
            -->
          </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">
            </mailet>
  
            <!-- 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!
      Anti-relay mailet: Add your network address here,
      e.g. "RemoteAddrNotInNetwork=127.0.0.1, abc.de.*"
  -->
  
            <!-- This matcher-mailet pair can prevent relaying... if you change
            this,  you risk making your mail server an open relay point for
            spammers .
            NOTE 1: the order of matcher-mailets is important: it must come after
            valid local recipients have been dealt with but before any attempt is
            made to delivery the mail remotely.
            NOTE 2: Add your own network, if you want to relay mail outwards
            NOTE 3: If you use SMTP AUTH, you may want to comment this
            so users who are on the road can still use the server -->
            <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>
              <!-- <outgoing> db://conf/mail-outgoing.properties </outgoing>-->
              <delayTime> 21600000 </delayTime>
              <maxRetries> 5 </maxRetries>
            </mailet>
          </processor>
  
          <!--  Processor CONFIGURATION SAMPLE: spam is where messages detected
          as relaying or other problems will get sent.  You can either log these,
          bounce these, or just ignore them. -->
          <processor name="spam">
            <!-- If you wanted, you could just destroy messages, uncomment this
            matcher/mailet
            <mailet match="All" class="Null">
            </mailet>
            -->
  
            <!-- If you want to notify the sender their message was marked as
            spam, uncomment this
            <mailet match="All" class="NotifySender">
            </mailet>
            -->
  
            <!-- If you want to notify the postmaster that a message was marked
            as  spam, uncomment this
            <mailet match="All" class="NotifyPostmaster">
            </mailet>
            -->
  
            <!-- Out of the box, this will log the message to a repository -->
            <mailet match="All" class="ToRepository">
              <repositoryPath>file://var/mail/spam/</repositoryPath>
              <!-- <repositoryPath> db://conf/spam.properties </repositoryPath> -->
            </mailet>
          </processor>
    </spoolmanager>
  
  
  <!-- CONFIRM? Enter ip address of your DNS server, one per element -->
    <dnsserver>
          <servers>
            <server>127.0.0.1</server>
            <!--<server> put extra dns server address here </server>-->
          </servers>
          <authoritative>false</authoritative>
    </dnsserver>
  
    <remotemanager>
          <port>4555</port>
          <!-- <bind>  </bind> uncomment this if you want to bind to a specific
          inetaddress -->
          <!-- <useTLS>TRUE</useTLS> uncomment this if you want to use TLS (SSL)
          on this port -->
          <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
            given OR 'localhost' -->
            <helloName autodetect="TRUE">myMailServer</helloName>
            <administrator_accounts>
  
  <!-- CHECKME! Change the default password! -->
              <!-- FILL ME!!!!!!  You must provide a password for your
               administrator accounts (cannot be blank) -->
              <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> -->
          <!-- need to check what if any IMAP over SSL uses -->
          <!-- <bind>  </bind>  uncomment this if you want to bind to a specific
          inetaddress-->
          <!-- <useTLS>TRUE</useTLS> uncomment this if you want to use TLS (SSL)
          on this port -->
          <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
            given OR 'localhost' -->
            <helloName autodetect="TRUE">myMailServer</helloName>
            <connectiontimeout>1800000</connectiontimeout>
  	</handler>
    </imapserver>
  
    <pop3server>
          <port>110</port>
          <!-- <port>995</port> -->
          <!-- port 995 is the well-known/IANA registered port for POP3S
          ie over SSL/TLS -->
          <!-- <bind>  </bind>  uncomment this if you want to bind to a specific
          inetaddress-->
          <!-- <useTLS>TRUE</useTLS> uncomment this if you want to use TLS (SSL)
          on this port -->
          <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
            given OR 'localhost' -->
            <helloName autodetect="TRUE">myMailServer</helloName>
            <connectiontimeout>120000</connectiontimeout>
          </handler>
    </pop3server>
  
    <smtpserver>
          <port>25</port>
          <!--<bind></bind> uncomment this if you want to bind to a specific
          inetaddress -->
          <!--<useTLS>TRUE</useTLS> uncomment this if you want to use TLS (SSL)
          on this port -->
          <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
            given OR 'localhost' -->
            <helloName autodetect="TRUE">myMailServer</helloName>
            <connectiontimeout>360000</connectiontimeout>
  
  	  <!--<authRequired>true</authRequired> uncomment this if you want
            SMTP AUTH support. This is useful if you have users who need to use
            the email server on the road, while not having your server act as an
            open relay! -->
  
  	  <!--<verifyIdentity>true</verifyIdentity> uncomment this if you want
            to verify that the MAIL FROM: address is the same user that
            authenticated. This prevents a user of your mail server from acting
            as somebody else -->
  
            <!-- This sets the maximum allowed message size for the smtphandler
            in KBytes. The value defaults to 0, which means no limit.  -->
            <maxmessagesize>0</maxmessagesize>
  
          </handler>
    </smtpserver>
  
    <nntpserver>
          <port>119</port>
          <!-- <port>563</port> -->
          <!-- port 563 is the well-known/IANA registered port for NNTPS
          ie over SSL/TLS -->
          <!-- <bind>  </bind>  uncomment this if you want to bind to a specific
          inetaddress-->
          <!-- <useTLS>TRUE</useTLS> uncomment this if you want to use TLS (SSL)
          on this port -->
          <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
            given OR 'localhost' -->
            <helloName autodetect="TRUE">myMailServer</helloName>
            <connectiontimeout>120000</connectiontimeout>
  	  <!-- make this true, if you want only authenticated users to access NNTP-->
            <authRequired>false</authRequired>
          </handler>
    </nntpserver>
  
    <nntp-repository>
          <!-- make this true to disallow posting to all newsgroups-->
  	<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>
  	<!-- these additional news groups would be created and exposed-->
  	<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>
  	    <!-- 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 High Level Storage block -->
    <mailstore>
        <repositories>
          <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>
  
          <repository
              class="org.apache.james.mailrepository.JDBCMailRepository">
            <protocols>
              <protocol>db</protocol>
            </protocols>
            <types>
              <type>MAIL</type>
            </types>
          </repository>
          <repository
              class="org.apache.james.mailrepository.JDBCSpoolRepository">
            <protocols>
              <protocol>db</protocol>
            </protocols>
            <types>
              <type>SPOOL</type>
            </types>
          </repository>
        </repositories>
  
        <!-- The spool repository is a singular location where incoming mails
        are temporarily stored before being processed.
       (ex. file://c:/james/spool/) -->
        <spoolRepository>
          <repository destinationURL="file://var/mail/spool/"
                      type="SPOOL">
          </repository>
        </spoolRepository>
  
        <!-- Alternative spool repository definition for JDBC use
        <spoolRepository>
          <repository destinationURL="db://../conf/spool.properties"
                      type="SPOOL">
          </repository>
        </spoolRepository>
        -->
    </mailstore>
  
    <!-- The User Storage block -->
    <users-store>
      <!-- Configure User Repositories here.
           User repositories are required for the following purposes:
              - hold information about Users of the James mail server
              - hold lists of users for the listserv mailet 
           
           Currently, 2 different storage options are available:
              - file-based storage using Java serialisation
              - database-backed storage
           (Use of database or file-system is defined on a "per-repository" basis)
           
           Note: Two user repositories are required for default configuration:
           LocalUsers - the users for whom you are providing POP3, IMAP or NNTP
                        AUTH service
           list-james - the users of the james@localhost mailiinglist
      -->
      
        <!-- File-based user repositories 
             Use these configurations to store user info in the filesystem  -->     
        <!--
        <repository name="LocalUsers"
                    class="org.apache.james.userrepository.UsersFileRepository">
          <destination URL="file://var/users/"/>
        </repository>
        
        <repository name="list-james"
                    class="org.apache.james.userrepository.UsersFileRepository">
          <destination URL="file://var/lists/list-james/"/>
        </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">
            <data-source>MailDb</data-source>
            <sqlFile>dist/conf/sqlResources.xml</sqlFile>
            <sqlParameters table="JamesUsers"/>
        </repository>
        <!-- The list used by the default ListServ mailet -->
        <repository name="list-james"
                    class="org.apache.james.userrepository.ListUsersJdbcRepository">
            <data-source>MailDb</data-source>
            <sqlFile>dist/conf/sqlResources.xml</sqlFile>
            <sqlParameters table="MailingLists" listName="list-james"/>
        </repository>
        <!-- An example mailinglist repository, with no "listName" parameter
             specified. Uses the default value from the sql definition file. -->
        <repository name="list-test"
                    class="org.apache.james.userrepository.ListUsersJdbcRepository">
            <data-source>MailDb</data-source>
            <sqlFile>dist/conf/sqlResources.xml</sqlFile>
            <sqlParameters table="MailingLists"/>
        </repository>
        <!-- An example UserRepository for DefaultUser implementations. Testing purposes
             only. -->
        <repository name="default-users"
                    class="org.apache.james.userrepository.DefaultUsersJdbcRepository">
            <data-source>MailDb</data-source>
            <sqlFile>dist/conf/sqlResources.xml</sqlFile>
            <sqlParameters table="DefaultUsers"/>
        </repository>
    </users-store>
  
    <!-- The database-connections block -->
    <database-connections>
      <!-- Confugure database connections here.
           These connections are referred to by name elsewhere in the config file -->
  <!-- CHECKME! To allow James to use a database, configure the database connection here -->
      <data-sources>
          <!-- Example, connecting to a MySQL database called "mail" on localhost
          -->
          <data-source name="MailDb" 
                       type="org.apache.avalon.excalibur.datasource.JdbcDataSource">
              <driver>org.gjt.mm.mysql.Driver</driver>
              <dburl>jdbc:mysql://127.0.0.1/mail</dburl>
              <user>username</user>
              <password>password</password>
          </data-source> 
      </data-sources>
    </database-connections>
  
    <!-- Configuration for Cornerstone Blocks only after here
         NOTHING BELOW THIS SHOULD NEED CHANGING,
         (unless you want secure sockets (TLS)) -->
  
    <!-- The Storage block -->
    <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 Socket Manager block -->
    <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>
  
  </config>
  
  
  
  1.1                  jakarta-james/proposals/userstore/conf/sqlResources.xml
  
  Index: sqlResources.xml
  ===================================================================
  <!-- SQL Statements used by James for database access. -->
  <sqlResources>
  
  <!-- 
       This section provided configuration to determine the determine the 
       database product which is being used for storage. Different database
       products may require different SQL syntax.
       
       The jdbc database connection is examined to see if it matches with the
       regular expressions specified in any of the defined matchers. The matchers
       are processed in the over provided here, with the first successful match
       defining the "db" value for this connection.
       
       This value is then used to choose between different definitions for various
       named sql statements, defined below. If no match is found, 
       the default sql statements are used.
  -->
  <dbMatchers>
      <dbMatcher db="mssql" databaseProductName="microsoft sql server"/>
      <dbMatcher db="oracle" databaseProductName="oracle.*"/>
      <dbMatcher db="mysql" databaseProductName="my.*"/>
      <dbMatcher db="postgresql" databaseProductName="postgres.*"/>
  </dbMatchers>
  
  <!-- SQL statements to use for various components.
  
       Parameter definitions ${param} are replaced with parameter values
       from the configuration file.
       
       If a named statement has a definition defined for the current database product,
       then that statement is used. Otherwise the default statement is used.
  -->
  <!-- SQL statements for the JamesUsersJdbcRepository -->
  <sqlDefs name="org.apache.james.userrepository.JamesUsersJdbcRepository">
      <sql name="tableName">${table}</sql>
      <sql name="select">SELECT username, pwdHash, pwdAlgorithm, useForwarding, 
                          forwardDestination, useAlias, alias 
                   FROM ${table}
      </sql>
      <sql name="selectByLowercaseName">
                  SELECT username, pwdHash, pwdAlgorithm, useForwarding, 
                          forwardDestination, useAlias, alias 
                  FROM ${table}
                  WHERE lower(username) = ?
      </sql>
      <sql name="insert">INSERT INTO ${table} 
                      (username, pwdHash, pwdAlgorithm, useForwarding, forwardDestination, useAlias, alias) 
                  VALUES (?,?,?,?,?,?,?)
      </sql>
      <sql name="update">UPDATE ${table} SET 
                      pwdHash = ?, pwdAlgorithm = ?, useForwarding = ?, forwardDestination = ?, useAlias = ?, alias = ? 
                  WHERE username = ?
      </sql>
      <sql name="delete">DELETE FROM ${table} WHERE username = ?</sql>
      <sql name="createTable">CREATE TABLE ${table} (username VARCHAR(50) NOT NULL, pwdHash VARCHAR(50), pwdAlgorithm VARCHAR(20), useForwarding SMALLINT, forwardDestination VARCHAR(250), useAlias SMALLINT, alias VARCHAR(250), PRIMARY KEY(username))</sql>
      <!-- An example of a database-specific sql statement
      <sql name="createTable" db="mssql">CREATE TABLE ${table} (uniqueId UNIQUEIDENTIFIER, username VARCHAR(50) NOT NULL, pwdHash VARCHAR(50), pwdAlgorithm VARCHAR(20), useForwarding SMALLINT, forwardDestination VARCHAR(250), useAlias SMALLINT, alias VARCHAR(250), PRIMARY KEY(username))</sql>
      -->
  </sqlDefs>
  
  <!-- SQL statements for the DefaultUsersJdbcRepository -->
  <sqlDefs name="org.apache.james.userrepository.DefaultUsersJdbcRepository">
      <sql name="tableName">${table}</sql>
      <sql name="select">SELECT username, pwdHash, pwdAlgorithm 
                   FROM ${table}
      </sql>
      <sql name="insert">INSERT INTO ${table} 
                      (username, pwdHash, pwdAlgorithm) 
                  VALUES (?,?,?)
      </sql>
      <sql name="update">UPDATE ${table} SET 
                      pwdHash = ?, pwdAlgorithm = ?
                  WHERE username = ?
      </sql>
      <sql name="delete">DELETE FROM ${table} WHERE username = ?</sql>
      <sql name="createTable">CREATE TABLE ${table} (username VARCHAR(50) NOT NULL, pwdHash VARCHAR(50), pwdAlgorithm VARCHAR(20), PRIMARY KEY(username))</sql>
  </sqlDefs>
  
  <!-- SQL statements for the ListUsersJdbcRepository 
          (allowing multiple lists in one table) -->
  <sqlDefs name="org.apache.james.userrepository.ListUsersJdbcRepository">
      <!-- An example of defining "default" parameters within the definition file.
           This is used if no listName is specified in config. -->
      <parameters listName="unknownList"/>
      
      <sql name="tableName">${table}</sql>
      <sql name="select">SELECT listSubscriber
                   FROM ${table}
                   WHERE listName = '${listName}'
      </sql>
      <sql name="insert">INSERT INTO ${table} 
                      (listSubscriber, listName) 
                  VALUES (?, '${listName}')
      </sql>
      <sql name="update">UPDATE ${table} SET 
                      listSubscriber = ?
                  WHERE listSubscriber = ? AND listName = '${listName}'
      </sql>
      <sql name="delete">DELETE FROM ${table} 
                  WHERE listSubscriber = ? AND listName = '${listName}'
      </sql>
      <sql name="createTable">CREATE TABLE ${table} (listName VARCHAR(50) NOT NULL, listSubscriber VARCHAR(50) NOT NULL)</sql>
  </sqlDefs>
  
  </sqlResources>
  
  
  
  1.1                  jakarta-james/proposals/userstore/java/org/apache/james/core/AvalonUsersStore.xinfo
  
  Index: AvalonUsersStore.xinfo
  ===================================================================
  <?xml version="1.0"?>
  
  <blockinfo>
  
    <meta>
  
      <contributors>
        <author name="Charles Benett" email="charles@benett1.demon.co.uk"/>
      </contributors>
  
    </meta>
  
    <!-- services that are offered by this block -->
    <services>
      <service name="org.apache.james.services.UsersStore" version="1.0" />
    </services>
  
    <dependencies>
      <dependency>
        <role>org.apache.avalon.cornerstone.services.store.Store</role>
        <service name="org.apache.avalon.cornerstone.services.store.Store" version="1.0"/>
      </dependency>
      <dependency>
        <role>org.apache.avalon.cornerstone.services.datasource.DataSourceSelector</role>
        <service name="org.apache.avalon.cornerstone.services.datasource.DataSourceSelector" version="1.0"/>
      </dependency>
    </dependencies>
  
  </blockinfo>
  
  
  
  1.1                  jakarta-james/proposals/userstore/java/org/apache/james/remotemanager/RemoteManagerHandler.java
  
  Index: RemoteManagerHandler.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.remotemanager;
  
  import java.io.*;
  import java.net.*;
  import java.util.*;
  import javax.mail.internet.ParseException;
  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.configuration.Configurable;
  import org.apache.avalon.framework.configuration.Configuration;
  import org.apache.avalon.framework.configuration.ConfigurationException;
  import org.apache.avalon.framework.logger.AbstractLoggable;
  import org.apache.avalon.cornerstone.services.connection.ConnectionHandler;
  import org.apache.avalon.cornerstone.services.scheduler.PeriodicTimeTrigger;
  import org.apache.avalon.cornerstone.services.scheduler.Target;
  import org.apache.avalon.cornerstone.services.scheduler.TimeScheduler;
  import org.apache.james.Constants;
  import org.apache.james.BaseConnectionHandler;
  import org.apache.james.services.MailServer;
  import org.apache.james.services.User;
  import org.apache.james.services.JamesUser;
  import org.apache.james.services.UsersRepository;
  import org.apache.james.services.UsersStore;
  import org.apache.mailet.MailAddress;
  import org.apache.james.userrepository.DefaultUser;
  
  /**
   * Provides a really rude network interface to administer James.
   * Allow to add accounts.
   * TODO: -improve protocol
   *       -add remove user
   *       -much more...
   * @version 1.0.0, 24/04/1999
   * @author  Federico Barbieri <sc...@pop.systemy.it>
   * @author <a href="mailto:donaldp@apache.org">Peter Donald</a>
   * @author <a href="mailto:charles@benett1.demon.co.uk">Charles Benett</a>
   *
   * Last changed by: $Author: darrell $ on $Date: 2001/08/15 04:41:30 $
   * $Revision: 1.1 $
   *
   */
  public class RemoteManagerHandler
      extends BaseConnectionHandler
      implements ConnectionHandler, Composable, Configurable, Target {
  
      private UsersStore usersStore;
      private UsersRepository users;
      private TimeScheduler scheduler;
      private MailServer mailServer;
  
      private BufferedReader in;
      private PrintWriter out;
      private HashMap admaccount = new HashMap();
      private Socket socket;
  
      public void configure( final Configuration configuration )
          throws ConfigurationException {
  
          timeout = configuration.getChild( "connectiontimeout" ).getValueAsInteger( 120000 );
  
          final Configuration admin = configuration.getChild( "administrator_accounts" );
          final Configuration[] accounts = admin.getChildren( "account" );
          for ( int i = 0; i < accounts.length; i++ )
          {
              admaccount.put( accounts[ i ].getAttribute( "login" ),
                              accounts[ i ].getAttribute( "password" ) );
          }
      }
  
      public void compose( final ComponentManager componentManager )
          throws ComponentException {
  
          scheduler = (TimeScheduler)componentManager.
              lookup( "org.apache.avalon.cornerstone.services.scheduler.TimeScheduler" );
          mailServer = (MailServer)componentManager.
              lookup( "org.apache.james.services.MailServer" );
          usersStore = (UsersStore)componentManager.
              lookup( "org.apache.james.services.UsersStore" );
          users = usersStore.getRepository("LocalUsers");;
      }
  
      /**
       * Handle a connection.
       * This handler is responsible for processing connections as they occur.
       *
       * @param connection the connection
       * @exception IOException if an error reading from socket occurs
       * @exception ProtocolException if an error handling connection occurs
       */
      public void handleConnection( final Socket connection )
          throws IOException {
  
          /*
            if( admaccount.isEmpty() ) {
            getLogger().warn("No Administrative account defined");
            getLogger().warn("RemoteManager failed to be handled");
            return;
            }
          */
  
          final PeriodicTimeTrigger trigger = new PeriodicTimeTrigger( timeout, -1 );
          scheduler.addTrigger( this.toString(), trigger, this );
          socket = connection;
          String remoteHost = socket.getInetAddress().getHostName();
          String remoteIP = socket.getInetAddress().getHostAddress();
  
          try {
              in = new BufferedReader(new InputStreamReader( socket.getInputStream() ));
              out = new PrintWriter( socket.getOutputStream(), true);
              getLogger().info( "Access from " + remoteHost + "(" + remoteIP + ")" );
              out.println( "JAMES RemoteAdministration Tool " + Constants.SOFTWARE_VERSION );
              out.println("Please enter your login and password");
  	    out.println("Login id:");
              String login = in.readLine();
  	    out.println("Password:");
              String password = in.readLine();
  
              while (!password.equals(admaccount.get(login)) || password.length() == 0) {
                  scheduler.resetTrigger(this.toString());
                  final String message = "Login failed for " + login;
                  out.println( message );
                  getLogger().info( message );
                  login = in.readLine();
                  password = in.readLine();
              }
  
              scheduler.resetTrigger(this.toString());
  
              out.println( "Welcome " + login + ". HELP for a list of commands" );
              getLogger().info("Login for " + login + " succesful");
  
              try {
                  while (parseCommand(in.readLine())) {
                      scheduler.resetTrigger(this.toString());
                  }
              }
              catch (Throwable thr) {
                  System.out.println("Exception: " + thr.getMessage());
                  thr.printStackTrace();
              }
              getLogger().info("Logout for " + login + ".");
              socket.close();
  
          } catch ( final IOException e ) {
              out.println("Error. Closing connection");
              out.flush();
              getLogger().error( "Exception during connection from " + remoteHost +
                                 " (" + remoteIP + ")");
          }
  
          scheduler.removeTrigger(this.toString());
      }
  
      public void targetTriggered( final String triggerName ) {
          getLogger().error("Connection timeout on socket");
          try {
              out.println("Connection timeout. Closing connection");
              socket.close();
          } catch ( final IOException ioe ) {
          }
      }
  
      private boolean parseCommand( String command ) {
          if (command == null) return false;
          StringTokenizer commandLine = new StringTokenizer(command.trim(), " ");
          int arguments = commandLine.countTokens();
          if (arguments == 0) {
              return true;
          } else if(arguments > 0) {
              command = commandLine.nextToken();
          }
          String argument = (String) null;
          if(arguments > 1) {
              argument = commandLine.nextToken();
          }
          String argument1 = (String) null;
          if(arguments > 2) {
              argument1 = commandLine.nextToken();
          }
          if (command.equalsIgnoreCase("ADDUSER")) {
              String username = argument;
              String passwd = argument1;
              try {
                  if (username.equals("") || passwd.equals("")) {
                      out.println("usage: adduser [username] [password]");
                      return true;
                  }
              } catch (NullPointerException e) {
                  out.println("usage: adduser [username] [password]");
                  return true;
              }
              if (users.contains(username)) {
                  out.println("user " + username + " already exist");
              } else {
                  DefaultUser user = new DefaultUser(username, "SHA");
                  user.setPassword(passwd);
                  if( users.addUser(user)) {
                      out.println("User " + username + " added");
                      getLogger().info("User " + username + " added");
                  } else {
                      out.println("Error adding user " + username);
                      getLogger().info("Error adding user " + username);
                  }
              }
              out.flush();
          } else if (command.equalsIgnoreCase("SETPASSWORD")) {
  	    if (argument == null || argument1 == null) {
                  out.println("usage: setpassword [username] [password]");
                  return true;
  	    }
              String username = argument;
              String passwd = argument1;
              if (username.equals("") || passwd.equals("")) {
                  out.println("usage: adduser [username] [password]");
                  return true;
  	    }
  	    User user = users.getUserByName(username);
  	    if (user == null) {
  		out.println("No such user");
  		return true;
  	    }
  	    boolean success;
  	    success = user.setPassword(passwd);
  	    if (success){
  		users.updateUser(user);
                  out.println("Password for " + username + " reset");
                  getLogger().info("Password for " + username + " reset");
  	    } else {
                  out.println("Error resetting password");
                  getLogger().info("Error resetting password");
  	    }
              out.flush();
  	    return true;
          } else if (command.equalsIgnoreCase("DELUSER")) {
              String user = argument;
              if (user.equals("")) {
                  out.println("usage: deluser [username]");
                  return true;
              }
              try {
                  users.removeUser(user);
              } catch (Exception e) {
                  out.println("Error deleting user " + user + " : " + e.getMessage());
                  return true;
              }
              out.println("User " + user + " deleted");
              getLogger().info("User " + user + " deleted");
          } else if (command.equalsIgnoreCase("LISTUSERS")) {
              out.println("Existing accounts " + users.countUsers());
              for (Iterator it = users.list(); it.hasNext();) {
                  out.println("user: " + (String) it.next());
              }
          } else if (command.equalsIgnoreCase("COUNTUSERS")) {
              out.println("Existing accounts " + users.countUsers());
          } else if (command.equalsIgnoreCase("VERIFY")) {
              String user = argument;
              if (user.equals("")) {
                  out.println("usage: verify [username]");
                  return true;
              }
              if (users.contains(user)) {
                  out.println("User " + user + " exist");
              } else {
                  out.println("User " + user + " does not exist");
              }
          } else if (command.equalsIgnoreCase("HELP")) {
              out.println("Currently implemented commans:");
              out.println("help                            display this help");
              out.println("adduser [username] [password]   add a new user");
              out.println("deluser [username]              delete existing user");
              out.println("listusers                       display existing accounts");
              out.println("countusers                      display the number of existing accounts");
              out.println("verify [username]               verify if specified user exist");
              out.println("quit                            close connection");
              out.flush();
          } else if (command.equalsIgnoreCase("SETALIAS")) {
  	    if (argument == null || argument1 == null) {
                  out.println("usage: setalias [username] [alias]");
                  return true;
  	    }
              String username = argument;
              String alias = argument1;
              if (username.equals("") || alias.equals("")) {
                  out.println("usage: adduser [username] [alias]");
                  return true;
  	    }
  	    JamesUser user = (JamesUser) users.getUserByName(username);
  	    if (user == null) {
  		out.println("No such user");
  		return true;
  	    }
  	    JamesUser aliasUser = (JamesUser) users.getUserByName(alias);
  	    if (aliasUser == null) {
  		out.println("Alias unknown to server" 
                              + " - create that user first.");
  		return true;
  	    }
  
    	    boolean success;
  	    success = user.setAlias(alias);
  	    if (success){
  	        user.setAliasing(true);
  		users.updateUser(user);
                  out.println("Alias for " + username + " set to:" + alias);
                  getLogger().info("Alias for " + username + " set to:" + alias);
  	    } else {
                  out.println("Error setting alias");
                  getLogger().info("Error setting alias");
  	    }
              out.flush();
  	    return true;
          } else if (command.equalsIgnoreCase("SETFORWARDING")) {
  	    if (argument == null || argument1 == null) {
                  out.println("usage: setforwarding [username] [emailaddress]");
                  return true;
  	    }
              String username = argument;
              String forward = argument1;
              if (username.equals("") || forward.equals("")) {
                  out.println("usage: adduser [username] [emailaddress]");
                  return true;
  	    }
  	    // Verify user exists
  	    User baseuser = users.getUserByName(username);
  	    if (baseuser == null) {
  		out.println("No such user");
  		return true;
  	    }
              else if (! (baseuser instanceof JamesUser ) ) {
                  out.println("Can't set forwarding for this user type.");
                  return true;
              }
              JamesUser user = (JamesUser)baseuser;
  	    // Veriy acceptable email address
  	    MailAddress forwardAddr;
              try {
                   forwardAddr = new MailAddress(forward);
              } catch(ParseException pe) {
  		out.println("Parse exception with that email address: "
                              + pe.getMessage());
  		out.println("Forwarding address not added for " + username);
  	        return true;
  	    }
  
    	    boolean success;
  	    success = user.setForwardingDestination(forwardAddr);
  	    if (success){
  	        user.setForwarding(true);
  		users.updateUser(user);
                  out.println("Forwarding destination for " + username
                               + " set to:" + forwardAddr.toString());
                  getLogger().info("Forwarding destination for " + username
                                   + " set to:" + forwardAddr.toString());
  	    } else {
                  out.println("Error setting forwarding");
                  getLogger().info("Error setting forwarding");
  	    }
              out.flush();
  	    return true;
          } else if (command.equalsIgnoreCase("UNSETALIAS")) {
  	    if (argument == null) {
                  out.println("usage: unsetalias [username]");
                  return true;
  	    }
              String username = argument;
              if (username.equals("")) {
                  out.println("usage: adduser [username]");
                  return true;
  	    }
  	    JamesUser user = (JamesUser) users.getUserByName(username);
  	    if (user == null) {
  		out.println("No such user");
  		return true;
  	    }
  
  	    if (user.getAliasing()){
  	        user.setAliasing(false);
  		users.updateUser(user);
                  out.println("Alias for " + username + " unset");
                  getLogger().info("Alias for " + username + " unset");
  	    } else {
                  out.println("Aliasing not active for" + username);
                  getLogger().info("Aliasing not active for" + username);
  	    }
              out.flush();
  	    return true;
          } else if (command.equalsIgnoreCase("USE")) {
  	    if (argument == null || argument.equals("")) {
                  out.println("usage: use [repositoryName]");
                  return true;
  	    }
              String repositoryName = argument;
              UsersRepository repos = usersStore.getRepository(repositoryName);
              if ( repos == null ) {
                  out.println("no such repository");
                  return true;
              }
              else {
                  users = repos;
                  out.println("Changed to repository '" + repositoryName + "'.");
                  return true;
              }
  
          } else if (command.equalsIgnoreCase("QUIT")) {
              out.println("bye");
              return false;
          } else {
              out.println("unknown command " + command);
          }
          return true;
      }
  }
  
  
  
  
  1.1                  jakarta-james/proposals/userstore/java/org/apache/james/userrepository/AbstractJdbcUsersRepository.java
  
  Index: AbstractJdbcUsersRepository.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.userrepository;
  
  import org.apache.james.services.*;
  import org.apache.james.util.SqlResources;
  import org.apache.mailet.MailAddress;
  import org.apache.avalon.framework.*;
  import org.apache.avalon.framework.activity.*;
  import org.apache.avalon.framework.component.*;
  import org.apache.avalon.framework.configuration.*;
  import org.apache.avalon.framework.context.*;
  import org.apache.avalon.framework.logger.*;
  import org.apache.avalon.excalibur.datasource.*;
  import org.apache.avalon.cornerstone.services.datasource.DataSourceSelector;
  
  import java.sql.*;
  import java.util.*;
  import java.io.File;
  
  /**
   * An abstract base class for creating UserRepository implementation
   * which use a database for persistence.
   * 
   * To implement a new UserRepository using by extending this class,
   * you need to implement the 3 abstract methods defined below,
   * and define the required SQL statements in an SQLResources
   * file.
   * 
   * The SQL statements used by this implementation are:
   * <TABLE>
   * <TH><TD><B>Required</B></TD></TH>
   * <TR><TD>select</TD><TD>Select all users.</TD></TR>
   * <TR><TD>insert</TD><TD>Insert a user.</TD></TR>
   * <TR><TD>update</TD><TD>Update a user.</TD></TR>
   * <TR><TD>delete</TD><TD>Delete a user by name.</TD></TR>
   * <TR><TD>createTable</TD><TD>Create the users table.</TD></TR>
   * <TH><TD><B>Optional</B></TD></TH>
   * <TR><TD>selectByLowercaseName</TD><TD>Select a user by name (case-insensitive lowercase).</TD></TR>
   * </TABLE>
   * 
   * @author Darrell DeBoer <dd...@bigdaz.com>
   */
  public abstract class AbstractJdbcUsersRepository extends AbstractUsersRepository
      implements UsersRepository, Loggable, Component, Composable, Configurable, Initializable
  {
      protected Map m_sqlParameters;
      private String m_sqlFileName;
      private String m_datasourceName;
      private DataSourceSelector m_datasources;
      private DataSourceComponent m_datasource;
  
      // Fetches all Users from the db.
      private String m_getUsersSql;
      
      // This fetch a user by name, ensuring case-insensitive matching.
      private String m_userByNameCaseInsensitiveSql;
  
      // Insert, update and delete sql statements are not guaranteed 
      //  to be case-insensitive; this is handled in code.
      private String m_insertUserSql;
      private String m_updateUserSql;
      private String m_deleteUserSql;
  
      // Creates a single table with "username" the Primary Key.
      private String m_createUserTableSql;
  
  
      /**
       * Compose the repository with the DataSourceSelector component.
       */
      public void compose( final ComponentManager componentManager )
          throws ComponentException
      {
          getLogger().debug("AbstractJdbcUsersRepository.compose()");
  
          m_datasources = 
              (DataSourceSelector)componentManager.lookup( DataSourceSelector.ROLE );
      }
  
      /**
       * Configures the UserRepository for JDBC access.
       * 
       * Requires a configuration element in the .conf.xml file of the form:
       * 
       *  <repository name="LocalUsers"
       *              class="org.apache.james.userrepository.JamesUsersJdbcRepository">
       *      <!-- Name of the datasource to use -->
       *      <data-source>MailDb</data-source>
       *      <!-- File to load the SQL definitions from -->
       *      <sqlFile>dist/conf/sqlResources.xml</sqlFile>
       *      <!-- replacement parameters for the sql file -->
       *      <sqlParameters table="JamesUsers"/>
       *  </repository>
       */
      public void configure(Configuration configuration) throws ConfigurationException 
      {
          getLogger().debug("AbstractJdbcUsersRepository.configure()");
  
          // Get the name of the datasource, and the table to use.
          m_datasourceName = configuration.getChild("data-source", true).getValue();
          m_sqlFileName = configuration.getChild("sqlFile", true).getValue();
  
          // Get the sql parameters from the configuration object.
          m_sqlParameters = new HashMap();
          Configuration sqlParamsConfig = configuration.getChild("sqlParameters");
          String[] paramNames = sqlParamsConfig.getAttributeNames();
          for (int i = 0; i < paramNames.length; i++ ) {
              String paramName = paramNames[i];
              String paramValue = sqlParamsConfig.getAttribute(paramName);
              m_sqlParameters.put(paramName, paramValue);
          }
      }
  
      /**
       * Initialises the JDBC repository.
       * 1) Tests the connection to the database.
       * 2) Loads SQL strings from the SQL definition file,
       *     choosing the appropriate SQL for this connection, 
       *     and performing paramter substitution,
       * 3) Initialises the database with the required tables, if necessary.
       * 
       */
      public void initialize() throws Exception 
      {
          getLogger().debug("AbstractJdbcUsersRepository.initialize()");
  
          // Get the data-source required.
          m_datasource = (DataSourceComponent)m_datasources.select(m_datasourceName);
  
          // Test the connection to the database, by getting the DatabaseMetaData.
          Connection conn = openConnection();
          try{
              DatabaseMetaData dbMetaData = conn.getMetaData();
  
              // Initialise the sql strings.
              java.io.File sqlFile = new java.io.File(m_sqlFileName);
              String resourceName = this.getClass().getName();
  
              getLogger().debug("Reading SQL resources from file: " + 
                                sqlFile.getAbsolutePath() + ", section " +
                                resourceName + ".");
  
              SqlResources sqlStatements = new SqlResources();
              sqlStatements.init(sqlFile, this.getClass().getName(), 
                                 conn, m_sqlParameters);
  
              // Create the SQL Strings to use for this table.
              // Fetches all Users from the db.
              m_getUsersSql = sqlStatements.getSqlString("select", true);
  
              // Get a user by lowercase name. (optional)
              // If not provided, the entire list is iterated to find a user.
              m_userByNameCaseInsensitiveSql = 
                  sqlStatements.getSqlString("selectByLowercaseName");
  
              // Insert, update and delete are not guaranteed to be case-insensitive
              // Will always be called with correct case in username..
              m_insertUserSql = sqlStatements.getSqlString("insert", true);
              m_updateUserSql = sqlStatements.getSqlString("update", true);
              m_deleteUserSql = sqlStatements.getSqlString("delete", true);
  
              // Creates a single table with "username" the Primary Key.
              m_createUserTableSql = sqlStatements.getSqlString("createTable", true);
  
              // Check if the required table exists. If not, create it.
              // The table name is defined in the SqlResources.
              String tableName = sqlStatements.getSqlString("tableName", true);
              
              // Need to ask in the case that identifiers are stored, ask the DatabaseMetaInfo.
              // NB this should work, but some drivers (eg mm MySQL) 
              // don't return the right details, hence the hackery below.
              /*
              String tableName = m_tableName;
              if ( dbMetaData.storesLowerCaseIdentifiers() ) {
                  tableName = tableName.toLowerCase();
              }
              else if ( dbMetaData.storesUpperCaseIdentifiers() ) {
                  tableName = tableName.toUpperCase();
              }
              */
  
              // Try UPPER, lower, and MixedCase, to see if the table is there.
              if (! ( tableExists(dbMetaData, tableName) ||
                      tableExists(dbMetaData, tableName.toUpperCase()) ||
                      tableExists(dbMetaData, tableName.toLowerCase()) )) 
              {
                  // Users table doesn't exist - create it.
                  PreparedStatement createStatement = 
                      conn.prepareStatement(m_createUserTableSql);
                  createStatement.execute();
                  createStatement.close();
  
                  getLogger().info("AbstractJdbcUsersRepository: Created table \'" + 
                                   tableName + "\'.");
              }
          
          }
          finally {
              closeConnection( conn );
          }
      }
  
      private boolean tableExists(DatabaseMetaData dbMetaData, String tableName)
          throws SQLException
      {
          ResultSet rsTables = dbMetaData.getTables(null, null, tableName, null);
          boolean found = rsTables.next();
          rsTables.close();
          return found;
      }
  
      //
      // Superclass methods - overridden in AbstractUsersRepository
      //
      /**
       * Returns a list populated with all of the Users in the repository.
       * @return an <code>Iterator</code> of <code>JamesUser</code>s.
       */
      protected Iterator listAllUsers() {
          List userList = new LinkedList(); // Build the users into this list.
  
          Connection conn = openConnection();
          try {
              // Get a ResultSet containing all users.
              PreparedStatement getUsersStatement = 
                  conn.prepareStatement(m_getUsersSql);
              ResultSet rsUsers = getUsersStatement.executeQuery();
  
              // Loop through and build a User for every row.
              while ( rsUsers.next() ) {
                  User user = readUserFromResultSet(rsUsers);
                  userList.add(user);
              }
  
              rsUsers.close();
              getUsersStatement.close();
          }
          catch ( SQLException sqlExc) {
              sqlExc.printStackTrace();
              throw new RuntimeException("Error accessing database");
          }
          finally {
              closeConnection(conn);
          }
  
          return userList.iterator();
      }
  
      /**
       * Adds a user to the underlying Repository.
       * The user name must not clash with an existing user.
       */
      protected void doAddUser(User user) {
          Connection conn = openConnection();
          // Insert into the database.
          try {
              // Get a PreparedStatement for the insert.
              PreparedStatement addUserStatement = 
                  conn.prepareStatement(m_insertUserSql);
  
              setUserForInsertStatement(user, addUserStatement);
  
              addUserStatement.execute();
              addUserStatement.close();
          }
          catch ( SQLException sqlExc) {
              sqlExc.printStackTrace();
              throw new RuntimeException("Error accessing database");
          }
          finally {
              closeConnection(conn);
          }
      }
  
      /**
       * Removes a user from the underlying repository.
       * If the user doesn't exist, returns ok.
       */
      protected void doRemoveUser(User user) {
          String username = user.getUserName();
  
          Connection conn = openConnection();
          // Delete from the database.
          try {
              PreparedStatement removeUserStatement = conn.prepareStatement(m_deleteUserSql);
              removeUserStatement.setString(1, username);
              removeUserStatement.execute();
              removeUserStatement.close();
          }
          catch ( SQLException sqlExc ) {
              sqlExc.printStackTrace();
              throw new RuntimeException("Error accessing database");
          }
          finally {
              closeConnection(conn);
          }
      }
  
      /**
       * Updates a user record to match the supplied User.
       */
      protected void doUpdateUser(User user)
      {
          Connection conn = openConnection();
  
          // Update the database.
          try {
              PreparedStatement updateUserStatement = conn.prepareStatement(m_updateUserSql);
  
              setUserForUpdateStatement(user, updateUserStatement);
  
              updateUserStatement.execute();
  
              updateUserStatement.close();
          }
          catch ( SQLException sqlExc ) {
              sqlExc.printStackTrace();
              throw new RuntimeException("Error accessing database");
          }
          finally {
              closeConnection(conn);
          }
      }
  
      /**
       * Gets a user by name, ignoring case if specified.
       * If the specified SQL statement has been defined, this method
       * overrides the basic implementation in AbstractUsersRepository
       * to increase performance.
       */
      protected User getUserByName(String name, boolean ignoreCase)
      {
          // See if this statement has been set, if not, use
          // simple superclass method.
          if ( m_userByNameCaseInsensitiveSql == null ) {
              return super.getUserByName(name, ignoreCase);
          }
  
          // Always get the user via case-insensitive SQL,
          // then check case if necessary.
          Connection conn = openConnection();
          try {
              // Get a ResultSet containing all users.
              String sql = m_userByNameCaseInsensitiveSql;
              PreparedStatement getUsersStatement = 
                  conn.prepareStatement(sql);
  
              getUsersStatement.setString(1, name.toLowerCase());
  
              ResultSet rsUsers = getUsersStatement.executeQuery();
  
              // For case-insensitive matching, the first matching user will be returned.
              User user = null;
              while ( rsUsers.next() ) {
                  User rowUser = readUserFromResultSet(rsUsers);
                  String actualName = rowUser.getUserName();
                      
                  // Check case before we assume it's the right one.
                  if ( ignoreCase || actualName.equals(name) ) {
                      user = rowUser;
                      break;
                  }
              }
              return user;
          }
          catch ( SQLException sqlExc ) {
              sqlExc.printStackTrace();
              throw new RuntimeException("Error accessing database");
          }
          finally {
              closeConnection(conn);
          }
      }
  
  
      /**
       * Reads properties for a User from an open ResultSet.
       * Subclass implementations of this method must have knowledge of the fields
       * presented by the "select" and "selectByLowercaseName" SQL statements.
       * These implemenations may generate a subclass-specific User instance.
       * 
       * @param rsUsers A ResultSet with a User record in the current row.
       * @return A User instance
       * @exception SQLException
       *                   if an exception occurs reading from the ResultSet
       */
      protected abstract User readUserFromResultSet(ResultSet rsUsers)
          throws SQLException;
  
      /**
       * Set parameters of a PreparedStatement object with 
       * property values from a User instance.
       * Implementations of this method have knowledge of the parameter
       * ordering of the "insert" SQL statement definition.
       * 
       * @param user       a User instance, which should be an implementation class which
       *                   is handled by this Repostory implementation.
       * @param userInsert a PreparedStatement initialised with SQL taken from the "insert" SQL definition.
       * @exception SQLException
       *                   if an exception occurs while setting parameter values.
       */
      protected abstract void setUserForInsertStatement(User user, 
                                                        PreparedStatement userInsert)
          throws SQLException;
  
      /**
       * Set parameters of a PreparedStatement object with
       * property values from a User instance.
       * Implementations of this method have knowledge of the parameter
       * ordering of the "update" SQL statement definition.
       * 
       * @param user       a User instance, which should be an implementation class which
       *                   is handled by this Repostory implementation.
       * @param userUpdate a PreparedStatement initialised with SQL taken from the "update" SQL definition.
       * @exception SQLException
       *                   if an exception occurs while setting parameter values.
       */
      protected abstract void setUserForUpdateStatement(User user, 
                                                        PreparedStatement userUpdate)
          throws SQLException;
  
      /**
       * Opens a connection, handling exceptions.
       */
      private Connection openConnection()
      {
          try {
              return m_datasource.getConnection();
          }
          catch (SQLException sqle) {
              throw new CascadingRuntimeException(
                  "An exception occurred getting a database connection.", sqle);
          }
      }
  
      /**
       * Closes a connection, handling exceptions.
       */
      private void closeConnection(Connection conn)
      {
          try {
              conn.close();
          }
          catch (SQLException sqle) {
              throw new CascadingRuntimeException(
                  "An exception occurred closing a database connection.", sqle);
          }
      }
  
  }    
  
  
  
  
  
  1.1                  jakarta-james/proposals/userstore/java/org/apache/james/userrepository/AbstractUsersRepository.java
  
  Index: AbstractUsersRepository.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.userrepository;
  
  import java.util.*;
  import org.apache.james.services.*;
  import org.apache.avalon.framework.logger.AbstractLoggable;
  
  /**
   * A partial implementation of a Repository to store users.
   * <p>This implements common functionality found in different UsersRespository 
   * implementations, and makes it easier to create new User repositories.</p>
   *
   * @author Darrell DeBoer <dd...@bigdaz.com>
   * @author Charles Benett <ch...@benett1.demon.co.uk>
   */
  public abstract class AbstractUsersRepository
      extends AbstractLoggable
      implements UsersRepository
  {
      //
      // Core Abstract methods - override these for a functional UserRepository.
      //
      /**
       * Returns a list populated with all of the Users in the repository.
       * @return an <code>Iterator</code> of <code>User</code>s.
       */
      protected abstract Iterator listAllUsers();
  
      /**
       * Adds a user to the underlying Repository.
       * The user name must not clash with an existing user.
       */
      protected abstract void doAddUser(User user);
  
      /**
       * Removes a user from the underlying repository.
       * If the user doesn't exist, returns ok.
       */
      protected abstract void doRemoveUser(User user);
  
      /**
       * Updates a user record to match the supplied User.
       */
      protected abstract void doUpdateUser(User user);
  
      //
      // Extended protected methods.
      // These provide very basic default implementations, which will work,
      // but may need to be overridden by subclasses for performance reasons.
      //
      /**
       * Produces the complete list of User names, with correct case.
       * @return a <code>List</code> of <code>String</code>s representing
       *         user names.
       */
      protected List listUserNames()
      {
          Iterator users = listAllUsers();
          List userNames = new LinkedList();
          while ( users.hasNext() ) {
              User user = (User)users.next();
              userNames.add(user.getUserName());
          }
  
          return userNames;
      }
  
      /**
       * Gets a user by name, ignoring case if specified.
       * This implementation gets the entire set of users,
       *  and scrolls through searching for one matching <code>name</code>.
       */
      protected User getUserByName(String name, boolean ignoreCase)
      {
          // Just iterate through all of the users until we find one matching.
          Iterator users = listAllUsers();
          while ( users.hasNext() ) 
          {
              User user = (User)users.next();
              String username = user.getUserName();
              if (( !ignoreCase && username.equals(name) ) ||
                  ( ignoreCase && username.equalsIgnoreCase(name) )) {
                  return user;
              }
          }
          // Not found - return null
          return null;
      }
  
      //
      // UsersRepository interface implementation.
      //
      /**
       * Adds a user to the repository with the specified User object.
       * Users names must be unique-case-insensitive in the repository.
       *
       * @returns true if succesful, false otherwise
       * @since James 1.2.2
       */
      public boolean addUser(User user)
      {
          String username = user.getUserName();
  
          if ( containsCaseInsensitive(username) ) {
              return false;
          }
          
          doAddUser(user);
          return true;
      }
  
      /**
       * Adds a user to the repository with the specified attributes.  In current
       * implementations, the Object attributes is generally a String password.
       */
      public void addUser(String name, Object attributes) 
      {
  	if (attributes instanceof String)
          {
  	    User newbie = new DefaultUser(name, "SHA");
              newbie.setPassword( (String) attributes );
  	    addUser(newbie);
  	}
          else
          {
              throw new RuntimeException("Improper use of deprecated method" 
                                         + " - use addUser(User user)");
          }
      }
  
      /**
       * Update the repository with the specified user object. A user object
       * with this username must already exist.
       *
       * @returns true if successful.
       */
      public boolean updateUser(User user)
      {
          // Return false if it's not found.
          if ( ! contains(user.getUserName()) ) {
              return false;
          }
          else {
              doUpdateUser(user);
              return true;
          }
      }
  
      /**
       * Removes a user from the repository
       */
      public void removeUser(String name)
      {
          User user = getUserByName(name);
          if ( user != null ) {
              doRemoveUser(user);
          }
      }
  
      /**
       * Gets the attribute for a user.  Not clear on behavior.
       *
       * @deprecated As of James 1.2.2 . Use the {@link #getUserByName(String) getUserByName} method.
       */
      public Object getAttributes(String name)
      {
          throw new RuntimeException("Improper use of deprecated method - read javadocs");
      }
  
      /**
       * Get the user object with the specified user name.  Return null if no
       * such user.
       *
       * @since James 1.2.2
       */
      public User getUserByName(String name)
      {
          return getUserByName(name, false);
      }
  
      /**
       * Get the user object with the specified user name. Match user naems on
       * a case insensitive basis.  Return null if no such user.
       *
       * @since James 1.2.2
       */
      public User getUserByNameCaseInsensitive(String name)
      {
          return getUserByName(name, true);
      }
  
      /**
       * Returns the user name of the user matching name on an equalsIgnoreCase
       * basis. Returns null if no match.
       */
      public String getRealName(String name)
      {
          // Get the user by name, ignoring case, and return the correct name.
          User user = getUserByName(name, true);
          if ( user == null ) {
              return null;
          }
          else {
              return user.getUserName();
          }
      }
  
      /**
       * Returns whether or not this user is in the repository
       */
      public boolean contains(String name)
      {
          User user = getUserByName(name, false);
          return ( user != null );
      }
  
      /**
       * Returns whether or not this user is in the repository. Names are
       * matched on a case insensitive basis.
       */
      public boolean containsCaseInsensitive(String name)
      {
          User user = getUserByName( name, true );
          return ( user != null );
      }
  
      /**
       * Tests a user with the appropriate attributes.  In current implementations,
       * this typically means "check the password" where a String password is passed
       * as the Object attributes.
       *
       * @deprecated As of James 1.2.2, use {@link #test(String, String) test(String name, String password)}
       */
      public boolean test(String name, Object attributes)
      {
          throw new RuntimeException("Improper use of deprecated method - read javadocs");
      }
  
      /**
       * Test if user with name 'name' has password 'password'.
       *
       * @since James 1.2.2
       */
      public boolean test(String name, String password)
      {
          User user = getUserByName(name, false);
          if ( user == null ) {
              return false;
          }
          else {
              return user.verifyPassword(password);
          }
      }
  
      /**
       * Returns a count of the users in the repository.
       */
      public int countUsers()
      {
          List usernames = listUserNames();
          return usernames.size();
      }
  
      /**
       * List users in repository.
       *
       * @returns Iterator over a collection of Strings, each being one user in the repository.
       */
      public Iterator list()
      {
          return listUserNames().iterator();
      }
  }
  
  
  
  1.1                  jakarta-james/proposals/userstore/java/org/apache/james/userrepository/DefaultUsersJdbcRepository.java
  
  Index: DefaultUsersJdbcRepository.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.userrepository;
  
  import org.apache.james.services.User;
  import java.sql.*;
  
  
  /**
   * A Jdbc-backed UserRepository which handles User instances
   * of the <CODE>DefaultUser</CODE> class.
   * Although this repository can handle subclasses of DefaultUser,
   * like <CODE>DefaultJamesUser</CODE>, only properties from
   * the DefaultUser class are persisted.
   * 
   * @author Darrell DeBoer <dd...@bigdaz.com>
   */
  public class DefaultUsersJdbcRepository extends AbstractJdbcUsersRepository
  {
      protected User readUserFromResultSet(ResultSet rsUsers) throws SQLException 
      {
          // Get the username, and build a DefaultUser with it.
          String username = rsUsers.getString(1);
          String passwordAlg = rsUsers.getString(2);
          String passwordHash = rsUsers.getString(3);
          DefaultUser user = new DefaultUser(username, passwordHash, passwordAlg);
          return user;
      }
  
      protected void setUserForInsertStatement(User user, 
                                               PreparedStatement userInsert) 
          throws SQLException 
      {
          DefaultUser defUser = (DefaultUser)user;
          userInsert.setString(1, defUser.getUserName());
          userInsert.setString(2, defUser.getHashAlgorithm());
          userInsert.setString(3, defUser.getHashedPassword());
      }
  
      protected void setUserForUpdateStatement(User user, 
                                               PreparedStatement userUpdate) 
          throws SQLException 
      {
          DefaultUser defUser = (DefaultUser)user;
          userUpdate.setString(3, defUser.getUserName());
          userUpdate.setString(1, defUser.getHashAlgorithm());
          userUpdate.setString(2, defUser.getHashedPassword());
      }
  }
  
  
  
  
  1.1                  jakarta-james/proposals/userstore/java/org/apache/james/userrepository/JamesUsersJdbcRepository.java
  
  Index: JamesUsersJdbcRepository.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.userrepository;
  
  import org.apache.james.services.User;
  import org.apache.mailet.MailAddress;
  
  import java.sql.ResultSet;
  import java.sql.PreparedStatement;
  import java.sql.SQLException;
  
  /**
   * A Jdbc-backed UserRepository which handles User instances
   * of the <CODE>DefaultJamesUser</CODE> class, or any superclass.
   * 
   * @author Darrell DeBoer <dd...@bigdaz.com>
   */
  public class JamesUsersJdbcRepository extends AbstractJdbcUsersRepository
  {
      protected User readUserFromResultSet(ResultSet rsUsers) throws SQLException 
      {
          // Get the column values
          String username = rsUsers.getString(1);
          String pwdHash = rsUsers.getString(2);
          String pwdAlgorithm = rsUsers.getString(3);
          boolean useForwarding = rsUsers.getBoolean(4);
          String forwardingDestination = rsUsers.getString(5);
          boolean useAlias = rsUsers.getBoolean(6);
          String alias = rsUsers.getString(7);
  
          MailAddress forwardAddress = null;
          if ( forwardingDestination != null ) {
              try {
                  forwardAddress = new MailAddress(forwardingDestination);
              }
              catch (javax.mail.internet.ParseException pe) {
                  throw new RuntimeException(
                      "Invalid mail address in database: " + forwardingDestination + 
                      ", for user " + username + ".");
              }
          }
  
          // Build a DefaultJamesUser with these values, and add to the list.
          DefaultJamesUser user = new DefaultJamesUser(username, pwdHash, pwdAlgorithm);
          user.setForwarding(useForwarding);
          user.setForwardingDestination(forwardAddress);
          user.setAliasing(useAlias);
          user.setAlias(alias);
  
          return user;
      }
  
      protected void setUserForInsertStatement(User user, 
                                               PreparedStatement userInsert) 
          throws SQLException 
      {
          setUserForStatement(user, userInsert, false);
      }
  
      protected void setUserForUpdateStatement(User user, 
                                               PreparedStatement userUpdate) 
          throws SQLException 
      {
          setUserForStatement(user, userUpdate, true);
      }
  
      private void setUserForStatement(User user, PreparedStatement stmt,
                                       boolean userNameLast) throws SQLException
      {
          // Determine column offsets to use, based on username column pos.
          int nameIndex = 1;
          int colOffset = 1;
          if ( userNameLast ) {
              nameIndex = 7;
              colOffset = 0;
          }
  
          // Can handle instances of DefaultJamesUser and DefaultUser.
          DefaultJamesUser jamesUser;
          if (user instanceof DefaultJamesUser) {
              jamesUser = (DefaultJamesUser)user;
          }
          else if ( user instanceof DefaultUser ) {
              DefaultUser aUser = (DefaultUser)user;
              jamesUser = new DefaultJamesUser(aUser.getUserName(),
                                               aUser.getHashedPassword(),
                                               aUser.getHashAlgorithm());
          } 
          // Can't handle any other implementations.
          else {
              throw new RuntimeException("An unknown implementation of User was " + 
                                         "found. This implementation cannot be " + 
                                         "persisted to a UsersJDBCRepsitory.");
          }
  
          // Get the user details to save.
          stmt.setString(nameIndex, jamesUser.getUserName());
          stmt.setString(1 + colOffset, jamesUser.getHashedPassword());
          stmt.setString(2 + colOffset, jamesUser.getHashAlgorithm());
          stmt.setInt(3 + colOffset, (jamesUser.getForwarding() ? 1 : 0));
  
          MailAddress forwardAddress = jamesUser.getForwardingDestination();
          String forwardDestination = null;
          if ( forwardAddress != null ) {
              forwardDestination = forwardAddress.toString();
          }
          stmt.setString(4 + colOffset, forwardDestination);
          stmt.setInt(5 + colOffset, (jamesUser.getAliasing() ? 1 : 0));
          stmt.setString(6 + colOffset, jamesUser.getAlias());
      }
  }
  
  
  
  1.1                  jakarta-james/proposals/userstore/java/org/apache/james/userrepository/ListUsersJdbcRepository.java
  
  Index: ListUsersJdbcRepository.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.userrepository;
  
  import org.apache.james.services.User;
  import java.sql.*;
  
  /**
   * A very lightweight UserRepository, which persists a list
   * of user names in a database. Password information is not 
   * persisted.
   * 
   * @author Darrell DeBoer <dd...@bigdaz.com>
   */
  public class ListUsersJdbcRepository extends AbstractJdbcUsersRepository
  {
      protected User readUserFromResultSet(ResultSet rsUsers) throws SQLException 
      {
          // Get the username, and build a DefaultUser with it.
          String username = rsUsers.getString(1);
          DefaultUser user = new DefaultUser(username, "SHA");
          return user;
      }
      protected void setUserForInsertStatement(User user, 
                                               PreparedStatement userInsert) 
          throws SQLException 
      {
          userInsert.setString(1, user.getUserName());
      }
  
      protected void setUserForUpdateStatement(User user, 
                                               PreparedStatement userUpdate) 
          throws SQLException 
      {
          throw new UnsupportedOperationException("Can't update a List User - " +
                                                  "only has a single attribute.");
      }
  }
  
  
  
  1.1                  jakarta-james/proposals/userstore/java/org/apache/james/util/SqlResources.java
  
  Index: SqlResources.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.util;
  
  import org.apache.avalon.framework.configuration.ConfigurationException;
  
  /** Jdbc and utilities */
  import java.sql.Connection;
  import java.sql.SQLException;
  import java.util.*;
  import java.io.*;
  
  /** W3C DOM classes **/
  import org.w3c.dom.Document;
  import org.w3c.dom.Element;
  import org.w3c.dom.Node;
  import org.w3c.dom.NodeList;
  import org.w3c.dom.NamedNodeMap;
  import org.w3c.dom.Attr;
  
  /** Parser classes **/
  import javax.xml.parsers.DocumentBuilder;
  import javax.xml.parsers.DocumentBuilderFactory;
  import javax.xml.parsers.ParserConfigurationException;
  
  /** ORO regex classes */
  import org.apache.oro.text.perl.Perl5Util;
  import org.apache.oro.text.perl.MalformedPerl5PatternException;
  
  
  /**
   * Provides a set of SQL String resources (eg SQL Strings)
   * to use for a database connection.
   * This class allows SQL strings to be customised to particular
   * database products, by detecting product information from the
   * jdbc DatabaseMetaData object.
   * 
   * @author Darrell DeBoer <dd...@bigdaz.com>
   */
  public class SqlResources
  {
      /**
       * Configures a DbResources object to provide SQL statements from a file.
       * 
       * SQL statements returned may be specific to the particular type
       * and version of the connected database, as well as the database driver.
       * 
       * Parameters encoded as $(parameter} in the input file are
       * replace by values from the parameters Map, if the named parameter exists.
       * Parameter values may also be specified in the resourceSection element.
       * 
       * @param sqlFile    the input file containing the string definitions
       * @param sqlDefsSection
       *                   the xml element containing the strings to be used
       * @param conn the Jdbc DatabaseMetaData, taken from a database connection
       * @param configParameters a map of parameters (name-value string pairs) which are
       *                   replaced where found in the input strings
       */
      public void init(File sqlFile, String sqlDefsSection,
                       Connection conn, Map configParameters)
          throws Exception
      {
          // Parse the sqlFile as an XML document.
          DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
          DocumentBuilder builder = factory.newDocumentBuilder();
          Document sqlDoc = builder.parse(sqlFile);
  
          // First process the database matcher, to determine the
          //  sql statements to use.
          Element dbMatcherElement = 
              (Element)(sqlDoc.getElementsByTagName("dbMatchers").item(0));
          String dbProduct = null;
          if ( dbMatcherElement != null ) {
              dbProduct = matchDbConnection(conn, dbMatcherElement);
          }
  
          // Now get the section defining sql for the repository required.
          NodeList sections = sqlDoc.getElementsByTagName("sqlDefs");
          int sectionsCount = sections.getLength();
          Element sectionElement = null;
          for (int i = 0; i < sectionsCount; i++ ) {
              sectionElement = (Element)(sections.item(i));
              String sectionName = sectionElement.getAttribute("name");
              if ( sectionName != null && sectionName.equals(sqlDefsSection) ) {
                  break;
              }
  
          }
          if ( sectionElement == null ) {
              throw new RuntimeException("Error loading sql definition file. " +
                                         "The element named \'" + sqlDefsSection +
                                         "\' does not exist.");
          }
  
          // Get parameters defined within the file as defaults,
          // and use supplied parameters as overrides.
          Map parameters = new HashMap();
          // First read from the <params> element, if it exists.
          Element parametersElement = 
              (Element)(sectionElement.getElementsByTagName("parameters").item(0));
          if ( parametersElement != null ) {
              NamedNodeMap params = parametersElement.getAttributes();
              int paramCount = params.getLength();
              for (int i = 0; i < paramCount; i++ ) {
                  Attr param = (Attr)params.item(i);
                  String paramName = param.getName();
                  String paramValue = param.getValue();
                  parameters.put(paramName, paramValue);
              }
          }
          // Then copy in the parameters supplied with the call.
          parameters.putAll(configParameters);
  
          // 2 maps - one for storing default statements,
          // the other for statements with a "db" attribute matching this 
          // connection.
          Map defaultSqlStatements = new HashMap();
          Map dbProductSqlStatements = new HashMap();
  
          // Process each sql statement, replacing string parameters,
          // and adding to the appropriate map..
          NodeList sqlDefs = sectionElement.getElementsByTagName("sql");
          int sqlCount = sqlDefs.getLength();
          for ( int i = 0; i < sqlCount; i++ ) {
              // See if this needs to be processed (is default or product specific)
              Element sqlElement = (Element)(sqlDefs.item(i));
              String sqlDb = sqlElement.getAttribute("db");
              Map sqlMap;
              if ( sqlDb.equals("")) {
                  // default
                  sqlMap = defaultSqlStatements;
              }
              else if (sqlDb.equals(dbProduct) ) {
                  // Specific to this product
                  sqlMap = dbProductSqlStatements;
              }
              else {
                  // for a different product
                  continue;
              }
  
              // Get the key and value for this SQL statement.
              String sqlKey = sqlElement.getAttribute("name");
              if ( sqlKey == null ) {
                  // ignore statements without a "name" attribute.
                  continue;
              }
              String sqlString = sqlElement.getFirstChild().getNodeValue();
  
              // Do parameter replacements for this sql string.
              Iterator paramNames = parameters.keySet().iterator();
              while ( paramNames.hasNext() ) {
                  String paramName = (String)paramNames.next();
                  String paramValue = (String)parameters.get(paramName);
  
                  String replace = "${" + paramName + "}";
                  sqlString = substituteSubString(sqlString, replace, paramValue);
              }
  
              // Add to the sqlMap - either the "default" or the "product" map
              sqlMap.put(sqlKey, sqlString);
          }
  
          // Copy in default strings, then overwrite product-specific ones.
          m_sql.putAll(defaultSqlStatements);
          m_sql.putAll(dbProductSqlStatements);
      }
  
      /**
       * Compares the DatabaseProductName value for a jdbc Connection
       * against a set of regular expressions defined in XML.
       * The first successful match defines the name of the database product
       * connected to. This value is then used to choose the specific SQL 
       * expressions to use.
       *
       */
      private String matchDbConnection(Connection conn, 
                                       Element dbMatchersElement)
          throws MalformedPerl5PatternException, SQLException
      {
          String dbProductName = conn.getMetaData().getDatabaseProductName();
      
          NodeList dbMatchers = 
              dbMatchersElement.getElementsByTagName("dbMatcher");
          for ( int i = 0; i < dbMatchers.getLength(); i++ ) {
              // Get the values for this matcher element.
              Element dbMatcher = (Element)dbMatchers.item(i);
              String dbMatchName = dbMatcher.getAttribute("db");
              String dbProductPattern = dbMatcher.getAttribute("databaseProductName");
              dbProductPattern = "/" + dbProductPattern + "/i";
  
              // If the connection databaseProcuctName matches the pattern,
              // use the match name from this matcher.
              if ( m_perl5Util.match(dbProductPattern, dbProductName) ) {
                  return dbMatchName;
              }
          }
          return null;
      }
  
      /**
       * Replace substrings of one string with another string and return altered string.
       * @param input input string
       * @param find the string to replace
       * @param replace the string to replace with
       * @return the substituted string
       */
      private String substituteSubString( String input, 
                                          String find,
                                          String replace )
      {
          int find_length = find.length();
          int replace_length = replace.length();
  
          StringBuffer output = new StringBuffer(input);
          int index = input.indexOf(find);
          int outputOffset = 0;
  
          while ( index > -1 ) {
              output.replace(index + outputOffset, index + outputOffset + find_length, replace);
              outputOffset = outputOffset - find_length + replace_length;
  
              index = input.indexOf(input, index + replace_length);
          }
  
          String result = output.toString();
          return result;
      }
  
      /**
       * Returns a named SQL string for the specified connection,
       * replacing parameters with the values set.
       * 
       * @param name   the name of the SQL resource required.
       * @return the requested resource
       */
      public String getSqlString(String name)
      {
          return (String)m_sql.get(name);
      }
  
      /**
       * Returns a named SQL string for the specified connection,
       * replacing parameters with the values set.
       * 
       * @param name     the name of the SQL resource required.
       * @param required true if the resource is required
       * @return the requested resource
       * @exception ConfigurationException
       *                   if a required resource cannot be found.
       */
      public String getSqlString(String name, boolean required)
          throws ConfigurationException
      {
          String sql = getSqlString(name);
  
          if ( sql == null ) {
              throw new RuntimeException("Required SQL resource: '" + name + 
                                         "' was not found.");
          }
          return sql;
      }
  
      private Map m_sql = new HashMap();
      private Perl5Util m_perl5Util = new Perl5Util();
  }
  
  
  
  1.1                  jakarta-james/proposals/userstore/lib/avalon-excalibur.jar
  
  	<<Binary file>>
  
  

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