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 se...@apache.org on 2001/06/27 06:34:24 UTC

cvs commit: jakarta-james/proposals/noparse-mimemessage/java/org/apache/mailet Mail.java

serge       01/06/26 21:34:24

  Added:       proposals/noparse-mimemessage README.txt
               proposals/noparse-mimemessage/conf james-config.xml
                        james-server.xml
               proposals/noparse-mimemessage/java/org/apache/james/core
                        MailImpl.java MimeMessageInputStreamSource.java
                        MimeMessageSource.java MimeMessageWrapper.java
               proposals/noparse-mimemessage/java/org/apache/james/imapserver
                        BaseCommand.java CommandFetch.java
                        CommandStore.java FileMailbox.java Mailbox.java
                        MimeMessageFileSource.java
                        SimpleMessageAttributes.java
               proposals/noparse-mimemessage/java/org/apache/james/mailrepository
                        AvalonMailRepository.java DebugInputStream.java
                        JDBCMailRepository.java JDBCSpoolRepository.java
                        MimeMessageAvalonSource.java
                        MimeMessageJDBCSource.java
               proposals/noparse-mimemessage/java/org/apache/mailet
                        Mail.java
  Log:
  New proposal to overhaul how MimeMessage data is handled.  Will delay parsing and instantiating the MimeMessage object itself at all costs.  Also removes use of Town.
  
  Revision  Changes    Path
  1.1                  jakarta-james/proposals/noparse-mimemessage/README.txt
  
  Index: README.txt
  ===================================================================
  This is an overhaul of how MimeMessages are handled. The goal is to avoid parsing the data, and simply pass a reference to the underlying data, only parsing and instantiating the MimeMessage when needed (by a Mailet for example).
  
  This also removes the dependency on Town.
  
  EnhancedMimeMessage and JamesMimeMessage are removed and rolled into the MimeMessageWrapper.
  
  
  Here's what to do:
  - Copy files into main tree.
  - Delete the following files:
    - town.jar
    - org.apache.james.core.JamesMimeMessageInputStream.java
    - org.apache.james.core.JamesMimeMessage.java
    - org.apache.james.core.EnhancedMimeMessage.java
    - org.apache.james.mailrepository.FileMimeMessageInputStream.java
    - org.apache.james.mailrepository.TownMimeMessageInputStream.java
    - org.apache.james.mailrepository.TownSpoolRepository.java
    - org.apache.james.transport.mailets.TownAlias.java
    - org.apache.james.transport.mailets.TownListserv.java
    - org.apache.james.userrepository.UsersTownRepository.java
  
  build like normal... you might want to tweak the james-server.xml to use alternate repositories (like the JDBC version), should you be ambitious.
  
  
  
  
  1.1                  jakarta-james/proposals/noparse-mimemessage/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/06/27 04:34:21 $ by: $Author: serge $
  -->
  <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 spool repository is a singular location where incoming mails
        are temporarily stored before being processed.
       (ex. file://c:/james/spool/) -->
        <spoolRepository>
          <repository destinationURL="db://EML_SPOOL/spool"
                      type="SPOOL"
                      model="SYNCHRONOUS">
          </repository>
        </spoolRepository>
  
        <!-- Alternative spool repository definition for Town use
        <spoolRepository>
          <repository destinationURL="town://mainspool"
                      type="SPOOL"
                      model="SYNCHRONOUS">
            <conn>file:///dev/james/dist/var/maildatabase</conn>
            <table>Message</table>
          </repository>
        </spoolRepository>
        -->
  
        <!-- The inbox repository is the location for users inboxes -->
        <inboxRepository>
          <repository destinationURL="db://EML_Spool/inboxes"
                      type="MAIL"
                      model="SYNCHRONOUS">
          </repository>
        </inboxRepository>
  
        <!-- Alternative inbox repository definition for Town use
        <inboxRepository>
          <repository destinationURL="town://inbox"
                      type="SPOOL"
                      model="SYNCHRONOUS">
            <conn>file:///dev/james/dist/var/maildatabase</conn>
            <table>Message</table>
          </repository>
        </inboxRepository>
        -->
    </James>
  
    <!-- The James Spool Manager block  -->
    <spoolmanager>
        <!-- The spool repository is a singular location where incoming mails
        are temporarily stored before being processed.
       (ex. file://c:/james/spool/)
  	The Spool Manager and James Block must point to the same repository
  	to add and process same messages. -->
        <spoolRepository>
          <repository destinationURL="db://EML_Spool/spool"
                      type="SPOOL"
                      model="SYNCHRONOUS">
          </repository>
        </spoolRepository>
  
        <!-- Alternative spool repository definition for Town use
        <spoolRepository>
          <repository destinationURL="town://mainspool"
                      type="SPOOL"
                      model="SYNCHRONOUS">
            <conn>file:///dev/james/dist/../../store/maildatabase</conn>
            <table>Message</table>
          </repository>
        </spoolRepository>
        -->
  
        <!-- 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">
              <membersPath> file://var/users/list-james </membersPath>
            </mailet>
  
            <mailet match="RecipientIs=james@localhost" class="AvalonListserv">
              <membersonly> false </membersonly>
              <attachmentsallowed> true </attachmentsallowed>
              <replytolist> true </replytolist>
              <membersPath>file://var/users/list-james</membersPath>
            </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> db://EML_Spool/error</repositoryPath>
              <!-- <repositoryPath> town://mail-error </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> db://EML_Spool/outgoing </outgoing>
              <!-- <outgoing> town://mail-outgoing </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>db://EML_Spool/spam</repositoryPath>
              <!-- <repositoryPath> town://spam </repositoryPath> -->
            </mailet>
          </processor>
    </spoolmanager>
  
  
  <!-- CONFIRM? Enter ip address of your DNS server, one per element -->
    <dnsserver>
          <servers>
            <server>216.181.1.2</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>
            <models>
              <model>SYNCHRONOUS</model>
              <model>ASYNCHRONOUS</model>
              <model>CACHE</model>
            </models>
          </repository>
          <repository
              class="org.apache.james.mailrepository.AvalonSpoolRepository">
            <protocols>
              <protocol>file</protocol>
            </protocols>
            <types>
              <type>SPOOL</type>
            </types>
            <models>
              <model>SYNCHRONOUS</model>
              <model>ASYNCHRONOUS</model>
              <model>CACHE</model>
            </models>
          </repository>
  
          <repository
              class="org.apache.james.mailrepository.JDBCMailRepository">
            <protocols>
              <protocol>db</protocol>
            </protocols>
            <types>
              <type>MAIL</type>
            </types>
            <models>
              <model>SYNCHRONOUS</model>
              <model>ASYNCHRONOUS</model>
              <model>CACHE</model>
            </models>
          </repository>
          <repository
              class="org.apache.james.mailrepository.JDBCSpoolRepository">
            <protocols>
              <protocol>db</protocol>
            </protocols>
            <types>
              <type>SPOOL</type>
            </types>
            <models>
              <model>SYNCHRONOUS</model>
              <model>ASYNCHRONOUS</model>
              <model>CACHE</model>
            </models>
          </repository>
        </repositories>
    </mailstore>
  
    <!-- The User Storage block -->
    <users-store>
        <!-- File-based user repository  -->
        <repository name="LocalUsers"
                    class="org.apache.james.userrepository.UsersFileRepository">
          <destination URL="file://var/users/"/>
        </repository>
  
        <!-- Uncomment this to store users in an RDBMS
        <repository name="LocalUsers"
                    class="org.apache.james.userrepository.UsersTownRepository">
          <destination URL="town://users/">
          <conn>file:///var/maildatabase</conn>
          <table>Users</table>
        </repository>
        -->
        <!-- The following are examples of database connections which have been
             tested.
             These should provided a guide to setting up other databases as well.
             The driver.class and destination.URL properties are require,
             but destination.user and destination.password are optional. -->
        <!-- Mssql server with Inet Sprinta
        <repository name="LocalUsers"
        			  class="org.apache.james.userrepository.UsersJDBCRepository">
              <destination>
                  <driver class="com.inet.tds.TdsDriver"/>
                  <datasource>
                      <dburl>jdbc:inetdae7:127.0.0.1?database=mail</dburl>
                      <user>user</user>
                      <password>password</password>
                  </datasource>
              </destination>
        </repository>
        -->
  
        <!-- MySQL server via ODBC
        <repository name="LocalUsers"
                    class="org.apache.james.userrepository.UsersJDBCRepository">
              <destination>
                  <driver class="sun.jdbc.odbc.JdbcOdbcDriver"/>
                  <datasource>
                      <dburl>jdbc:odbc:mail-mysql</dburl>
                  </datasource>
              </destination>
        </repository>
        -->
        <!-- MySQL server via mm mysql driver
        <repository name="LocalUsers"
                    class="org.apache.james.userrepository.UsersJDBCRepository">
              <destination>
                  <driver class="org.gjt.mm.mysql.Driver"/>
                  <datasource>
                      <dburl>jdbc:mysql://127.0.0.1/mail</dburl>
                      <user>user</user>
                      <password>password</password>
                  </datasource>
              </destination>
        </repository>
        -->
        <!-- Oracle8i server via thin driver
        <repository name="LocalUsers"
                    class="org.apache.james.userrepository.UsersJDBCRepository">
              <destination>
                  <driver class="oracle.jdbc.driver.OracleDriver"/>
                  <datasource>
                      <dburl>jdbc:oracle:thin:@127.0.0.1:1521:mail</dburl>
                      <user>user</user>
                      <password>password</password>
                  </datasource>
              </destination>
        </repository>
        -->
  
        <repository name="list-james"
                    class="org.apache.james.userrepository.UsersFileRepository">
          <destination URL="file://var/lists/list-james/"/>
        </repository>
    </users-store>
  
    <!-- 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/noparse-mimemessage/conf/james-server.xml
  
  Index: james-server.xml
  ===================================================================
  <?xml version="1.0"?>
  <!--
                                  README!
  
      Basic config file that sets up context for server application.
  
  -->
  <server>
  
      <logs>
       <!--  Specify the target for each category of logger listed below.
             The name is the name in the code and should not be changed by assemblers, deployers or administrators.
             The priority is the lowest priority of logging mesage recorded.
        -->
  
          <category name="" target="default" priority="DEBUG" />
          <log-target name="default" location="/logs/James-default.log" />
          <category name="James.Mailet" target="James-Mailet-target" priority="DEBUG" />
          <log-target name="James-Mailet-target" location="/logs/James.Mailet.log" />
  
          <category name="James" target="James-target" priority="DEBUG"/>
          <log-target name="James-target" location="/logs/James.log"/>
          <category name="spoolmanager" target="spoolmanager-target" priority="DEBUG"/>
          <log-target name="spoolmanager-target" location="/logs/spoolmanager.log"/>
          <category name="dnsserver" target="dnsserver-target" priority="DEBUG"/>
          <log-target name="dnsserver-target" location="/logs/dnsserver.log"/>
          <category name="remotemanager" target="remotemanager-target" priority="DEBUG"/>
          <log-target name="remotemanager-target" location="/logs/remotemanager.log"/>
          <category name="imapserver" target="imapserver-target" priority="DEBUG"/>
          <log-target name="imapserver-target" location="/logs/imapserver.log"/>
          <category name="pop3server" target="pop3server-target" priority="DEBUG"/>
          <log-target name="pop3server-target" location="/logs/pop3server.log"/>
          <category name="smtpserver" target="smtpserver-target" priority="DEBUG"/>
          <log-target name="smtpserver-target" location="/logs/smtpserver.log"/>
          <category name="nntpserver" target="nntpserver-target" priority="DEBUG"/>
          <log-target name="nntpserver-target" location="/logs/nntpserver.log"/>
          <category name="nntp-repository" target="nntp-repository-target" priority="DEBUG"/>
          <log-target name="nntp-repository-target" location="/logs/nntp-repository.log"/>
          <category name="mailstore" target="mailstore-target" priority="DEBUG"/>
          <log-target name="mailstore-target" location="/logs/mailstore.log"/>
          <category name="users-store" target="users-store-target" priority="DEBUG"/>
          <log-target name="users-store-target" location="/logs/users-store.log"/>
          <category name="objectstorage" target="objectstorage-target" priority="DEBUG"/>
          <log-target name="objectstorage-target" location="/logs/objectstorage.log"/>
          <category name="connections" target="connections-target" priority="DEBUG"/>
          <log-target name="connections-target" location="/logs/connections.log"/>
          <category name="sockets" target="sockets-target" priority="DEBUG"/>
          <log-target name="sockets-target" location="/logs/sockets.log"/>
          <category name="scheduler" target="scheduler-target" priority="DEBUG"/>
          <log-target name="scheduler-target" location="/logs/scheduler.log"/>
  
      </logs>
  
      <threads>
        <thread-group>
          <name>default</name>
  
          <!-- normal priority == 5, max-priority = 10 -->
          <priority>5</priority>
  
          <!-- are threads deamon threads ? -->
          <is-daemon>false</is-daemon>
  
          <max-threads>40</max-threads>
          <!-- these are ignored at the moment but will be fixed in later revisions -->
          <min-threads>20</min-threads>
          <min-spare-threads>20</min-spare-threads>
  
        </thread-group>
  
      </threads>
  
      <policy>
        <grant code-base="file:${app.home}${/}blocks${/}*">
          <permission class="java.security.AllPermission" />
        </grant>
  
        <grant code-base="file:${app.home}${/}lib${/}*">
          <permission class="java.security.AllPermission" />
        </grant>
      </policy>
  
  </server>
  
  
  
  1.1                  jakarta-james/proposals/noparse-mimemessage/java/org/apache/james/core/MailImpl.java
  
  Index: MailImpl.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.core;
  
  import java.io.*;
  import java.net.*;
  import java.util.*;
  import javax.mail.*;
  import javax.mail.internet.*;
  import org.apache.mailet.*;
  
  /**
   * Wrap a MimeMessage adding routing informations (from SMTP) and same simple API.
   * @author Federico Barbieri <sc...@systemy.it>
   * @author Serge Knystautas <se...@lokitech.com>
   * @version 0.9
   */
  public class MailImpl implements Mail {
      //We hardcode the serialVersionUID so that from James 1.2 on,
      //  MailImpl will be deserializable (so your mail doesn't get lost)
      public static final long serialVersionUID = -4289663364703986260L;
  
      private String errorMessage;
      private String state;
      private MimeMessage message;
      private MailAddress sender;
      private Collection recipients;
      private String name;
      private String remoteHost = "localhost";
      private String remoteAddr = "127.0.0.1";
      private Date lastUpdated = new Date();
  
      public MailImpl() {
          setState(Mail.DEFAULT);
      }
  
      public MailImpl(String name, MailAddress sender, Collection recipients) {
          this();
          this.name = name;
          this.sender = sender;
          this.recipients = recipients;
      }
  
      public MailImpl(String name, MailAddress sender, Collection recipients, InputStream messageIn)
      throws MessagingException {
          this(name, sender, recipients);
          MimeMessageSource source = new MimeMessageInputStreamSource(name, messageIn);
          MimeMessageWrapper wrapper = new MimeMessageWrapper(source);
          this.setMessage(wrapper);
      }
  
      public MailImpl(String name, MailAddress sender, Collection recipients, MimeMessage message) {
          this(name, sender, recipients);
          this.setMessage(message);
      }
  
      public void clean() {
          message = null;
      }
  
      public Mail duplicate() {
          try {
              return new MailImpl(name, sender, recipients, getMessage());
          } catch (MessagingException me) {
          }
          return (Mail) null;
      }
  
      public Mail duplicate(String newName) {
          try {
              return new MailImpl(newName, sender, recipients, getMessage());
          } catch (MessagingException me) {
          }
          return (Mail) null;
      }
  
      public String getErrorMessage() {
          return errorMessage;
      }
  
      public MimeMessage getMessage() throws MessagingException {
          return message;
      }
  
      public void setName(String name) {
          this.name = name;
      }
  
      public String getName() {
          return name;
      }
  
      public Collection getRecipients() {
          return recipients;
      }
  
      public MailAddress getSender() {
          return sender;
      }
  
      public String getState() {
          return state;
      }
  
      public String getRemoteHost() {
          return remoteHost;
      }
  
      public String getRemoteAddr() {
          return remoteAddr;
      }
  
      public Date getLastUpdated() {
          return lastUpdated;
      }
  /*
      private void parse(InputStream messageIn) throws MessagingException {
          if (messageIn != null) {
              message = new EnhancedMimeMessage(Session.getDefaultInstance(System.getProperties(), null), messageIn);
          } else {
  	    throw new MessagingException("Attempt to parse null input stream.");
  	}
      }
  */
      /**
       * <p>Return the size of the message including its headers.
       * MimeMessage.getSize() method only returns the size of the
       * message body.</p>
       *
       * <p>Note: this size is not guaranteed to be accurate - see Sun's
       * documentation of MimeMessage.getSize().</p>
       *
       * @return approximate size of full message including headers.
       *
       * @author Stuart Roebuck <st...@adolos.co.uk>
       */
      public int getSize() throws MessagingException {
          //SK: Should probably eventually store this as a locally
          //  maintained value (so we don't have to load and reparse
          //  messages each time).
          int size = message.getSize();
          Enumeration e = message.getAllHeaders();
          while (e.hasMoreElements()) {
              size += ((Header)e.nextElement()).toString().length();
           }
          return size;
      }
  
      private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException {
          try {
              sender = new MailAddress((String) in.readObject());
          } catch (ParseException pe) {
              throw new IOException("Error parsing sender address: " + pe.getMessage());
          }
          recipients = (Collection) in.readObject();
          state = (String) in.readObject();
          errorMessage = (String) in.readObject();
          name = (String) in.readObject();
          remoteHost = (String) in.readObject();
          remoteAddr = (String) in.readObject();
          lastUpdated = (Date) in.readObject();
      }
  
      public void setErrorMessage(String msg) {
          this.errorMessage = msg;
      }
  /*
      public void setMessage(InputStream in) throws MessagingException {
          this.message = new JamesMimeMessage(Session.getDefaultInstance(System.getProperties(), null), in);
      }
  */
      public void setMessage(MimeMessage message) {
          this.message = message;
      }
  
      public void setRecipients(Collection recipients) {
          this.recipients = recipients;
      }
  
      public void setSender(MailAddress sender) {
          this.sender = sender;
      }
  
      public void setState(String state) {
          this.state = state;
      }
  
      public void setRemoteHost(String remoteHost) {
          this.remoteHost = remoteHost;
      }
  
      public void setRemoteAddr(String remoteAddr) {
          this.remoteAddr = remoteAddr;
      }
  
      public void setLastUpdated(Date lastUpdated) {
          this.lastUpdated = lastUpdated;
      }
  
      public void writeMessageTo(OutputStream out) throws IOException, MessagingException {
          if (message != null) {
              message.writeTo(out);
          } else {
  	    throw new MessagingException("No message set for this MailImpl.");
  	}
      }
  
      private void writeObject(java.io.ObjectOutputStream out) throws IOException {
          //System.err.println("saving object");
          lastUpdated = new Date();
          out.writeObject(sender.toString());
          out.writeObject(recipients);
          out.writeObject(state);
          out.writeObject(errorMessage);
          out.writeObject(name);
          out.writeObject(remoteHost);
          out.writeObject(remoteAddr);
          out.writeObject(lastUpdated);
      }
  
      public Mail bounce(String message) throws MessagingException {
  
          //This sends a message to the james component that is a bounce of the sent message
          MimeMessage original = getMessage();
          MimeMessage reply = (MimeMessage) original.reply(false);
          reply.setSubject("Re: " + original.getSubject());
          Collection recipients = new HashSet();
          recipients.add(getSender());
          InternetAddress addr[] = {new InternetAddress(getSender().toString())};
          reply.setRecipients(Message.RecipientType.TO, addr);
          reply.setFrom(new InternetAddress(getRecipients().iterator().next().toString()));
          reply.setText(message);
          reply.setHeader("Message-Id", "replyTo-" + getName());
  
          return new MailImpl("replyTo-" + getName(), new MailAddress(getRecipients().iterator().next().toString()), recipients, reply);
      }
  
      public void writeContentTo(OutputStream out, int lines)
             throws IOException, MessagingException {
          String line;
          BufferedReader br;
          if(message != null) {
              br = new BufferedReader(new InputStreamReader(message.getInputStream()));
              while(lines-- > 0) {
                  if((line = br.readLine()) == null)  break;
                  line += "\r\n";
                  out.write(line.getBytes());
              }
          } else {
  	    throw new MessagingException("No message set for this MailImpl.");
  	}
      }
  }
  
  
  
  1.1                  jakarta-james/proposals/noparse-mimemessage/java/org/apache/james/core/MimeMessageInputStreamSource.java
  
  Index: MimeMessageInputStreamSource.java
  ===================================================================
  package org.apache.james.core;
  
  import java.io.File;
  import java.io.FileInputStream;
  import java.io.FileOutputStream;
  import java.io.IOException;
  import java.io.InputStream;
  
  public class MimeMessageInputStreamSource extends MimeMessageSource {
  
      String key = null;
  	InputStream in = null;
      File file = null;
  
      //If you try to access this size first, it will load it into a temp file
      //  and work from there.
  
  	public MimeMessageInputStreamSource(String key, InputStream in) {
          this.key = key;
  		this.in = in;
  	}
  
  	/**
  	 * Return an input stream to the data
  	 */
      public synchronized InputStream getInputStream() throws IOException {
          if (file == null) {
              return in;
          } else {
              return new FileInputStream(file);
          }
  	}
  
      /**
       * If not already, read the stream into a temp file
       */
      public synchronized long getSize() throws IOException {
          if (file == null) {
              //Create a temp file and channel the input stream into it
              file = File.createTempFile(key, ".m64");
              FileOutputStream fout = new FileOutputStream(file);
              int b = -1;
              while ((b = in.read()) != -1) {
                  fout.write(b);
              }
              fout.close();
              in.close();
              file.deleteOnExit();
          }
          return file.length();
      }
  }
  
  
  
  1.1                  jakarta-james/proposals/noparse-mimemessage/java/org/apache/james/core/MimeMessageSource.java
  
  Index: MimeMessageSource.java
  ===================================================================
  package org.apache.james.core;
  
  import java.io.IOException;
  import java.io.InputStream;
  
  public abstract class MimeMessageSource {
  	/**
  	 * Return an input stream to the data
  	 */
      public abstract InputStream getInputStream() throws IOException;
  
      /**
       * Return the size of all the data.
       * Default implementation... others can override to do this much faster
       */
      public long getSize() throws IOException {
  		int size = 0;
  		InputStream in = getInputStream();
  		int read = 0;
  		byte[] data = new byte[1024];
  		while ((read = in.read(data)) > 0) {
  			size += read;
  		}
          System.err.println("size of " + this + " is " + size);
  		return size;
  	}
  }
  
  
  
  1.1                  jakarta-james/proposals/noparse-mimemessage/java/org/apache/james/core/MimeMessageWrapper.java
  
  Index: MimeMessageWrapper.java
  ===================================================================
  package org.apache.james.core;
  
  import java.io.*;
  import java.util.*;
  import javax.activation.*;
  import javax.mail.*;
  import javax.mail.internet.*;
  
  public class MimeMessageWrapper extends MimeMessage {
  
      /**
       * Can provide an input stream to the data
       */
      MimeMessageSource source = null;
      /**
       * The mime message in memory
       */
      MimeMessage message = null;
      /**
       * Record whether a change was made to this message
       */
      boolean modified = false;
  
  /*
      public MimeMessageWrapper(InputStream in) {
          this(new MimeMessageInputStreamSource(in));
      }
  */
      public MimeMessageWrapper(MimeMessageSource source) {
          super(javax.mail.Session.getDefaultInstance(System.getProperties(), null));
          this.source = source;
      }
  
      public MimeMessageWrapper(Session session, MimeMessageSource source) {
          super(session);
          this.source = source;
      }
  
      /**
       * Internal implementations
       */
      private synchronized void loadMessage() throws MessagingException {
          if (message != null) {
              //Another thread has already loaded this message
              return;
          }
          try {
              InputStream in = source.getInputStream();
              message = new MimeMessage(session, in);
              in.close();
          } catch (IOException ioe) {
              ioe.printStackTrace();
              throw new MessagingException("Unable to parse stream: " + ioe.getMessage());
          }
      }
  
      /**
       * Special methods you can call
       */
      public boolean isModified() {
          return modified;
      }
  
      /**
       * Methods that should be rewritten for optimization purposes
       */
      public void writeTo(OutputStream os) throws IOException, MessagingException {
          if (message == null) {
              loadMessage();
          }
          message.writeTo(os);
      }
  
      public void writeTo(OutputStream os, String[] ignoreList) throws IOException, MessagingException {
          if (message == null) {
              loadMessage();
          }
          message.writeTo(os, ignoreList);
      }
  
  
      /**
       * Various reader methods
       */
  
      public Address[] getFrom() throws MessagingException {
          if (message == null) {
              loadMessage();
          }
          return message.getFrom();
      }
  
      public Address[] getRecipients(Message.RecipientType type) throws MessagingException {
          if (message == null) {
              loadMessage();
          }
          return message.getRecipients(type);
      }
  
      public Address[] getAllRecipients() throws MessagingException {
          if (message == null) {
              loadMessage();
          }
          return message.getAllRecipients();
      }
  
      public Address[] getReplyTo() throws MessagingException {
          if (message == null) {
              loadMessage();
          }
          return message.getReplyTo();
      }
  
      public String getSubject() throws MessagingException {
          if (message == null) {
              loadMessage();
          }
          return message.getSubject();
      }
  
      public Date getSentDate() throws MessagingException {
          if (message == null) {
              loadMessage();
          }
          return message.getSentDate();
      }
  
      public Date getReceivedDate() throws MessagingException {
          if (message == null) {
              loadMessage();
          }
          return message.getReceivedDate();
      }
  
      public int getSize() throws MessagingException {
          try {
              if (message == null) {
                  return (int)source.getSize();
              } else {
                  //Would be better to use in memory mimemessage
                  //Need to figure out size of message plus size of headers...
                  return message.getSize();
                  //return source.getSize();
              }
          } catch (IOException ioe) {
              ioe.printStackTrace();
              throw new MessagingException("Trouble in getSize", ioe);
          }
      }
  
      /**
       * Corrects JavaMail 1.1 version which always returns -1.
       * Only corrected for content less than 5000 bytes,
       * to avoid memory hogging.
       */
      public int getLineCount() throws MessagingException {
          if (message == null) {
              loadMessage();
          }
          int size = content.length; // size of byte array
          int lineCount = 0;
          if (size < 5000) {
              for (int i=0; i < size -1; i++) {
                  if (content[i] == '\r' && content[i+1] == '\n') {
                      lineCount++;
                  }
              }
          } else {
              lineCount = -1;
          }
          return lineCount;
      }
  
      /**
       * Returns size of message, ie headers and content. Current implementation
       * actually returns number of characters in headers plus number of bytes
       * in the internal content byte array.
       */
      public int getMessageSize() throws MessagingException {
          int contentSize = content.length;
          int headerSize = 0;
          Enumeration e = getAllHeaderLines();
          while (e.hasMoreElements()) {
              headerSize += ((String)e.nextElement()).length();
          }
          return headerSize + contentSize;
      }
  
      public String getContentType() throws MessagingException {
          if (message == null) {
              loadMessage();
          }
          return message.getContentType();
      }
  
      public boolean isMimeType(String mimeType) throws MessagingException {
          if (message == null) {
              loadMessage();
          }
          return message.isMimeType(mimeType);
      }
  
      public String getDisposition() throws MessagingException {
          if (message == null) {
              loadMessage();
          }
          return message.getDisposition();
      }
  
      public String getEncoding() throws MessagingException {
          if (message == null) {
              loadMessage();
          }
          return message.getEncoding();
      }
  
      public String getContentID() throws MessagingException {
          if (message == null) {
              loadMessage();
          }
          return message.getContentID();
      }
  
      public String getContentMD5() throws MessagingException {
          if (message == null) {
              loadMessage();
          }
          return message.getContentMD5();
      }
  
      public String getDescription() throws MessagingException {
          if (message == null) {
              loadMessage();
          }
          return message.getDescription();
      }
  
      public String[] getContentLanguage() throws MessagingException {
          if (message == null) {
              loadMessage();
          }
          return message.getContentLanguage();
      }
  
      public String getMessageID() throws MessagingException {
          if (message == null) {
              loadMessage();
          }
          return message.getMessageID();
      }
  
      public String getFileName() throws MessagingException {
          if (message == null) {
              loadMessage();
          }
          return message.getFileName();
      }
  
      public InputStream getInputStream() throws IOException, MessagingException {
          if (message == null) {
              return source.getInputStream();
          } else {
              return message.getInputStream();
          }
      }
  
      public DataHandler getDataHandler() throws MessagingException {
          if (message == null) {
              loadMessage();
          }
          return message.getDataHandler();
      }
  
      public Object getContent() throws IOException, MessagingException {
          if (message == null) {
              loadMessage();
          }
          return message.getContent();
      }
  
      public String[] getHeader(String name) throws MessagingException {
          if (message == null) {
              loadMessage();
          }
          return message.getHeader(name);
      }
  
      public String getHeader(String name, String delimiter) throws MessagingException {
          if (message == null) {
              loadMessage();
          }
          return message.getHeader(name, delimiter);
      }
  
      public Enumeration getAllHeaders() throws MessagingException {
          if (message == null) {
              loadMessage();
          }
          return message.getAllHeaders();
      }
  
      public Enumeration getMatchingHeaders(String[] names) throws MessagingException {
          if (message == null) {
              loadMessage();
          }
          return message.getMatchingHeaders(names);
      }
  
      public Enumeration getNonMatchingHeaders(String[] names) throws MessagingException {
          if (message == null) {
              loadMessage();
          }
          return message.getNonMatchingHeaders(names);
      }
  
      public Enumeration getAllHeaderLines() throws MessagingException {
          if (message == null) {
              loadMessage();
          }
          return message.getAllHeaderLines();
      }
  
      public Enumeration getMatchingHeaderLines(String[] names) throws MessagingException {
          if (message == null) {
              loadMessage();
          }
          return message.getMatchingHeaderLines(names);
      }
  
      public Enumeration getNonMatchingHeaderLines(String[] names) throws MessagingException {
          if (message == null) {
              loadMessage();
          }
          return message.getNonMatchingHeaderLines(names);
      }
  
      public Flags getFlags() throws MessagingException {
          if (message == null) {
              loadMessage();
          }
          return message.getFlags();
      }
  
      public boolean isSet(Flags.Flag flag) throws MessagingException {
          if (message == null) {
              loadMessage();
          }
          return message.isSet(flag);
      }
  
  
  
      /**
       * Writes content only, ie not headers, to the specified outputstream.
       */
      public void writeContentTo(OutputStream outs)
              throws java.io.IOException, MessagingException {
          int size = content.length; // size of byte array
          int chunk = 1000; //arbitrary choice - ideas welcome
          int pointer = 0;
          while (pointer < size) {
              if ((size - pointer) > chunk) {
                  outs.write(content, pointer, chunk);
              } else {
                  outs.write(content, pointer, size-pointer);
              }
              pointer += chunk;
          }
      }
  
  
  
  
  
  
  
  
  
  
  
  
      /**
       * Various writer methods
       */
      public void setFrom(Address address) throws MessagingException {
          if (message == null) {
              loadMessage();
          }
          modified = true;
          message.setFrom(address);
      }
  
      public void setFrom() throws MessagingException {
          if (message == null) {
              loadMessage();
          }
          modified = true;
          message.setFrom();
      }
  
      public void addFrom(Address[] addresses) throws MessagingException {
          if (message == null) {
              loadMessage();
          }
          modified = true;
          message.addFrom(addresses);
      }
  
      public void setRecipients(Message.RecipientType type, Address[] addresses) throws MessagingException {
          if (message == null) {
              loadMessage();
          }
          modified = true;
          message.setRecipients(type, addresses);
      }
  
      public void addRecipients(Message.RecipientType type, Address[] addresses) throws MessagingException {
          if (message == null) {
              loadMessage();
          }
          modified = true;
          message.addRecipients(type, addresses);
      }
  
      public void setReplyTo(Address[] addresses) throws MessagingException {
          if (message == null) {
              loadMessage();
          }
          modified = true;
          message.setReplyTo(addresses);
      }
  
      public void setSubject(String subject) throws MessagingException {
          if (message == null) {
              loadMessage();
          }
          modified = true;
          message.setSubject(subject);
      }
  
      public void setSubject(String subject, String charset) throws MessagingException {
          if (message == null) {
              loadMessage();
          }
          modified = true;
          message.setSubject(subject, charset);
      }
  
      public void setSentDate(Date d) throws MessagingException {
          if (message == null) {
              loadMessage();
          }
          modified = true;
          message.setSentDate(d);
      }
  
      public void setDisposition(String disposition) throws MessagingException {
          if (message == null) {
              loadMessage();
          }
          modified = true;
          message.setDisposition(disposition);
      }
  
      public void setContentID(String cid) throws MessagingException {
          if (message == null) {
              loadMessage();
          }
          modified = true;
          message.setContentID(cid);
      }
  
      public void setContentMD5(String md5) throws MessagingException {
          if (message == null) {
              loadMessage();
          }
          modified = true;
          message.setContentMD5(md5);
      }
  
      public void setDescription(String description) throws MessagingException {
          if (message == null) {
              loadMessage();
          }
          modified = true;
          message.setDescription(description);
      }
  
      public void setDescription(String description, String charset) throws MessagingException {
          if (message == null) {
              loadMessage();
          }
          modified = true;
          message.setDescription(description, charset);
      }
  
      public void setContentLanguage(String[] languages) throws MessagingException {
          if (message == null) {
              loadMessage();
          }
          modified = true;
          message.setContentLanguage(languages);
      }
  
      public void setFileName(String filename) throws MessagingException {
          if (message == null) {
              loadMessage();
          }
          modified = true;
          message.setFileName(filename);
      }
  
      public void setDataHandler(DataHandler dh) throws MessagingException {
          if (message == null) {
              loadMessage();
          }
          modified = true;
          message.setDataHandler(dh);
      }
  
      public void setContent(Object o, String type) throws MessagingException {
          if (message == null) {
              loadMessage();
          }
          modified = true;
          message.setContent(o, type);
      }
  
      public void setText(String text) throws MessagingException {
          if (message == null) {
              loadMessage();
          }
          modified = true;
          message.setText(text);
      }
  
      public void setText(String text, String charset) throws MessagingException {
          if (message == null) {
              loadMessage();
          }
          modified = true;
          message.setText(text, charset);
      }
  
      public void setContent(Multipart mp) throws MessagingException {
          if (message == null) {
              loadMessage();
          }
          modified = true;
          message.setContent(mp);
      }
  
      public Message reply(boolean replyToAll) throws MessagingException {
          if (message == null) {
              loadMessage();
          }
          modified = true;
          return message.reply(replyToAll);
      }
  
      public void setHeader(String name, String value) throws MessagingException {
          if (message == null) {
              loadMessage();
          }
          modified = true;
          message.setHeader(name, value);
      }
  
      public void addHeader(String name, String value) throws MessagingException {
          if (message == null) {
              loadMessage();
          }
          modified = true;
          message.addHeader(name, value);
      }
  
      public void removeHeader(String name) throws MessagingException {
          if (message == null) {
              loadMessage();
          }
          modified = true;
          message.removeHeader(name);
      }
  
      public void addHeaderLine(String line) throws MessagingException {
          if (message == null) {
              loadMessage();
          }
          message.addHeaderLine(line);
      }
  
      public void setFlags(Flags flag, boolean set) throws MessagingException {
          if (message == null) {
              loadMessage();
          }
          modified = true;
          message.setFlags(flag, set);
      }
  
      public void saveChanges() throws MessagingException {
          if (message == null) {
              loadMessage();
          }
          modified = true;
          message.saveChanges();
      }
  }
  
  
  
  1.1                  jakarta-james/proposals/noparse-mimemessage/java/org/apache/james/imapserver/BaseCommand.java
  
  Index: BaseCommand.java
  ===================================================================
  /*
   * Copyright (C) The Apache Software Foundation. All rights reserved.
   *
   * This software is published under the terms of the Apache Software License
   * version 1.1, a copy of which has been included with this distribution in
   * the LICENSE file.
   */
  package org.apache.james.imapserver;
  
  import java.io.IOException;
  import java.io.OutputStream;
  import java.io.PrintWriter;
  import java.util.*;
  import javax.mail.MessagingException;
  import javax.mail.internet.InternetHeaders;
  import org.apache.avalon.framework.logger.AbstractLoggable;
  import org.apache.james.AccessControlException;
  import org.apache.james.AuthorizationException;
  import org.apache.james.BaseConnectionHandler;
  //import org.apache.james.core.EnhancedMimeMessage;
  
  
  /**
   * Provides methods useful for IMAP command objects.
   *
   * References: rfc 2060, rfc 2193, rfc 2221
   * @author <a href="mailto:charles@benett1.demon.co.uk">Charles Benett</a>
   * @version 0.1 on 17 Jan 2001
   */
  
  public abstract class BaseCommand
      extends BaseConnectionHandler {
  
      //mainly to switch on stack traces and catch responses;
      private static final boolean DEEP_DEBUG = true;
  
      /**
       * Turns a protocol-compliant string representing a message sequence
       * number set into a List of integers. Use of the wildcard * (star) relies
       * on contiguous property of msns.
       *
       * @param rawSet the IMAP protocol compliant string to be decoded
       * @param exists the number of messages in this mailbox
       * @returns a List of Integers, one per message in set
       */
      protected List decodeSet( String rawSet, int exists ) throws IllegalArgumentException {
          if (rawSet == null) {
              getLogger().debug("Null argument in decodeSet");
              throw new IllegalArgumentException("Null argument");
          } else if (rawSet.equals("")) {
              getLogger().debug("Empty argument in decodeSet");
              throw new IllegalArgumentException("Empty string argument");
          }
          getLogger().debug(" decodeSet called for: " + rawSet);
          List response = new ArrayList();
          int checkComma = rawSet.indexOf(",");
          if (checkComma == -1) {
              int checkColon = rawSet.indexOf(":");
              if (checkColon == -1) {
                  Integer seqNum = new Integer(rawSet.trim());
                  if (seqNum.intValue() < 1) {
                      throw new IllegalArgumentException("Not a positive integer");
                  } else {
                      response.add(seqNum);
                  }
              } else {
                  Integer firstNum = new Integer(rawSet.substring(0, checkColon));
                  int first = firstNum.intValue();
                  Integer lastNum;
                  int last;
                  if (rawSet.indexOf("*") != -1) {
                      last = exists;
                      lastNum = new Integer(last);
                  } else {
                      lastNum = new Integer(rawSet.substring(checkColon + 1));
                      last = lastNum.intValue();
                  }
                  if (first < 1 || last < 1) {
                      throw new IllegalArgumentException("Not a positive integer");
                  } else if (first < last) {
                      response.add(firstNum);
                      for (int i = (first + 1); i < last; i++) {
                          response.add(new Integer(i));
                      }
                      response.add(lastNum);
                  } else if (first == last) {
                      response.add(firstNum);
                  } else {
                      throw new IllegalArgumentException("Not an increasing range");
                  }
              }
  
          } else {
              try {
                  String firstRawSet = rawSet.substring(0, checkComma);
                  String secondRawSet = rawSet.substring(checkComma + 1);
                  response.addAll(decodeSet(firstRawSet, exists));
                  response.addAll(decodeSet(secondRawSet, exists));
              } catch (IllegalArgumentException e) {
                  getLogger().debug("Wonky arguments in: " + rawSet + " " + e);
                  throw e;
              }
          }
          return response;
      }
  
      /**
       * Turns a protocol-compliant string representing a uid set into a
       * List of integers. Where the string requests ranges or uses the * (star)
       * wildcard, the results are uids that exist in the mailbox. This
       * minimizes attempts to refer to non-existent messages.
       *
       * @param rawSet the IMAP protocol compliant string to be decoded
       * @param uidsList List of uids of messages in mailbox
       * @returns a List of Integers, one per message in set
       */
      protected List decodeUIDSet( String rawSet, List uidsList )
          throws IllegalArgumentException {
          if (rawSet == null) {
              getLogger().debug("Null argument in decodeSet");
              throw new IllegalArgumentException("Null argument");
          } else if (rawSet.equals("")) {
              getLogger().debug("Empty argument in decodeSet");
              throw new IllegalArgumentException("Empty string argument");
          }
          getLogger().debug(" decodeUIDSet called for: " + rawSet);
          Iterator it = uidsList.iterator();
          while (it.hasNext()) {
              getLogger().info ("uids present : " + (Integer)it.next() );
          }
          List response = new ArrayList();
          int checkComma = rawSet.indexOf(",");
          if (checkComma == -1) {
              int checkColon = rawSet.indexOf(":");
              if (checkColon == -1) {
                  Integer seqNum = new Integer(rawSet.trim());
                  if (seqNum.intValue() < 1) {
                      throw new IllegalArgumentException("Not a positive integer");
                  } else {
                      response.add(seqNum);
                  }
              } else {
                  Integer firstNum = new Integer(rawSet.substring(0, checkColon));
                  int first = firstNum.intValue();
  
                  Integer lastNum;
                  if (rawSet.indexOf("*") == -1) {
                      lastNum = new Integer(rawSet.substring(checkColon + 1));
                  } else {
                      lastNum = (Integer)uidsList.get(uidsList.size()-1);
                  }
                  int last;
                  last = lastNum.intValue();
                  if (first < 1 || last < 1) {
                      throw new IllegalArgumentException("Not a positive integer");
                  } else if (first < last) {
                      response.add(firstNum);
                      Collection uids;
                      if(uidsList.size() > 50) {
                          uids = new HashSet(uidsList);
                      } else {
                          uids = uidsList;
                      }
                      for (int i = (first + 1); i < last; i++) {
                          Integer test = new Integer(i);
                          if (uids.contains(test)) {
                              response.add(test);
                          }
                      }
                      response.add(lastNum);
  
                  } else if (first == last) {
                      response.add(firstNum);
                  } else {
                      throw new IllegalArgumentException("Not an increasing range");
                  }
  
              }
  
          } else {
              try {
                  String firstRawSet = rawSet.substring(0, checkComma);
                  String secondRawSet = rawSet.substring(checkComma + 1);
                  response.addAll(decodeUIDSet(firstRawSet, uidsList));
                  response.addAll(decodeUIDSet(secondRawSet, uidsList));
              } catch (IllegalArgumentException e) {
                  getLogger().debug("Wonky arguments in: " + rawSet + " " + e);
                  throw e;
              }
          }
          return response;
      }
  }
  
  
  
  1.1                  jakarta-james/proposals/noparse-mimemessage/java/org/apache/james/imapserver/CommandFetch.java
  
  Index: CommandFetch.java
  ===================================================================
  /*
   * Copyright (C) The Apache Software Foundation. All rights reserved.
   *
   * This software is published under the terms of the Apache Software License
   * version 1.1, a copy of which has been included with this distribution in
   * the LICENSE file.
   */
  package org.apache.james.imapserver;
  
  import java.io.IOException;
  import java.io.OutputStream;
  import java.io.PrintWriter;
  import java.util.*;
  import javax.mail.MessagingException;
  import javax.mail.internet.InternetHeaders;
  import org.apache.james.AccessControlException;
  import org.apache.james.AuthorizationException;
  import org.apache.james.core.MimeMessageWrapper;
  
  /**
   * Implements the IMAP FETCH command for a given ImapRequest.
   *
   * References: rfc 2060, rfc 2193, rfc 2221
   * @author <a href="mailto:charles@benett1.demon.co.uk">Charles Benett</a>
   * @version 0.1 on 17 Jan 2001
   */
  public class CommandFetch
      extends BaseCommand {
  
      //mainly to switch on stack traces and catch responses;
      private static final boolean DEEP_DEBUG = true;
  
      private static final String OK = "OK";
      private static final String NO = "NO";
      private static final String BAD = "BAD";
      private static final String UNTAGGED = "*";
      private static final String SP = " ";
  
      private StringTokenizer commandLine;
      private boolean useUIDs;
      private ACLMailbox currentMailbox;
      private String commandRaw;
      private PrintWriter out;
      private OutputStream outs;
      private String tag;
      private String user;
      private SingleThreadedConnectionHandler caller;
      private String currentFolder;
  
      /**
       * Debugging method - will probably disappear
       */
      public void setRequest(ImapRequest request) {
          commandLine = request.getCommandLine();
          useUIDs = request.useUIDs();
          currentMailbox = request.getCurrentMailbox();
          commandRaw = request.getCommandRaw();
          tag = request.getTag();
          currentFolder = request.getCurrentFolder();
  
          caller = request.getCaller();
          out = caller.getPrintWriter();
          outs = caller.getOutputStream();
          user = caller.getUser();
      }
  
      /**
       * Implements IMAP fetch commands given an ImapRequest.
       * This implementation attempts to satisfy the fetch command with the
       * smallest objects deserialized from storage.
       * <p>Warning - maybecome service(ImapRequest request)
       * <p>Not yet complete - no partial (octet-counted or sub-parts) fetches.
       */
      public void service() {
          // decode the message set
          List set;
          List uidsList = null;
          String setArg = commandLine.nextToken();
          if (useUIDs) {
              uidsList = currentMailbox.listUIDs(user);
              set = decodeUIDSet(setArg, uidsList);
          } else {
              set = decodeSet(setArg, currentMailbox.getExists());
          }
          if (DEEP_DEBUG) {
              getLogger().debug("Fetching message set of size: " + set.size());
          }
          String firstFetchArg = commandLine.nextToken();
          int pos =  commandRaw.indexOf(firstFetchArg);
          //int pos = commandRaw.indexOf(setArg) + setArg.length() + 1;
          String fetchAttrsRaw = null;
          if (firstFetchArg.startsWith("(")) { //paranthesised fetch attrs
              fetchAttrsRaw = commandRaw.substring(pos + 1, commandRaw.lastIndexOf(")"));
          } else {
              fetchAttrsRaw = commandRaw.substring(pos);
          }
  
          if (DEEP_DEBUG) {
              getLogger().debug("Found fetchAttrsRaw: " + fetchAttrsRaw);
          }
          // decode the fetch attributes
          List fetchAttrs = new ArrayList();
          StringTokenizer fetchTokens = new StringTokenizer(fetchAttrsRaw);
          while (fetchTokens.hasMoreTokens()) {
              String  attr = fetchTokens.nextToken();
              if (attr.indexOf("(") == -1 ) { //not the start of a fields list
                  fetchAttrs.add(attr);
              } else {
                  StringBuffer attrWithFields = new StringBuffer();
                  attrWithFields.append(fetchAttrs.remove(fetchAttrs.size() -1));
                  attrWithFields.append(" " + attr);
                  boolean endOfFields = false;
                  while (! endOfFields) {
                      String field = fetchTokens.nextToken();
                      attrWithFields.append(" " + field);
                      if (field.indexOf(")") != -1) {
                          endOfFields = true;
                      }
                  }
                  fetchAttrs.add(attrWithFields.toString());
              }
          }
  
  
          // convert macro fetch commands to basic commands
          for(int k = 0; k < fetchAttrs.size(); k++) {
              String arg = (String)fetchAttrs.get(k);
              if (arg.equalsIgnoreCase("FAST")) {
                  fetchAttrs.add("FLAGS");
                  fetchAttrs.add("INTERNALDATE");
                  fetchAttrs.add("RFC822.SIZE");
              } else if (arg.equalsIgnoreCase("ALL")) {
                  fetchAttrs.add("FLAGS");
                  fetchAttrs.add("INTERNALDATE");
                  fetchAttrs.add("RFC822.SIZE");
                  fetchAttrs.add("ENVELOPE");
              } else if (arg.equalsIgnoreCase("FULL")) {
                  fetchAttrs.add("FLAGS");
                  fetchAttrs.add("INTERNALDATE");
                  fetchAttrs.add("RFC822.SIZE");
                  fetchAttrs.add("ENVELOPE");
                  fetchAttrs.add("BODY");
              }
              getLogger().debug("Found fetchAttrs: " + arg);
          }
  
          try {
              for (int i = 0; i < set.size(); i++) {
                  Integer uidObject = null;
                  int uid = 0;
                  int msn = 0;
                  if (useUIDs) {
                      uidObject = (Integer)set.get(i);
                      uid = uidObject.intValue();
                      msn = uidsList.indexOf(uidObject) + 1;
                  } else {
                      msn = ((Integer)set.get(i)).intValue();
                  }
                  MessageAttributes  attrs = null;
                  String flags = null;
                  //EnhancedMimeMessage msg = null;
                  MimeMessageWrapper msg = null;
                  String response = UNTAGGED + SP + msn + SP + "FETCH (";
                  boolean responseAdded = false;
                  Iterator it = fetchAttrs.iterator();
                  while(it.hasNext()) {
                      String  arg = (String) it.next();
                      // commands that only need flags object
                      if (arg.equalsIgnoreCase("FLAGS")) {
                          if (flags == null) {
                              if (useUIDs) {
                                  flags = currentMailbox.getFlagsUID(uid, user);
                              } else {
                                  flags = currentMailbox.getFlags(msn, user);
                              }
                          }
                          if (flags == null) { // bad
                              out.println(tag + SP + msn + SP + NO + "Error retrieving message flags.");
                              getLogger().error("Retrieved null flags for msn:" + msn);
                              return;
                          }
                          if (responseAdded) {
                              response += SP + "FLAGS " + flags ;
                          } else {
                              response +=  "FLAGS " + flags ;
                              responseAdded = true;
                          }
                      }
                      // command that only need MessageAttributes object
                      else if (arg.equalsIgnoreCase("INTERNALDATE")) {
                          if (attrs == null) {
                              if (useUIDs) {
                                  attrs = currentMailbox.getMessageAttributesUID(uid, user);
                              } else {
                                  attrs = currentMailbox.getMessageAttributes(msn, user);
                              }
                          }
                          if (attrs == null) { // bad
                              out.println(tag + SP + msn + SP + NO + "Error retrieving message attributes.");
                              getLogger().error("Retrieved null attributes for msn:" + msn);
                              return;
                          }
                          if (responseAdded) {
                              response += SP + "INTERNALDATE \""
                                  + attrs.getInternalDateAsString() + "\")" ;
                          } else {
                              response += "INTERNALDATE \""
                                  + attrs.getInternalDateAsString() + "\")" ;
                              responseAdded = true;
                          }
                      } else if (arg.equalsIgnoreCase("RFC822.SIZE")) {
                          if (attrs == null) {
                              if (useUIDs) {
                                  attrs = currentMailbox.getMessageAttributesUID(uid, user);
                              } else {
                                  attrs = currentMailbox.getMessageAttributes(msn, user);
                              }
                          }
                          if (attrs == null) { // bad
                              out.println(tag + SP + msn + SP + NO + "Error retrieving message attributes.");
                              getLogger().error("Retrieved null attributes for msn:" + msn);
                              return;
                          }
                          if (responseAdded) {
                              response += SP + "RFC822.SIZE " + attrs.getSize();
                          } else {
                              response +=  "RFC822.SIZE " + attrs.getSize();
                              responseAdded = true;
                          }
                      } else   if (arg.equalsIgnoreCase("ENVELOPE")) {
                          if (attrs == null) {
                              if (useUIDs) {
                                  attrs = currentMailbox.getMessageAttributesUID(uid, user);
                              } else {
                                  attrs = currentMailbox.getMessageAttributes(msn, user);
                              }
                          }
                          if (attrs == null) { // bad
                              out.println(tag + SP + msn + SP + NO + "Error retrieving message attributes.");
                              getLogger().error("Retrieved null attributes for msn:" + msn);
                              return;
                          }
                          if (responseAdded) {
                              response += SP + "ENVELOPE " + attrs.getEnvelope();
                          } else {
                              response +=  "ENVELOPE " + attrs.getEnvelope();
                              responseAdded = true;
                          }
                      } else if (arg.equalsIgnoreCase("BODY")) {
                          if (attrs == null) {
                              if (useUIDs) {
                                  attrs = currentMailbox.getMessageAttributesUID(uid, user);
                              } else {
                                  attrs = currentMailbox.getMessageAttributes(msn, user);
                              }
                          }
                          if (attrs == null) { // bad
                              out.println(tag + SP + msn + SP + NO + "Error retrieving message attributes.");
                              getLogger().error("Retrieved null attributes for msn:" + msn);
                              return;
                          }
                          if (responseAdded) {
                              response += SP + "BODY " + attrs.getBodyStructure();
                          } else {
                              response +=  "BODY " + attrs.getBodyStructure();
                              responseAdded = true;
                          }
                      } else if (arg.equalsIgnoreCase("BODYSTRUCTURE")) {
                          if (attrs == null) {
                              if (useUIDs) {
                                  attrs = currentMailbox.getMessageAttributesUID(uid, user);
                              } else {
                                  attrs = currentMailbox.getMessageAttributes(msn, user);
                              }
                          }
                          if (attrs == null) { // bad
                              out.println(tag + SP + msn + SP + NO + "Error retrieving message attributes.");
                              getLogger().error("Retrieved null attributes for msn:" + msn);
                              return;
                          }
                          if (responseAdded) {
                              response += SP + "BODYSTRUCTURE "+ attrs.getBodyStructure();
                          } else {
                              response +=  "BODYSTRUCTURE "+ attrs.getBodyStructure();
                              responseAdded = true;
                          }
                      }  else if (arg.equalsIgnoreCase("UID")) {
                          if (!useUIDs){
                              if (attrs == null) {
                                  attrs = currentMailbox.getMessageAttributes(msn, user);
                                  uid = attrs.getUID();
                              }
                              if (attrs == null) { // bad
                                  out.println(tag + SP + msn + SP + NO + "Error retrieving message attributes.");
                                  getLogger().error("Retrieved null attributes for msn:" + msn);
                                  return;
                              }
  
                              if (responseAdded) {
                                  response += SP + "UID "+ uid;
                              } else {
                                  response +=  "UID "+ uid;
                                  responseAdded = true;
                              }
                          } // don't duplicate on UID FETCH requests
                      }
                      // commands that can be satisifed with just top-level headers of message and flags
                      else if (arg.equalsIgnoreCase("BODY[HEADER]")
                               || arg.equalsIgnoreCase("BODY.PEEK[HEADER]")) {
                          if (responseAdded) { // unlikely
                              if (useUIDs) {
                                  response += " UID " + uid + ")";
                              } else {
                                  response += ")";
                              }
                              out.println(response);
                              getLogger().debug("Sending: " + response);
                          }
                          InternetHeaders headers = null;
                          if (useUIDs) {
                              headers = currentMailbox.getInternetHeadersUID(uid, user);
                          } else {
                              headers = currentMailbox.getInternetHeaders(msn, user);
                          }
                          if (headers == null) { // bad
                              out.println(tag + SP + msn + SP + NO + "Error retrieving message.");
                              getLogger().error("Retrieved null headers for msn:" + msn);
                              return;
                          }
                          if (flags == null) {
                              if (useUIDs) {
                                  flags = currentMailbox.getFlagsUID(uid, user);
                              } else {
                                  flags = currentMailbox.getFlags(msn, user);
                              }
                          }
                          response = UNTAGGED + SP + msn + SP + "FETCH (";
                          //if (arg.equalsIgnoreCase("BODY[Header]")) {
                          response += "BODY[HEADER] ";
                          //} else {
                          //    response += "BODY.PEEK[HEADER] ";
                          //}
                          Enumeration enum = headers.getAllHeaderLines();
                          List lines = new ArrayList();
                          int count = 0;
                          while (enum.hasMoreElements()) {
                              String line = (String)enum.nextElement();
                              count += line.length() + 2;
                              lines.add(line);
                          }
                          response += "{" + (count + 2) + "}";
                          out.println(response);
                          getLogger().debug("Sending: " + response);
                          Iterator lit = lines.iterator();
                          while (lit.hasNext()) {
                              String line = (String)lit.next();
                              out.println(line);
                              getLogger().debug("Sending: " + line);
                          }
                          out.println();
                          getLogger().debug("Sending blank line");
                          if (useUIDs) {
                              out.println(  " UID " + uid + ")");
                              getLogger().debug("Sending: UID " + uid + ")");
                          } else {
                              out.println( ")" );
                              getLogger().debug("Sending: )");
                          }
                          if (! arg.equalsIgnoreCase("BODY.PEEK[HEADER]")) {
                              try { // around setFlags()
                                  if (flags.indexOf("Seen") == -1 ) {
                                      String newflags;
                                      if (useUIDs) {
                                          currentMailbox.setFlagsUID(uid, user, "+flags (\\Seen)");
                                          newflags = currentMailbox.getFlagsUID(uid, user);
                                          out.println(UNTAGGED + SP + msn + SP + "FETCH (FLAGS "
                                                      + newflags + " UID " + uid +")");
                                      } else {
                                          currentMailbox.setFlags(msn, user, "+flags (\\Seen)");
                                          newflags = currentMailbox.getFlags(msn, user);
                                          out.println(UNTAGGED + SP + msn + SP + "FETCH (FLAGS "
                                                      + newflags + ")");
                                      }
                                  }
                              } catch (AccessControlException ace) {
                                  getLogger().error("Exception storing flags for message: " + ace);
                              } catch (AuthorizationException aze) {
                                  getLogger().error("Exception storing flags for message: " + aze);
                              } catch (Exception e) {
                                  getLogger().error("Unanticipated exception storing flags for message: " + e);
                              }
                          }
                          response = UNTAGGED + SP + msn + SP + "FETCH (";
                          responseAdded = false;
                      } else if (arg.toUpperCase().startsWith("BODY[HEADER.FIELDS")
                                 || arg.toUpperCase().startsWith("BODY.PEEK[HEADER.FIELDS")) {
                          if (responseAdded) {
                              if (useUIDs) {
                                  response += " UID " + uid + ")";
                              } else {
                                  response += ")";
                              }
                              out.println(response);
                              getLogger().debug("Sending: " + response);
                          }
                          InternetHeaders headers = null;
                          if (useUIDs) {
                              headers = currentMailbox.getInternetHeadersUID(uid, user);
                          } else {
                              headers = currentMailbox.getInternetHeaders(msn, user);
                          }
                          if (headers == null) { // bad
                              out.println(tag + SP + msn + SP + NO + "Error retrieving message.");
                              getLogger().error("Retrieved null headers for msn:" + msn);
                              return;
                          }
                          if (flags == null) {
                              if (useUIDs) {
                                  flags = currentMailbox.getFlagsUID(uid, user);
                              } else {
                                  flags = currentMailbox.getFlags(msn, user);
                              }
                          }
                          boolean not = (commandRaw.toUpperCase().indexOf("HEADER.FIELDS.NOT") != -1);
                          boolean peek = (commandRaw.toUpperCase().indexOf("PEEK") != -1);
                          response = UNTAGGED + SP + msn + SP + "FETCH (BODY" ;
                          //if (peek) {response += ".PEEK";}
                          if (not) {
                              response += "[HEADER.FIELDS.NOT (";
                          } else {
                              response += "[HEADER.FIELDS (";
                          }
                          responseAdded = false;
                          //int h = commandRaw.indexOf("[");
                          int left = arg.indexOf("(");
                          int right = arg.indexOf(")");
                          String fieldsRequested = arg.substring(left + 1, right);
                          response += fieldsRequested + ")] ";
                          ArrayList fields = new ArrayList();
                          if (fieldsRequested.indexOf(" ") == -1) { //only one field
                              fields.add(fieldsRequested);
                          } else {
                              StringTokenizer tok = new StringTokenizer(fieldsRequested);
                              while (tok.hasMoreTokens()) {
                                  fields.add((String)tok.nextToken());
                              }
                          }
                          Iterator it2 = fields.iterator();
                          while (it2.hasNext()) {
                              getLogger().debug("request for field: " + (String)it2.next());
                          }
                          String[] names = (String[])fields.toArray(new String[fields.size()]);
                          Enumeration enum = null;
                          if (not) {
                              enum = headers.getNonMatchingHeaderLines(names);
                          } else {
                              enum = headers.getMatchingHeaderLines(names);
                          }
                          List lines = new ArrayList();
                          int count = 0;
                          while (enum.hasMoreElements()) {
                              String line = (String)enum.nextElement();
                              count += line.length() + 2;
                              lines.add(line);
                          }
                          response += "{" + (count + 2) + "}";
                          out.println(response);
                          getLogger().debug("Sending: " + response);
                          Iterator lit = lines.iterator();
                          while (lit.hasNext()) {
                              String line = (String)lit.next();
                              out.println(line);
                              getLogger().debug("Sending: " + line);
                          }
                          out.println();
                          if (useUIDs) {
                              out.println(  " UID " + uid + ")");
                          } else {
                              out.println(")");
                          }
                          if (! peek) {
                              if (flags.indexOf("Seen") == -1 ) {
                                  try {
                                      String newflags;
                                      if (useUIDs) {
                                          currentMailbox.setFlagsUID(uid, user, "+flags (\\Seen)");
                                          newflags = currentMailbox.getFlagsUID(uid, user);
                                          out.println(UNTAGGED + SP + msn + SP + "FETCH (FLAGS "
                                                      + newflags + " UID " + uid +")");
                                      } else {
                                          currentMailbox.setFlags(msn, user, "+flags (\\Seen)");
                                          newflags = currentMailbox.getFlags(msn, user);
                                          out.println(UNTAGGED + SP + msn + SP + "FETCH (FLAGS "
                                                      + newflags + ")");
                                      }
                                  } catch (AccessControlException ace) {
                                      getLogger().error("Exception storing flags for message: " + ace);
                                  } catch (AuthorizationException aze) {
                                      getLogger().error("Exception storing flags for message: " + aze);
                                  } catch (Exception e) {
                                      getLogger().error("Unanticipated exception storing flags for message: " + e);
                                  }
                              }
                          }
                          response = UNTAGGED + SP + msn + SP + "FETCH (";
                          responseAdded = false;
                      }
                      // Calls to underlying MimeMessage
                      else if (arg.equalsIgnoreCase("RFC822")
                               || arg.equalsIgnoreCase("BODY[]")
                               || arg.equalsIgnoreCase("BODY.PEEK[]")) {
                          if (responseAdded) { // unlikely
                              if (useUIDs) {
                                  response += " UID " + uid + ")";
                              } else {
                                  response += ")";
                              }
                              out.println(response);
                          }
                          if (msg == null) { // probably
                              if (useUIDs) {
                                  msg = currentMailbox.retrieveUID(uid, user);
                              } else {
                                  msg = currentMailbox.retrieve(msn, user);
                              }
                          }
                          if (flags == null) {
                              if (useUIDs) {
                                  flags = currentMailbox.getFlagsUID(uid, user);
                              } else {
                                  flags = currentMailbox.getFlags(msn, user);
                              }
                          }
                          if (msg == null) { // bad
                              out.println(tag + SP + msn + SP + BAD + "Error retrieving message.");
                              getLogger().error("Retrieved null message");
                              return;
                          }
                          try {
                              int size = msg.getMessageSize();
                              if (arg.equalsIgnoreCase("RFC822")) {
                                  out.println(UNTAGGED + SP + msn + SP + "FETCH ( RFC822 {" + size + "}");
                              } else {
                                  out.println(UNTAGGED + SP + msn + SP + "FETCH ( BODY[] {" + size + "}");
                              }
                              msg.writeTo(outs);
                              if (useUIDs) {
                                  out.println(" UID " + uid + ")");
                              } else {
                                  out.println(")");
                              }
                              if (! arg.equalsIgnoreCase("BODY.PEEK[]")) {
                                  if (flags.indexOf("Seen") == -1 ) {
                                      String newflags;
                                      if (useUIDs) {
                                          currentMailbox.setFlagsUID(uid, user, "+flags (\\Seen)");
                                          newflags = currentMailbox.getFlagsUID(uid, user);
                                          out.println(UNTAGGED + SP + msn + SP + "FETCH (FLAGS "
                                                      + newflags + " UID " + uid +")");
                                      } else {
                                          currentMailbox.setFlags(msn, user, "+flags (\\Seen)");
                                          newflags = currentMailbox.getFlags(msn, user);
                                          out.println(UNTAGGED + SP + msn + SP + "FETCH (FLAGS "
                                                      + newflags + ")");
                                      }
                                  }
                              }
                          } catch (MessagingException me) {
                              out.println(UNTAGGED + SP + NO + SP + "Error retrieving message");
                              getLogger().error("Exception retrieving message: " + me);
                          } catch (IOException ioe) {
                              out.println(UNTAGGED + SP + NO + SP + "Error retrieving message");
                              getLogger().error("Exception sending message: " + ioe);
                          } catch (Exception e) {
                              out.println(UNTAGGED + SP + NO + SP + "Error retrieving message");
                              getLogger().error("Unanticipated exception retrieving message: " + e);
                          }
                          response = UNTAGGED + SP + msn + SP + "FETCH (";
                          responseAdded = false;
                      } else if (arg.equalsIgnoreCase("RFC822.TEXT")
                                 || arg.equalsIgnoreCase("BODY[TEXT]")
                                 || arg.equalsIgnoreCase("BODY.PEEK[TEXT]")) {
                          if (responseAdded) { // unlikely
                              if (useUIDs) {
                                  response += " UID " + uid + ")";
                              } else {
                                  response += ")";
                              }
                              out.println(response);
                          }
                          if (msg == null) { // probably
                              if (useUIDs) {
                                  msg = currentMailbox.retrieveUID(uid, user);
                              } else {
                                  msg = currentMailbox.retrieve(msn, user);
                              }
                          }
                          if (flags == null) {
                              if (useUIDs) {
                                  flags = currentMailbox.getFlagsUID(uid, user);
                              } else {
                                  flags = currentMailbox.getFlags(msn, user);
                              }
                          }
                          if (msg == null) { // bad
                              out.println(tag + SP + msn + SP + NO + "Error retrieving message.");
                              getLogger().error("Retrieved null message");
                              return;
                          }
                          try {
                              int size = msg.getSize();
                              if (arg.equalsIgnoreCase("RFC822.TEXT")) {
                                  out.println(UNTAGGED + SP + msn + SP + "FETCH ( RFC822.TEXT {" + size + "}");
                              } else {
                                  out.println(UNTAGGED + SP + msn + SP + "FETCH ( BODY[TEXT] {" + size + "}");
                              }
                              msg.writeContentTo(outs);
                              if (useUIDs) {
                                  out.println(  " UID " + uid + ")");
                              } else {
                                  out.println(")");
                              }
                              if (! arg.equalsIgnoreCase("BODY.PEEK[TEXT]")) {
                                  if (flags.indexOf("Seen") == -1 ) {
                                      String newflags;
                                      if (useUIDs) {
                                          currentMailbox.setFlagsUID(uid, user, "+flags (\\Seen)");
                                          newflags = currentMailbox.getFlagsUID(uid, user);
                                          out.println(UNTAGGED + SP + msn + SP + "FETCH (FLAGS "
                                                      + newflags + " UID " + uid +")");
                                      } else {
                                          currentMailbox.setFlags(msn, user, "+flags (\\Seen)");
                                          newflags = currentMailbox.getFlags(msn, user);
                                          out.println(UNTAGGED + SP + msn + SP + "FETCH (FLAGS "
                                                      + newflags + ")");
                                      }
                                  }
                              }
                          } catch (MessagingException me) {
                              out.println(UNTAGGED + SP + NO + SP + "Error retrieving message");
                              getLogger().error("Exception retrieving message: " + me);
                          } catch (IOException ioe) {
                              out.println(UNTAGGED + SP + NO + SP + "Error retrieving message");
                              getLogger().error("Exception sending message: " + ioe);
                          } catch (Exception e) {
                              out.println(UNTAGGED + SP + NO + SP + "Error retrieving message");
                              getLogger().error("Unanticipated exception retrieving message: " + e);
                          }
                          response = UNTAGGED + SP + msn + SP + "FETCH (";
                          responseAdded = false;
                      }   else { //unrecognised or not yet implemented
                          if (responseAdded) {
                              if (useUIDs) {
                                  response += " UID " + uid + ")";
                              } else {
                                  response += ")";
                              }
                              out.println(response);
                          }
                          out.println(tag + SP + NO + SP
                                      + "FETCH attribute not recognized");
                          getLogger().error("Received: " + arg + " as argument to fetch");
                          return;
                      }
                  } // end while loop
                  if (responseAdded) {
                      if (useUIDs) {
                          response += " UID " + uid + ")";
                      } else {
                          response += ")";
                      }
                      out.println(response);
                  }
              } // end for loop
  
              out.println(tag + SP + OK + SP + "FETCH completed");
              caller.checkSize();
              return;
          } catch (AccessControlException ace) {
              out.println(tag + SP + NO + SP + "No such mailbox");
              caller.logACE(ace);
              return;
          } catch (AuthorizationException aze) {
              out.println(tag + SP + NO + SP
                          + "You do not have the rights to read from mailbox: " + currentFolder);
              caller.logAZE(aze);
              return ;
          } catch (Exception e) {
              out.println(tag + SP + NO + SP
                          + "Unknown server error.");
              getLogger().error("Exception expunging mailbox " + currentFolder + " by user " + user + " was : " + e);
              if (DEEP_DEBUG) {e.printStackTrace();}
              return;
          }
      }
  }
  
  
  
  1.1                  jakarta-james/proposals/noparse-mimemessage/java/org/apache/james/imapserver/CommandStore.java
  
  Index: CommandStore.java
  ===================================================================
  /*
   * Copyright (C) The Apache Software Foundation. All rights reserved.
   *
   * This software is published under the terms of the Apache Software License
   * version 1.1, a copy of which has been included with this distribution in
   * the LICENSE file.
   */
  package org.apache.james.imapserver;
  
  import java.io.IOException;
  import java.io.OutputStream;
  import java.io.PrintWriter;
  import java.util.*;
  import javax.mail.MessagingException;
  import javax.mail.internet.InternetHeaders;
  import org.apache.james.AccessControlException;
  import org.apache.james.AuthorizationException;
  //import org.apache.james.core.EnhancedMimeMessage;
  
  /**
   * Implements the IMAP FETCH command for a given ImapRequest.
   *
   * References: rfc 2060, rfc 2193, rfc 2221
   * @author <a href="mailto:charles@benett1.demon.co.uk">Charles Benett</a>
   * @version 0.1 on 17 Jan 2001
   */
  public class CommandStore
      extends BaseCommand {
      //mainly to switch on stack traces and catch responses;
      private static final boolean DEEP_DEBUG = true;
  
      private static final String OK = "OK";
      private static final String NO = "NO";
      private static final String BAD = "BAD";
      private static final String UNTAGGED = "*";
      private static final String SP = " ";
  
      private StringTokenizer commandLine;
      private boolean useUIDs;
      private ACLMailbox currentMailbox;
      private String commandRaw;
      private PrintWriter out;
      private OutputStream outs;
      private String tag;
      private String user;
      private SingleThreadedConnectionHandler caller;
      private String currentFolder;
  
      /**
       * Debugging method - will probably disappear
       */
      public void setRequest(ImapRequest request) {
          commandLine = request.getCommandLine();
          useUIDs = request.useUIDs();
          currentMailbox = request.getCurrentMailbox();
          commandRaw = request.getCommandRaw();
          tag = request.getTag();
          currentFolder = request.getCurrentFolder();
  
          caller = request.getCaller();
          out = caller.getPrintWriter();
          outs = caller.getOutputStream();
          user = caller.getUser();
      }
  
      /**
       * Implements IMAP store commands given an ImapRequest.
       * <p>Warning - maybecome service(ImapRequest request)
       */
      public void service() {
          List set;
          List uidsList = null;
          if (useUIDs) {
              set = decodeUIDSet(commandLine.nextToken(),
                                 currentMailbox.listUIDs(user));
          } else {
              set = decodeSet(commandLine.nextToken(),
                              currentMailbox.getExists());
          }
          StringBuffer buf = new StringBuffer();
          while (commandLine.hasMoreTokens()) {
              buf.append(commandLine.nextToken());
          }
          String request = buf.toString();
          try {
              for (int i = 0; i < set.size(); i++) {
                  if (useUIDs) {
                      Integer uidObject = (Integer)set.get(i);
                      int uid = uidObject.intValue();
                      if (currentMailbox.setFlagsUID(uid, user, request)) {
                          if (request.toUpperCase().indexOf("SILENT") == -1) {
                              String newflags
                                  = currentMailbox.getFlagsUID(uid, user);
                              int msn = uidsList.indexOf(uidObject) + 1;
                              out.println(UNTAGGED + SP + msn + SP
                                          + "FETCH (FLAGS " + newflags
                                          + " UID " + uid + ")");
                          } else {
                                  //silent
                          }
                      } else {
                          //failed
                          out.println(tag + SP + NO + SP
                                      + "Unable to store flags for message: "
                                      + uid);
                      }
                  } else {
                      int msn = ((Integer)set.get(i)).intValue();
                      if (currentMailbox.setFlags(msn, user, request)) {
                          if (request.toUpperCase().indexOf("SILENT") == -1) {
                              String newflags
                                  = currentMailbox.getFlags(msn, user);
                              out.println(UNTAGGED + SP + msn + SP
                                          + "FETCH (FLAGS " + newflags + ")");
                          } else {
                                  //silent
                          }
                      } else {
                          //failed
                          out.println(tag + SP + NO + SP
                                      + "Unable to store flags for message: "
                                      + msn);
                      }
                  }
              }
              caller.checkSize();
              out.println(tag + SP + OK + SP + "STORE completed");
  
          } catch (AccessControlException ace) {
              out.println(tag + SP + NO + SP + "No such mailbox");
              caller.logACE(ace);
              return;
          } catch (AuthorizationException aze) {
              out.println(tag + SP + NO + SP
                          + "You do not have the rights to store those flags");
              caller.logAZE(aze);
              return;
          } catch (IllegalArgumentException iae) {
              out.println(tag + SP + BAD + SP
                          + "Arguments to store not recognised.");
              getLogger().error("Unrecognised arguments for STORE by user "  + user
                           + " with " + commandRaw);
              return;
          }
          return;
      }
  }
  
  
  
  1.1                  jakarta-james/proposals/noparse-mimemessage/java/org/apache/james/imapserver/FileMailbox.java
  
  Index: FileMailbox.java
  ===================================================================
  /*
   * Copyright (C) The Apache Software Foundation. All rights reserved.
   *
   * This software is published under the terms of the Apache Software License
   * version 1.1, a copy of which has been included with this distribution in
   * the LICENSE file.
   */
  package org.apache.james.imapserver;
  
  import java.io.*;
  import java.net.*;
  import java.util.*;
  import javax.mail.*;
  import javax.mail.internet.*;
  import org.apache.avalon.framework.component.ComponentManager;
  import org.apache.avalon.framework.configuration.Configuration;
  import org.apache.avalon.framework.configuration.ConfigurationException;
  import org.apache.avalon.framework.context.Context;
  import org.apache.avalon.framework.logger.AbstractLoggable;
  import org.apache.james.AccessControlException;
  import org.apache.james.AuthorizationException;
  import org.apache.james.Constants;
  import org.apache.james.core.MimeMessageWrapper;
  import org.apache.james.services.UsersRepository;
  import org.apache.mailet.Mail;
  
  /**
   * Object representing an IMAP4rev1 mailbox (folder) on a local file system.
   * The mailbox contains messages, message attributes and an access control
   * list.
   *
   * <p> from Interface Mailbox
   *
   * <p>Mailbox Related Flags (rfc2060 name attributes)
   * <br>    \Noinferiors   It is not possible for any child levels of hierarchy
   * to exist under this name; no child levels exist now and none can be created
   * in the future.
   * <br>    \Noselect      It is not possible to use this name as a selectable
   * mailbox.
   * <br>    \Marked        The mailbox has been marked "interesting" by the
   * server; the mailbox probably contains messages that have been added since
   * the last time the mailbox was selected.
   * <br>      \Unmarked      The mailbox does not contain any additional
   * messages since the last time the mailbox was selected.
   *
   * <p>Message related flags.
   * <br>The flags allowed per message are specific to each mailbox.
   * <br>The minimum list (rfc2060 system flags) is:
   * <br> \Seen       Message has been read
   * <br> \Answered   Message has been answered
   * <br> \Flagged    Message is "flagged" for urgent/special attention
   * <br> \Deleted    Message is "deleted" for removal by later EXPUNGE
   * <br> \Draft      Message has not completed composition (marked as a draft).
   * <br> \Recent     Message is "recently" arrived in this mailbox.  This
   * session is the first session to have been notified about this message;
   * subsequent sessions will not see \Recent set for this message.  This flag
   * can not be altered by the client.
   *  <br>            If it is not possible to determine whether or not this
   * session is the first session to be notified about a message, then that
   * message SHOULD be considered recent.
   *
   * <p> From interface ACL </p>
   *
   * The standard rights in RFC2086 are:
   * <br>l - lookup (mailbox is visible to LIST/LSUB commands)
   * <br>r - read (SELECT the mailbox, perform CHECK, FETCH, PARTIAL, SEARCH,
   * COPY from mailbox)
   * <br>s - keep seen/unseen information across sessions (STORE SEEN flag)
   * <br>w - write (STORE flags other than SEEN and DELETED)
   * <br>i - insert (perform APPEND, COPY into mailbox)
   * <br>p - post (send mail to submission address for mailbox, not enforced by
   * IMAP4 itself)
   * <br>c - create (CREATE new sub-mailboxes in any implementation-defined
   * hierarchy)
   * <br>d - delete (STORE DELETED flag, perform EXPUNGE)
   * <br>a - administer (perform SETACL)
   *
   * <p> Serialization. Not all fields are serialized. Dispose() must be called to
   * write serialized fiels to disc before finishing with this object.
   *
   * <p> Deserialization. On recover from disc, configure, compose,
   * contextualize and reInit must be called before object is ready for use.
   *
   * Reference: RFC 2060
   * @author <a href="mailto:charles@benett1.demon.co.uk">Charles Benett</a>
   * @version 0.1 on 14 Dec 2000
   */
  public class FileMailbox
      extends AbstractLoggable
      implements ACLMailbox, Serializable {
  
      public static final String MAILBOX_FILE_NAME = "mailbox.mbr";
  
      private static final String MESSAGE_EXTENSION = ".msg";
      private static final String ATTRIBUTES_EXTENSION = ".att";
      private static final String FLAGS_EXTENSION = ".flags";
  
      private static final int NUMBER_OF_RIGHTS = 9;
      // Map string identities to boolean[NUMBER_OF_RIGHTS] arrays of rights
      //The rights are, in order: l,r,s,w,i,p,c,d,a
      private static final int LOOKUP = 0;
      private static final int READ = 1;
      private static final int KEEP_SEEN = 2;
      private static final int WRITE = 3;
      private static final int INSERT = 4;
      private static final int POST = 5;
      private static final int CREATE = 6;
      private static final int DELETE = 7;
      private static final int ADMIN = 8;
      private static final boolean[] NO_RIGHTS
          = {false, false, false, false, false, false, false, false, false};
      private static final boolean[] ALL_RIGHTS
          = {true, true, true, true, true, true, true, true, true};
      private static final String DENY_ACCESS = "Access denied by ACL";
      private static final String DENY_AUTH = "Action not authorized for: ";
      private static final String OPTIONAL_RIGHTS = "l r s w i p c d a";
      private static final char[] DELETE_MODS
          = {'-', 'l', 'r', 's', 'w', 'i', 'p', 'c', 'd', 'a'};
  
      /* Transient fields - reInit must be called on recover from disc. */
      private transient Context context;
      private transient Configuration conf;
      private transient ComponentManager compMgr;
      private transient UsersRepository localUsers;
      private transient HashSet listeners;
  
      /* Serialized fileds */
      private String path; // does not end with File.separator
      private File directory ;
      private String owner;
      private String absoluteName;
      private Map acl;
      private String name;
      private int uidValidity;
      private int highestUID;
      private int mailboxSize; // octets
      private boolean inferiorsAllowed;
      private boolean marked;
      private boolean notSelectableByAnyone;
  
      // The message sequence number of a msg is its index in List sequence + 1
      private List sequence; //List of UIDs of messages in mailbox
  
      // Set of UIDs of messages with recent flag set
      private Set recentMessages;
  
      // Set of UIDs of messages with delete flag set
      private Set messagesForDeletion;
  
      //map of user String to Integer uid, 0 for no unseen messages
      private Map oldestUnseenMessage;
  
      public void configure(Configuration conf) throws ConfigurationException {
          this.conf = conf;
      }
  
      public void contextualize(Context context) {
          this.context = context;
      }
  
      public void compose(ComponentManager comp) {
          compMgr = comp;
      }
  
      public void prepareMailbox(String user, String absName,
                                 String initialAdminUser) {
          if (user != null && (user.length() > 0)) {
              owner = user;
          } else {
              throw new RuntimeException("Incorrect user argument  for a"
                                         + " FileMailbox constructor.");
          }
          if (absName != null && (absName.length() > 0)) {
              absoluteName = absName;
          } else {
              throw new RuntimeException("Incorrect absoluteName argument for a"
                                         + " FileMailbox constructor.");
          }
          if (initialAdminUser != null && (initialAdminUser.length() > 0)) {
              acl = new HashMap(7);
              acl.put(initialAdminUser, ALL_RIGHTS);
              //acl = new SimpleACL(initialAdminUser);
          } else {
              throw new RuntimeException("Incorrect initialAdminUser argument"
                                         + " for a FileMailbox constructor.");
          }
      }
  
      public void initialize() throws Exception {
          uidValidity = 1;
          highestUID = 0;
          mailboxSize = 0;
          inferiorsAllowed = true;
          marked = false;
          notSelectableByAnyone = false;
          oldestUnseenMessage = new HashMap();
          listeners = new HashSet();
          sequence = new ArrayList();
          recentMessages = new HashSet();
          messagesForDeletion = new HashSet();
          getLogger().info("FileMailbox init for " + absoluteName);
          localUsers = (UsersRepository)compMgr.lookup("org.apache.james.services.UsersRepository");
          String rootPath
              = conf.getChild("mailboxRepository").getValue();
          if (!rootPath.endsWith(File.separator)) {
              rootPath = rootPath + File.separator;
          }
          Configuration namespaces = conf.getChild("namespaces");
          String namespaceToken = namespaces.getAttribute("token");
          String privateNamespace
              = namespaces.getChild("privateNamespace").getValue();
          String privateNamespaceSeparator
              = namespaces.getChild("privateNamespace").getAttribute("separator");
          String sharedNamespace
              = namespaces.getChild("sharedNamespace").getValue();
          String sharedNamespaceSeparator
              = namespaces.getChild("sharedNamespace").getAttribute("separator");
          if (absoluteName.startsWith(privateNamespace)) {
              String path1
                  = absoluteName.substring(privateNamespace.length()
                                           + privateNamespaceSeparator.length()
                                           + owner.length());
              path = rootPath + owner
                  + path1.replace(privateNamespaceSeparator.charAt(0),
                                  File.separatorChar);
              name = absoluteName.substring(absoluteName.lastIndexOf(privateNamespaceSeparator) + 1);
              if (name.equals(owner)) {
                  name = "";
              }
          } else if (absoluteName.startsWith(sharedNamespace)) {
              String path2 = absoluteName.substring(namespaceToken.length());
              path = rootPath + path2.replace(sharedNamespaceSeparator.charAt(0),
                                              File.separatorChar);
              name = absoluteName.substring(absoluteName.lastIndexOf(sharedNamespaceSeparator) + 1);
              if (name.equals(sharedNamespace)) {
                  name = "";
              }
          } else {
              getLogger().error("FileMailbox init error: unknown namespace - "
                           + absoluteName);
              throw new RuntimeException("Unknown namespace for absoluteName"
                                         +" argument for a FileMailbox"
                                         +" constructor." + absoluteName);
          }
          //Check for writable directory
          File mailboxDir = new File(path);
          if (mailboxDir.exists()) {
              throw new RuntimeException("Error: Attempt to overwrite mailbox directory at " + path);
          } else if (! mailboxDir.mkdir()){
              throw new RuntimeException("Error: Cannot create mailbox directory at " + path);
          } else if (!mailboxDir.canWrite()) {
              throw new RuntimeException("Error: Cannot write to directory at " + path);
          }
          writeMailbox();
          getLogger().info("FileMailbox init complete: " + absoluteName);
      }
  
      /**
       * Re-initialises mailbox after reading from file-system. Cannot assume that this is the same instance of James that wrote it.
       *
       * <p> Contract is that re-init must be called after configure, contextualize, compose.
       */
      public void reinitialize() throws Exception {
          listeners = new HashSet();
          getLogger().info("FileMailbox reInit for " + absoluteName);
          localUsers = (UsersRepository)compMgr.lookup("org.apache.james.services.UsersRepository");
          String rootPath
              = conf.getChild("mailboxRepository").getValue();
          if (!rootPath.endsWith(File.separator)) {
              rootPath = rootPath + File.separator;
          }
      }
  
      /**
       * Call when host has finished with a mailbox.
       * This is really a stop rather than destroy.
       * Writes current mailbox object to disc.
       */
      public void dispose() {
          writeMailbox();
          getLogger().info("FileMailbox object destroyed: " + absoluteName);
      }
  
      /**
       * Returns true once this Mailbox has been checkpointed.
       * This implementation just writes its mailbox record to disc.  Unless something is
       * broken all messages added, amended or removed from this mailbox will have been
       * handled by this object.
       * <br> This implementation always returns true.
       *
       * @returns true
       */
      public synchronized  boolean checkpoint() {
          writeMailbox();
          getLogger().info("FileMailbox: " + absoluteName + " checkpointed.");
          return true;
      }
  
      /**
       * Remove \Recent flag from all messages in mailbox. Should be called
       * whenever a user session finishes.
       */
      public synchronized void unsetRecent() {
          Iterator it = recentMessages.iterator();
          while(it.hasNext()) {
              Integer uidObj =(Integer)it.next();
              int uid = uidObj.intValue();
              Flags flags = readFlags(uid);
              if (flags != null) {
                  flags.setRecent(false);
                  writeFlags(uid, flags);
              }
          }
          recentMessages.clear();
      }
  
  
      // Methods that don't involve the ACL ------------------
  
      /**
       * Returns name of this mailbox relative to its parent in the mailbox
       * hierarchy.
       * Example: 'NewIdeas'
       *
       * @returns String name of mailbox relative to its immeadiate parent in
       * the mailbox hierarchy.
       */
      public String getName() {
          return name;
      }
  
      /**
       * Returns absolute, that is user-independent, hierarchical name of
       * mailbox (including namespace)
       * Example: '#mail.fred.flintstone.apache.James.NewIdeas'
       *
       * @returns String name of mailbox in absolute form
       */
      public String getAbsoluteName() {
          return absoluteName;
      }
  
      /** Returns namespace starting with namespace token.
       * Example: '#mail'
       *
       * @returns String containing user-independent namespace of this mailbox.
       */
      //  public String getNamespace();
  
      /** Returns true if the argument is the relative or absolute name of
       * this mailbox
       *
       * @param name possible name for this Mailbox
       * @returns true if name matches either getName() or getAbsoluteName()
       */
      public boolean matchesName(String testName) {
          return (name == testName || name == absoluteName);
      }
  
      /**
       * Returns the current unique id validity value of this mailbox.
       *
       * @returns int current 32 bit unique id validity value of this mailbox
       */
      public int getUIDValidity() {
          return uidValidity;
      }
  
      /**
       * Returns the 32 bit uid available for the next message.
       *
       * @returns int the next UID that would be used.
       */
      public int getNextUID() {
          return highestUID + 1;
      }
  
      /**
       * Returns mailbox size in octets. Should only include actual messages
       * and not any implementation-specific data, such as message attributes.
       *
       * @returns int mailbox size in octets
       */
      public synchronized int getMailboxSize() {
          return mailboxSize;
      }
  
      /**
       * Indicates if child folders may be created. It does not indicate which
       * users can create child folders.
       *
       * @returns boolean TRUE if inferiors aree allowed
       */
      public boolean getInferiorsAllowed() {
          return inferiorsAllowed;
      }
  
      /**
       * Indicates that messages have been added since this mailbox was last
       * selected by any user.
       *
       * @returns boolean TRUE if new messages since any user last selected
       * mailbox
       */
      public  synchronized  boolean isMarked() {
          return marked;
      }
  
      /**
       * Returns all flags supported by this mailbox.
       * e.g. \Answered \Deleted
       *
       * @returns String a space seperated list of message flags which are
       * supported by this mailbox.
       */
      public String getSupportedFlags() {
          return SYSTEM_FLAGS;
      }
      /**
       * Indicates no of messages with \Recent flag set
       *
       * @returns int no of messages with \Recent flag set
       */
      public  synchronized  int getRecent() {
          return recentMessages.size();
      }
  
      /**
       * Indicates the oldest unseen message for the specified user.
       *
       * @returns int Message Sequence Number of first message without \Seen
       * flag set for this User.  0 means no unseen messages in this mailbox.
       */
      public  synchronized  int getOldestUnseen(String user) {
          int response = 0;
          if (oldestUnseenMessage.containsKey(user)) {
              Integer uidObj = ((Integer)oldestUnseenMessage.get(user));
              if (! (uidObj.intValue() == 0)) {
                  response = sequence.indexOf(uidObj);
              }
          } else {
              if (sequence.size() > 0) {
                  response = 1;
                  oldestUnseenMessage.put(user, (Integer)sequence.get(0));
              } else {
                  oldestUnseenMessage.put(user, (new Integer(0)));
              }
          }
          return response;
      }
  
      /**
       * Indicates number of messages in folder
       *
       * @returns int number of messages
       */
      public  synchronized  int getExists() {
          return sequence.size();
      }
  
      /**
       * Indicates the number of  unseen messages for the specified user.
       *
       * @returns int number of messages without \Seen flag set for this User.
       */
      public int getUnseen(String user) {
          if (oldestUnseenMessage.containsKey(user)) {
              int response = 0; //indicates no unseen messages
              Integer uidObj = ((Integer)oldestUnseenMessage.get(user));
              int oldUID = uidObj.intValue();
              if (oldUID != 0) {
                  ListIterator lit
                      = sequence.listIterator(sequence.indexOf(uidObj));
                  while (lit.hasNext() ) {
                      int uid = ((Integer)lit.next()).intValue();
                      Flags flags = readFlags(uid);
                      if (!flags.isSeen(user)) {
                          response ++;
                      }
                  }
              }
              return response;
          } else { // user has never selected mailbox
              return sequence.size();
          }
      }
  
      /** Mailbox Events are used to inform registered listeners of events in the Mailbox.
       * E.g. if mail is delivered to an Inbox or if another user appends/ deletes a message.
       */
      public synchronized void addMailboxEventListener(MailboxEventListener mel) {
          listeners.add(mel);
      }
  
  
      public synchronized void removeMailboxEventListener(MailboxEventListener mel) {
          listeners.remove(mel);
      }
  
      /**
       * Mark this mailbox as not selectable by anyone.
       * Example folders at the roots of hierarchies, e. #mail for each user.
       *
       * @param state true if folder is not selectable by anyone
       */
      public void setNotSelectableByAnyone(boolean state) {
          notSelectableByAnyone = state;
      }
  
      public boolean isNotSelectableByAnyone() {
          return notSelectableByAnyone;
      }
  
      // Methods for the embedded ACL ------------------------
  
      /**
       * Store access rights for a given identity.
       * The setter is the user setting the rights, the identifier is the user
       * whose rights are affected.
       * The setter and identifier arguments must be non-null and non-empty.
       * The modification argument must be non-null and follow the syntax of the
       * third argument to a SETACL command.
       * If the modification argument is an empty string, that identifier is
       * removed from the ACL, if currently present.
       *
       * @param setter String representing user attempting to set rights, must
       * be non-null and non-empty
       * @param identity String representing user whose rights are being set,
       * must be non-null and non-empty
       * @param modification String representing the change in rights, following
       * the syntax specified in rfc 2086. Note a blank string means delete all
       * rights for given identity.
       * @returns true if requested modification succeeded. A return value of
       * false means an error other than an AccessControlException or
       * AuthorizationException.
       * @throws AccessControlException if setter does not have lookup rights for
       * this mailbox (ie they should not know this mailbox exists).
       * @throws AuthorizationException if specified setter does not have the
       * administer right (ie the right to write ACL rights), or if the result
       * of this method would leave no identities with admin rights.
       */
      public boolean setRights(String setter, String identifier,
                               String modification)
          throws AccessControlException, AuthorizationException {
  
          boolean[] settersRights = (boolean[]) acl.get(setter);
          if (settersRights == null
              || (settersRights[LOOKUP] == false)) {
              throw new AccessControlException(DENY_ACCESS);
          } else if (settersRights[ADMIN] == false) {
              throw new AuthorizationException(DENY_AUTH + setter);
          }
          boolean[] existingRights = (boolean[]) acl.get(identifier);
          char[] mods = modification.toCharArray();
          if (mods.length == 0) { // means delete all
              mods = DELETE_MODS;
          }
          if(existingRights == null) {
              if ( mods[0] == REMOVE_RIGHTS ) {
                  return false;
              } else {
                  existingRights = new boolean[NUMBER_OF_RIGHTS];
                  System.arraycopy(NO_RIGHTS, 0, existingRights, 0,
                                   NUMBER_OF_RIGHTS);
              }
          }
  
          boolean change;
          boolean[] rights = new boolean[NUMBER_OF_RIGHTS];
  
          if (mods[0] == ADD_RIGHTS) {
              change = true;
              System.arraycopy(existingRights, 0, rights, 0,
                               NUMBER_OF_RIGHTS);
          } else if (mods[0] == REMOVE_RIGHTS) {
              change = false;
              System.arraycopy(existingRights, 0, rights, 0,
                               NUMBER_OF_RIGHTS);
          } else {                                             // means replace
              System.arraycopy(NO_RIGHTS, 0, rights, 0,
                               NUMBER_OF_RIGHTS);
              char[] new_mods = new char[mods.length + 1];
              System.arraycopy(mods, 0, new_mods, 1, mods.length);
              mods = new_mods;
              change = true;
          }
  
          for (int i=1; i <mods.length; i++) {
              switch(mods[i]) {
              case LOOKUP_RIGHTS: rights[LOOKUP] = change;
                  break;
              case READ_RIGHTS: rights[READ] = change;
                  break;
              case KEEP_SEEN_RIGHTS: rights[KEEP_SEEN] = change;
                  break;
              case WRITE_RIGHTS: rights[WRITE] = change;
                  break;
              case INSERT_RIGHTS: rights[INSERT] = change;
                  break;
              case POST_RIGHTS: rights[POST] = change;
                  break;
              case CREATE_RIGHTS: rights[CREATE] = change;
                  break;
              case DELETE_RIGHTS: rights[DELETE] = change;
                  break;
              case ADMIN_RIGHTS: rights[ADMIN] = change;
                  break;
              default: return false;
              }
          }
  
          //  All rights above lookup require lookup
          if(rights[LOOKUP] == false  &&  !Arrays.equals(rights, NO_RIGHTS)) {
              return false;
          }
          // Each right requires all the rights before it.
          int count = 0;
          for (int i=1; i< NUMBER_OF_RIGHTS; i++) {
              if(rights[i-1] ^ rights[i]) {
                  count++;
              }
          }
          switch (count) {
          case 0:                              // now Admin or deleted
              if (rights[ADMIN]) {
                  acl.put(identifier, rights);
                  break;
              } else {
                  if (otherAdmin(identifier)) {
                      acl.remove(identifier);
                      break;
                  } else {
                      return false;
                  }
              }
          case 2:              // not allowed
              return false;
          case 1:             // not Admin, check there remains an Admin
              // Iterator namesIt = acl.keySet().iterator();
              //boolean otherAdmin = false;
              //while(namesIt.hasNext() && !otherAdmin) {
              //String name = (String)namesIt.next();
              //if (name != identifier) {
              //    boolean[] otherRights = (boolean[]) acl.get(name);
              //        otherAdmin = otherRights[ADMIN];
              //}
              //}
              if (otherAdmin(identifier)) {
                  acl.put(identifier, rights);
                  break;
              } else {
                  return false;
              }
          default:             // not allowed
              return false;
          }
          writeMailbox();
          return true;
      }
  
      /**
       * Check there is a person other than identifier who has Admin rights.
       */
      private boolean otherAdmin(String identifier) {
          Iterator namesIt = acl.keySet().iterator();
          boolean result = false;
          while(namesIt.hasNext() && !result) {
              String name = (String)namesIt.next();
              if (!name.equals(identifier)) {
                  boolean[] otherRights = (boolean[]) acl.get(name);
                  result = otherRights[ADMIN];
              }
          }
          return result;
      }
  
      /**
       * Retrieve access rights for a specific identity.
       *
       * @param getter String representing user attempting to get the rights,
       * must be non-null and non-empty
       * @param identity String representing user whose rights are being got,
       * must be non-null and non-empty
       * @returns String of rights usingrfc2086 syntax, empty if identity has no
       * rights in this mailbox.
       * @throws AccessControlException if getter does not have lookup rights for
       * this mailbox (ie they should not know this mailbox exists).
       * @throws AuthorizationException if implementation does not wish to expose
       * ACL for this identity to this getter.
       */
      public String getRights(String getter, String identity)
          throws AccessControlException, AuthorizationException {
          boolean[] gettersRights = (boolean[])  acl.get(getter);
          if (gettersRights == null
              || (gettersRights[LOOKUP] == false)) {
              throw new AccessControlException(DENY_ACCESS);
          } else if (!getter.equals(identity) && gettersRights[ADMIN] == false) {
              throw new AuthorizationException(DENY_AUTH + getter);
          }
          boolean[] rights = (boolean[]) acl.get(identity);
          if (rights == null) {
              return null;
          } else {
              StringBuffer buf = new StringBuffer(NUMBER_OF_RIGHTS);
              for (int i = 0; i<NUMBER_OF_RIGHTS; i++) {
                  if (rights[i]) {
                      buf.append(RIGHTS[i]);
                  }
              }
              return buf.toString();
          }
      }
  
      /**
       * Retrieves a String of one or more <identity space rights> who have
       * rights in this ACL
       *
       * @param getter String representing user attempting to get the rights,
       * must be non-null and non-empty
       * @returns String of rights sets usingrfc2086 syntax
       * @throws AccessControlException if getter does not have lookup rights for
       * this mailbox (ie they should not know this mailbox exists).
       * @throws AuthorizationException if implementation does not wish to expose
       * ACL to this getter.
       */
      public String getAllRights(String getter)
          throws AccessControlException, AuthorizationException {
          boolean[] gettersRights = (boolean[]) acl.get(getter);
          if (gettersRights == null
              || (gettersRights[LOOKUP] == false)) {
              throw new AccessControlException(DENY_ACCESS);
          } else if ( gettersRights[ADMIN] == false) {
              throw new AuthorizationException(DENY_AUTH + getter);
          }
          Iterator namesIt = acl.keySet().iterator();
          StringBuffer response = new StringBuffer(20*acl.size());
          while(namesIt.hasNext()) {
              String name = (String)namesIt.next();
              response.append("<" + name + " ");
              boolean[] rights = (boolean[]) acl.get(name);
              for (int i = 0; i<NUMBER_OF_RIGHTS; i++) {
                  if (rights[i]) {
                      response.append(RIGHTS[i]);
                  }
              }
              response.append("> ");
          }
  
          return response.toString();
      }
  
      /**
       * Retrieve rights which will always be granted to the specified identity.
       *
       * @param getter String representing user attempting to get the rights,
       * must be non-null and non-empty
       * @param identity String representing user whose rights are being got,
       * must be non-null and non-empty
       * @returns String of rights usingrfc2086 syntax, empty if identity has no
       * guaranteed rights in this mailbox.
       * @throws AccessControlException if getter does not have lookup rights for
       * this mailbox (ie they should not know this mailbox exists).
       * @throws AuthorizationException if implementation does not wish to expose
       * ACL for this identity to this getter.
       */
      public String getRequiredRights(String getter, String identity)
          throws AccessControlException, AuthorizationException {
          boolean[] gettersRights = (boolean[]) acl.get(getter);
          if (gettersRights == null
              || (gettersRights[LOOKUP] == false)) {
              throw new AccessControlException(DENY_ACCESS);
          } else if (!getter.equals(identity) && gettersRights[ADMIN] == false) {
              throw new AuthorizationException(DENY_AUTH + getter);
          }
  
          return "\"\"";
      }
  
      /**
       * Retrieve rights which may be granted to the specified identity.
       * @param getter String representing user attempting to get the rights,
       * must be non-null and non-empty
       * @param identity String representing user whose rights are being got,
       * must be non-null and non-empty
       * @returns String of rights usingrfc2086 syntax, empty if identity has no
       * guaranteed rights in this mailbox.
       * @throws AccessControlException if getter does not have lookup rights for
       * this mailbox (ie they should not know this mailbox exists).
       * @throws AuthorizationException if implementation does not wish to expose
       * ACL for this identity to this getter.
       */
      public String getOptionalRights(String getter, String identity)
          throws AccessControlException, AuthorizationException {
          boolean[] gettersRights = (boolean[]) acl.get(getter);
          if (gettersRights == null
              || (gettersRights[LOOKUP] == false)) {
              throw new AccessControlException(DENY_ACCESS);
          } else if (!getter.equals(identity) && gettersRights[ADMIN] == false) {
              throw new AuthorizationException(DENY_AUTH + getter);
          }
  
          return OPTIONAL_RIGHTS;
      }
  
      /**
       * Helper boolean methods.
       * Provided for cases where you need to check the ACL before selecting the
       * mailbox.
       *
       * @param username String representing user
       * @returns true if user has the requested right.
       * &throws AccessControlException if username does not have lookup rights.
       * (Except for hasLookupRights which just returns false.
       */
      public boolean hasLookupRights(String username) {
          boolean[] usersRights = (boolean[]) acl.get(username);
          return (( usersRights == null || (usersRights[LOOKUP] == false))
                  ? false : true);
      }
  
      public boolean hasReadRights(String username)
          throws AccessControlException {
          boolean[] usersRights = (boolean[]) acl.get(username);
          if (usersRights == null  || (usersRights[LOOKUP] == false)) {
              throw new AccessControlException(DENY_ACCESS);
          }
          return usersRights[READ];
      }
  
      public boolean hasKeepSeenRights(String username)
          throws AccessControlException {
          boolean[] usersRights = (boolean[]) acl.get(username);
          if (usersRights == null  || (usersRights[LOOKUP] == false)) {
              throw new AccessControlException(DENY_ACCESS);
          }
          return usersRights[KEEP_SEEN];
      }
  
      public boolean hasWriteRights(String username)
          throws AccessControlException {
          boolean[] usersRights = (boolean[]) acl.get(username);
          if (usersRights == null  || (usersRights[LOOKUP] == false)) {
              throw new AccessControlException(DENY_ACCESS);
          }
          return usersRights[WRITE];
      }
  
      public boolean hasInsertRights(String username)
          throws AccessControlException {
          boolean[] usersRights = (boolean[]) acl.get(username);
          if (usersRights == null  || (usersRights[LOOKUP] == false)) {
              throw new AccessControlException(DENY_ACCESS);
          }
          return usersRights[INSERT];
      }
  
      public boolean hasCreateRights(String username)
          throws AccessControlException {
          boolean[] usersRights = (boolean[]) acl.get(username);
          if (usersRights == null  || (usersRights[LOOKUP] == false)) {
              throw new AccessControlException(DENY_ACCESS);
          }
          return usersRights[CREATE];
      }
  
      public boolean hasDeleteRights(String username)
          throws AccessControlException {
          boolean[] usersRights = (boolean[]) acl.get(username);
          if (usersRights == null  || (usersRights[LOOKUP] == false)) {
              throw new AccessControlException(DENY_ACCESS);
          }
          return usersRights[DELETE];
      }
  
      public boolean hasAdminRights(String username)
          throws AccessControlException {
          boolean[] usersRights = (boolean[]) acl.get(username);
          if (usersRights == null  || (usersRights[LOOKUP] == false)) {
              throw new AccessControlException(DENY_ACCESS);
          }
          return usersRights[ADMIN];
      }
  
      // Mailbox methods using the ACL ---------------------------
  
      /**
       * Indicates if this folder may be selected by the specified user. Requires
       * user to have at least read rights. It does not indicate whether user
       * can write to mailbox
       *
       * @param username String represnting user
       * @returns boolean TRUE if specified user can Select mailbox.
       * @throws AccessControlException if username does not have lookup rights
       */
      public  synchronized  boolean isSelectable(String username)
          throws AccessControlException {
          return (!notSelectableByAnyone && hasReadRights(username));
      }
  
      /**
       * Indicates if specified user can change any flag on a permanent basis,
       * except for \Recent which can never be changed by a user.
       *
       * @param username String represnting user
       * @returns true if specified user can change all flags permanently.
       */
      public synchronized boolean allFlags(String username)
          throws AccessControlException {
          // relies on implementation that each right implies those
          // before it in list:  l,r,s,w,i,p,c,d,a
          return hasDeleteRights(username);
      }
  
      /**
       * Indicates which flags this user can change permanently. If allFlags()
       * returns true for this user, then this method must have the same return
       * value as getSupportedFlags.
       *
       * @param username String represnting user
       * @returns String a space seperated list of message flags which this user
       * can set permanently
       */
      public  synchronized  String getPermanentFlags(String username)
          throws AccessControlException {
          if (hasDeleteRights(username)) {
              return SYSTEM_FLAGS;
          } else if (hasWriteRights(username)) {
              return "\\Seen \\Answered \\Flagged \\Draft";
          } else if (hasKeepSeenRights(username)) {
              return "\\Seen";
          } else {
              return "";
          }
      }
  
      /**
       * Provides a reference to the access control list for this mailbox.
       *
       * @returns the AccessControlList for this Mailbox
       */
      //   public ACL getACL();
  
      /**
       * Indicates state in which  the mailbox will be opened by specified user.
       * A return value of true indicates Read Only, false indicates Read-Write
       * and an AccessControlException is thrown if user does not have read
       * rights.
       * <p>Implementations decide if Read Only means only lookup and read
       * rights (lr) or lookup, read and keep seen rights (lrs). This may even
       * vary between mailboxes.
       *
       * @param username String represnting user
       * @returns true if specified user can only open the mailbox Read-Only.
       * @throws AccessControlException if the user can not open this mailbox
       * at least Read-Only.
       */
      public synchronized boolean isReadOnly(String username)
          throws AccessControlException {
          return (! hasWriteRights(username));
      }
  
      // Message handling methods ---------------------------
  
      /**
       * Stores a message in this mailbox. User must have insert rights.
       *
       * @param message the MimeMessage to be stored
       * @param username String represnting user
       * @returns boolean true if successful
       * @throws AccessControlException if username does not have lookup rights
       * for this mailbox.
       * @throws AuthorizationException if username has lookup rights but does
       * not have insert rights.
       */
      public synchronized boolean store(MimeMessage message, String username)
          throws AccessControlException, AuthorizationException,
                 IllegalArgumentException {
  
          if (message == null || username == null) {
              getLogger().error("Null argument received in store.");
              throw new IllegalArgumentException("Null argument received in store.");
          }
          if (!hasInsertRights(username)) { //throws AccessControlException
              throw new AuthorizationException("Not authorized to insert.");
          }
  
          SimpleMessageAttributes attrs = new SimpleMessageAttributes();
          try {
              setupLogger(attrs);
              attrs.setAttributesFor(message);
          } catch (javax.mail.MessagingException me) {
              throw new RuntimeException("Exception creating SimpleMessageAttributes: " + me);
          }
          Flags flags = new Flags();
          flags.initialize();
          return store(message, username, attrs, flags);
      }
  
      /**
       * Stores a message in this mailbox, using passed MessageAttributes and
       * Flags. User must have insert rights.
       * <br>Current implementation requires MessageAttributs to be of
       * class SimpleMessageAttributes
       *
       * @param mail the message to be stored
       * @param username String represnting user
       * @param msgAttrs non-null MessageAttributes for use with this Message
       * @returns boolean true if successful
       * @throws AccessControlException if username does not have lookup
       * rights for this mailbox.
       * @throws AuthorizationException if username has lookup rights but does
       * not have insert rights.
       */
      public boolean store(MimeMessage message, String username,
                           MessageAttributes msgAttrs, Flags flags)
          throws AccessControlException, AuthorizationException,
                 IllegalArgumentException {
  
          if (msgAttrs == null || message == null || username == null) {
              getLogger().error("Null argument received in store.");
              throw new IllegalArgumentException("Null argument received in store.");
          }
          if (! (msgAttrs instanceof SimpleMessageAttributes)) {
              getLogger().error("Wrong class for Attributes");
              throw new IllegalArgumentException("Wrong class for Attributes");
          }
          SimpleMessageAttributes attrs = (SimpleMessageAttributes)msgAttrs;
  
          int newUID = ++highestUID;
          attrs.setUID(newUID);
          sequence.add(new Integer(newUID));
          attrs.setMessageSequenceNumber(sequence.size());
  
          BufferedOutputStream outMsg = null;
          ObjectOutputStream outAttrs = null;
  
          try {
              outMsg = new BufferedOutputStream( new FileOutputStream(path + File.separator + newUID + MESSAGE_EXTENSION));
              message.writeTo(outMsg);
              outMsg.close();
              outAttrs = new ObjectOutputStream( new FileOutputStream(path + File.separator + newUID + ATTRIBUTES_EXTENSION));
              outAttrs.writeObject(attrs);
              outAttrs.close();
          } catch(Exception e) {
              getLogger().error("Error writing message to disc: " + e);
              e.printStackTrace();
              throw new
                  RuntimeException("Exception caught while storing Mail: "
                                   + e);
          } finally {
              try {
                  outMsg.close();
                  outAttrs.close();
              } catch (IOException ie) {
                  getLogger().error("Error closing streams: " + ie);
              }
          }
          marked = true;
          if (flags.isRecent()) {
              recentMessages.add(new Integer(newUID));
          }
          if (flags.isDeleted()) {
              messagesForDeletion.add(new Integer(newUID));
          }
          //if (!flags.isSeen(username)) {
          //If a user had no unseen messages, they do, now.
          Iterator it = oldestUnseenMessage.keySet().iterator();
          while (it.hasNext()) {
              String user = (String)it.next();
              if ( ((Integer)oldestUnseenMessage.get(user)).intValue() == -1) {
                  oldestUnseenMessage.put(user, new Integer(newUID));
              }
          }
          //}
          writeFlags(newUID, flags);
          getLogger().info("Mail " + newUID + " written in " + absoluteName);
  
          return true;
      }
  
      /**
       * Retrieves a message given a message sequence number.
       *
       * @param msn the message sequence number
       * @param username String represnting user
       * @returns an  MimeMessageWrapper object containing the message, null if no message with
       * the given msn.
       * @throws AccessControlException if user does not have read rights for
       * this mailbox.
       * @throws AuthorizationException if user has lookup rights but does not
       * have read rights.
       */
      public synchronized MimeMessageWrapper retrieve(int msn, String user)
          throws AccessControlException, AuthorizationException {
          if (!hasReadRights(user)) { //throws AccessControlException
              throw new AuthorizationException("Not authorized to read.");
          }
  
          if (msn > sequence.size()) {
              return null;
          } else {
              int uid = ((Integer)sequence.get(msn - 1)).intValue();
              return retrieveUID(uid, user);
          }
      }
  
  
      /**
       * Retrieves a message given a unique identifier.
       *
       * @param uid the unique identifier of a message
       * @param username String represnting user
       * @returns an MimeMessageWrapper object containing the message, null if no message with
       * the given msn.
       * @throws AccessControlException if user does not have read rights for
       * this mailbox.
       * @throws AuthorizationException if user has lookup rights but does not
       * have read rights.
       */
      public synchronized MimeMessageWrapper retrieveUID(int uid, String user)
          throws AccessControlException, AuthorizationException {
          if (!hasReadRights(user)) { //throws AccessControlException
              throw new AuthorizationException("Not authorized to read.");
          }
          MimeMessageWrapper response = null;
          if (sequence.contains(new Integer(uid))) {
              BufferedInputStream inMsg = null;
              try {
  				MimeMessageFileSource source = new MimeMessageFileSource(path + File.separator + uid + MESSAGE_EXTENSION);
                  response = new MimeMessageWrapper(source);
                  inMsg.close();
              } catch(Exception e) {
                  getLogger().error("Error reading message from disc: " + e);
                  e.printStackTrace();
                  throw new
                      RuntimeException("Exception caught while retrieving Mail: "
                                       + e);
              } finally {
                  try {
                      inMsg.close();
                  } catch (IOException ie) {
                      getLogger().error("Error closing streams: " + ie);
                  }
              }
              getLogger().info("MimeMessageWrapper " + uid + " read from " + absoluteName);
              return response;
          } else {
              return null;
          }
      }
  
      /**
       * Marks a message for deletion given a message sequence number.
       *
       * @param msn the message sequence number
       * @param username String represnting user
       * @returns boolean true if successful.
       * @throws AccessControlException if user does not have read rights for
       * this mailbox.
       * @throws AuthorizationException if user has lookup rights but does not
       * have delete rights.
       */
      public synchronized boolean markDeleted(int msn, String user)
          throws AccessControlException, AuthorizationException {
          if (!hasDeleteRights(user)) { //throws AccessControlException
              throw new AuthorizationException("Not authorized to delete.");
          }
  
          //TBD
          return false;
      }
  
      /**
       * Marks a message for deletion given a unique identifier.
       *
       * @param uidunique identifier
       * @param username String represnting user
       * @returns boolean true if successful, false if failed including no
       * message with the given uid.
       * @throws AccessControlException if user does not have read rights for
       * this mailbox.
       * @throws AuthorizationException if user has lookup rights but does not
       * have delete rights.
       */
      public synchronized boolean markDeletedUID(int uid, String user)
          throws AccessControlException, AuthorizationException {
          if (!hasDeleteRights(user)) { //throws AccessControlException
              throw new AuthorizationException("Not authorized to delete.");
          }
  
          //TBD
          return false;
      }
  
      /**
       * Returns the message attributes for a message.
       *
       * @param msn message sequence number
       * @param username String represnting user
       * @returns MessageAttributes for message, null if no such message.
       * Changing the MessageAttributes object must not affect the actual
       * MessageAttributes.
       * @throws AccessControlException if user does not have read rights for
       * this mailbox.
       * @throws AuthorizationException if user has lookup rights but does not
       * have delete rights.
       */
      public synchronized MessageAttributes getMessageAttributes(int msn, String user)
          throws AccessControlException, AuthorizationException {
          if (!hasReadRights(user)) { //throws AccessControlException
              throw new AuthorizationException("Not authorized to read.");
          }
          if (msn > sequence.size()) {
              return null;
          } else {
              int uid = ((Integer)sequence.get(msn - 1)).intValue();
              return getMessageAttributesUID(uid, user);
          }
      }
  
      /**
       * Returns the message attributes for a message.
       *
       * @param uid unique identifier
       * @param username String represnting user
       * @returns MessageAttributes for message, null if no such message.
       * Changing the MessageAttributes object must not affect the actual
       * MessageAttributes.
       * @throws AccessControlException if user does not have read rights for
       * this mailbox.
       * @throws AuthorizationException if user has lookup rights but does not
       * have delete rights.
       */
      public synchronized MessageAttributes getMessageAttributesUID(int uid, String user)
          throws AccessControlException, AuthorizationException {
          if (!hasReadRights(user)) { //throws AccessControlException
              throw new AuthorizationException("Not authorized to read.");
          }
          SimpleMessageAttributes response = null;
          if (sequence.contains(new Integer(uid))) {
              ObjectInputStream inAttrs = null;
              try {
                  inAttrs = new ObjectInputStream( new FileInputStream(path + File.separator + uid + ATTRIBUTES_EXTENSION));
                  response = (SimpleMessageAttributes)inAttrs.readObject();
                  setupLogger(response);
              } catch(Exception e) {
                  getLogger().error("Error reading attributes from disc: " + e);
                  e.printStackTrace();
                  throw new
                      RuntimeException("Exception caught while retrieving Message attributes: "
                                       + e);
              } finally {
                  try {
                      inAttrs.close();
                  } catch (IOException ie) {
                      getLogger().error("Error closing streams: " + ie);
                  }
              }
              getLogger().info("MessageAttributes for " + uid + " read from " + absoluteName);
              return response;
          } else {
              return null;
          }
      }
  
      /**
       * Updates the attributes of a message.This may be incorporated into setFlags().
       *
       * @param MessageAttributes of a message already in this Mailbox
       * @throws AccessControlException if user does not have read rights for
       * this mailbox.
       * @throws AuthorizationException if user has lookup rights but does not
       * have delete rights.
       */
      public boolean updateMessageAttributes(MessageAttributes attrs, String user)
          throws AccessControlException, AuthorizationException {
          if (!hasKeepSeenRights(user)) { //throws AccessControlException
              throw new AuthorizationException("Not authorized to store flags.");
          }
          int uid = attrs.getUID();
          if (sequence.contains(new Integer(uid))) {
  
              // Really, we should check whether the exact change is authorized.
              ObjectOutputStream outAttrs = null;
              try {
                  outAttrs = new ObjectOutputStream( new FileOutputStream(path + File.separator + uid + ATTRIBUTES_EXTENSION));
                  outAttrs.writeObject(attrs);
                  outAttrs.close();
              } catch(Exception e) {
                  getLogger().error("Error writing message to disc: " + e);
                  e.printStackTrace();
                  throw new
                      RuntimeException("Exception caught while storing Attributes: "
                                       + e);
              } finally {
                  try {
                      outAttrs.close();
                  } catch (IOException ie) {
                      getLogger().error("Error closing streams: " + ie);
                  }
              }
              getLogger().info("MessageAttributes for " + uid + " written in " + absoluteName);
  
              return true;
          } else {
              return false;
          }
      }
  
      /**
       * Get the IMAP-formatted String of flags for specified message.
       *
       * @param msn message sequence number for a message in this mailbox
       * @param username String represnting user
       * @returns flags for this message and user, null if no such message.
       * @throws AccessControlException if user does not have lookup rights for
       * this mailbox.
       * @throws AuthorizationException if user has lookup rights but does not
       * have read rights.
       */
      public synchronized  String getFlags(int msn, String user)
          throws AccessControlException, AuthorizationException {
          if (!hasReadRights(user)) { //throws AccessControlException
              throw new AuthorizationException("Not authorized to read.");
          }
          if (msn > sequence.size()) {
              return null;
          } else {
              int uid = ((Integer)sequence.get(msn - 1)).intValue();
              return getFlagsUID(uid, user);
          }
      }
  
      /**
       * Get the IMAP-formatted String of flags for specified message.
       *
       * @param uid UniqueIdentifier for a message in this mailbox
       * @param username String represnting user
       * @returns flags for this message and user, null if no such message.
       * @throws AccessControlException if user does not have lookup rights for
       * this mailbox.
       * @throws AuthorizationException if user has lookup rights but does not
       * have read rights.
       */
      public synchronized  String getFlagsUID(int uid, String user)
          throws AccessControlException, AuthorizationException {
          if (!hasReadRights(user)) { //throws AccessControlException
              throw new AuthorizationException("Not authorized to read.");
          }
          if (!sequence.contains(new Integer(uid))) {
              return null;
          } else {
              Flags flags = readFlags(uid);
              return flags.getFlags(user);
          }
      }
      /**
       * Updates the flags for a message.
       *
       * @param msn MessageSequenceNumber of a message already in this Mailbox
       * @param username String represnting user
       * @param request IMAP-formatted String representing requested change to
       * flags.
       * @returns true if succeeded, false otherwise, including no such message
       * @throws AccessControlException if user does not have read rights for
       * this mailbox.
       * @throws AuthorizationException if user has lookup rights but does not
       * have delete rights.
       */
      public synchronized  boolean setFlags(int msn, String user, String request)
          throws AccessControlException, AuthorizationException,
                 IllegalArgumentException {
          if (!hasKeepSeenRights(user)) { //throws AccessControlException
              throw new AuthorizationException("Not authorized to store any flags.");
          }
          if (msn > sequence.size()) {
              return false;
          } else {
              int uid = ((Integer)sequence.get(msn - 1)).intValue();
              return setFlagsUID(uid, user, request);
          }
      }
  
      /**
       * Updates the flags for a message.
       *
       * @param uid Unique Identifier of a message already in this Mailbox
       * @param username String represnting user
       * @param request IMAP-formatted String representing requested change to
       * flags.
       * @throws AccessControlException if user does not have read rights for
       * this mailbox.
       * @throws AuthorizationException if user has lookup rights but does not
       * have delete rights.
       */
      public synchronized boolean setFlagsUID(int uid, String user, String request)
          throws AccessControlException, AuthorizationException,
                 IllegalArgumentException {
          if (!hasKeepSeenRights(user)) { //throws AccessControlException
              throw new AuthorizationException("Not authorized to store any flags.");
          }
          if ((request.toUpperCase().indexOf("DELETED") != -1) && (!hasDeleteRights(user))) { //throws AccessControlException
              throw new AuthorizationException("Not authorized to delete.");
          }
          if (sequence.contains(new Integer(uid))) {
  
              Flags flags = readFlags(uid);
              boolean wasRecent = flags.isRecent();
              boolean wasDeleted = flags.isDeleted();
              boolean wasSeen = flags.isSeen(user);
  
              if  (flags.setFlags(request, user)) {
  
                  if (flags.isDeleted()) {
                      if (! wasDeleted) { messagesForDeletion.add(new Integer(uid)); }
                  }
                  if (flags.isSeen(user) != wasSeen) {
                      if (flags.isSeen(user)) {
                          int previousOld = ((Integer)oldestUnseenMessage.get(user)).intValue();
                          if (uid == previousOld) {
                              int newOld = findOldestUnseen(user, previousOld);
                              oldestUnseenMessage.put(user, (new Integer(newOld)));
                          }
                      } else { // seen flag unset
                          if (uid < ((Integer)oldestUnseenMessage.get(user)).intValue()) {
                              oldestUnseenMessage.put(user, (new Integer(uid)));
                          }
                      }
                  }
  
                  writeFlags(uid, flags);
                  getLogger().debug("Flags for message uid " + uid + " in " + absoluteName + " updated.");
                  return true;
              } else {
                  return false;
              }
          } else {
              return false;
          }
  
      }
  
      private int findOldestUnseen(String user, int previousOld)
          throws AccessControlException, AuthorizationException {
          int response = 0; //indicates no unseen messages
          ListIterator lit = sequence.listIterator(previousOld);
          boolean found = false;
          while (!found && lit.hasNext() ) {
              int uid = ((Integer)lit.next()).intValue();
              Flags flags = readFlags(uid);
              if (!flags.isSeen(user)) {
                  response = uid;
                  found = true;
              }
          }
          return response;
      }
  
      private Flags readFlags(int uid) {
          Flags response = null;
          if (sequence.contains(new Integer(uid))) {
              ObjectInputStream inFlags = null;
              try {
                  inFlags = new ObjectInputStream( new FileInputStream(path + File.separator + uid + FLAGS_EXTENSION));
                  response = (Flags)inFlags.readObject();
              } catch(Exception e) {
                  getLogger().error("Error reading flags from disc: " + e);
                  e.printStackTrace();
                  throw new
                      RuntimeException("Exception caught while retrieving Message flags: "
                                       + e);
              } finally {
                  try {
                      inFlags.close();
                  } catch (IOException ie) {
                      getLogger().error("Error closing streams: " + ie);
                  }
              }
              getLogger().info("Flags for " + uid + " read from " + absoluteName);
          }
          return response;
      }
  
      private boolean writeFlags(int uid, Flags flags) {
          if (sequence.contains(new Integer(uid))) {
              ObjectOutputStream outFlags = null;
              try {
                  outFlags = new ObjectOutputStream( new FileOutputStream(path + File.separator + uid + FLAGS_EXTENSION));
                  outFlags.writeObject(flags);
                  outFlags.close();
              } catch(Exception e) {
                  getLogger().error("Error writing message to disc: " + e);
                  e.printStackTrace();
                  throw new
                      RuntimeException("Exception caught while storing Flags: "
                                       + e);
              } finally {
                  try {
                      outFlags.close();
                  } catch (IOException ie) {
                      getLogger().error("Error closing streams: " + ie);
                  }
              }
              getLogger().info("Flags for " + uid + " written in " + absoluteName);
              return true;
          } else {
              return false;
          }
      }
  
      /**
       * Removes all messages marked Deleted.  User must have delete rights.
       *
       * @param username String represnting user
       * @returns true if successful
       * @throws AccessControlException if user does not have read rights for
       * this mailbox.
       * @throws AuthorizationException if user has delete rights but does not
       * have delete rights.
       */
      public synchronized boolean expunge(String user)
          throws AccessControlException, AuthorizationException {
          if (!hasDeleteRights(user)) { //throws AccessControlException
              throw new AuthorizationException("Not authorized to delete.");
          }
          Iterator it = messagesForDeletion.iterator();
          while (it.hasNext()) {
              Integer uidObj = (Integer)it.next();
              int uid = uidObj.intValue();
              if (sequence.contains(uidObj)) {
                  try  {
                      final File msgFile = new File(path + File.separator + uid + MESSAGE_EXTENSION );
                      msgFile.delete();
                      final File attrFile = new File(path + File.separator + uid + ATTRIBUTES_EXTENSION );
                      attrFile.delete();
                      sequence.remove(uidObj);
                      getLogger().debug( "Removed message uid " + uid );
                  } catch ( final Exception e )  {
                      throw new RuntimeException( "Exception caught while removing" +
                                                  " a message: " + e );
                  }
              }
          }
          for (int i = 0; i < sequence.size(); i++) {
              System.err.println("Message with msn " + i + " has uid " + sequence.get(i));
          }
          return true;
      }
  
      private void writeMailbox() {
          String mailboxRecordFile = path + File.separator + MAILBOX_FILE_NAME;
          ObjectOutputStream out = null;
          try {
              out = new ObjectOutputStream( new FileOutputStream(mailboxRecordFile));
              out.writeObject(this);
              out.close();
          } catch(Exception e) {
              if (out != null) {
                  try {
                      out.close();
                  } catch (Exception ignored) {
                  }
              }
              e.printStackTrace();
              throw new
                  RuntimeException("Exception caught while storing Mailbox: " + e);
          }
          getLogger().info("FileMailbox written: " + absoluteName);
      }
  
  
      /**
       * Lists uids of messages in mailbox indexed by MSN.
       *
       * @param username String represnting user
       * @returns List of Integers wrapping uids of message
       */
      public List listUIDs(String user) {
          return new ArrayList(Collections.unmodifiableList(sequence));
      }
  
      public Set getUsersWithLookupRights() {
          Set response = new  HashSet();
          Iterator it = acl.keySet().iterator();
          while (it.hasNext()) {
              String user = (String) it.next();
              boolean[] rights = (boolean[]) acl.get(user);
              if (rights[LOOKUP] == true) {
                  response.add(user);
              }
          }
          return response;
      }
  
      public Set getUsersWithReadRights() {
          Set response = new  HashSet();
          Iterator it = acl.keySet().iterator();
          while (it.hasNext()) {
              String user = (String) it.next();
              boolean[] rights = (boolean[]) acl.get(user);
              if (rights[READ] == true) {
                  response.add(user);
              }
          }
          return response;
      }
  
      public Map getUnseenByUser() {
          Map response = new HashMap();
          Iterator it = oldestUnseenMessage.keySet().iterator();
          while (it.hasNext()) {
              String user = (String) it.next();
              Integer uidObj = ((Integer)oldestUnseenMessage.get(user));
              int oldUID = uidObj.intValue();
              if (oldUID == 0) {
                  response.put(user, uidObj);
              } else {
                  int count = 0;
                  ListIterator lit
                      = sequence.listIterator(sequence.indexOf(uidObj));
                  while (lit.hasNext() ) {
                      int uid = ((Integer)lit.next()).intValue();
                      Flags flags = readFlags(uid);
                      if (!flags.isSeen(user)) {
                          count ++;
                      }
                  }
                  response.put(user, new Integer(count));
              }
          }
          return response;
      }
  
  
      public InternetHeaders getInternetHeaders(int msn, String user)
          throws AccessControlException, AuthorizationException {
          if (!hasReadRights(user)) { //throws AccessControlException
              throw new AuthorizationException("Not authorized to read.");
          }
          if (msn > sequence.size()) {
              return null;
          } else {
              int uid = ((Integer)sequence.get(msn - 1)).intValue();
              return getInternetHeadersUID(uid, user);
          }
      }
  
      public InternetHeaders getInternetHeadersUID(int uid, String user)
          throws AccessControlException, AuthorizationException {
          InternetHeaders response = null;
          if (sequence.contains(new Integer(uid))) {
              BufferedInputStream inMsg = null;
              try {
                  inMsg = new BufferedInputStream( new FileInputStream(path + File.separator + uid + MESSAGE_EXTENSION));
                  response = new InternetHeaders(inMsg);
                  inMsg.close();
              } catch(Exception e) {
                  getLogger().error("Error reading headers of message from disc: " + e);
                  e.printStackTrace();
                  throw new
                      RuntimeException("Exception caughtt while retrieving InternetHeaders: "    + e);
              } finally {
                  try {
                      inMsg.close();
                  } catch (IOException ie) {
                      getLogger().error("Error closing streams: " + ie);
                  }
              }
              getLogger().info("InternetHeaders for message " + uid + " read from "
                          + absoluteName);
              return response;
          } else {
              return null;
          }
      }
  }
  
  
  
  
  
  
  1.1                  jakarta-james/proposals/noparse-mimemessage/java/org/apache/james/imapserver/Mailbox.java
  
  Index: Mailbox.java
  ===================================================================
  /*
   * Copyright (C) The Apache Software Foundation. All rights reserved.
   *
   * This software is published under the terms of the Apache Software License
   * version 1.1, a copy of which has been included with this distribution in
   * the LICENSE file.
   */
  package org.apache.james.imapserver;
  
  import java.util.List;
  import java.util.Map;
  import javax.mail.internet.InternetHeaders;
  import javax.mail.internet.MimeMessage;
  import org.apache.avalon.framework.component.Composable;
  import org.apache.avalon.framework.configuration.Configurable;
  import org.apache.james.AccessControlException;
  import org.apache.james.AuthorizationException;
  import org.apache.james.core.MimeMessageWrapper;
  import org.apache.mailet.Mail;
  
  /**
   * Interface for objects representing an IMAP4rev1 mailbox (folder). Contains
   * logical information and provides a simple API. There should be one instance
   * of this class for every open IMAP mailbox.
   * Implementations may choose to store this object or recreate it on access.
   * Storing is recommended.
   * <p>Several methods throw AccessControlException. In normal use, these
   * shouldn't get thrown because the Host will have checked access before
   * returning a reference to this mailbox. However, having the methods here
   * throw this exception allows the acl to be changed while a mailbox is
   * selected.
   *
   * Mailbox Related Flags (rfc2060 name attributes)
   *     \Noinferiors   It is not possible for any child levels of hierarchy to
   * exist under this name; no child levels exist now and none can be created
   * in the future.
   *     \Noselect      It is not possible to use this name as a selectable
   * mailbox.
   *     \Marked        The mailbox has been marked "interesting" by the server;
   * the mailbox probably contains messages that have been added since the last
   * time the mailbox was selected.
   *      \Unmarked      The mailbox does not contain any additional messages
   * since the last time the mailbox was selected.
   *
   * Message related flags.
   * The flags allowed per message are specific to each mailbox.
   * The minimum list (rfc2060 system flags) is:
   *  \Seen       Message has been read
   *  \Answered   Message has been answered
   *  \Flagged    Message is "flagged" for urgent/special attention
   *  \Deleted    Message is "deleted" for removal by later EXPUNGE
   *  \Draft      Message has not completed composition (marked as a draft).
   *  \Recent     Message is "recently" arrived in this mailbox.  This session
   * is the first session to have been notified about this message; subsequent
   * sessions will not see \Recent set for this message.  This flag can not be
   * altered by the client.
   *              If it is not possible to determine whether or not this session
   * is the first session to be notified about a message, then that message
   * SHOULD be considered recent.
   *
   * Reference: RFC 2060
   * @author <a href="mailto:charles@benett1.demon.co.uk">Charles Benett</a>
   * @version 0.1 on 14 Dec 2000
   */
  public interface Mailbox
      extends Configurable, Composable {
  
      String SYSTEM_FLAGS = "\\Seen \\Answered \\Flagged \\Deleted \\Draft";
      String RECENT_FLAG =  "\\Recent";
  
      /**
       * Returns name of this mailbox relative to its parent in the mailbox
       * hierarchy.
       * Example: 'NewIdeas'
       *
       * @returns String name of mailbox relative to its immeadiate parent in
       * the mailbox hierarchy.
       */
      String getName();
  
      /**
       * Returns absolute, that is user-independent, hierarchical name of
       * mailbox (including namespace)
       * Example: '#mail.fred.flintstone.apache.James.NewIdeas'
       *
       * @returns String name of mailbox in absolute form
       */
      String getAbsoluteName();
  
      /** Returns namespace starting with namespace token.
       * Example: '#mail'
       *
       * @returns String containing user-independent namespace of this mailbox.
       */
      //   public String getNamespace();
  
      /** Returns true if the argument is the relative or absolute name of
       * this mailbox
       *
       * @param name possible name for this Mailbox
       * @returns true if name matches either getName() or getAbsoluteName()
       */
      boolean matchesName(String name);
  
      /**
       * Returns the current unique id validity value of this mailbox.
       *
       * @returns int current 32 bit unique id validity value of this mailbox
       */
      int getUIDValidity();
  
      /**
       * Returns the 32 bit uid available for the next message.
       *
       * @returns int the next UID that would be used.
       */
      int getNextUID();
  
      /**
       * Returns mailbox size in octets. Should only include actual messages
       * and not any implementation-specific data, such as message attributes.
       *
       * @returns int mailbox size in octets
       */
      int getMailboxSize();
  
      /**
       * Indicates if child folders may be created. It does not indicate which
       * users can create child folders.
       *
       * @returns boolean TRUE if inferiors aree allowed
       */
      boolean getInferiorsAllowed();
  
      /**
       * Indicates if this folder may be selected by the specified user.
       * Requires both that the mailbox is not NotSelectableByAnyone and that the
       * user has at least read rights. It does not indicate whether user
       * can write to mailbox
       *
       * @param username String represnting user
       * @returns boolean TRUE if specified user can Select mailbox.
       * @throws AccessControlException if username does not have lookup rights
       */
      boolean isSelectable(String username) throws AccessControlException;
  
      /**
       * Indicates that messages have been added since this mailbox was last
       * selected by any user.
       *
       * @returns boolean TRUE if new messages since any user last selected
       * mailbox
       */
      boolean isMarked();
  
      /**
       * Returns all flags supported by this mailbox.
       * e.g. \Answered \Deleted
       *
       * @returns String a space seperated list of message flags which are
       * supported by this mailbox.
       */
      String getSupportedFlags();
  
      /**
       * Indicates if specified user can change any flag on a permanent basis,
       * except for \Recent which can never be changed by a user.
       *
       * @param username String represnting user
       * @returns true if specified user can change all flags permanently.
       * @throws AccessControlException if username does not have lookup rights
       */
      boolean allFlags(String username) throws AccessControlException;
  
      /**
       * Indicates which flags this user can change permanently. If allFlags()
       * returns true for this user, then this method must have the same return
       * value as getSupportedFlags.
       *
       * @param username String represnting user
       * @returns String a space seperated list of message flags which this user
       * can set permanently
       */
      String getPermanentFlags( String username )
          throws AccessControlException;
  
      /**
       * Indicates number of messages in folder
       *
       * @returns int number of messages
       */
      int getExists();
  
      /**
       * Indicates no of messages with \Recent flag set
       *
       * @returns int no of messages with \Recent flag set
       */
      int getRecent();
  
  
      /**
       * Remove \Recent flag from all messages in mailbox. Should be called
       * whenever a user session finishes.
       */
      void unsetRecent();
  
      /**
       * Indicates the oldest unseen message for the specified user.
       *
       * @returns int Message Sequence Number of first message without \Seen
       * flag set for this User.
       * <br> -1 means all messages have \Seen flag set for this user.
       * <br> 0 means no message (Seen or unseen) in this mailbox.
       */
      int getOldestUnseen( String user );
  
     /**
       * Indicates the number of  unseen messages for the specified user.
       *
       * @returns int number of messages without \Seen flag set for this User.
       */
      int getUnseen( String user );
  
      /**
       * Indicates state in which  the mailbox will be opened by specified user.
       * A return value of true indicates Read Only, false indicates Read-Write
       * and an AccessControlException is thrown if user does not have read
       * rights.
       * <p>Implementations decide if Read Only means only lookup and read
       * rights (lr) or lookup, read and keep seen rights (lrs). This may even
       * vary between mailboxes.
       *
       * @param username String represnting user
       * @returns true if specified user can only open the mailbox Read-Only.
       * @throws AccessControlException if the user can not open this mailbox
       * at least Read-Only.
       */
      boolean isReadOnly( String username )
          throws AccessControlException;
  
      /**
       * Mailbox Events are used to inform registered listeners of events in the
       * Mailbox.
       * Example if mail is delivered to an Inbox or if another user appends/
       * deletes a message.
       */
      void addMailboxEventListener( MailboxEventListener mel );
      void removeMailboxEventListener( MailboxEventListener mel );
  
      /**
       * Stores a message in this mailbox. User must have insert rights.
       *
       * @param mail the message to be stored
       * @param username String represnting user
       * @returns boolean true if successful
       * @throws AccessControlException if username does not have lookup rights for this mailbox.
       * @throws AuthorizationException if username has lookup rights but does not have insert rights.
       */
      boolean store( MimeMessage message, String username )
          throws AccessControlException, AuthorizationException, IllegalArgumentException;
  
      /**
       * Stores a message in this mailbox, using passed MessageAttributes and
       * Flags. User must have insert rights.
       *
       * @param mail the message to be stored
       * @param username String represnting user
       * @param attrs non-null MessageAttributes for use with this Message
       * @param flags a Flags object for this message
       * @returns boolean true if successful
       * @throws AccessControlException if username does not have lookup rights
       * for this mailbox.
       * @throws AuthorizationException if username has lookup rights but does
       * not have insert rights.
       */
      boolean store( MimeMessage message,
                     String username,
                     MessageAttributes attrs,
                     Flags flags )
          throws AccessControlException, AuthorizationException, IllegalArgumentException;
  
      /**
       * Retrieves a message given a message sequence number.
       *
       * @param msn the message sequence number
       * @param username String represnting user
       * @returns a Mail object containing the message, null if no message with
       * the given msn.
       * @throws AccessControlException if user does not have lookup rights for
       * this mailbox.
       * @throws AuthorizationException if user has lookup rights but does not
       * have read rights.
       */
      MimeMessageWrapper retrieve( int msn, String user )
          throws AccessControlException, AuthorizationException;
  
      /**
       * Retrieves a message given a unique identifier.
       *
       * @param uid the unique identifier of a message
       * @param username String represnting user
       * @returns a Mail object containing the message, null if no message with
       * the given msn.
       * @throws AccessControlException if user does not have read rights for
       * this mailbox.
       * @throws AuthorizationException if user has lookup rights but does not
       * have read rights.
       */
      MimeMessageWrapper retrieveUID( int uid, String user )
          throws AccessControlException, AuthorizationException;
  
      /**
       * Marks a message for deletion given a message sequence number.
       *
       * @param msn the message sequence number
       * @param username String represnting user
       * @returns boolean true if successful.
       * @throws AccessControlException if user does not have read rights for
       * this mailbox.
       * @throws AuthorizationException if user has lookup rights but does not
       * have delete rights.
       */
      boolean markDeleted( int msn, String user )
          throws AccessControlException, AuthorizationException;
  
      /**
       * Marks a message for deletion given a unique identifier.
       *
       * @param uidunique identifier
       * @param username String represnting user
       * @returns boolean true if successful, false if failed including no
       * message with the given uid.
       * @throws AccessControlException if user does not have read rights for
       * this mailbox.
       * @throws AuthorizationException if user has lookup rights but does not
       * have delete rights.
       */
      boolean markDeletedUID( int uid, String user )
          throws AccessControlException, AuthorizationException;
  
      /**
       * Returns the message attributes for a message.
       *
       * @param msn message sequence number
       * @param username String represnting user
       * @returns MessageAttributes for message, null if no such message.
       * Changing the MessageAttributes object must not affect the actual
       * MessageAttributes.
       * @throws AccessControlException if user does not have read rights for
       * this mailbox.
       * @throws AuthorizationException if user has lookup rights but does not
       * have delete rights.
       */
      MessageAttributes getMessageAttributes( int msn, String user )
          throws AccessControlException, AuthorizationException;
  
      /**
       * Returns the message attributes for a message.
       *
       * @param uid unique identifier
       * @param username String represnting user
       * @returns MessageAttributes for message, null if no such message.
       * Changing the MessageAttributes object must not affect the actual
       * MessageAttributes.
       * @throws AccessControlException if user does not have read rights for
       * this mailbox.
       * @throws AuthorizationException if user has lookup rights but does not
       * have delete rights.
       */
      MessageAttributes getMessageAttributesUID( int uid, String user )
          throws AccessControlException, AuthorizationException;
  
      /**
       * Updates the attributes of a message.
       *
       * @param MessageAttributes of a message already in this Mailbox
       * @throws AccessControlException if user does not have read rights for
       * this mailbox.
       * @throws AuthorizationException if user has lookup rights but does not
       * have delete rights.
       */
      boolean updateMessageAttributes( MessageAttributes attrs, String user )
          throws AccessControlException, AuthorizationException;
  
      /**
       * Get the IMAP-formatted String of flags for specified message.
       *
       * @param msn message sequence number for a message in this mailbox
       * @param username String represnting user
       * @returns flags for this message and user, null if no such message.
       * @throws AccessControlException if user does not have lookup rights for
       * this mailbox.
       * @throws AuthorizationException if user has lookup rights but does not
       * have read rights.
       */
      String getFlags( int msn, String user )
          throws AccessControlException, AuthorizationException;
  
     /**
       * Get the IMAP-formatted String of flags for specified message.
       *
       * @param uid UniqueIdentifier for a message in this mailbox
       * @param username String represnting user
       * @returns flags for this message and user, null if no such message.
       * @throws AccessControlException if user does not have lookup rights for
       * this mailbox.
       * @throws AuthorizationException if user has lookup rights but does not
       * have read rights.
       */
      String getFlagsUID(int uid, String user)
          throws AccessControlException, AuthorizationException;
  
      /**
       * Updates the flags of a message.
       *
       * @param msn message sequence number for a message in this mailbox
       * @param username String represnting user
       * @param request IMAP formatted string of flag request
       * @throws AccessControlException if user does not have read rights for
       * this mailbox.
       * @throws AuthorizationException if user has lookup rights but does not
       * have delete rights.
       */
      boolean setFlags( int msn, String user, String request )
          throws AccessControlException, AuthorizationException, IllegalArgumentException;
  
      /**
       * Updates the flags of a message.
       *
       * @param uid UniqueIdentifier for a message in this mailbox
       * @param username String represnting user
       * @param request IMAP formatted string of flag request
       * @throws AccessControlException if user does not have read rights for
       * this mailbox.
       * @throws AuthorizationException if user has lookup rights but does not
       * have delete rights.
       */
      boolean setFlagsUID( int uid, String user, String request )
          throws AccessControlException, AuthorizationException, IllegalArgumentException;
  
      /**
       * Returns the Internet Headers for a message.  These are the top-level
       * headers only, ie not MIME part headers or headers of encapsulated
       * messages.
       *
       * @param msn message sequence number
       * @param username String represnting user
       * @returns InternetHeaders for message, null if no such message.
       * Changing the InternetHeaders object must not affect the actual
       * InternetHeaders of the underlying message.
       * @throws AccessControlException if user does not have read rights for
       * this mailbox.
       * @throws AuthorizationException if user has lookup rights but does not
       * have delete rights.
       */
      InternetHeaders getInternetHeaders( int msn, String user )
          throws AccessControlException, AuthorizationException;
  
      /**
       * Returns the Internet Headers for a message.  These are the top-level
       * headers only, ie not MIME part headers or headers of encapsulated
       * messages.
       *
       * @param uid UniqueIdentifier for a message in this mailbox
       * @param username String represnting user
       * @returns InternetHeaders for message, null if no such message.
       * Changing the InternetHeaders object must not affect the actual
       * InternetHeaders of the underlying message.
       * @throws AccessControlException if user does not have read rights for
       * this mailbox.
       * @throws AuthorizationException if user has lookup rights but does not
       * have delete rights.
       */
      InternetHeaders getInternetHeadersUID( int uid, String user )
          throws AccessControlException, AuthorizationException;
  
      /**
       * Removes all messages marked Deleted.  User must have delete rights.
       *
       * @param username String represnting user
       * @returns true if successful
       * @throws AccessControlException if user does not have read rights for
       * this mailbox.
       * @throws AuthorizationException if user has delete rights but does not
       * have delete rights.
       */
      boolean expunge( String user )
          throws AccessControlException, AuthorizationException, IllegalArgumentException;
  
      /**
       * Establishes if specified user has lookup rights for this mailbox.
       *
       * @param username String represnting user
       * @returns true if user has at least lookup rights
       */
      boolean hasLookupRights( String user );
  
      /**
       * Establishes if specified user has create rights for this mailbox.
       *
       * @param username String represnting user
       * @returns true if user has at create rights
       * @throws AccessControlException if user does not have lookup rights for
       * this mailbox.
       */
      boolean hasCreateRights( String user )
          throws AccessControlException;
  
      /**
       * Lists uids of messages in mailbox indexed by MSN.
       *
       * @param username String represnting user
       * @returns List of Integers wrapping uids of message
       */
      List listUIDs( String user );
  
      /**
       * Returns true once this Mailbox has been checkpointed.
       * This may include resolving in-memory state with disk state.
       * Implementations not requiring checkpointing should return immeadiately.
       *
       * @returns true if check completes normally, false otherwise.
       */
      boolean checkpoint();
  
      /**
       * Mark this mailbox as not selectable by anyone.
       * Example folders at the roots of hierarchies, e. #mail for each user.
       *
       * @param state true if folder is not selectable by anyone
       */
      void setNotSelectableByAnyone( boolean state );
  
      boolean isNotSelectableByAnyone();
  
      /**
       * Gets map of users to number of unseen messages. User key will only be
       * present if getOldestUnseen() has been called, usually as a result of
       * an IMAP SELECT or EXAMINE.
       */
      Map getUnseenByUser();
  }
  
  
  
  
  
  1.1                  jakarta-james/proposals/noparse-mimemessage/java/org/apache/james/imapserver/MimeMessageFileSource.java
  
  Index: MimeMessageFileSource.java
  ===================================================================
  /*
   * Copyright (C) The Apache Software Foundation. All rights reserved.
   *
   * This software is published under the terms of the Apache Software License
   * version 1.1, a copy of which has been included with this distribution in
   * the LICENSE file.
   */
  package org.apache.james.imapserver;
  
  import java.io.FileInputStream;
  import java.io.IOException;
  import java.io.InputStream;
  import org.apache.james.core.MimeMessageSource;
  
  public class MimeMessageFileSource extends MimeMessageSource {
  
      //Define how to get to the data
      String filename = null;;
  
      public MimeMessageFileSource(String filename) {
          this.filename = filename;
      }
  
      public InputStream getInputStream() throws IOException {
          return new FileInputStream(filename);
      }
  
  }
  
  
  
  1.1                  jakarta-james/proposals/noparse-mimemessage/java/org/apache/james/imapserver/SimpleMessageAttributes.java
  
  Index: SimpleMessageAttributes.java
  ===================================================================
  /*
   * Copyright (C) The Apache Software Foundation. All rights reserved.
   *
   * This software is published under the terms of the Apache Software License
   * version 1.1, a copy of which has been included with this distribution in
   * the LICENSE file.
   */
  package org.apache.james.imapserver;
  
  import java.io.*;
  import java.util.*;
  import javax.mail.BodyPart;
  import javax.mail.Header;
  import javax.mail.MessagingException;
  import javax.mail.Session;
  import javax.mail.internet.*;
  import org.apache.avalon.framework.logger.AbstractLoggable;
  import org.apache.james.core.MimeMessageWrapper;
  import org.apache.james.util.RFC822DateFormat;
  import org.apache.log.Logger;
  import org.apache.mailet.*;
  
  /**
   * Attributes of a Message in IMAP4rev1 style. Message
   * Attributes should be set when a message enters a mailbox.
   * <p> Note that the message in a mailbox have the same order using either
   * Message Sequence Numbers or UIDs.
   * <p> reinitialize() must be called on deserialization to reset Logger
   *
   * Reference: RFC 2060 - para 2.3
   * @author <a href="mailto:charles@benett1.demon.co.uk">Charles Benett</a>
   * @version 0.1 on 14 Dec 2000
   */
  public class SimpleMessageAttributes
      extends AbstractLoggable
      implements MessageAttributes, Serializable  {
  
      private final static String SP = " ";
      private final static String NIL = "NIL";
      private final static String Q = "\"";
      private final static String LB = "(";
      private final static String RB = ")";
      private final static boolean DEBUG = true;
      private final static String MULTIPART = "MULTIPART";
      private final static String MESSAGE = "MESSAGE";
  
      //Only available in first incarnation of object
      private transient Logger logger;
  
      private int uid;
      private int messageSequenceNumber;
      private Date internalDate;
      private String internalDateString;
      private String bodyStructure;
      private String envelope;
      private int size;
      private int lineCount;
      private MessageAttributes[] parts;
      private List headers;
  
      //rfc822 or MIME header fields
      //arrays only if multiple values allowed under rfc822
      private String subject;
      private String[] from;
      private String[] sender;
      private String[] replyTo;
      private String[] to;
      private String[] cc;
      private String[] bcc;
      private String[] inReplyTo;
      private String[] date;
      private String[] messageID;
      private String contentType;
      private String primaryType;   // parsed from contentType
      private String secondaryType; // parsed from contentType
      private Set parameters;      // parsed from contentType
      private String contentID;
      private String contentDesc;
      private String contentEncoding;
  
      SimpleMessageAttributes() {
      }
  
      void setAttributesFor(MimeMessage msg) throws MessagingException {
          size = msg.getSize();
  
          try {
              internalDate = msg.getSentDate();
              if (DEBUG) getLogger().debug("setAttributes - getSentDate: " + internalDate);
          } catch (MessagingException me) {
              if (DEBUG) getLogger().debug("Messaging Exception for getSentDate: " + me);
              internalDate = new Date();
          }
  
          if (DEBUG) {
              getLogger().debug("HeaderLines recieved were: ");
              Enumeration enum = msg.getAllHeaderLines();
              while(enum.hasMoreElements()) {
                  getLogger().debug((String)enum.nextElement());
              }
              //  getLogger().debug("Header objects available are:");
              //   Enumeration e = msg.getAllHeaders();
              //   while(e.hasMoreElements()) {
              //Header h = (Header) e.nextElement();
              //getLogger().debug("Name: " + h.getName());
              //getLogger().debug("Value: " + h.getValue());
              //  }
          }
          internalDateString = RFC822DateFormat.toString(internalDate); // not right format
          parseMimePart(msg);
          envelope = null;
          bodyStructure = null;
      }
  
      void setUID(int thisUID) {
          uid = thisUID;
      }
  
      /**
       * Parses key data items from a MimeMessage for seperate storage.
       */
      void parseMimePart(MimePart part) {
          // Section 1 - Message Headers
          if (part instanceof MimeMessage) {
              try {
                  subject = ((MimeMessage)part).getSubject();
                  if (DEBUG) getLogger().debug("parseMessage - subject: " + subject);
              } catch (MessagingException me) {
                  if (DEBUG) getLogger().debug("Messaging Exception for getSubject: " + me);
              }
          }
          try {
              from = part.getHeader("From");
              if (DEBUG)  getLogger().debug("parseMessage - from: " + from);
          } catch (MessagingException me) {
              if (DEBUG) getLogger().debug("Messaging Exception for getHeader(From): " + me);
          }
          try {
              sender = part.getHeader("Sender");
              if (DEBUG) getLogger().debug("parseMessage - sender: " + sender);
          } catch (MessagingException me) {
              if (DEBUG) getLogger().debug("Messaging Exception for getHeader(Sender): " + me);
          }
          try {
              replyTo = part.getHeader("Reply To");
              if (DEBUG) getLogger().debug("parseMessage - ReplyTo: " + replyTo);
          } catch (MessagingException me) {
              if (DEBUG) getLogger().debug("Messaging Exception for getHeader(Reply To): " + me);
          }
          try {
              to = part.getHeader("To");
              if (DEBUG) getLogger().debug("parseMessage - To: " + to);
          } catch (MessagingException me) {
              if (DEBUG) getLogger().debug("Messaging Exception for getHeader(To): " + me);
          }
          try {
              cc = part.getHeader("Cc");
              if (DEBUG) getLogger().debug("parseMessage - cc: " + cc);
          } catch (MessagingException me) {
              if (DEBUG) getLogger().debug("Messaging Exception for getHeader(To): " + me);
          }
          try {
              bcc = part.getHeader("Bcc");
              if (DEBUG) getLogger().debug("parseMessage - bcc: " + bcc);
          } catch (MessagingException me) {
              if (DEBUG) getLogger().debug("Messaging Exception for getHeader(To): " + me);
          }
          try {
              inReplyTo = part.getHeader("In Reply To");
              if (DEBUG) getLogger().debug("parseMessage - In Reply To: " + inReplyTo);
          } catch (MessagingException me) {
              if (DEBUG) getLogger().debug("Messaging Exception for getHeader(In Reply To): " + me);
          }
          try {
              date = part.getHeader("Date");
              if (DEBUG) getLogger().debug("parseMessage - date: " + date);
          } catch (MessagingException me) {
              if (DEBUG) getLogger().debug("Messaging Exception for getHeader(Date): " + me);
          }
          try {
              messageID = part.getHeader("Message-ID");
              if (DEBUG) getLogger().debug("parseMessage - messageID: " + messageID);
          } catch (MessagingException me) {
              if (DEBUG) getLogger().debug("Messaging Exception for getHeader(messageID): " + me);
          }
          String contentTypeLine = null;
          try {
              contentTypeLine = part.getContentType();
              if (DEBUG) getLogger().debug("parseMessage - contentType: " + contentTypeLine);
          } catch (MessagingException me) {
              if (DEBUG) getLogger().debug("Messaging Exception for getContentType(): " + me);
          }
          if (contentTypeLine !=null ) {
              decodeContentType(contentTypeLine);
          }
          try {
              contentID = part.getContentID();
              if (DEBUG) getLogger().debug("parseMessage - contentID: " + contentID);
          } catch (MessagingException me) {
              if (DEBUG) getLogger().debug("Messaging Exception for getContentUD(): " + me);
          }
          try {
              contentDesc = part.getDescription();
              if (DEBUG) getLogger().debug("parseMessage - contentDesc: " + contentDesc);
          } catch (MessagingException me) {
              if (DEBUG) getLogger().debug("Messaging Exception for getDescription(): " + me);
          }
          try {
              contentEncoding = part.getEncoding();
              if (DEBUG) getLogger().debug("parseMessage - contentEncoding: " + contentEncoding);
          } catch (MessagingException me) {
              if (DEBUG) getLogger().debug("Messaging Exception for getEncoding(): " + me);
          }
          if (DEBUG) {
              try {
                  String contentDisposition = part.getDisposition();
                  getLogger().debug("parseMessage - contentDisposition: " + contentEncoding);
              } catch (MessagingException me) {
                  getLogger().debug("Messaging Exception for getEncoding(): " + me);
              }
          }
  
          try {
              lineCount = part.getLineCount();
              if (DEBUG) getLogger().debug("parseMessage - Line Count: " + lineCount);
          } catch (MessagingException me) {
              if (DEBUG) getLogger().debug("Messaging Exception for getLineCount(): " + me);
              if (DEBUG) getLogger().debug(me.getMessage());
          } catch (Exception e) {
              if (DEBUG) getLogger().debug("Exception for getLineCount(): " + e);
              if (DEBUG) getLogger().debug("Exception message was: " +  e.getMessage());
          }
  
          // Recurse through any embedded parts
          if (primaryType.equalsIgnoreCase(MULTIPART)) {
              MimeMultipart container;
              try {
                  container =(MimeMultipart) part.getContent();
                  int count = container.getCount();
                  getLogger().info("This part contains " + count + " parts.");
                  parts = new SimpleMessageAttributes[count];
                  for (int i = 0; i < count ; i ++) {
                      getLogger().info("Getting embedded part: " + i);
                      BodyPart nextPart = container.getBodyPart(i);
  
                      if (nextPart instanceof MimePart) {
                          SimpleMessageAttributes partAttrs = new SimpleMessageAttributes();
                          partAttrs.parseMimePart((MimePart)nextPart);
                          parts[i] = partAttrs;
  
                      } else {
                          getLogger().info("Found a non-Mime bodyPart");
                      }
                      getLogger().info("Finished with embedded part: " + i);
                  }
              } catch (Exception e) {
                  getLogger().debug("Messaging Exception for getContent(): " + e);
              }
          } else if (primaryType.equalsIgnoreCase("message")) {
              getLogger().info("This part contains an embedded message of subtype: " + secondaryType);
              getLogger().info("Uses java class: " + part.getClass().getName());
              if (secondaryType.equalsIgnoreCase("RFC822")) {
                  try {
  
                      /*
                      MimeMessageWrapper message = new MimeMessageWrapper(part.getInputStream());
                      SimpleMessageAttributes msgAttrs = new SimpleMessageAttributes();
                      msgAttrs.setAttributesFor(message);
  
                      if (part instanceof MimeMessage) {
  						Comments out because I don't know what it should do here
                          MimeMessage msg1 = (MimeMessage) part;
                          MimeMessageWrapper message2 = new MimeMessageWrapper(msg1);
                          SimpleMessageAttributes msgAttrs2 = new SimpleMessageAttributes();
                          msgAttrs.setAttributesFor(message2);
                      }
  
                      parts = new SimpleMessageAttributes[1];
                      parts[0] = msgAttrs;
                      */
                  } catch (Exception e) {
                      getLogger().error("Error interpreting a message/rfc822: " + e);
                      e.printStackTrace();
                  }
              } else {
                  getLogger().info("Unknown subtype of message encountered.");
              }
              getLogger().info("Finished with embedded message. " );
          }
      }
  
      /**
       * Builds IMAP envelope String from pre-parsed data.
       */
      String parseEnvelope() {
          List response = new ArrayList();
          response.add( LB + Q + internalDateString + Q + SP);
          if (subject != null && (!subject.equals(""))) {
              response.add( Q +  subject + Q + SP );
          } else {
              response.add( NIL + SP );
          }
          if (from != null && from.length > 0) {
              response.add(LB);
              for (int i=0; i<from.length; i++) {
                  response.add(parseAddress( from[i]) );
              }
              response.add(RB);
          } else {
              response.add( NIL);
          }
          response.add(SP);
          if (sender != null && sender.length >0) {
              if (DEBUG) getLogger().debug("parsingEnvelope - sender[0] is: " + sender[0]);
              //Check for Netscape feature - sender is local part only
              if (sender[0].indexOf("@") == -1) {
                  response.add(LB + (String)response.get(3) + RB); //first From address
              } else {
                  response.add(LB);
                  for (int i=0; i<sender.length; i++) {
                      response.add( parseAddress(sender[i]));
                  }
                  response.add(RB);
              }
          } else {
              if (from != null && from.length > 0) {
                  response.add(LB + (String)response.get(3) + RB); //first From address
              } else {
                  response.add( NIL);
              }
          }
          response.add(SP);
          if (replyTo != null && replyTo.length >0) {
              if (replyTo[0].indexOf("@") == -1) {
                  response.add(LB + (String)response.get(3) + RB); //first From address
              } else {
                  response.add(LB);
                  for (int i=0; i<replyTo.length; i++) {
                      response.add( parseAddress(replyTo[i]));
                  }
                  response.add(RB);
              }
          } else {
              if (from != null && from.length > 0) {
                  response.add(LB + (String)response.get(3) + RB); //first From address
              } else {
                  response.add( NIL);
              }
          }
          response.add(SP);
          if (to != null && to.length >0) {
              response.add(LB);
              for (int i=0; i<to.length; i++) {
                  response.add( parseAddress(to[i]));
              }
              response.add(RB);
          } else {
              response.add( NIL);
          }
          response.add(SP);
          if (cc != null && cc.length >0) {
              response.add(LB);
              for (int i=0; i<cc.length; i++) {
                  response.add( parseAddress(cc[i]));
              }
              response.add(RB);
          } else {
              response.add( NIL);
          }
          response.add(SP);
          if (bcc != null && bcc.length >0) {
              response.add(LB);
              for (int i=0; i<bcc.length; i++) {
                  response.add( parseAddress(bcc[i]));
              }
              response.add(RB);
          } else {
              response.add( NIL);
          }
          response.add(SP);
          if (inReplyTo != null && inReplyTo.length>0) {
              response.add( inReplyTo[0]);
          } else {
              response.add( NIL);
          }
          response.add(SP);
          if (messageID != null && messageID.length>0) {
              response.add(Q + messageID[0] + Q);
          } else {
              response.add( NIL);
          }
          response.add(RB);
  
          StringBuffer buf = new StringBuffer(16 * response.size());
          for (int j=0; j<response.size(); j++) {
              buf.append((String)response.get(j));
          }
  
          return buf.toString();
      }
  
      /**
       * Parses a String email address to an IMAP address string.
       */
      String parseAddress(String address) {
          getLogger().info("Parsing address: " + address);
          int comma = address.indexOf(",");
          StringBuffer buf = new StringBuffer();
          if (comma == -1) { //single address
              buf.append(LB);
              InternetAddress netAddr = null;
              try {
                  netAddr = new InternetAddress(address);
              } catch (AddressException ae) {
                  return null;
              }
              String personal = netAddr.getPersonal();
              if (personal != null && (!personal.equals(""))) {
                  buf.append(Q + personal + Q);
              } else {
                  buf.append( NIL);
              }
              buf.append( SP);
              buf.append( NIL) ; // should add route-addr
              buf.append( SP);
              try {
                  MailAddress mailAddr = new MailAddress(netAddr);
                  buf.append(Q + mailAddr.getUser() + Q);
                  buf.append(SP);
                  buf.append(Q + mailAddr.getHost() + Q);
              } catch (ParseException pe) {
                  buf.append( NIL + SP + NIL);
              }
              buf.append(RB);
          } else {
              buf.append(parseAddress(address.substring(0, comma)));
              buf.append(SP);
              buf.append(parseAddress(address.substring(comma + 1)));
          }
          return buf.toString();
      }
  
      /**
       * Decode a content Type header line into types and parameters pairs
       */
      void decodeContentType(String rawLine) {
          if (DEBUG) getLogger().debug("decoding: " + rawLine);
          int slash = rawLine.indexOf("/");
          if( slash == -1){
              if (DEBUG) getLogger().debug("decoding ... no slash found");
              return;
          } else {
              primaryType = rawLine.substring(0, slash).trim();
          }
          int semicolon = rawLine.indexOf(";");
          if (semicolon == -1) {
              if (DEBUG) getLogger().debug("decoding ... no semicolon found");
              secondaryType = rawLine.substring(slash + 1).trim();
              return;
          }
          // have parameters
          parameters = new HashSet();
          secondaryType = rawLine.substring(slash + 1, semicolon).trim();
          int pos = semicolon;
          int nextsemi = rawLine.indexOf(";", pos+1);
          while (nextsemi != -1) {
              if (DEBUG) getLogger().debug("decoding ... found another semicolon");
              String param = rawLine.substring(pos + 1, nextsemi);
              int esign = param.indexOf("=") ;
              if (esign == -1) {
                  if (DEBUG) getLogger().debug("Whacky parameter found: " + param);
              } else {
                  String name = param.substring(0, esign).trim();
                  String value = param.substring(esign + 1).trim();
                  parameters.add(name + SP + value);
                  if (DEBUG) getLogger().debug("Found parameter: " + name + SP + value);
              }
              pos = nextsemi;
              nextsemi = rawLine.indexOf(";", pos +1);
          }
          String lastParam = rawLine.substring(pos + 1);
          int esign = lastParam.indexOf("=") ;
          if (esign == -1) {
              if (DEBUG) getLogger().debug("Whacky parameter found: " + lastParam);
          } else {
              String name = lastParam.substring(0, esign).trim();
              String value = lastParam.substring(esign + 1).trim();
              parameters.add(Q + name + Q + SP + Q + value + Q);
              if (DEBUG) getLogger().debug("Found parameter: " + name + SP + value);
          }
      }
  
      String parseBodyFields() {
          getLogger().debug("Parsing body fields");
          StringBuffer buf = new StringBuffer();
          if (parameters == null || parameters.isEmpty()) {
              buf.append(NIL);
          } else {
              buf.append(LB);
              Iterator it = parameters.iterator();
              while(it.hasNext()) {
                  buf.append((String)it.next());
              }
              buf.append(RB);
          }
          buf.append(SP);
          if(contentID == null) {
              buf.append(NIL);
          } else {
              buf.append(Q + contentID + Q);
          }
          buf.append(SP);
          if(contentDesc == null) {
              buf.append(NIL);
          } else {
              buf.append(Q + contentDesc + Q);
          }
          buf.append(SP);
          if(contentEncoding == null) {
              buf.append(NIL);
          } else {
              buf.append(Q + contentEncoding + Q);
          }
          buf.append(SP);
          buf.append(size);
          return buf.toString();
      }
  
      /**
       * Produce the IMAP formatted String for the BodyStructure of a pre-parsed MimeMessage
       */
      String parseBodyStructure() {
          getLogger().debug("Parsing bodyStructure.");
          try {
              String fields = parseBodyFields();
              StringBuffer buf = new StringBuffer();
              buf.append(LB);
              if (primaryType.equalsIgnoreCase("Text")) {
                  getLogger().debug("Assembling bodystrucuture for type TEXT.");
                  buf.append("\"Text\" \"" + secondaryType + "\" ");
                  buf.append(fields + " " + lineCount);
              } else if  (primaryType.equalsIgnoreCase(MESSAGE) && secondaryType.equalsIgnoreCase("rfc822")) {
                  getLogger().debug("Assembling bodyStructure for type MESSAGE/FRC822");
                  buf.append("\"MESSAGE\" \"RFC822\" ");
                  buf.append(fields + SP);
                  setupLogger(parts[0]); // reset transient logger
                  buf.append(parts[0].getEnvelope() + SP);
                  buf.append(parts[0].getBodyStructure() + SP);
                  buf.append(lineCount);
              } else if (primaryType.equalsIgnoreCase(MULTIPART)) {
                  getLogger().debug("Assembling bodystructure for type MULTIPART");
                  for (int i=0; i<parts.length; i++) {
                      getLogger().debug("Parsing part: " + i);
                      setupLogger(parts[i]); // reset transient getLogger()
                      buf.append(parts[i].getBodyStructure());
                  }
                  buf.append(SP + secondaryType);
              }
              buf.append(RB);
              return buf.toString();
          } catch (Exception e) {
              getLogger().error("Exception while parsing BodyStrucuture: " + e);
              e.printStackTrace();
              throw new RuntimeException("Exception in parseBodyStructure");
          }
      }
  
      /**
       * Provides the current Message Sequence Number for this message. MSNs
       * change when messages are expunged from the mailbox.
       *
       * @returns int a positive non-zero integer
       */
      public int getMessageSequenceNumber() {
          return messageSequenceNumber;
      }
  
      void setMessageSequenceNumber(int newMsn) {
          messageSequenceNumber = newMsn;
      }
  
  
      /**
       * Provides the unique identity value for this message. UIDs combined with
       * a UIDValidity value form a unique reference for a message in a given
       * mailbox. UIDs persist across sessions unless the UIDValidity value is
       * incremented. UIDs are not copied if a message is copied to another
       * mailbox.
       *
       * @returns int a 32-bit value
       */
      public int getUID() {
          return uid;
      }
  
      /**
       * Provides the date and time at which the message was received. In the
       * case of delivery by SMTP, this SHOULD be the date and time of final
       * delivery as defined for SMTP. In the case of messages copied from
       * another mailbox, it shuld be the internalDate of the source message. In
       * the case of messages Appended to the mailbox, example drafts,  the
       * internalDate is either specified in the Append command or is the
       * current dat and time at the time of the Append.
       *
       * @returns Date imap internal date
       */
      public Date getInternalDate() {
          return internalDate;
      }
  
      public String getInternalDateAsString() {
          return internalDateString;
      }
  
      /**
       * Provides the sizeof the message in octets.
       *
       * @returns int number of octets in message.
       */
      public int getSize() {
          return size;
      }
  
      /**
       * Provides the Envelope structure information for this message. This is a parsed representation of the rfc-822 envelope information. This is not to be confused with the SMTP envelope!
       *
       * @returns String satisfying envelope syntax in rfc 2060.
       */
      public String getEnvelope() {
          return parseEnvelope();
      }
  
  
      /**
       * Provides the Body Structure information for this message. This is a parsed representtion of the MIME structure of the message.
       *
       * @returns String satisfying body syntax in rfc 2060.
       */
      public String getBodyStructure() {
          return parseBodyStructure();
      }
  }
  
  
  
  1.1                  jakarta-james/proposals/noparse-mimemessage/java/org/apache/james/mailrepository/AvalonMailRepository.java
  
  Index: AvalonMailRepository.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.mailrepository;
  
  import java.io.InputStream;
  import java.io.OutputStream;
  import java.util.Iterator;
  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.configuration.DefaultConfiguration;
  import org.apache.avalon.framework.logger.AbstractLoggable;
  import org.apache.avalon.cornerstone.services.store.ObjectRepository;
  import org.apache.avalon.cornerstone.services.store.Store;
  import org.apache.avalon.cornerstone.services.store.StreamRepository;
  import org.apache.james.core.MailImpl;
  import org.apache.james.core.MimeMessageWrapper;
  import org.apache.james.services.MailRepository;
  import org.apache.james.services.MailStore;
  import org.apache.james.util.Lock;
  import org.apache.james.util.LockException;
  
  /**
   * Implementation of a MailRepository on a FileSystem.
   *
   * Requires a configuration element in the .conf.xml file of the form:
   *  <repository destinationURL="file://path-to-root-dir-for-repository"
   *              type="MAIL"
   *              model="SYNCHRONOUS"/>
   * Requires a logger called MailRepository.
   *
   * @version 1.0.0, 24/04/1999
   * @author  Federico Barbieri <sc...@pop.systemy.it>
   * @author Charles Benett <ch...@benett1.demon.co.uk>
   */
  public class AvalonMailRepository
      extends AbstractLoggable
      implements MailRepository, Component, Configurable, Composable {
  
      protected Lock lock;
      protected static boolean DEEP_DEBUG = false;
      private static final String TYPE = "MAIL";
      private Store store;
      private StreamRepository sr;
      private ObjectRepository or;
      private MailStore mailstore;
      private String destination;
  
  
      public void configure(Configuration conf) throws ConfigurationException {
          destination = conf.getAttribute("destinationURL");
          String checkType = conf.getAttribute("type");
          if (! (checkType.equals("MAIL") || checkType.equals("SPOOL")) ) {
              getLogger().warn( "Attempt to configure AvalonMailRepository as " +
                                checkType);
              throw new ConfigurationException("Attempt to configure AvalonMailRepository as " + checkType);
          }
          // ignore model
      }
  
      public void compose( final ComponentManager componentManager )
          throws ComponentException {
          try {
              store = (Store)componentManager.
                  lookup( "org.apache.avalon.cornerstone.services.store.Store" );
  
              //prepare Configurations for object and stream repositories
              DefaultConfiguration objectConfiguration
                  = new DefaultConfiguration( "repository",
                                              "generated:AvalonFileRepository.compose()" );
  
              objectConfiguration.setAttribute("destinationURL", destination);
              objectConfiguration.setAttribute("type", "OBJECT");
              objectConfiguration.setAttribute("model", "SYNCHRONOUS");
  
              DefaultConfiguration streamConfiguration
                  = new DefaultConfiguration( "repository",
                                              "generated:AvalonFileRepository.compose()" );
  
              streamConfiguration.setAttribute( "destinationURL", destination );
              streamConfiguration.setAttribute( "type", "STREAM" );
              streamConfiguration.setAttribute( "model", "SYNCHRONOUS" );
  
              sr = (StreamRepository) store.select(streamConfiguration);
              or = (ObjectRepository) store.select(objectConfiguration);
              lock = new Lock();
  	    getLogger().debug(this.getClass().getName() + " created in " + destination);
          } catch (Exception e) {
              final String message = "Failed to retrieve Store component:" + e.getMessage();
              getLogger().error( message, e );
              throw new ComponentException( message, e );
          }
      }
  
      public synchronized void unlock(Object key) {
  
          if (lock.unlock(key)) {
              notifyAll();
          } else {
              throw new LockException("Your thread do not own the lock of record " + key);
          }
      }
  
      public synchronized void lock(Object key) {
  
          if (lock.lock(key)) {
              notifyAll();
          } else {
              throw new LockException("Record " + key + " already locked by another thread");
          }
      }
  
      public synchronized void store(MailImpl mc) {
          try {
              String key = mc.getName();
              OutputStream out = sr.put(key);
              mc.writeMessageTo(out);
              out.close();
              or.put(key, mc);
  	    if(DEEP_DEBUG) getLogger().debug("Mail " + key + " stored." );
              notifyAll();
          } catch (Exception e) {
  	    getLogger().error("Exception storing mail: " + e);
              e.printStackTrace();
              throw new RuntimeException("Exception caught while storing Message Container: " + e);
          }
      }
  
      public MailImpl retrieve(String key) {
  	if(DEEP_DEBUG) getLogger().debug("Retrieving mail: " + key);
          MailImpl mc = (MailImpl) or.get(key);
          try {
  			MimeMessageAvalonSource source = new MimeMessageAvalonSource(sr, key);
  			mc.setMessage(new MimeMessageWrapper(source));
  
              //InputStream in = new FileMimeMessageInputStream(sr, key);
              //mc.setMessage(in);
              //in.close();
          } catch (Exception me) {
  	    getLogger().error("Exception retrieving mail: " + me);
              throw new RuntimeException("Exception while retrieving mail: " + me.getMessage());
          }
          return mc;
      }
  
      public void remove(MailImpl mail) {
          remove(mail.getName());
      }
  
      public void remove(String key) {
          try {
              lock( key);
              sr.remove(key);
              or.remove(key);
          } finally {
              unlock(key);
          }
      }
  
      public Iterator list() {
          return sr.list();
      }
  }
  
  
  
  1.1                  jakarta-james/proposals/noparse-mimemessage/java/org/apache/james/mailrepository/DebugInputStream.java
  
  Index: DebugInputStream.java
  ===================================================================
  package org.apache.james.mailrepository;
  
  import java.io.*;
  
  public class DebugInputStream extends InputStream {
      InputStream in = null;
  
      public DebugInputStream(InputStream in) {
          this.in = in;
      }
  
  	public int read() throws IOException {
          int b = in.read();
  	    System.err.write(b);
          return b;
  	}
  
      public void close() throws IOException {
          in.close();
      }
  }
  
  
  1.1                  jakarta-james/proposals/noparse-mimemessage/java/org/apache/james/mailrepository/JDBCMailRepository.java
  
  Index: JDBCMailRepository.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.mailrepository;
  
  import java.sql.Connection;
  import java.sql.DriverManager;
  import java.sql.PreparedStatement;
  import java.sql.ResultSet;
  import java.sql.SQLException;
  import java.io.ByteArrayOutputStream;
  import java.io.InputStream;
  import java.text.SimpleDateFormat;
  import java.util.ArrayList;
  import java.util.Date;
  import java.util.HashSet;
  import java.util.Iterator;
  import java.util.List;
  import java.util.Set;
  import java.util.StringTokenizer;
  import javax.mail.internet.MimeMessage;
  import org.apache.avalon.framework.component.Component;
  import org.apache.avalon.framework.component.Composable;
  import org.apache.avalon.framework.component.ComponentManager;
  import org.apache.avalon.framework.component.ComponentException;
  import org.apache.avalon.framework.configuration.Configurable;
  import org.apache.avalon.framework.configuration.Configuration;
  import org.apache.avalon.framework.configuration.ConfigurationException;
  import org.apache.avalon.framework.configuration.DefaultConfiguration;
  import org.apache.avalon.framework.logger.AbstractLoggable;
  import org.apache.james.core.MimeMessageWrapper;
  import org.apache.james.core.MailImpl;
  import org.apache.james.services.MailRepository;
  import org.apache.james.services.SpoolRepository;
  import org.apache.james.util.Lock;
  import org.apache.james.util.LockException;
  import org.apache.mailet.Mail;
  import org.apache.mailet.MailAddress;
  
  /**
   * Implementation of a MailRepository on a database.
   *
   * <p>Requires a configuration element in the .conf.xml file of the form:
   *  <br><repository destinationURL="town://path"
   *  <br>            type="MAIL"
   *  <br>            model="SYNCHRONOUS"/>
   *  <br>            <driver>sun.jdbc.odbc.JdbcOdbcDriver</conn>
   *  <br>            <conn>jdbc:odbc:LocalDB</conn>
   *  <br>            <table>Message</table>
   *  <br></repository>
   * <p>destinationURL specifies..(Serge??)
   * <br>Type can be SPOOL or MAIL
   * <br>Model is currently not used and may be dropped
   * <br>conn is the location of the ...(Serge)
   * <br>table is the name of the table in the Database to be used
   *
   * <p>Requires a logger called MailRepository.
   *
   * @version 1.0.0, 24/04/1999
   * @author  Serge Knystautas <se...@lokitech.com>
   */
  public class JDBCMailRepository
      extends AbstractLoggable
      implements MailRepository, Component, Configurable, Composable {
  
      private SimpleDateFormat sqlFormat = new SimpleDateFormat("yyyy MMM dd h:mm:ss a");
  
      protected Lock lock;
      protected String destination;
      protected String tableName = "EML_Spool";
      protected String repositoryName;
  
      //The table where this is stored
      private String driverClassName = "com.inet.tds.TdsDriver";
      protected String jdbcURL = "jdbc:inetdae7:127.0.0.1?database=James";
      protected String jdbcUsername = "sa";   //optional
      protected String jdbcPassword = "rufus4811";    //optional
  
  
      private String checkMessageExistsSQL =
              "SELECT count(*) FROM " + tableName + " WHERE message_name = ? AND repository_name = ?";
  
      private String updateMessageSQL =
              "UPDATE " + tableName + " SET message_state = ?, error_message = ?, sender = ?, recipients = ?, "
              + "remote_host = ?, remote_addr = ?, last_updated = ? "
              + "WHERE message_name = ? AND repository_name = ?";
  
      private String updateMessageBodySQL =
              "UPDATE " + tableName + " SET message_body = ? WHERE message_name = ? AND repository_name = ?";
  
      private String insertMessageSQL =
              "INSERT INTO " + tableName + " (message_name, repository_name, message_state, "
              + "error_message, sender, recipients, remote_host, remote_addr, last_updated, message_body) "
              + "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
  
      private String retrieveMessageSQL =
              "SELECT message_state, error_message, sender, recipients, remote_host, remote_addr, last_updated "
              + "FROM " + tableName + " WHERE message_name = ? AND repository_name = ?";
  
      private String removeMessageSQL =
              "DELETE FROM " + tableName + " WHERE message_name = ? AND repository_name = ?";
  
      private String listMessagesSQL =
              "SELECT message_name FROM " + tableName + " WHERE repository_name = ? ORDER BY last_updated ASC";
  
      public void configure(Configuration conf) throws ConfigurationException {
          destination = conf.getAttribute("destinationURL");
          destination = destination.substring(destination.indexOf("//") + 2);
          tableName = destination.substring(0, destination.indexOf("/"));
          repositoryName = destination.substring(destination.indexOf("/") + 1);
          /*
          String checkType = conf.getAttribute("type");
          if (! (checkType.equals("MAIL") || checkType.equals("SPOOL")) ) {
              final String message =
                  "Attempt to configure TownSpoolRepository as " + checkType;
              getLogger().warn( message );
              throw new ConfigurationException( message );
          }
          // ignore model
          conndefinition = conf.getChild("conn").getValue();
          tableName = conf.getChild("table").getValue();
          */
          try {
              Class.forName(driverClassName);
          } catch (ClassNotFoundException cnfe) {
              String message = "Unable to load JDBC driver: " + driverClassName;
              getLogger().error(message);
              throw new ConfigurationException(message);
          }
          //lock = new Lock();
          System.err.println("jdbc spool initialized for " + repositoryName);
      }
  
      public void compose( final ComponentManager componentManager )
          throws ComponentException {
          try {
              //store = (Store)componentManager.
              //    lookup( "org.apache.avalon.cornerstone.services.store.Store" );
  
              //prepare Configurations for object and stream repositories
              DefaultConfiguration objectConfiguration
                  = new DefaultConfiguration( "repository",
                                              "generated:JDBCMailRepository.compose()" );
  
              objectConfiguration.setAttribute("destinationURL", destination);
              objectConfiguration.setAttribute("type", "OBJECT");
              objectConfiguration.setAttribute("model", "SYNCHRONOUS");
  
              DefaultConfiguration streamConfiguration
                  = new DefaultConfiguration( "repository",
                                              "generated:JDBCMailRepository.compose()" );
  
              streamConfiguration.setAttribute( "destinationURL", destination );
              streamConfiguration.setAttribute( "type", "STREAM" );
              streamConfiguration.setAttribute( "model", "SYNCHRONOUS" );
  
              //sr = (StreamRepository) store.select(streamConfiguration);
              //or = (ObjectRepository) store.select(objectConfiguration);
              lock = new Lock();
          getLogger().debug(this.getClass().getName() + " created in " + destination);
          } catch (Exception e) {
              final String message = "Failed to retrieve Store component:" + e.getMessage();
              getLogger().error( message, e );
              throw new ComponentException( message, e );
          }
      }
  
      public synchronized void unlock(Object key) {
          if (lock.unlock(key)) {
              notifyAll();
          } else {
              throw new LockException("Your thread does not own the lock of record " + key);
          }
      }
  
      public synchronized void lock(Object key) {
          if (lock.lock(key)) {
              notifyAll();
          } else {
              throw new LockException("Record " + key + " already locked by another thread");
          }
      }
  
      public void store(MailImpl mc) {
          System.err.println("storing " + mc.getName());
          try {
              Connection conn = getConnection();
  
              //Need to determine whether need to insert this record, or update it.
  
              //Begin a transaction
              conn.setAutoCommit(false);
  
              PreparedStatement checkMessageExists = conn.prepareStatement(checkMessageExistsSQL);
              checkMessageExists.setString(1, mc.getName());
              checkMessageExists.setString(2, repositoryName);
              ResultSet rsExists = checkMessageExists.executeQuery();
              boolean exists = rsExists.next() && rsExists.getInt(1) > 0;
              rsExists.close();
              checkMessageExists.close();
  
              if (exists) {
                  //Update the existing record
                  PreparedStatement updateMessage = conn.prepareStatement(updateMessageSQL);
                  updateMessage.setString(1, mc.getState());
                  updateMessage.setString(2, mc.getErrorMessage());
                  updateMessage.setString(3, mc.getSender().toString());
                  StringBuffer recipients = new StringBuffer();
                  for (Iterator i = mc.getRecipients().iterator(); i.hasNext(); ) {
                      recipients.append(i.next().toString());
                      if (i.hasNext()) {
                          recipients.append("\r\n");
                      }
                  }
                  updateMessage.setString(4, recipients.toString());
                  updateMessage.setString(5, mc.getRemoteHost());
                  updateMessage.setString(6, mc.getRemoteAddr());
                  updateMessage.setDate(7, new java.sql.Date(mc.getLastUpdated().getTime()));
                  updateMessage.setString(8, mc.getName());
                  updateMessage.setString(9, repositoryName);
                  updateMessage.execute();
                  updateMessage.close();
  
                  //Determine whether the message body has changed, and possibly avoid
                  //  updating the database.
                  MimeMessage messageBody = mc.getMessage();
                  boolean saveBody = false;
                  if (messageBody instanceof MimeMessageWrapper) {
                      MimeMessageWrapper message = (MimeMessageWrapper)messageBody;
                      saveBody = message.isModified();
                  } else {
                      saveBody = true;
                  }
  
                  if (saveBody) {
                      updateMessage = conn.prepareStatement(updateMessageBodySQL);
                      int size = (int)messageBody.getSize();
                      updateMessage.setBinaryStream(1, new DebugInputStream(messageBody.getInputStream()), size);
                      updateMessage.setString(2, mc.getName());
                      updateMessage.setString(3, repositoryName);
                      updateMessage.execute();
                      updateMessage.close();
                  }
              } else {
                  //Insert the record into the database
                  PreparedStatement insertMessage = conn.prepareStatement(insertMessageSQL);
                  insertMessage.setString(1, mc.getName());
                  insertMessage.setString(2, repositoryName);
                  insertMessage.setString(3, mc.getState());
                  insertMessage.setString(4, mc.getErrorMessage());
                  insertMessage.setString(5, mc.getSender().toString());
                  StringBuffer recipients = new StringBuffer();
                  for (Iterator i = mc.getRecipients().iterator(); i.hasNext(); ) {
                      recipients.append(i.next().toString());
                      if (i.hasNext()) {
                          recipients.append("\r\n");
                      }
                  }
                  insertMessage.setString(6, recipients.toString());
                  insertMessage.setString(7, mc.getRemoteHost());
                  insertMessage.setString(8, mc.getRemoteAddr());
                  //java.sql.Date lastUpdated = new java.sql.Date(mc.getLastUpdated().getTime());
                  //System.err.println(lastUpdated);
                  //insertMessage.setDate(9, lastUpdated);
                  insertMessage.setString(9, sqlFormat.format(mc.getLastUpdated()));
                  MimeMessage messageBody = mc.getMessage();
                  int size = messageBody.getSize();
                  insertMessage.setBinaryStream(10, new DebugInputStream(messageBody.getInputStream()), size);
                  insertMessage.execute();
                  insertMessage.close();
              }
  
              conn.commit();
              conn.setAutoCommit(true);
              conn.close();
  
              synchronized (this) {
                  System.err.println("everything is notified on " + this);
                  notifyAll();
              }
          } catch (Exception e) {
              e.printStackTrace();
              throw new RuntimeException("Exception caught while storing mail Container: " + e);
          }
      }
  
      public MailImpl retrieve(String key) {
          System.err.println("retrieving " + key);
          try {
              Connection conn = getConnection();
  
              PreparedStatement retrieveMessage = conn.prepareStatement(retrieveMessageSQL);
              retrieveMessage.setString(1, key);
              retrieveMessage.setString(2, repositoryName);
              ResultSet rsMessage = retrieveMessage.executeQuery();
              if (!rsMessage.next()) {
                  throw new RuntimeException("Did not find a record " + key + " in " + repositoryName);
              }
              MailImpl mc = new MailImpl();
              mc.setName(key);
              mc.setState(rsMessage.getString(1));
              mc.setErrorMessage(rsMessage.getString(2));
              mc.setSender(new MailAddress(rsMessage.getString(3)));
              StringTokenizer st = new StringTokenizer(rsMessage.getString(4), "\r\n", false);
              Set recipients = new HashSet();
              while (st.hasMoreTokens()) {
                  recipients.add(new MailAddress(st.nextToken()));
              }
              mc.setRecipients(recipients);
              mc.setRemoteHost(rsMessage.getString(5));
              mc.setRemoteAddr(rsMessage.getString(6));
              mc.setLastUpdated(new java.util.Date(rsMessage.getDate(7).getTime()));
  
              //Create a reference to a JDBCMimeMessageInputStream
              //InputStream in = new JDBCMimeMessageInputStream(this, key);
              //InputStream in = new TownMimeMessageInputStream(conndefinition, tableName, key, repositoryName);
              //InputStream in = new ByteArrayInputStream(message.getAsBytes("message_body"));
              MimeMessageJDBCSource source = new MimeMessageJDBCSource(this, key);
              MimeMessageWrapper message = new MimeMessageWrapper(source);
              mc.setMessage(message);
              rsMessage.close();
              retrieveMessage.close();
              conn.close();
              return mc;
          } catch (SQLException sqle) {
              System.err.println("Error retrieving message");
              System.err.println(sqle.getMessage());
              System.err.println(sqle.getErrorCode());
              System.err.println(sqle.getSQLState());
              System.err.println(sqle.getNextException());
              sqle.printStackTrace();
              throw new RuntimeException("Exception while retrieving mail: " + sqle.getMessage());
          } catch (Exception me) {
              me.printStackTrace();
              throw new RuntimeException("Exception while retrieving mail: " + me.getMessage());
          }
      }
  
      public void remove(MailImpl mail) {
          remove(mail.getName());
      }
  
      public void remove(String key) {
          System.err.println("removing " + key);
          try {
              lock(key);
  
              Connection conn = getConnection();
              PreparedStatement removeMessage = conn.prepareStatement(removeMessageSQL);
              removeMessage.setString(1, key);
              removeMessage.setString(2, repositoryName);
              removeMessage.execute();
              removeMessage.close();
              conn.close();
          } catch (Exception me) {
              throw new RuntimeException("Exception while removing mail: " + me.getMessage());
          } finally {
              unlock(key);
          }
      }
  
      public Iterator list() {
          System.err.println("listing messages");
          try {
              Connection conn = getConnection();
              PreparedStatement listMessages = conn.prepareStatement(listMessagesSQL);
              listMessages.setString(1, repositoryName);
              ResultSet rsListMessages = listMessages.executeQuery();
  
              List messageList = new ArrayList();
              while (rsListMessages.next()) {
                  messageList.add(rsListMessages.getString(1));
              }
              rsListMessages.close();
              listMessages.close();
              conn.close();
              return messageList.iterator();
          } catch (Exception me) {
              me.printStackTrace();
              throw new RuntimeException("Exception while listing mail: " + me.getMessage());
          }
      }
  
      //
      // Private methods
      //
      /**
       * Opens a database connection.
       */
      protected Connection getConnection() {
          try {
              if (jdbcUsername == null ) {
                  return DriverManager.getConnection(jdbcURL);
              } else {
                  return DriverManager.getConnection(jdbcURL, jdbcUsername, jdbcPassword);
              }
          } catch (SQLException sqlExc) {
              sqlExc.printStackTrace();
              throw new RuntimeException("Error connecting to database");
          }
      }
  
      public boolean equals(Object obj) {
          if (!(obj instanceof JDBCMailRepository)) {
              return false;
          }
          JDBCMailRepository repository = (JDBCMailRepository)obj;
          return repository.tableName.equals(tableName) && repository.repositoryName.equals(repositoryName);
      }
  }
  
  
  
  1.1                  jakarta-james/proposals/noparse-mimemessage/java/org/apache/james/mailrepository/JDBCSpoolRepository.java
  
  Index: JDBCSpoolRepository.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.mailrepository;
  
  import java.sql.Connection;
  import java.sql.DriverManager;
  import java.sql.PreparedStatement;
  import java.sql.ResultSet;
  import java.sql.SQLException;
  import java.io.ByteArrayOutputStream;
  import java.io.InputStream;
  import java.text.SimpleDateFormat;
  import java.util.ArrayList;
  import java.util.Date;
  import java.util.HashSet;
  import java.util.Iterator;
  import java.util.List;
  import java.util.Set;
  import java.util.StringTokenizer;
  import javax.mail.internet.MimeMessage;
  import org.apache.avalon.framework.component.Component;
  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.james.core.MimeMessageWrapper;
  import org.apache.james.core.MailImpl;
  import org.apache.james.services.SpoolRepository;
  import org.apache.james.util.Lock;
  import org.apache.james.util.LockException;
  import org.apache.mailet.Mail;
  import org.apache.mailet.MailAddress;
  
  /**
   * Implementation of a SpoolRepository on a database.
   *
   * <p>Requires a configuration element in the .conf.xml file of the form:
   *  <br><repository destinationURL="town://path"
   *  <br>            type="MAIL"
   *  <br>            model="SYNCHRONOUS"/>
   *  <br>            <driver>sun.jdbc.odbc.JdbcOdbcDriver</conn>
   *  <br>            <conn>jdbc:odbc:LocalDB</conn>
   *  <br>            <table>Message</table>
   *  <br></repository>
   * <p>destinationURL specifies..(Serge??)
   * <br>Type can be SPOOL or MAIL
   * <br>Model is currently not used and may be dropped
   * <br>conn is the location of the ...(Serge)
   * <br>table is the name of the table in the Database to be used
   *
   * <p>Requires a logger called MailRepository.
   *
   * @version 1.0.0, 24/04/1999
   * @author  Serge Knystautas <se...@lokitech.com>
   */
  public class JDBCSpoolRepository
      extends JDBCMailRepository
      implements SpoolRepository {
  
      public synchronized String accept() {
          while (true) {
              System.err.println("calling accept");
              for(Iterator it = list(); it.hasNext(); ) {
                  Object o = it.next();
                  if (lock.lock(o)) {
                      return o.toString();
                  }
              }
              try {
                  System.out.println("waiting for " + this);
                  wait();
              } catch (InterruptedException ignored) {
              }
          }
      }
  
      public synchronized String accept(long delay) {
          while (true) {
              long youngest = 0;
              //Really unoptimized query here... should be much smart about this...
              for (Iterator it = list(); it.hasNext(); ) {
                  String s = it.next().toString();
                  if (lock.lock(s)) {
                      //We have a lock on this object... let's grab the message
                      //  and see if it's a valid time.
                      MailImpl mail = retrieve(s);
                      if (mail.getState().equals(Mail.ERROR)) {
                          //Test the time...
                          long timeToProcess = delay + mail.getLastUpdated().getTime();
                          if (System.currentTimeMillis() > timeToProcess) {
                              //We're ready to process this again
                              return s;
                          } else {
                              //We're not ready to process this.
                              if (youngest == 0 || youngest > timeToProcess) {
                                  //Mark this as the next most likely possible mail to process
                                  youngest = timeToProcess;
                              }
                          }
                      } else {
                          //This guy is good to go... return him
                          return s;
                      }
                  }
              }
              //We did not find any... let's wait for a certain amount of time
              try {
                  if (youngest == 0) {
                      wait();
                  } else {
                      wait(youngest - System.currentTimeMillis());
                  }
              } catch (InterruptedException ignored) {
              }
              System.err.println("done waiting in long accept");
          }
      }
  }
  
  
  
  1.1                  jakarta-james/proposals/noparse-mimemessage/java/org/apache/james/mailrepository/MimeMessageAvalonSource.java
  
  Index: MimeMessageAvalonSource.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.mailrepository;
  
  import java.io.IOException;
  import java.io.InputStream;
  import org.apache.avalon.cornerstone.services.store.StreamRepository;
  import org.apache.james.core.MimeMessageSource;
  
  public class MimeMessageAvalonSource extends MimeMessageSource {
  
      //Define how to get to the data
      StreamRepository sr = null;
      String key = null;
  
      public MimeMessageAvalonSource(StreamRepository sr, String key) {
          this.sr = sr;
          this.key = key;
      }
  
      public InputStream getInputStream() throws IOException {
          return sr.get(key);
      }
  
  }
  
  
  
  1.1                  jakarta-james/proposals/noparse-mimemessage/java/org/apache/james/mailrepository/MimeMessageJDBCSource.java
  
  Index: MimeMessageJDBCSource.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.mailrepository;
  
  import java.io.ByteArrayInputStream;
  import java.io.IOException;
  import java.io.InputStream;
  import java.sql.Connection;
  import java.sql.DriverManager;
  import java.sql.PreparedStatement;
  import java.sql.ResultSet;
  import java.sql.SQLException;
  import java.sql.Statement;
  import org.apache.james.core.MimeMessageSource;
  
  public class MimeMessageJDBCSource
      extends MimeMessageSource {
  
      private String retrieveMessageStreamSQL;
  
      //Define how to get to the data
      JDBCMailRepository repository = null;
      String key = null;
  
      //The inputstream, if closed, is null, if open contains appropriate references
      InputStream in;
      Statement inStatement;
      ResultSet inResultSet;
      Connection conn;
  
      public MimeMessageJDBCSource(JDBCMailRepository repository,
                                        String key) throws IOException {
          if (repository == null) {
              throw new IOException("Repository is null");
          }
          if (key == null) {
              throw new IOException("Message name (key) was not defined");
          }
          this.repository = repository;
          this.key = key;
  
          retrieveMessageStreamSQL = "SELECT message_body FROM " + repository.tableName
                  + " WHERE message_name = ? AND repository_name = ?";
      }
  
      public synchronized InputStream getInputStream() throws IOException {
          //System.err.println("loading data for " + key + "/" + repository);
  
          try {
              conn = repository.getConnection();
  
              PreparedStatement retrieveMessageStream = conn.prepareStatement(retrieveMessageStreamSQL);
              retrieveMessageStream.setString(1, key);
              retrieveMessageStream.setString(2, repository.repositoryName);
              //System.err.println(retrieveMessageStream);
              //System.err.println(retrieveMessageStreamSQL);
              //System.err.println("'" + key + "'");
              //System.err.println("'" + repository.repositoryName + "'");
              ResultSet rsRetrieveMessageStream = retrieveMessageStream.executeQuery();
  
              if (!rsRetrieveMessageStream.next()) {
                  throw new IOException("Could not find message");
              }
  
              in = rsRetrieveMessageStream.getBinaryStream(1);
              inResultSet = rsRetrieveMessageStream;
              inStatement = retrieveMessageStream;
              return in;
          } catch (SQLException sqle) {
              throw new IOException(sqle.toString());
          } finally {
              //Do we really want to do this?  I think not
              /*
              try {
                  conn.close();
              } catch (Exception e) {
              }
              */
          }
      }
  
      public boolean equals(Object obj) {
          if (obj instanceof MimeMessageJDBCSource) {
              MimeMessageJDBCSource source = (MimeMessageJDBCSource)obj;
              return source.key.equals(key) && source.repository.equals(repository);
          }
          return false;
      }
  }
  
  
  
  1.1                  jakarta-james/proposals/noparse-mimemessage/java/org/apache/mailet/Mail.java
  
  Index: Mail.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.mailet;
  
  import java.io.*;
  import java.net.*;
  import java.util.*;
  import javax.mail.*;
  import javax.mail.internet.*;
  
  /**
   * Wrap a MimeMessage with routing information (from SMTP) such
   * as SMTP specified recipients, sender, and ip address and hostname
   * of sending server.  It also contains its state which represents
   * which processor in the mailet container it is currently running.
   * Special processor names are "root" and "error".
   *
   * @author Federico Barbieri <sc...@systemy.it>
   * @author Serge Knystautas <se...@lokitech.com>
   * @version 0.9
   */
  public interface Mail extends Serializable, Cloneable {
      String GHOST = "ghost";
      String DEFAULT = "root";
      String ERROR = "error";
      String TRANSPORT = "transport";
  
      /**
       * Returns the MimeMessage stored in this message
       *
       * @return the MimeMessage that this Mail object wraps
       * @throws MessagingException - an error occured while loading this object
       */
      MimeMessage getMessage() throws MessagingException;
  
      /**
       * Returns a Collection of MailAddress objects that are recipients of this message
       *
       * @return a Collection of MailAddress objects that are recipients of this message
       */
      Collection getRecipients();
  
      /**
       * The sender of the message, as specified by the MAIL FROM header, or internally defined
       *
       * @return a MailAddress of the sender of this message
       */
      MailAddress getSender();
  
      /**
       * The current state of the message, such as GHOST, ERROR, or DEFAULT
       *
       * @return the state of this message
       */
      String getState();
  
      /**
       * The remote hostname of the server that connected to send this message
       *
       * @return a String of the hostname of the server that connected to send this message
       */
      String getRemoteHost();
  
      /**
       * The remote ip address of the server that connected to send this message
       *
       * @return a String of the ip address of the server that connected to send this message
       */
      String getRemoteAddr();
  
      /**
       * The error message, if any, associated with this message.  Not sure why this is needed.
       *
       * @return a String of a descriptive error message
       */
      String getErrorMessage();
  
      /**
       * Sets the error message associated with this message.  Not sure why this is needed.
       *
       * @param msg - a descriptive error message
       */
      void setErrorMessage(String msg);
  
      /**
       * Sets the MimeMessage associated with this message via an inputstream.  The Mail
       * object will parse out the inputstream and construct a MimeMessage object.
       *
       * @param in - the inputstream to read to construct the MimeMessage
       * @throws MessagingException - if there was an error parsing the inputstream
       */
  /*
      void setMessage(InputStream in) throws MessagingException;
  */
  
      /**
       * Sets the MimeMessage associated with this message via the object.
       *
       * @param message - the new MimeMessage that this Mail object will wrap
       */
      void setMessage(MimeMessage message);
  
      /**
       * Sets the state of this message.
       *
       * @param state - the new state of this message
       */
      void setState(String state);
  }
  
  
  

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