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 ch...@apache.org on 2001/05/11 13:01:59 UTC

cvs commit: jakarta-james/src/org/apache/james/imapserver ACL.java ACLMailbox.java BaseCommand.java CommandFetch.java CommandStore.java DefaultRecordRepository.java FileMailbox.java Flags.java FolderRecord.java Host.java IMAPServer.java IMAPServer.xinfo IMAPSystem.java ImapRequest.java JamesHost.java Mailbox.java MailboxEvent.java MailboxEventListener.java MailboxEventSource.java MailboxException.java MessageAttributes.java MessageHeader.java RecordRepository.java SimpleFolderRecord.java SimpleMessageAttributes.java SimpleSystem.java SingleThreadedConnectionHandler.java

charlesb    01/05/11 04:01:57

  Modified:    .        build.xml
  Added:       src/java/org/apache/james/imapserver ACL.java
                        ACLMailbox.java BaseCommand.java CommandFetch.java
                        CommandStore.java DefaultRecordRepository.java
                        FileMailbox.java Flags.java FolderRecord.java
                        Host.java IMAPServer.java IMAPServer.xinfo
                        ImapRequest.java JamesHost.java Mailbox.java
                        MailboxEvent.java MailboxEventListener.java
                        MailboxEventSource.java MailboxException.java
                        MessageAttributes.java MessageHeader.java
                        RecordRepository.java SimpleFolderRecord.java
                        SimpleMessageAttributes.java SimpleSystem.java
                        SingleThreadedConnectionHandler.java
  Removed:     src/org/apache/james/imapserver ACL.java ACLMailbox.java
                        BaseCommand.java CommandFetch.java
                        CommandStore.java DefaultRecordRepository.java
                        FileMailbox.java Flags.java FolderRecord.java
                        Host.java IMAPServer.java IMAPServer.xinfo
                        IMAPSystem.java ImapRequest.java JamesHost.java
                        Mailbox.java MailboxEvent.java
                        MailboxEventListener.java MailboxEventSource.java
                        MailboxException.java MessageAttributes.java
                        MessageHeader.java RecordRepository.java
                        SimpleFolderRecord.java
                        SimpleMessageAttributes.java SimpleSystem.java
                        SingleThreadedConnectionHandler.java
  Log:
  Moving from src/org to src/java/org
  
  Revision  Changes    Path
  1.59      +1 -1      jakarta-james/build.xml
  
  Index: build.xml
  ===================================================================
  RCS file: /home/cvs/jakarta-james/build.xml,v
  retrieving revision 1.58
  retrieving revision 1.59
  diff -u -r1.58 -r1.59
  --- build.xml	2001/05/10 15:28:14	1.58
  +++ build.xml	2001/05/11 11:01:06	1.59
  @@ -88,7 +88,7 @@
     -->
   
     <property name="src.dir" value="src"/>
  -  <property name="java.dir" value="${src.dir}"/>
  +  <property name="java.dir" value="${src.dir}/java"/>
     <property name="conf.dir" value="conf"/>
     <property name="lib.dir" value="lib"/>
     <property name="tools.dir" value="tools"/>
  
  
  
  1.1                  jakarta-james/src/java/org/apache/james/imapserver/ACL.java
  
  Index: ACL.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.Serializable;
  import java.util.Set;
  import org.apache.james.AccessControlException;
  import org.apache.james.AuthorizationException;
  
  /**
   * Interface for objects representing for an IMAP4rev1 Access Control List.
   * There should be one instance of this class per open mailbox. An Access
   * control list, for IMAP purposes, is a list of <identifier, rights> pairs.
   *
   * <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>References: rfc 2060, rfc 2086
   * @author <a href="mailto:charles@benett1.demon.co.uk">Charles Benett</a>
   * @version 0.1  on 14 Dec 2000
   */
  public interface ACL 
      extends Serializable {
  
      char LOOKUP_RIGHTS = 'l';
      char READ_RIGHTS = 'r';
      char KEEP_SEEN_RIGHTS = 's';
      char WRITE_RIGHTS = 'w';
      char INSERT_RIGHTS = 'i';
      char POST_RIGHTS = 'p';
      char CREATE_RIGHTS = 'c';
      char DELETE_RIGHTS = 'd';
      char ADMIN_RIGHTS = 'a';
      char ADD_RIGHTS = '+';
      char REMOVE_RIGHTS = '-';
      char[] RIGHTS = 
      {
          LOOKUP_RIGHTS, READ_RIGHTS, KEEP_SEEN_RIGHTS, WRITE_RIGHTS,
          INSERT_RIGHTS, POST_RIGHTS, CREATE_RIGHTS, DELETE_RIGHTS,
          ADMIN_RIGHTS
      };
  
      /**
       * 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
       * @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.
       */
      boolean setRights( String setter, 
                         String identifier,
                         String modification)
          throws AccessControlException, AuthorizationException;
  
      /**
       * 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.
       */
      String getRights( String getter, String identity )
          throws AccessControlException, AuthorizationException;
  
      /**
       * 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.
       */
      String getAllRights( String getter )
          throws AccessControlException, AuthorizationException;
  
      /**
       * 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.
       */
      String getRequiredRights( String getter, String identity )
          throws AccessControlException, AuthorizationException;
  
      /**
       * 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.
       */
      String getOptionalRights( String getter, String identity )
          throws AccessControlException, AuthorizationException;
  
      /**
       * 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.
       */
      boolean hasReadRights( String username )
          throws AccessControlException;
      boolean hasKeepSeenRights( String username )
          throws AccessControlException;
      boolean hasWriteRights( String username )
          throws AccessControlException;
      boolean hasInsertRights( String username )
          throws AccessControlException;
      boolean hasDeleteRights( String username )
          throws AccessControlException;
      boolean hasAdminRights( String username )
          throws AccessControlException;
  
      Set getUsersWithLookupRights();
  
      Set getUsersWithReadRights();
  }
  
  
  
  
  1.1                  jakarta-james/src/java/org/apache/james/imapserver/ACLMailbox.java
  
  Index: ACLMailbox.java
  ===================================================================
  /*
   * Copyright (C) The Apache Software Foundation. All rights reserved.
   *
   * This software is published under the terms of the Apache Software License
   * version 1.1, a copy of which has been included with this distribution in
   * the LICENSE file.
   */
  package org.apache.james.imapserver;
  
  import org.apache.avalon.framework.activity.Disposable;
  import org.apache.avalon.framework.activity.Initializable;
  import org.apache.avalon.framework.context.Contextualizable;
  
  /**
   * Interface for objects representing an IMAP4rev1 mailbox (folder) with
   * embedded Access Control List.
   *
   * Reference: RFC 2060
   * @author <a href="mailto:charles@benett1.demon.co.uk">Charles Benett</a>
   * @version 0.1 on 14 Dec 2000
   * @see Mailbox
   * @see ACL
   */
  public interface ACLMailbox
      extends ACL, Mailbox, Contextualizable, Initializable, Disposable {
  
      /**
       * Set the details particular to this Mailbox. Should only be called once,
       * at creation, and not when restored from storage.
       *
       * @param user String email local part of owner of a personal mailbox.
       * @param abName String absolute, ie user-independent, name of mailbox.
       * @param initialAdminUser String email local-part of a user who will be assigned admin rights on this mailbox
       */
      void prepareMailbox( String user, String absName, String initialAdminUser );
  
      /**
       * Re-initialises mailbox when restored from storage. Must be called after
       * setConfiguration, setContext, setComponentManager, if they are called,
       * but before any opertional methods are called.
       */
      void reinitialize()
          throws Exception;
  }
  
  
  
  
  
  1.1                  jakarta-james/src/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/src/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.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 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;
                  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/src/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/src/java/org/apache/james/imapserver/DefaultRecordRepository.java
  
  Index: DefaultRecordRepository.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 org.apache.log.LogKit;
  import org.apache.log.Logger;
  
  /**
   * Implementation of a RecordRepository on a FileSystem.
   *
   * @author <a href="mailto:charles@benett1.demon.co.uk">Charles Benett</a>
   * @version 0.1 on 14 Dec 2000
   * @see RecordRepository
   */
  public class DefaultRecordRepository implements RecordRepository   {
   
      private String path;
      private File repository;
      private Logger logger =  LogKit.getLoggerFor("james.mailstore");
  
      public void setPath(final String rootPath) {
          if (path != null) {
              throw new RuntimeException("Error: Attempt to reset AvalonRecordRepository");
          }
          path = rootPath;
          
          repository = new File(rootPath);
  
          if (!repository.isDirectory()) {
              if (! repository.mkdirs()){
                  throw new RuntimeException("Error: Cannot create directory for AvalonRecordRepository at: " + rootPath);
              }
          } else if (!repository.canWrite()) {
              throw new RuntimeException("Error: Cannot write to directory for AvalonRecordRepository at: " + rootPath);
          }
  
                  
      }
  
      public synchronized void store( final FolderRecord fr) {
          ObjectOutputStream out = null;
          try {
              String key = path + File.separator + fr.getAbsoluteName();
              out = new ObjectOutputStream( new FileOutputStream(key) );
              out.writeObject(fr);
              out.close();
              logger.info("Record stored for: " + fr.getAbsoluteName());
              notifyAll();
          } catch (Exception e) {
              if (out != null) {
                  try {
                      out.close();
                  } catch (Exception ignored) {
                  }
              }
              e.printStackTrace();
              throw new
                  RuntimeException("Exception caught while storing Folder Record: " + e);
          }
      }
  
      public synchronized Iterator getAbsoluteNames() {
          String[] names = repository.list();
          return Collections.unmodifiableList(Arrays.asList(names)).iterator();
      }
  
      public synchronized FolderRecord retrieve(final String folderAbsoluteName) {
          FolderRecord fr = null;
          ObjectInputStream in = null;
          try {
              String key = path + File.separator + folderAbsoluteName;
              in        = new ObjectInputStream( new FileInputStream(key) );
              fr = (FolderRecord) in.readObject();
              in.close();
    
          } catch (Exception e) {
              if (in != null) {
                  try {
                      in.close();
                  } catch (Exception ignored) {
                  }
              }
              e.printStackTrace();
              throw new
                  RuntimeException("Exception caught while reading Folder Record: " + e);
          } finally {
              notifyAll();
          }
          return fr;
      }
         
      public boolean containsRecord(String folderAbsoluteName) {
          File testFile = new File(repository, folderAbsoluteName);
          return testFile.exists();
      }
  }
  
      
  
  
  
  1.1                  jakarta-james/src/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.james.AccessControlException;
  import org.apache.james.AuthorizationException;
  import org.apache.james.Constants;
  import org.apache.james.core.EnhancedMimeMessage;
  import org.apache.james.services.UsersRepository;
  import org.apache.log.LogKit;
  import org.apache.log.Logger;
  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
      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 Logger logger = LogKit.getLoggerFor("james.MailRepository");
      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();
          logger.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 {
              logger.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();
          logger.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();
          logger = LogKit.getLoggerFor("james.MailRepository");
          logger.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() throws Exception {
          writeMailbox();
          logger.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();
          logger.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) {
              logger.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 {
              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) {
              logger.error("Null argument received in store.");
              throw new IllegalArgumentException("Null argument received in store.");
          }
          if (! (msgAttrs instanceof SimpleMessageAttributes)) {
              logger.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) {
              logger.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) {
                  logger.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);
          logger.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  EnhancedMimeMessage 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 EnhancedMimeMessage 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 EnhancedMimeMessage 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 EnhancedMimeMessage retrieveUID(int uid, String user)
          throws AccessControlException, AuthorizationException {
          if (!hasReadRights(user)) { //throws AccessControlException
              throw new AuthorizationException("Not authorized to read.");
          }
          EnhancedMimeMessage 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 EnhancedMimeMessage(Session.getDefaultInstance(System.getProperties(), null),inMsg);
                  inMsg.close();
              } catch(Exception e) {
                  logger.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) {
                      logger.error("Error closing streams: " + ie);
                  }
              }
              logger.info("EnhancedMimeMessage " + 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();
                  response.reinitialize();
              } catch(Exception e) {
                  logger.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) {
                      logger.error("Error closing streams: " + ie);
                  }
              }
              logger.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) {
                  logger.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) {
                      logger.error("Error closing streams: " + ie);
                  }
              }
              logger.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);
                  logger.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) {
                  logger.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) {
                      logger.error("Error closing streams: " + ie);
                  }
              }
              logger.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) {
                  logger.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) {
                      logger.error("Error closing streams: " + ie);
                  }
              }
              logger.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);
                      logger.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);
          }
          logger.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
       * @throws AccessControlException if user does not have lookup rights for
       * this mailbox.
       */
      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) {
                  logger.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) {
                      logger.error("Error closing streams: " + ie);
                  }
              }
              logger.info("InternetHeaders for message " + uid + " read from "
                          + absoluteName);
              return response;
          } else {
              return null;
          }
      }
  }
  
  
  
  
  
  
  1.1                  jakarta-james/src/java/org/apache/james/imapserver/Flags.java
  
  Index: Flags.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.Serializable;
  import java.util.HashSet;
  import java.util.Set;
  import org.apache.avalon.framework.activity.Initializable;
  
  /**
   * The set of flags associated with a message. The \Seen flag is maintained
   * on a per-user basis.
   *
   * <p>Reference: RFC 2060 - para 2.3
   * @author <a href="mailto:charles@benett1.demon.co.uk">Charles Benett</a>
   * @version 0.1 on 14 Dec 2000
   */
  public class Flags 
      implements Serializable, Initializable {
  
      public static final int ANSWERED  = 0;
      public static final int DELETED   = 1;
      public static final int DRAFT     = 2;
      public static final int FLAGGED   = 3;
      public static final int RECENT    = 4;
      public static final int SEEN      = 5;
  
      // Array does not include seen flag
      private boolean[] flags = {false, false, false, false, true};
  
      //users who have seen this message
      private Set users; 
  
      public Flags() {
      }
  
      /**
       * Initialisation - only for object creation not on deserialisation.
       */
      public void initialize() {
          users = new HashSet();
      }
  
      /**
       * Returns IMAP formatted String of Flags for named user
       */
      public String getFlags(String user) {
          StringBuffer buf = new StringBuffer();
          buf.append("(");
          if (flags[ANSWERED]) { buf.append("\\ANSWERED ");}
          if (flags[DELETED]) { buf.append("\\DELETED ");}
          if (flags[DRAFT]) { buf.append("\\DRAFT ");}
          if (flags[FLAGGED]) { buf.append("\\FLAGGED ");}
          if (flags[RECENT]) { buf.append("\\RECENT ");}
          if (users.contains(user)) { buf.append("\\SEEN ");}
          buf.append(")");
          return buf.toString();
      }
  
      /**
       * Sets Flags for message from IMAP-forammted string parameter.
       * <BR> The FLAGS<list> form overwrites existing flags, ie sets all other
       * flags to false.
       * <BR> The +FLAGS<list> form adds the flags in list to the existing flags
       * <BR> The -FLAGS<list> form removes the flags in list from the existing
       * flags
       * <BR> Note that the Recent flag cannot be set by user and is ignored by
       * this method.
       *
       * @param flagString a string formatted according to
       * RFC2060 store_att_flags
       * @param user the String email address of the user
       * @returns true if successful, false if not (including uninterpretable
       * argument)
       */
      public boolean setFlags(String flagString, String user) {
          flagString = flagString.toUpperCase();
          if (flagString.startsWith("FLAGS")) {
              boolean [] newflags = new boolean[5];
              newflags[ANSWERED]
                  = (flagString.indexOf("\\ANSWERED") != -1) ? true : false;
              newflags[DELETED]
                  = (flagString.indexOf("\\DELETED") != -1) ? true : false;
              newflags[DRAFT]
                  = (flagString.indexOf("\\DRAFT") != -1) ? true : false;
              newflags[FLAGGED]
                  = (flagString.indexOf("\\FLAGGED") != -1) ? true : false;
              newflags[RECENT] =  false;
              if (flagString.indexOf("\\SEEN") != -1) {
                  users.add(user);
              }
              System.arraycopy(newflags, 0, flags, 0, newflags.length);
              return true;
          } else if (flagString.startsWith("+FLAGS") ||flagString.startsWith("-FLAGS") ) {
              boolean mod = (flagString.startsWith("+") ? true : false);
              if (flagString.indexOf("\\ANSWERED") != -1) {
                  flags[ANSWERED] = mod;
              }
              if (flagString.indexOf("\\DELETED") != -1) {
                  flags[DELETED] = mod;
              }
              if (flagString.indexOf("\\DRAFT") != -1) {
                  flags[DRAFT] = mod;
              }
              if (flagString.indexOf("\\FLAGGED") != -1) {
                  flags[FLAGGED] = mod;
              }
              if (flagString.indexOf("\\SEEN") != -1) {
                  if( mod) {
                      users.add(user);
                  } else {
                      users.remove(user);
                  }
              }
              return true;
          } else {
              return false;
          }
      }
  
      public void setAnswered(boolean newState) {
          flags[ANSWERED] = newState;
      }
  
      public boolean isAnswered() {
          return flags[ANSWERED];
      }
  
      public void setDeleted(boolean newState) {
          flags[DELETED] = newState;
      }
  
      public boolean isDeleted() {
          return flags[DELETED];
      }
  
      public void setDraft(boolean newState) {
          flags[DRAFT] = newState;
      }
  
      public boolean isDraft() {
          return flags[DRAFT];
      }
  
      public void setFlagged(boolean newState) {
          flags[FLAGGED] = newState;
      }
  
      public boolean isFlagged() {
          return flags[FLAGGED];
      }
  
      public void setRecent(boolean newState) {
          flags[RECENT] = newState;
      }
  
      public boolean isRecent() {
          return flags[RECENT];
      }
  
      public void setSeen(boolean newState, String user) {
          if( newState) {
              users.add(user);
          } else {
              users.remove(user);
          }
      }
  
      public boolean isSeen(String user) {
          return users.contains(user);
      }
  }
  
  
  
  
  1.1                  jakarta-james/src/java/org/apache/james/imapserver/FolderRecord.java
  
  Index: FolderRecord.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.Map;
  import java.util.Set;
  
  /**
   * Interface for objects representing the record of a folder on an IMAP host.
   * 
   * @author  <a href="mailto:charles@benett1.demon.co.uk">Charles Benett</a>
   * @version 0.1 on 14 Dec 2000
   */
  
  public interface FolderRecord {
               
      /**
       * Returns the full name, including namespace, of this mailbox. 
       * Example 1: '#mail.projectBonzi'
       * Example 2: '#shared.projectBonzi'
       *
       * @returns String mailbox hierarchical name including namespace
       */
      String getFullName();
  
      /**
       * Returns the user in whose namespace the mailbox existed.
       * Example 1: 'fred.flintstone'
       * Example 2: ''
       *
       * @param user String a user.An empty string indicates that the 
       * mailbox name is absolute.
       */
      String getUser();
  
      /**
       * Returns the absolute name of this mailbox. The absolute name is
       * user-independent and unique for a given host.
       * Example 1: 'privatemail.fred.flintstone.projectBonzi'
       * Example 2: '#shared.projectBonzi'
       *
       * @returns String mailbox absolute name
       */
      String getAbsoluteName();
  
      /**
       * Records if this mailbox name is currently in use. The mailbox name is
       * in use when a mailbox with this name has been created. Implementations
       * that allow shared mailboxes may encounter a sate where the mailbox has
       * been deleted but there are clients who were already connected to the
       * mailbox. In this case the name remains in use until all clients have
       * either de-selected the mailbox or been disconnected from the server. 
       *
       * @param state boolean true when mailbox created, false when name no
       * longer in use.
       */
      void setNameInUse(boolean state);
  
      /**
       * Returns unavailability of name for a new mailbox.
       *
       * @returns true if this name is in use. Must return true if isDeleted
       * returns false.
       */
      boolean isNameInUse();
  
      /**
       *  Records if the corresponding mailbox has been deleted.
       *
       * @param state boolean true when mailbox deleted, false when created
       */
      void setDeleted(boolean state);
  
      /**
       * Returns whether mailbox has been deleted. A deleted mailbox is an
       * invalid argument to any IMAP command..
       *
       * @returns boolean true if mailbox does not exist
       */
      boolean isDeleted();
  
      /**
       * Records the Unique Identifier Validity Value for this mailbox.
       *
       * @param uidValidity int the uid validity value must be incremented if
       * the current uid values overlap uid values of this or a previous
       * incarnation of the mailbox.
       */
      void setUidValidity(int uidValidity);
  
      /**
       * Returns current uid validity value
       *
       * @returns int uid validity value
       */
      int getUidValidity();
  
      /**
       * Records the highest assigned Unique Identifier Value for this mailbox.
       *
       * @param uid int the highest uid assigned to a message in this mailbox.
       */
      void setHighestUid(int uid);
  
      /**
       * Returns current highest assigned uid value
       *
       * @returns int uid  value
       */
      int getHighestUid();
  
      /**
       * Record which users have LookupRights.
       *
       * @param users Set of Strings, one per user with Lookup rights
       */
      void setLookupRights(Set users);
  
      /**
       * Indicates if given user has lookup rights for this mailbox.  Need
       * lookup rights to be included in a List response.
       *
       * @returns boolean true if user has lookup rights
       */
      boolean hasLookupRights(String user);
  
      /**
       * Record which users have ReadRights.
       *
       * @param users Set of Strings, one per user with read rights
       */
      void setReadRights(Set users);
  
      /**
       * Indicates if given user has read rights for this mailbox. Need read
       * rights for user to select or examine mailbox.  
       *
       * @returns boolean true if user has read rights
       */
      boolean hasReadRights(String user);
  
      /**
       * Record if mailbox is marked.
       */
      void setMarked(boolean mark);
  
      /**
       * Indicates if the mailbox is marked. Usually means unseen mail.
       *
       * @returns boolean true if marked
       */
      boolean isMarked();
  
      /**
       * 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();
  
      /**
       * A folder is selectable by a given user if both it is not
       * NotSelectableByAnyone and the named user has read rights.
       *
       * @parm user the user to be tested
       * @returns true if user can SELECT this mailbox.
       */
      boolean isSelectable(String user);
  
      /**
       * Set number of messages in this folder
       */
      void setExists(int num);
  
      /**
       * Indicates number of messages in folder
       *
       * @returns int number of messages
       */
      int getExists();
  
      /**
       * Set number of messages in this folder with Recent flag set
       */
      void setRecent(int num);
  
      /**
       * Indicates no of messages with \Recent flag set
       *
       * @returns int no of messages with \Recent flag set
       */
      int getRecent();
  
      /**
       * Set map of users versus number of messages in this folder without
       * \Seen flag set for them
       */
      void setUnseenbyUser(Map unseen);
  
      /** 
       * 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);
  }
  
  
  
  1.1                  jakarta-james/src/java/org/apache/james/imapserver/Host.java
  
  Index: Host.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.Collection;
  import java.util.List;
  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.context.Contextualizable;
  import org.apache.avalon.phoenix.Service;
  import org.apache.james.AccessControlException;
  import org.apache.james.AuthorizationException;
  
  /**
   * A host machine that has an IMAP4rev1 messaging server. There should be one
   * instance of this class per instance of James.
   * An IMAP messaging system may span more than one host.
   * <p><code>String</code> parameters representing mailbox names must be the
   * full hierarchical name of the target, with namespace, as used by the
   * specified user. Examples:
   * '#mail.Inbox' or '#shared.finance.Q2Earnings'.
   * <p>An imap Host must keep track of existing and deleted mailboxes.
   *
   * References: rfc 2060, rfc 2193, rfc 2221
   * @author <a href="mailto:charles@benett1.demon.co.uk">Charles Benett</a>
   * @version 0.1 on 14 Dec 2000
   * @see FolderRecord
   * @see RecordRepository
   */
  public interface Host
      extends Configurable, Composable, Contextualizable, Service {
  
      String IMAP_HOST = "IMAP_HOST";
  
      /**
       * Establishes whether this host is the Home Server for the specified user.
       * Used during login to decide whether a LOGIN_REFERRAL has to be sent to
       * the client.
       *
       * @param username an email address
       * @returns true if inbox (and private mailfolders) are accessible through
       * this host.
       */
      boolean isHomeServer (String username);
  
      /**
       * Establishes if the specified user can access any mailboxes on this host.
       * Used during login process to decide what sort of LOGIN-REFERRAL must be
       * sent to client.
       *
       * @param username an email address
       * @returns true if the specified user has at least read access to any
       * mailboxes on this host.
       */
      boolean hasLocalAccess (String username);
  
      /**
       * Returns a reference to an existing Mailbox. The requested mailbox
       * must already exists on this server and the requesting user must have at
       * least lookup rights.
       *
       * @param user email address on whose behalf the request is made.
       * @param mailboxName String name of the target.
       * @returns an Mailbox reference.
       * @throws AccessControlException if the user does not have at least
       * lookup rights.
       * @throws MailboxException if mailbox does not exist locally.
       */
      ACLMailbox getMailbox(String user, String mailboxName)
          throws AccessControlException, MailboxException;
  
  
      /**
       * Returns a reference to a newly created Mailbox. The request should
       * specify a mailbox that does not already exist on this server, that
       * could exist on this server and that the user has rights to create.
       * If a system allocates different namespaces to different hosts then a
       * request to create a mailbox in a namespace not served by this host would
       * be an error.
       * It is an error to create a mailbox with the name of a mailbox that has
       * been deleted, if that name is still in use.
       *
       * @param user email address on whose behalf the request is made.
       * @param mailboxName String name of the target
       * @returns an Mailbox reference.
       * @throws MailboxException if mailbox already exists, locally or remotely,
       * or if mailbox cannot be created locally.
       * @throws AccessControlException if the user does not have lookup rights
       * for parent or any needed ancestor folder
       * lookup rights.
       * @throws AuthorizationException if mailbox could be created locally but
       * user does not have create rights.
       * @see FolderRecord
       */
      ACLMailbox createMailbox(String user, String mailboxName)
          throws AccessControlException, AuthorizationException, MailboxException;
  
  
      /**
       * Deletes an existing MailBox. Specified mailbox must already exist on
       * this server, and the user must have rights to delete it. (Mailbox delete
       * rights are implementation defined, one way is if the user would have the
       * right to create it).
       * Implementations must track deleted mailboxes
       *
       * @param user email address on whose behalf the request is made.
       * @param mailboxName String name of the target
       * @returns true if mailbox deleted successfully
       * @throws MailboxException if mailbox does not exist locally or is any
       * identities INBOX.
       * @throws AuthorizationException if mailbox exists locally but user does
       * not have rights to delete it.
       * @see FolderRecord
       */
      boolean deleteMailbox(String user, String mailboxName)
          throws MailboxException, AuthorizationException;
  
  
      /**
       * Renames an existing MailBox. The specified mailbox must already
       * exist locally, the requested name must not exist locally already but
       * must be able to be created locally and the user must have rights to
       * delete the existing mailbox and create a mailbox with the new name.
       * Any inferior hierarchical names must also be renamed.
       * If INBOX is renamed, the contents of INBOX are transferred to a new
       * folder with the new name, but INBOX is not deleted. If INBOX has
       * inferior mailboxes these are not renamed.
       * It is an error to create a mailbox with the name of a mailbox that has
       * been deleted, if that name is still in use.
       * Implementations must track deleted mailboxes
       *
       * @param user email address on whose behalf the request is made.
       * @param oldMailboxName String name of the existing mailbox
       * @param newMailboxName String target new name
       * @returns true if rename completed successfully
       * @throws MailboxException if mailbox does not exist locally, or there
       * is an existing mailbox with the new name.
       * @throws AuthorizationException if user does not have rights to delete
       * the existing mailbox or create the new mailbox.
       * @see FolderRecord
       */
      boolean renameMailbox( String user,
                             String oldMailboxName,
                             String newMailboxName )
          throws MailboxException, AuthorizationException;
  
      /**
       * Releases a reference to a mailbox, allowing Host to do any housekeeping.
       *
       * @param username String user who has finished with this mailbox
       * @param mbox a non-null reference to an ACL Mailbox.
       */
      void releaseMailbox( String user, ACLMailbox mbox );
  
      /**
       * Returns the namespace which should be used for this user unless they
       * expicitly request another.
       *
       * @param username String an email address
       * @returns a String of a namespace
       */
      String getDefaultNamespace( String username );
  
  
      /**
       * Return UIDValidity for named mailbox. Implementations should track
       * existing and deleted folders.
       *
       * @param mailbox String name of the existing mailbox
       * @returns  an integer containing the current UID Validity value.
       */
      //  public int getUIDValidity(String mailbox);
  
  
      /**
       * Returns an iterator over an unmodifiable collection of Strings
       * representing mailboxes on this host and their attributes. The specified
       * user must have at least lookup rights for each mailbox returned.
       * If the subscribedOnly flag is set, only mailboxes to which the
       * specified user is currently subscribed should be returned.
       * Implementations that may export circular hierarchies SHOULD restrict the
       * levels of hierarchy returned. The depth suggested by rfc 2683 is 20
       * hierarchy levels.
       * <p>The reference name must be non-empty. If the mailbox name is empty,
       * implementations must not throw either exception but must return a single
       * String (described below) if the reference name specifies a local mailbox
       * accessible to the user and a one-character String containing the
       * hierarchy delimiter of the referenced namespace, otherwise.
       * <p>Each String returned should be a space seperated triple of name
       * attributes, hierarchy delimiter and full mailbox name.   The mailbox
       * name should include the namespace and be relative to the specified user.
       * <p> RFC comments: Implementations SHOULD return quickly. They SHOULD
       * NOT go to excess trouble to calculate\Marked or \Unmarked status.
       * <p>JAMES comment: By elimination, implementations should usually include
       * \Noinferiors or \Noselect, if appropriate. Also, if the reference name
       * and mailbox name resolve to a single local mailbox, implementations
       * should establish all attributes.
       * <p> Note that servers cannot unilaterally remove mailboxes from the
       * subscribed list. A request with the subscribedOnly flag set that
       * attempts to list a deleted mailbox must return that mailbox with the
       * \Noselect attribute.
       *
       * @param username String non-empty email address of requester
       * @param referenceName String non-empty name, including namespace, of a
       * mailbox or level of mailbox hierarchy, relative to user.
       * @param mailboxName String name of a mailbox possible including a
       * wildcard.
       * @param subscribedOnly only return mailboxes currently subscribed.
       * @returns Collection of strings representing a set of mailboxes.
       * @throws AccessControlException if the user does not have at least
       * lookup rights to at least one mailbox in the set requested.
       * @throws MailboxException if the referenceName is not local or if
       * referenceName and mailbox name resolve to a single mailbox which does
       * not exist locally.
       */
      Collection listMailboxes( String username,
                                String referenceName,
                                String mailboxName,
                                boolean subscribedOnly )
          throws MailboxException, AccessControlException;
  
      /**
       * Subscribes a user to a mailbox. The mailbox must exist locally and the
       * user must have at least lookup rights to it.
       *
       * @param username String representation of an email address
       * @param mailbox String representation of a mailbox name.
       * @returns true if subscribe completes successfully
       * @throws AccessControlException if the mailbox exists but the user does
       * not have lookup rights.
       * @throws MailboxException if the mailbox does not exist locally.
       */
      boolean subscribe( String username, String mailbox )
          throws MailboxException, AccessControlException;
  
      /**
       * Unsubscribes from a given mailbox.
       *
       * @param username String representation of an email address
       * @param mailbox String representation of a mailbox name.
       * @returns true if unsubscribe completes successfully
       */
      boolean unsubscribe( String username, String mailbox )
          throws MailboxException, AccessControlException;
  
      /**
       * Returns a string giving the status of a mailbox on requested criteria.
       * Currently defined staus items are:
       * MESSAGES - Nummber of messages in mailbox
       * RECENT - Number of messages with \Recent flag set
       * UIDNEXT - The UID that will be assigned to the next message entering
       * the mailbox
       * UIDVALIDITY - The current UIDValidity value for the mailbox
       * UNSEEN - The number of messages which do not have the \Seen flag set.
       *
       * @param username String non-empty email address of requester
       * @param mailboxName String name of a mailbox (no wildcards allowed).
       * @param dataItems Vector of one or more Strings each of a single
       * status item.
       * @returns String consisting of space seperated pairs:
       * dataItem-space-number.
       * @throws AccessControlException if the user does not have at least
       * lookup rights to the mailbox requested.
       * @throws MailboxException if the mailboxName does not exist locally.
       */
      String getMailboxStatus( String username,
                               String mailboxName,
                               List dataItems )
          throws MailboxException, AccessControlException;
  }
  
  
  
  
  1.1                  jakarta-james/src/java/org/apache/james/imapserver/IMAPServer.java
  
  Index: IMAPServer.java
  ===================================================================
  /*
   * Copyright (C) The Apache Software Foundation. All rights reserved.
   *
   * This software is published under the terms of the Apache Software License
   * version 1.1, a copy of which has been included with this distribution in
   * the LICENSE file.
   */
  package org.apache.james.imapserver;
  
  import java.net.InetAddress;
  import java.net.UnknownHostException;
  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.cornerstone.services.connection.AbstractService;
  import org.apache.avalon.cornerstone.services.connection.ConnectionHandlerFactory;
  import org.apache.avalon.cornerstone.services.connection.DefaultHandlerFactory;
  
  /**
   * The Server listens on a specified port and passes connections to a
   * ConnectionHandler. In this implementation, each ConnectionHandler runs in
   * its own thread.
   *
   * @version o.1 on 14 Dec 2000
   * @author  Federico Barbieri <sc...@pop.systemy.it>
   * @author  <a href="mailto:charles@benett1.demon.co.uk">Charles Benett</a>
   */
  public class IMAPServer 
      extends AbstractService {
  
      protected ConnectionHandlerFactory createFactory()
      {
          return new DefaultHandlerFactory( SingleThreadedConnectionHandler.class );
      }
  
      public void configure( final Configuration configuration )
          throws ConfigurationException {
  
          m_port = configuration.getChild( "port" ).getValueAsInteger( 143 );
  
          try 
          { 
              final String bindAddress = configuration.getChild( "bind" ).getValue( null );
              if( null != bindAddress )
              {
                  m_bindTo = InetAddress.getByName( bindAddress ); 
              }
          }
          catch( final UnknownHostException unhe ) 
          {
              throw new ConfigurationException( "Malformed bind parameter", unhe );
          }
  
          final String useTLS = configuration.getChild("useTLS").getValue( "" );
          if( useTLS.equals( "TRUE" ) ) m_serverSocketType = "ssl";
  
          super.configure( configuration.getChild( "handler" ) );
      }
  
      public void initialize() throws Exception {
  
          getLogger().info( "IMAPServer init..." );
          getLogger().info( "IMAPListener using " + m_serverSocketType + " on port " + m_port );
          super.initialize();
          getLogger().info("IMAPServer ...init end");
          System.out.println("Started IMAP Server "+m_connectionName);
      }
  }
      
  
  
  
  1.1                  jakarta-james/src/java/org/apache/james/imapserver/IMAPServer.xinfo
  
  Index: IMAPServer.xinfo
  ===================================================================
  <?xml version="1.0"?>
  
  <blockinfo>
    <services>
      <service name="org.apache.avalon.framework.component.Component" version="1.0"/>
    </services>
  
    <dependencies>
      <dependency>
        <role>org.apache.james.services.MailStore</role>
        <service name="org.apache.james.services.MailStore" version="1.0"/>
      </dependency>
      <dependency>
        <role>org.apache.james.services.UsersStore</role>
        <service name="org.apache.james.services.UsersStore" version="1.0"/>
      </dependency>
      <dependency>
        <role>org.apache.avalon.cornerstone.services.connection.ConnectionManager</role>
        <service name="org.apache.avalon.cornerstone.services.connection.ConnectionManager" 
                 version="1.0"/>
      </dependency>
      <dependency>
        <role>org.apache.avalon.cornerstone.services.sockets.SocketManager</role>
        <service name="org.apache.avalon.cornerstone.services.sockets.SocketManager" version="1.0"/>
      </dependency>
      <dependency>
        <role>org.apache.avalon.cornerstone.services.scheduler.TimeScheduler</role>
        <service name="org.apache.avalon.cornerstone.services.scheduler.TimeScheduler" version="1.0"/>
      </dependency> 
      <dependency>
        <role>org.apache.james.services.MailServer</role>
        <service name="org.apache.james.services.MailServer" version="1.0"/>
      </dependency> 
    </dependencies>  
  </blockinfo>
  
  
  
  1.1                  jakarta-james/src/java/org/apache/james/imapserver/ImapRequest.java
  
  Index: ImapRequest.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.OutputStream;
  import java.io.PrintWriter;
  import java.util.StringTokenizer;
  
  /**
   * An single client request to an IMAP server, with necessary details for
   * command processing
   *
   * @author <a href="mailto:charles@benett1.demon.co.uk">Charles Benett</a>
   * @version 0.1 on 17 Jan 2001
   */
  public class ImapRequest {
  
      private StringTokenizer commandLine;
      private boolean useUIDs;
      private ACLMailbox currentMailbox;
      private String commandRaw;
      private String tag;
      private SingleThreadedConnectionHandler caller;
      private String currentFolder;
  
      public ImapRequest(SingleThreadedConnectionHandler handler) {
          caller = handler;
      }
  
      public SingleThreadedConnectionHandler getCaller() {
          return caller;
      }
  
      public void setCommandLine(StringTokenizer st) {
          commandLine = st;
      }
  
      public StringTokenizer getCommandLine() {
          return commandLine;
      }
  
      public void setUseUIDs(boolean state) {
          useUIDs = state;
      }
  
      public boolean useUIDs() {
          return useUIDs;
      }
  
      public void setCurrentMailbox(ACLMailbox mbox) {
          currentMailbox = mbox;
      }
  
      public ACLMailbox getCurrentMailbox() {
          return currentMailbox;
      }
  
      public void setCommandRaw(String raw) {
          commandRaw = raw;
      }
  
      public String getCommandRaw() {
          return commandRaw;
      }
  
      public void setTag(String t) {
          tag = t;
      }
  
      public String getTag() {
          return tag;
      }
  
      public void setCurrentFolder(String f) {
          currentFolder = f;
      }
  
      public String getCurrentFolder() {
          return currentFolder;
      }
  }
  
  
  
  1.1                  jakarta-james/src/java/org/apache/james/imapserver/JamesHost.java
  
  Index: JamesHost.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.activity.Initializable;
  import org.apache.avalon.framework.component.Component;
  import org.apache.avalon.framework.component.ComponentManager;
  import org.apache.avalon.framework.context.Context;
  import org.apache.avalon.framework.configuration.Configuration;
  import org.apache.avalon.framework.configuration.ConfigurationException;
  import org.apache.james.AccessControlException;
  import org.apache.james.AuthorizationException;
  import org.apache.james.services.*;
  import org.apache.log.LogKit;
  import org.apache.log.Logger;
  
  /**
   * A single host that has an IMAP4rev1 messaging server.
   * There should be one instance of this class per instance of James.
   * An IMAP messaging system may span more than one host.
   * <p><code>String</code> parameters representing mailbox names must be the
   * full hierarchical name of the target, with namespace, as used by the
   * specified user. Examples: 
   * '#mail.Inbox' or '#shared.finance.Q2Earnings'.
   * <p>An imap Host must keep track of existing and deleted mailboxes. 
   *
   * References: rfc 2060, rfc 2193, rfc 2221
   * @author <a href="mailto:charles@benett1.demon.co.uk">Charles Benett</a>
   * @version 0.1 on 14 Dec 2000
   * @see FolderRecord
   * @see RecordRepository
   */
  public class JamesHost 
      implements Host, Component, Initializable {
  
      private Context context;
      private Configuration conf;
      private ComponentManager compMgr;
      private Logger logger  = LogKit.getLoggerFor("james.JamesHost");
      private String rootPath; // ends with File.seperator
      private File rootFolder;
      private IMAPSystem imapSystem;
      //private UserManager usersManager;
      private UsersRepository localUsers;
      private RecordRepository recordRep;
      private Map openMailboxes; //maps absoluteName to ACLMailbox
      private Map mailboxCounts; // maps absoluteName to Integer count of extant references.
      private String namespaceToken;
      private String privateNamespace;
      private String privateNamespaceSeparator;
      private String otherUsersNamespace;
      private String otherUsersNamespaceSeparator;
      private String sharedNamespace;
      private String sharedNamespaceSeparator;
     
  
      /*
       * Note on implemented namespaces.
       * 3 namespaces are (partially) implemented.
       * 1) Private namespace ie access to a user's own mailboxes.
       * Full mailbox names (ie what user sees) of the form:
       *   #mail.Inbox or #mail.apache.James
       * Absolute names of the form:
       *   #mail.fred.flintstone.Inbox or #mail.fred.flintstone.apache.James
       * 2) Other users namespace ie access to other users mailboxes
       * subject to access control, of course
       * Full mailbox names (ie what user sees) of the form:
       *   #users.captain.scarlet.Inbox or #users.RobinHood.apache.James
       * Absolute names of the form:
       *   #mail.captain.scarlet.Inbox or #mail.RobinHood.apache.James
       * 3) Shared mailboxes
       * not fully implemented.
       * Full mailbox names (ie what user sees) of the form:
       *   #shared.projectAlpha.masterplan or #shared.customerservice.Inbox
       * Absolute names of the form:
       *   #shared.projectAlpha.masterplan or #shared.customerservice.Inbox
       */
  
      /*
       * Note on filename extensions
       * Records in AvalonRecordRepository - no extension
       * Mailboxes - mbr
       * MimeMessage - msg
       * MessageAttributes - att
       */
  
      /* No constructor */
  
      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 initialize() throws Exception {
  
          logger.info("JamesHost init...");
          imapSystem = (IMAPSystem) compMgr.lookup("org.apache.james.imapserver.IMAPSystem");
          localUsers = (UsersRepository)compMgr.lookup("org.apache.james.services.UsersRepository");
          String recordRepDest
              = conf.getChild("recordRepository").getValue();
          recordRep = new DefaultRecordRepository();
          recordRep.setPath(recordRepDest);
          logger.info("AvalonRecordRepository opened at " + recordRepDest);
          rootPath = conf.getChild("mailboxRepository").getValue();
          if (!rootPath.endsWith(File.separator)) {
              rootPath = rootPath + File.separator;
          }
          rootFolder =  new File(rootPath);
          if (!rootFolder.isDirectory()) {
              if (! rootFolder.mkdir()){
                  throw new RuntimeException("Error: Cannot create directory for MailboxRepository");
              }
          } else if (!rootFolder.canWrite()) {
              throw new RuntimeException("Error: Cannot write to directory for MailboxRepository");
          }
          logger.info("IMAP Mailbox Repository opened at " + rootPath);
          Configuration namespaces = conf.getChild("namespaces");
          namespaceToken = namespaces.getAttribute("token");
          privateNamespace
              = namespaces.getChild("privateNamespace").getValue();
          privateNamespaceSeparator
              = namespaces.getChild("privateNamespace").getAttribute("separator");
          otherUsersNamespace
              = namespaces.getChild("otherusersNamespace").getValue();
          otherUsersNamespaceSeparator
              = namespaces.getChild("otherusersNamespace").getAttribute("separator");
          sharedNamespace
              = namespaces.getChild("sharedNamespace").getValue();
          sharedNamespaceSeparator
              = namespaces.getChild("sharedNamespace").getAttribute("separator");
          logger.info("Handling mail for namespaces: "+ privateNamespace + ", " + otherUsersNamespace + ", " + sharedNamespace);
          openMailboxes = new HashMap(31); // how big should this start?
          mailboxCounts = new HashMap(31);
          logger.info("JamesHost ...init end");
      }
  
      /**
       * Establishes whether this host is the Home Server for the specified user.
       * Used during login to decide whether a LOGIN_REFERRAL has to be sent to
       * the client.
       *
       * @param username an email address
       * @returns true if inbox (and private mailfolders) are accessible through
       * this host. 
       */
      public boolean isHomeServer (String username) {
          return localUsers.contains(username);
      }
  
      /**
       * Establishes if the specified user can access any mailboxes on this host.
       * Used during login process to decide what sort of LOGIN-REFERRAL must be
       * sent to client.
       *
       * @param username an email address
       * @returns true if the specified user has at least read access to any
       * mailboxes on this host.
       */
      public boolean hasLocalAccess (String username) {
          return localUsers.contains(username);
      }
  
      /**
       * Returns a reference to an existing Mailbox. The requested mailbox
       * must already exists on this server and the requesting user must have at
       * least lookup rights.
       *
       * @param user email address on whose behalf the request is made.
       * @param mailboxName String name of the target.
       * @returns an Mailbox reference.
       * @throws AccessControlException if the user does not have at least
       * lookup rights.
       * @throws MailboxException if mailbox does not exist locally.
       */
      public synchronized ACLMailbox getMailbox(String user, String mailboxName)
          throws AccessControlException, MailboxException { 
          if (user == null || mailboxName == null) {
              logger.error("Null parameters received in getMailbox(). " );
              throw new RuntimeException("Null parameters received.");
          } else if (user.equals("")
                     ||(!mailboxName.startsWith(namespaceToken))) {
              logger.error("Empty/ incorrect parameters received in getMailbox().");
              throw new RuntimeException("Empty/incorrect parameters received.");
          }
          logger.debug("Getting mailbox " + mailboxName + " for " + user);
          String absoluteName = getAbsoluteName(user, mailboxName);
          if (absoluteName == null) {
              logger.error("Parameters in getMailbox() cannot be interpreted. ");
              throw new RuntimeException("Parameters in getMailbox() cannot be interpreted.");
          }
          return getAbsoluteMailbox(user, absoluteName);
      }
  
      private synchronized ACLMailbox getAbsoluteMailbox(String user, String absoluteName)
          throws AccessControlException, MailboxException { 
  
          ACLMailbox mailbox = null;
          FolderRecord record = null;
  
          // Has a folder with this name ever been created?
          if(! recordRep.containsRecord(absoluteName)) {
              throw new MailboxException("Mailbox: " + absoluteName + " has never been created.", MailboxException.NOT_LOCAL);
          } else {
              record = recordRep.retrieve(absoluteName);
              if (record.isDeleted()) {
                  throw new MailboxException("Mailbox has been deleted", MailboxException.LOCAL_BUT_DELETED);
              } else if (openMailboxes.containsKey(absoluteName)) {
                  mailbox = (ACLMailbox) openMailboxes.get(absoluteName);
                  if (!mailbox.hasLookupRights(user)) {
                      throw new AccessControlException("No lookup rights.");
                  } else {
                      Integer c = (Integer)mailboxCounts.get(absoluteName);
                      int count = c.intValue() + 1;
                      mailboxCounts.put(absoluteName, (new Integer(count)));
                      logger.info("Request no " + count + " for " + absoluteName);
                      return mailbox;
                  }
              } else {
                  String owner = record.getUser();
                  String key = getPath(absoluteName, owner);
                  ObjectInputStream in = null;
                  try {
                      in        = new ObjectInputStream( new FileInputStream(key + File.separator + FileMailbox.MAILBOX_FILE_NAME) );
                      mailbox = (FileMailbox) in.readObject();
                      mailbox.configure(conf);
                      mailbox.contextualize(context);
                      mailbox.compose(compMgr);
                      mailbox.reinitialize();
                  } catch (Exception e) {
                      e.printStackTrace();
                      throw new
                          RuntimeException("Exception caught while reading FileMailbox: " + e);
                  } finally {
                      if (in != null) {
                          try {
                              in.close();
                          } catch (Exception ignored) {
                          }
                      }
                      notifyAll();
                  }
                  if (!mailbox.hasLookupRights(user)) {
                      throw new AccessControlException("No lookup rights.");
                  }
                  openMailboxes.put(absoluteName, mailbox);
                  mailboxCounts.put(absoluteName, new Integer(1));
                  return mailbox;
              }
          }
      }
  
      /**
       * Returns a reference to a newly created Mailbox. The request should
       * specify a mailbox that does not already exist on this server, that
       * could exist on this server and that the user has rights to create.
       * If a system allocates different namespaces to different hosts then a
       * request to create a mailbox in a namespace not served by this host would
       * be an error.
       * It is an error to create a mailbox with the name of a mailbox that has
       * been deleted, if that name is still in use. 
       *
       * @param user email address on whose behalf the request is made.
       * @param mailboxName String name of the target
       * @returns an Mailbox reference.
       * @throws AccessControlException if the user does not have lookup rights
       * for parent or any needed ancestor folder
       * lookup rights.
       * @throws AuthorizationException if mailbox could be created locally but
       * user does not have create rights.
       * @throws MailboxException if mailbox already exists, locally or remotely,
       * or if mailbox cannot be created locally.
       * @see FolderRecord
       */
      public synchronized ACLMailbox createMailbox(String user, String mailboxName)
          throws AccessControlException, AuthorizationException,
          MailboxException {
          if (user == null || mailboxName == null) {
              logger.error("Null parameters received in createMailbox(). " );
              throw new RuntimeException("Null parameters received.");
          } else if (user.equals("")
                     ||(!mailboxName.startsWith(namespaceToken))) {
              logger.error("Empty/ incorrect parameters received in createMailbox().");
              throw new RuntimeException("Empty/incorrect parameters received.");
          }
          String absoluteName = getAbsoluteName(user, mailboxName);
          if (absoluteName == null) {
              logger.error("Parameters in createMailbox() cannot be interpreted. ");
              throw new RuntimeException("Parameters in createMailbox() cannot be interpreted.");
          }
          logger.debug("JamesHost createMailbox() for:  " + absoluteName);
  
          return createAbsoluteMailbox(user, absoluteName);
      }
  
      private synchronized ACLMailbox createAbsoluteMailbox(String user, String absoluteName)
          throws AccessControlException, AuthorizationException,
          MailboxException {
          ACLMailbox mailbox = null;
          FolderRecord record = null;
          ACLMailbox parentMailbox = null;
  
          // Has a folder with this name ever been created?
          if( recordRep.containsRecord(absoluteName)) {
              record = recordRep.retrieve(absoluteName);
              if (!record.isDeleted()) {
                  logger.error("Attempt to create an existing Mailbox.");
                  throw new MailboxException("Mailbox already exists", MailboxException.ALREADY_EXISTS_LOCALLY);
              }
          } else {
              String parent
                  = absoluteName.substring(0, absoluteName.lastIndexOf(privateNamespaceSeparator));
              if (!(parent.startsWith(privateNamespace + privateNamespaceSeparator) || parent.startsWith(sharedNamespace + sharedNamespaceSeparator))) {
                  logger.warn("No such parent: " + parent);
                  throw new MailboxException("No such parent: " + parent);
              }
              //Recurse to a created and not deleted mailbox
              try {
                  parentMailbox = getAbsoluteMailbox(user, parent);
              } catch (MailboxException mbe) {
                  if (mbe.getStatus().equals(MailboxException.NOT_LOCAL)
                      || mbe.getStatus().equals(MailboxException.LOCAL_BUT_DELETED)) {
                      parentMailbox = createAbsoluteMailbox(user, parent);
                  } else {
                      throw new MailboxException(mbe.getMessage(), mbe.getStatus());
                  }
              }
              // Does user have create rights in parent mailbox?
              if (!parentMailbox.hasCreateRights(user)) {
                  throw new AuthorizationException("User does not have create rights.");
              } 
              try {
                  mailbox = new FileMailbox();
                  mailbox.configure(conf);
                  mailbox.contextualize(context);
                  mailbox.compose(compMgr);
                  mailbox.prepareMailbox(user, absoluteName, user);
                  mailbox.initialize();
              } catch (Exception e) {
                  logger.error("Exception creating mailbox: " + e);
                  throw new MailboxException("Exception creating mailbox: " + e);
              }
              String mailboxName
                  = absoluteName.substring(0, absoluteName.indexOf(user))
                  + absoluteName.substring(absoluteName.indexOf(user) + user.length(), absoluteName.length());
              SimpleFolderRecord fr
                  = new SimpleFolderRecord(mailboxName, user, absoluteName);
              fr.initialize();
              recordRep.store(fr);
              openMailboxes.put(absoluteName, mailbox);
              mailboxCounts.put(absoluteName, new Integer(1));
          }
          
          return mailbox;
      }
  
      /**
       * Releases a reference to a mailbox, allowing Host to do any housekeeping.
       *
       * @param mbox a non-null reference to an ACL Mailbox.
       */
      public void releaseMailbox(String user, ACLMailbox mailbox) {
          if (mailbox == null) {
              logger.debug("Attempt to release mailbox with null reference");
              return;
          }
          if (user != MailServer.MDA) {
              mailbox.unsetRecent();
          }
          String absName = mailbox.getAbsoluteName();
          Integer c = (Integer)mailboxCounts.get(absName);
          int count = c.intValue() - 1;
          if (count < 1) {
              openMailboxes.remove(absName);
              mailboxCounts.remove(absName);
              try {
                  FolderRecord fr = recordRep.retrieve(absName);
                  fr.setUidValidity(mailbox.getUIDValidity());
                  fr.setHighestUid(mailbox.getNextUID() -1);
                  fr.setLookupRights(mailbox.getUsersWithLookupRights());
                  fr.setReadRights(mailbox.getUsersWithReadRights());
                  fr.setMarked(mailbox.isMarked());
                  fr.setNotSelectableByAnyone(mailbox.isNotSelectableByAnyone());
                  fr.setExists(mailbox.getExists());
                  fr.setRecent(mailbox.getRecent());
                  fr.setUnseenbyUser(mailbox.getUnseenByUser());
                  recordRep.store(fr);
                  mailbox.dispose();
                  mailbox = null;
                  logger.info("Mailbox object destroyed: " + absName);
              } catch (Exception e) {
                  logger.error("Exception destroying mailbox object: " + e);
                  e.printStackTrace();
              }
          } else {
              logger.info("Mailbox " + absName + " now has " + count + "live references");
              mailboxCounts.put(absName, (new Integer(count)));
          }
      }
  
      /**
       * Deletes an existing MailBox. Specified mailbox must already exist on
       * this server, and the user must have rights to delete it. (Mailbox delete
       * rights are implementation defined, one way is if the user would have the
       * right to create it).
       * Implementations must track deleted mailboxes
       *
       * @param user email address on whose behalf the request is made.
       * @param mailboxName String name of the target
       * @returns true if mailbox deleted successfully
       * @throws MailboxException if mailbox does not exist locally or is any
       * identities INBOX.
       * @throws AuthorizationException if mailbox exists locally but user does
       * not have rights to delete it.
       * @see FolderRecord
       */
      public boolean deleteMailbox(String user, String mailboxName)
          throws MailboxException, AuthorizationException {
          if (user == null || mailboxName == null) {
              logger.error("Null parameters received in deleteMailbox(). ");
              throw new RuntimeException("Null parameters received.");
          } else if (user.equals("")
                     ||(!mailboxName.startsWith(namespaceToken))) {
              logger.error("Empty/ incorrect parameters received in deleteMailbox().");
              throw new RuntimeException("Empty/incorrect parameters received.");
          }
          String absoluteName = getAbsoluteName(user, mailboxName);
          if (absoluteName == null) {
              logger.error("Parameters in deleteMailbox() cannot be interpreted. ");
              throw new RuntimeException("Parameters in deleteMailbox() cannot be interpreted.");
          }
          logger.debug("JamesHost deleteMailbox() called for:  " + absoluteName);
          return false;
          //return deleteAbsoluteMailbox(user, absoluteName);
      }
  
      /**
       * Renames an existing MailBox. The specified mailbox must already
       * exist locally, the requested name must not exist locally already but
       * must be able to be created locally and the user must have rights to
       * delete the existing mailbox and create a mailbox with the new name.
       * Any inferior hierarchical names must also be renamed.
       * If INBOX is renamed, the contents of INBOX are transferred to a new
       * folder with the new name, but INBOX is not deleted. If INBOX has
       * inferior mailboxes these are not renamed.
       * It is an error to create a mailbox with the name of a mailbox that has
       * been deleted, if that name is still in use. 
       * Implementations must track deleted mailboxes
       *
  
       * @param user email address on whose behalf the request is made.
       * @param oldMailboxName String name of the existing mailbox
       * @param newMailboxName String target new name
       * @returns true if rename completed successfully
       * @throws MailboxException if mailbox does not exist locally, or there
       * is an existing mailbox with the new name. 
       * @throws AuthorizationException if user does not have rights to delete
       * the existing mailbox or create the new mailbox.
       * @see FolderRecord
       */
      public boolean renameMailbox(String user, String oldMailboxName,
                                   String newMailboxName)
          throws MailboxException, AuthorizationException {
          return false;
      }
  
      /**
       * Returns the namespace which should be used for this user unless they
       * expicitly request another.
       * 
       * @param username String an email address
       * @returns a String of a namespace
       */
      public String getDefaultNamespace(String username) {
          return privateNamespace;
      }
  
      /**
       * Return UIDValidity for named mailbox. Implementations should track
       * existing and deleted folders. 
       *
       * @param mailbox String name of the existing mailbox
       * @returns  an integer containing the current UID Validity value.
       */
      //  public int getUIDValidity(String mailbox);
  
      /**
       * Returns an iterator over an unmodifiable collection of Strings
       * representing mailboxes on this host and their attributes. The specified
       * user must have at least lookup rights for each mailbox returned.
       * If the subscribedOnly flag is set, only mailboxes to which the
       * specified user is currently subscribed should be returned.
       * Implementations that may export circular hierarchies SHOULD restrict the
       * levels of hierarchy returned. The depth suggested by rfc 2683 is 20
       * hierarchy levels.
       * <p>The reference name must be non-empty. If the mailbox name is empty,
       * implementations must not throw either exception but must return a single
       * String (described below) if the reference name specifies a local mailbox
       * accessible to the user and a one-character String containing the
       * hierarchy delimiter of the referenced namespace, otherwise. 
       * <p>Each String returned should be a space seperated triple of name
       * attributes, hierarchy delimiter and full mailbox name.   The mailbox
       * name should include the namespace and be relative to the specified user.
       * <p> RFC comments: Implementations SHOULD return quickly. They SHOULD
       * NOT go to excess trouble to calculate\Marked or \Unmarked status.
       * <p>JAMES comment: By elimination, implementations should usually include
       * \Noinferiors or \Noselect, if appropriate. Also, if the reference name
       * and mailbox name resolve to a single local mailbox, implementations
       * should establish all attributes.
       * <p> Note that servers cannot unilaterally remove mailboxes from the
       * subscribed list. A request with the subscribedOnly flag set that
       * attempts to list a deleted mailbox must return that mailbox with the
       * \Noselect attribute.
       *
       * @param username String non-empty email address of requester
       * @param referenceName String non-empty name, including namespace, of a
       * mailbox or level of mailbox hierarchy, relative to user.
       * @param mailboxName String name of a mailbox possible including a
       * wildcard.
       * @param subscribedOnly only return mailboxes currently subscribed.
       * @returns Collection of strings representing a set of mailboxes.
       * @throws AccessControlException if the user does not have at least
       * lookup rights to at least one mailbox in the set requested.
       * @throws MailboxException if the referenceName is not local or if
       * referenceName and mailbox name resolve to a single mailbox which does
       * not exist locally.
       */
      public synchronized Collection listMailboxes(String username,
                                                   String referenceName,
                                                   String mailboxName,
                                                   boolean subscribedOnly)
          throws MailboxException, AccessControlException {
          logger.debug("Listing for user: " + username + " ref " + referenceName + " mailbox " + mailboxName);
          List responseList = new ArrayList();
          if (subscribedOnly == true ) {
              return null;
          }
          if (mailboxName.equals("")) {
              // means don't List but give root of hierarchy and separator
              String response;
              if (referenceName.startsWith(privateNamespace)) {
                  response = "(\\Noselect) \"" + privateNamespaceSeparator
                      + "\" " + privateNamespace;
              } else if (referenceName.startsWith(otherUsersNamespace)) {
                  response = "(\\Noselect) \"" + otherUsersNamespaceSeparator
                      + "\" " + otherUsersNamespace;
              } else if (referenceName.startsWith(sharedNamespace)) {
                  response = "(\\Noselect) \"" + sharedNamespaceSeparator
                      + "\" " + sharedNamespace;
              } else {
                  logger.error("Weird arguments for LIST? referenceName was: " + referenceName + " and mailbox names was " + mailboxName);
                  return null;
              }
              responseList.add(response);
              return responseList;
          }
  
          //short-circuit evaluation for namespaces
          String response = null;
          if (mailboxName.equals(privateNamespace + "%")) {
              response = "(\\Noselect) \"" + privateNamespaceSeparator +  "\" " + privateNamespace;
          } else if (mailboxName.equals(otherUsersNamespace + "%")) {
              response = "(\\Noselect) \"" + otherUsersNamespaceSeparator +  "\" " + otherUsersNamespace;
          } else if (mailboxName.equals(otherUsersNamespace + "%")) {
              response = "(\\Noselect) \"" + sharedNamespaceSeparator +  "\" " + sharedNamespace;
          }
          if (response != null) {
              responseList.add(response);
              return responseList;
          }
          try { // for debugging purposes
            
              //Short-circuit for Netscape client calls - remove first % in, e.g. #mail%.%
              // Eventually we need to handle % anywhere in mailboxname
              if (mailboxName.startsWith(privateNamespace + "%")) {
                  mailboxName = privateNamespace + mailboxName.substring(privateNamespace.length() + 1);
              } else if (mailboxName.startsWith(otherUsersNamespace + "%")) {
                  mailboxName = otherUsersNamespace + mailboxName.substring(otherUsersNamespace.length() + 1);
              } else if (mailboxName.startsWith(sharedNamespace + "%")) {
                  mailboxName = sharedNamespace + mailboxName.substring(sharedNamespace.length() + 1);
              }
              
              //mailboxName = mailboxName.substring(0,mailboxName.length() -1);
              logger.debug("Refined mailboxName to: " + mailboxName);
              String userTarget;
              if (mailboxName.startsWith("#")) {
                  userTarget = mailboxName;
              } else {
                  if (referenceName.endsWith(".")) {
                      userTarget = referenceName + mailboxName;
                  } else {
                      userTarget = referenceName + "." + mailboxName;
                  }
              }
              String target = getAbsoluteName(username, userTarget);
              logger.info("Target is: " + target);
              if (target == null) { return new HashSet();}
              int firstPercent = target.indexOf("%");
              int firstStar = target.indexOf("*");
              logger.info("First percent at index: " + firstPercent);
              logger.info("First star at index: " + firstStar);
              Iterator all = recordRep.getAbsoluteNames();
              Set matches = new HashSet();
  
              while (all.hasNext()) {
                  boolean match = false;
                  String test = (String)all.next();
                  logger.info("Test is: " + test);
                  if (firstPercent == -1 && firstStar == -1) {
                      // no wildcards so exact or nothing
                      match = test.equals(target);
                      logger.debug("match/ no match at test 1"); 
                  } else if (firstStar == -1) {
                      // only % wildcards
                      if (!test.startsWith(target.substring(0, firstPercent))) {
                          match = false;
                          logger.debug("fail match at test 2");
                      } else if (firstPercent == target.length() -1) {
                          // only one % and it is terminating char
                          target = target.substring(0, firstPercent);
                          logger.debug("Refined target to: " + target);
                          if (test.equals(target)) {
                              match = true;
                              logger.debug("pass match at test 3");
                          } else if ( (test.length() > target.length())
                                      &&  (test.indexOf('.', target.length())
                                           == -1)
                                      ) {
                              match = true;
                              logger.debug("pass match at test 4");
                          } else {
                              match = false;
                              logger.debug("fail match at test 5");
                          }
                      } else {
                          int secondPercent = target.indexOf("%", firstPercent + 1);
                          match = false; // unfinished
                          logger.debug("fail match at test 6");
                      }
                  } else {
                      //at least one star
                      int firstWildcard = -1;
                      if (firstPercent != -1 && firstStar == -1) {
                          firstWildcard = firstPercent;
                      } else if (firstStar != -1 && firstPercent == -1) {
                          firstWildcard = firstStar;
                      } else if (firstPercent < firstStar) {
                          firstWildcard = firstPercent;
                      } else {
                          firstWildcard = firstStar;
                      }
  
                      if (!test.startsWith(target.substring(0, firstWildcard))) {
                          match = false;
                      } else {
                          match = false;
                      }
                  }
                    
                  if (match)  {
                      logger.info("Processing match for : " + test);
                      FolderRecord record = recordRep.retrieve(test);
                      ACLMailbox mailbox = null;
                      StringBuffer buf = new StringBuffer();
                      buf.append("(");
                      if (!record.isDeleted() && openMailboxes.containsKey(target)) {
                          mailbox = (ACLMailbox) openMailboxes.get(target);
                      }
                      if (record.isDeleted()) {
                          buf.append("\\Noselect");
                      } else if(openMailboxes.containsKey(target)) {
                          mailbox = (ACLMailbox) openMailboxes.get(target);
                          if (!mailbox.isSelectable(username)) {
                              buf.append("\\Noselect");
                          }
                          if (mailbox.isMarked()) {
                              buf.append("\\Marked");
                          } else {
                              buf.append("\\Unmarked");
                          }
                      } else {
                          if (!record.isSelectable(username)) {
                              buf.append("\\Noselect");
                          }
                          if (record.isMarked()) {
                              buf.append("\\Marked");
                          } else {
                              buf.append("\\Unmarked");
                          }
                      }
                      buf.append(") \"");
                      if(userTarget.startsWith(privateNamespace)) {
                          buf.append(privateNamespaceSeparator);
                      } else if(userTarget.startsWith(otherUsersNamespace)) {
                          buf.append(otherUsersNamespaceSeparator);
                      } else {
                          buf.append(sharedNamespaceSeparator);
                      }
                      buf.append("\" ");
                      if (test.toUpperCase().indexOf("INBOX") == -1) {
                          buf.append(getFullMailboxName(username, test) );
                      } else {
                          buf.append( "INBOX");
                      }
                      matches.add(buf.toString());
                  }
              }
              return matches;
          } catch (Exception e) {
              logger.error("Exception with list request for mailbox " + mailboxName);
              e.printStackTrace();
              return null;
          }
      }
      
      /**
       * Subscribes a user to a mailbox. The mailbox must exist locally and the
       * user must have at least lookup rights to it.
       *
       * @param username String representation of an email address
       * @param mailbox String representation of a mailbox name.
       * @returns true if subscribe completes successfully
       * @throws AccessControlException if the mailbox exists but the user does
       * not have lookup rights.
       * @throws MailboxException if the mailbox does not exist locally.
       */
      public boolean subscribe(String username, String mailbox)
          throws MailboxException, AccessControlException {
          return false;
      }
  
      /**
       * Unsubscribes from a given mailbox. 
       *
       * @param username String representation of an email address
       * @param mailbox String representation of a mailbox name.
       * @returns true if unsubscribe completes successfully
       */
      public boolean unsubscribe(String username, String mailbox)
          throws MailboxException, AccessControlException {
          return false;
      }
  
      /**
       * Returns a string giving the status of a mailbox on requested criteria.
       * Currently defined staus items are:
       * MESSAGES - Nummber of messages in mailbox
       * RECENT - Number of messages with \Recent flag set
       * UIDNEXT - The UID that will be assigned to the next message entering
       * the mailbox
       * UIDVALIDITY - The current UIDValidity value for the mailbox
       * UNSEEN - The number of messages which do not have the \Seen flag set.
       *
       * @param username String non-empty email address of requester
       * @param mailboxName String name of a mailbox (no wildcards allowed).
       * @param dataItems Vector of one or more Strings each of a single
       * status item.
       * @returns String consisting of space seperated pairs:
       * dataItem-space-number.
       * @throws AccessControlException if the user does not have at least
       * lookup rights to the mailbox requested.
       * @throws MailboxException if the mailboxName does not exist locally. 
       */
      public String getMailboxStatus(String username, String mailboxName,
                                     List dataItems)
          throws MailboxException, AccessControlException {
          String absoluteName = getAbsoluteName(username, mailboxName);
          ACLMailbox mailbox = null;
          FolderRecord record = null;
          Iterator it = dataItems.iterator();
          String response = null;
  
          // Has a folder with this name ever been created?
          if(! recordRep.containsRecord(absoluteName)) {
              throw new MailboxException("Mailbox: " + absoluteName + " has never been created.", MailboxException.NOT_LOCAL);
          } else {
              record = recordRep.retrieve(absoluteName);
              if (record.isDeleted()) {
                  throw new MailboxException("Mailbox has been deleted", MailboxException.LOCAL_BUT_DELETED);
              } else if (openMailboxes.containsKey(absoluteName)) {
                  response = new String();
                  mailbox = (ACLMailbox) openMailboxes.get(absoluteName);
                  if (!mailbox.hasLookupRights(username)) {
                      throw new AccessControlException("No lookup rights.");
                  } 
                  while(it.hasNext()) {
                      String dataItem = (String) it.next();
                      if (dataItem.equalsIgnoreCase("MESSAGES")) {
                          response += "MESSAGES " + mailbox.getExists();
                      } else if (dataItem.equalsIgnoreCase("RECENT")) {
                          response += "RECENT " + mailbox.getRecent();
                      } else if (dataItem.equalsIgnoreCase("UIDNEXT")) {
                          response += "UIDNEXT " + mailbox.getNextUID();
                      } else if (dataItem.equalsIgnoreCase("UIDVALIDITY")) {
                          response += "UIDVALIDITY " + mailbox.getUIDValidity();
                      } else if (dataItem.equalsIgnoreCase("UNSEEN")) {
                          response += "UNSEEN " + mailbox.getUnseen(username);
                      }
                      if (it.hasNext()) { response += " ";}
                  }
                  return response;
              } else {
                  if (!record.hasLookupRights(username)) {
                      throw new AccessControlException("No lookup rights.");
                  } 
                  response = new String();
                  while(it.hasNext()) {
                      String dataItem = (String) it.next();
                      if (dataItem.equalsIgnoreCase("MESSAGES")) {
                          response += "MESSAGES " + record.getExists();
                      } else if (dataItem.equalsIgnoreCase("RECENT")) {
                          response += "RECENT " + record.getRecent();
                      } else if (dataItem.equalsIgnoreCase("UIDNEXT")) {
                          response += "UIDNEXT " + (record.getHighestUid() + 1);
                      } else if (dataItem.equalsIgnoreCase("UIDVALIDITY")) {
                          response += "UIDVALIDITY " + record.getUidValidity();
                      } else if (dataItem.equalsIgnoreCase("UNSEEN")) {
                          response += "UNSEEN " + record.getUnseen(username);
                      }
                      if (it.hasNext()) { response += " ";}
                  }
                  return response;
              }
          }
      }
  
      /**
       * Convert a user-based full mailbox name into a server absolute name.
       * Example:
       * <br> Convert from "#mail.INBOX" for user "Fred.Flinstone" into 
       * absolute name: "#mail.Fred.Flintstone.INBOX"
       *
       * @returns String of absoluteName, null if not valid selection
       */
      private String getAbsoluteName(String user, String fullMailboxName) {
           
          if (fullMailboxName.equals(privateNamespace)) {
              return fullMailboxName + user + privateNamespaceSeparator;
          } else if (fullMailboxName.equals(privateNamespace
                                            + privateNamespaceSeparator)) {
              return fullMailboxName + user + privateNamespaceSeparator;
          } else if (fullMailboxName.startsWith(privateNamespace)) {
              return new String(privateNamespace + privateNamespaceSeparator
                                + user + privateNamespaceSeparator
                                + fullMailboxName.substring(privateNamespace.length()  + privateNamespaceSeparator.length()));
          } else if (fullMailboxName.equals(otherUsersNamespace)) {
              return null;
          }else if (fullMailboxName.equals(otherUsersNamespace
                                           + otherUsersNamespaceSeparator)) {
              return null;
          } else if (fullMailboxName.startsWith(otherUsersNamespace)) {
             
              return new String(privateNamespace + privateNamespaceSeparator
                                + fullMailboxName.substring(otherUsersNamespace.length() + otherUsersNamespaceSeparator.length()));
              
          } else if (fullMailboxName.startsWith(sharedNamespace)) {
              return fullMailboxName;
          } else {
              return null;
          }
      }
  
      private String getFullMailboxName(String user, String absoluteName) {
  
          if(absoluteName.startsWith(privateNamespace)) {
              if (absoluteName.equals(privateNamespace + privateNamespaceSeparator  + user)) {
                  return new String(privateNamespace );
              } else if (absoluteName.startsWith(privateNamespace + privateNamespaceSeparator  + user)){
                  return new String(privateNamespace 
                                    + absoluteName.substring(privateNamespace.length()  + privateNamespaceSeparator.length()  + user.length()));
              } else {
                  // It's another users mailbox
                  // Where is separator between name and mailboxes?
                  int pos = absoluteName.substring(privateNamespace.length() + privateNamespaceSeparator.length()).indexOf(privateNamespaceSeparator);
                  return new String(otherUsersNamespace
                                    + otherUsersNamespaceSeparator
                                    + absoluteName.substring(pos ));
              }
          } else if (absoluteName.startsWith(sharedNamespace)) {
              return absoluteName;
          } else {
              return null;
          }
      }
  
      /**
       * Return the file-system path to a given absoluteName mailbox.
       *
       * @param absoluteName the user-independent name of the mailbox
       * @param owner string name of owner of mailbox
       */
      private String getPath(String absoluteName, String owner) {
          String path;
          if (absoluteName.startsWith(privateNamespace)) {
              String path1 = rootPath + owner;
              String path2
                  = absoluteName.substring(privateNamespace.length()
                                           + privateNamespaceSeparator.length()
                                           + owner.length());
              path = path1 + path2.replace(privateNamespaceSeparator.charAt(0), File.separatorChar);
          } else if (absoluteName.startsWith(sharedNamespace)) {
              String path3 = absoluteName.substring(namespaceToken.length());
              path = rootPath + File.separator + path3.replace(privateNamespaceSeparator.charAt(0), File.separatorChar);
          } else {
              path = null;
          }
          return path;
      }
  
      public boolean createPrivateMailAccount(String user) {
          if (user == null || user.equals("")) {
              throw new RuntimeException("Bad parameter for createPrivateMailAccount.");
          }
          
          String userRootAbsName
              =  privateNamespace + privateNamespaceSeparator + user;
          String userInboxAbsName
              = userRootAbsName + privateNamespaceSeparator + "INBOX";
          SimpleFolderRecord userRootRecord
              = new SimpleFolderRecord(privateNamespace
                                       + privateNamespaceSeparator, user,
                                       userRootAbsName);
          SimpleFolderRecord userInboxRecord
              = new SimpleFolderRecord(privateNamespace
                                       + privateNamespaceSeparator + "INBOX",
                                       user, userInboxAbsName);
  
          ACLMailbox userRootFolder = new FileMailbox();
          ACLMailbox userInbox = new FileMailbox();
          try{
              userRootFolder.configure(conf);
              userRootFolder.contextualize(context);
              userRootFolder.compose(compMgr);
              userRootFolder.prepareMailbox(user, userRootAbsName, user);
              userInbox.configure(conf);
              userInbox.contextualize(context);
              userInbox.compose(compMgr);
              userInbox.prepareMailbox(user, userInboxAbsName, user);
              userRootFolder.initialize();
              userRootFolder.setNotSelectableByAnyone(true);
              userInbox.initialize();
              userInbox.setRights(user, MailServer.MDA, "lrswi");
          } catch (Exception e) {
              logger.error("Exception creating new account: " + e);
              return false;
          }
          userInboxRecord.initialize();
          userInboxRecord.setUidValidity(userInbox.getUIDValidity());
          userInboxRecord.setHighestUid(userInbox.getNextUID() -1);
          userInboxRecord.setLookupRights(userInbox.getUsersWithLookupRights());
          userInboxRecord.setReadRights(userInbox.getUsersWithReadRights());
          userInboxRecord.setNotSelectableByAnyone(userInbox.isNotSelectableByAnyone());
          userRootRecord.initialize();
          userRootRecord.setLookupRights(userRootFolder.getUsersWithLookupRights());
          userRootRecord.setReadRights(userRootFolder.getUsersWithReadRights());
          userRootRecord.setNotSelectableByAnyone(userRootFolder.isNotSelectableByAnyone());
          recordRep.store(userRootRecord);
          recordRep.store(userInboxRecord);
  
          //No one is using these mailboxes
          //try {
          //      userRootFolder.destroy();
          //      userInbox.destroy();
          //} catch (Exception e) {
          //    logger.error("Exception closing new account mailbox: " + e);
          //    return false;
          //} 
          userRootFolder = null;
          userInbox = null;
  
          return true;
      }
  }
  
  
  
  1.1                  jakarta-james/src/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.EnhancedMimeMessage;
  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.
       */
      EnhancedMimeMessage 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.
       */
      EnhancedMimeMessage 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
       * @throws AccessControlException if user does not have lookup rights for
       * this mailbox.
       */
      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/src/java/org/apache/james/imapserver/MailboxEvent.java
  
  Index: MailboxEvent.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.EventObject;
  
  /**
   * EventObject representing  a change in a Mailbox which needs to be
   * communicated to MailboxEventListeners.
   * Uses include warning of addition/ deletion of messages.
   *
   * <p>Not currently used in this implementation
   *
   * @author  <a href="mailto:charles@benett1.demon.co.uk">Charles Benett</a>
   * @version 0.1 on 14 Dec 2000
   */
  public class MailboxEvent 
      extends EventObject {
     
      private String callingMailbox = null;
  
      public MailboxEvent( final Object source, final String mailbox ) {
          super( source );
          callingMailbox = mailbox;
      }
  
      public String getMailbox() {       
          return callingMailbox;
      }
  }
  
  
  
  1.1                  jakarta-james/src/java/org/apache/james/imapserver/MailboxEventListener.java
  
  Index: MailboxEventListener.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.Serializable;
  import java.util.EventListener;
  
  /**
   * Interface for objects that need to be informed of changes in a Mailbox.
   *
   * <p>Not currently active in this implementaiton
   * 
   * @author <a href="mailto:charles@benett1.demon.co.uk">Charles Benett</a>
   * @version 0.1 on 14 Dec 2000
   */
  public interface MailboxEventListener
      extends EventListener, Serializable {
  
      void receiveEvent( MailboxEvent me );
  }
  
  
  
  1.1                  jakarta-james/src/java/org/apache/james/imapserver/MailboxEventSource.java
  
  Index: MailboxEventSource.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;
  
  /**
   * Interface for objects that are sources for Mailbox Events. Mailbox Events
   * are used to inform registered listeners of events in this Source. For
   * example, if mail is delivered to an Inbox or if another user appends or 
   * deletes a message.
   *
   * <p>Not currently active in this implementation
   *
   * @author <a href="mailto:charles@benett1.demon.co.uk">Charles Benett</a>
   * @version 0.1 on 14 Dec 2000
   */
  public interface MailboxEventSource  {
    
      /**
       * Registers a MailboxEventListener.
       *
       * @param mel MailboxEventListener to be registered with this source.
       */
      void addMailboxEventListener( MailboxEventListener mel );
  
      /**
       * Deregisters a MailboxEventListener.
       *
       * @param mel MailboxEventListener to be deregistered from this source.
       */
      void removeMailboxEventListener( MailboxEventListener mel );
  }
   
  
  
  
  
  1.1                  jakarta-james/src/java/org/apache/james/imapserver/MailboxException.java
  
  Index: MailboxException.java
  ===================================================================
  /*
   * Copyright (C) The Apache Software Foundation. All rights reserved.
   *
   * This software is published under the terms of the Apache Software License
   * version 1.1, a copy of which has been included with this distribution in
   * the LICENSE file.
   */
  package org.apache.james.imapserver;
  
  /**
   * Thrown on an inappropriate attempt to reference a mailbox.
   * Includes attempting to create a mailbox that already exists and attempting
   * to open a mailbox that does not exist.
   * If status is ALREADY_EXISTS_REMOTELY or IF_CREATED_REMOTE then field
   * remoteServer should be set to the url of the remote server, formatted for
   * Mailbox Referral.
   *
   * @author <a href="mailto:charles@benett1.demon.co.uk">Charles Benett</a>
   * @version 0.1 on 14 Dec 2000
   */
  public class MailboxException extends Exception {
  
      public final static String ALREADY_EXISTS_LOCALLY
          = "Already exists locally";
      public final static String ALREADY_EXISTS_REMOTELY
          = "Already exists remotely";
      public final static String IF_CREATED_LOCAL
          = "If created, mailbox would be local";
      public final static String IF_CREATED_REMOTE
          = "If created, mailbox would be remote";
      public final static String NOT_LOCAL
          = "Does not exist locally, no further information available";
      public final static String LOCAL_BUT_DELETED
          = "Was local but has been deleted.";
  
      protected String status = null; 
      protected String remoteServer = null;
  
      /**
       * Construct a new <code>MailboxException</code> instance.
       *
       * @param message The detail message for this exception (mandatory).
       */
      public MailboxException(String message) {
          super(message);
      }
  
      /**
       * Construct a new <code>MailBoxException</code> instance.
       *
       * @param message The detail message for this exception (mandatory).
       * @param aStatus String constant indicating condition
       */
      public MailboxException(String message, String aStatus) {
          super(message);
          this.status = aStatus;
      }
  
      /**
       * Construct a new <code>MailBoxException</code> instance.
       *
       * @param message The detail message for this exception (mandatory).
       * @param aStatus String constant indicating condition
       * @param sServer String indicating another server where Mailbox should be.
       */
      public MailboxException(String message, String aStatus, String aServer) {
          super(message);
          this.status = aStatus;
          this.remoteServer = aServer;
      }
  
      public String getStatus() {
          return status;
      }
  
      public String getRemoteServer() {
          return remoteServer;
      }
  
      public boolean isRemote() {
          return ( status.equals(ALREADY_EXISTS_REMOTELY)
                   || status.equals(IF_CREATED_REMOTE) ) ;
      }
  }
  
  
  
  1.1                  jakarta-james/src/java/org/apache/james/imapserver/MessageAttributes.java
  
  Index: MessageAttributes.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.Date;
  
  /**
   * Interface for objects holding IMAP4rev1 Message Attributes. Message
   * Attributes should be set when a message enters a mailbox. Implementations
   * are encouraged to implement and store MessageAttributes apart from the
   * underlying message. This allows the Mailbox to respond to questions about
   * very large message without needing to access them directly.
   * <p> Note that the message in a mailbox have the same order using either
   * Message Sequence Numbers or UIDs.
   *
   * Reference: RFC 2060 - para 2.3
   * @author <a href="mailto:charles@benett1.demon.co.uk">Charles Benett</a>
   * @version 0.1 on 14 Dec 2000
   */
  public interface MessageAttributes  {
  
      /**
       * 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
       */
      int getMessageSequenceNumber();
  
      /**
       * 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
       */
      int getUID();
  
      /**
       * 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
       */
      Date getInternalDate();
  
      /**
       * Returns IMAP formatted String representation of Date
       */
      String getInternalDateAsString();
  
      /**
       * Provides the sizeof the message in octets.
       *
       * @returns int number of octets in message.
       */
      int getSize();
  
      /**
       * Provides the Envelope structure information for this message. 
       * This is a parsed representation of the rfc-822 envelope information. 
       * This is not to be confused with the SMTP envelope!
       *
       * @returns String satisfying envelope syntax in rfc 2060.
       */
      String getEnvelope();
  
      /**
       * Provides the Body Structure information for this message. 
       * This is a parsed representtion of the MIME structure of the message.
       *
       * @returns String satisfying body syntax in rfc 2060.
       */
      String getBodyStructure();
  }
  
  
  
  
  
  1.1                  jakarta-james/src/java/org/apache/james/imapserver/MessageHeader.java
  
  Index: MessageHeader.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.Serializable;
  
  /**
   * Class for holding the name-value pairs of an RFC822 or MIME header.
   * Like javax.mail.Header but serializable
   *
   * @author <a href="mailto:charles@benett1.demon.co.uk">Charles Benett</a>
   * @version 0.1 on 14 Dec 2000
   */
  public class MessageHeader implements Serializable {
      public static final String CRLF =  "\r\n";
      public static final String CRLFHTAB =  "\r\n\t";
      public static final String CRLFWS = "\r\n ";
  
      private final String name;
      private final String value;
  
      public MessageHeader(String headerLine) {
          int colon = headerLine.indexOf(":");
          name = headerLine.substring(0, colon);
          StringBuffer unwrapped = new StringBuffer(headerLine.length());
          boolean finished = false;
          int pos = colon + 1;
          while (!finished) {
              int crlf = headerLine.indexOf(CRLF, pos);
              if (crlf == -1) {
                  unwrapped.append(headerLine.substring(pos));
                  finished = true;
              } else {
                  unwrapped.append(headerLine.substring(pos, crlf));
                  unwrapped.append(" ");
                  pos = crlf +3;
              }
          }
          value = unwrapped.toString();
      }
  
      public MessageHeader(String name, String value) {
          this.name = name;
          this.value = value;
      }
  
      /**
       * Get the name, aka field name, of this header.
       *
       * @returns a String
       */
      public String getName() {
          return name;
      }
  
      /**
       * Get the value, aka field value, of this Header
       *
       * @returns String
       */
      public String getValue() {
          return value;
      }
  }
  
  
  
  
  
  1.1                  jakarta-james/src/java/org/apache/james/imapserver/RecordRepository.java
  
  Index: RecordRepository.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.Iterator;
  
  /**
   * Interface for objects representing a Repository of FolderRecords.
   * There should be a RecordRepository for every Host.
   * <p>Note that there is no method for removing Records: an IMAP host is
   * meant to retain information about deleted folders.
   *
   * @author <a href="mailto:charles@benett1.demon.co.uk">Charles Benett</a>
   * @version 0.1 on 14 Dec 2000
   * @see FolderRecord
   */
  public interface RecordRepository {
  
      String RECORD = "RECORD";
  
      /**
       * Sets the location of this repository.
       *
       * @param rootPath String location of this repository
       */
      void setPath( String rootPath );
  
      /**
       * Stores a folder record in this repository.
       *
       * @param fr FolderRecord to be stored
       */
      void store( FolderRecord fr );
        
      /**
       * Returns Iterator over names of folders in repository
       *
       * @returns Iterator over Strings of AbsoluteNames of Folders. Calling
       * objects cannot change contents of Iterator.
       */
      Iterator getAbsoluteNames();
  
      /**
       * Retrieves a folder record given the folder's full name. 
       *
       * @param folderAbsoluteName String name of a folder
       * @returns FolderRecord for specified folder, null if no such FolderRecord
       */
      FolderRecord retrieve( String folderAbsoluteName );
      
      /**
       * Tests if there is a folder record for the given folder name.
       *
       * @param folderAbsoluteName String name of a folder
       * @returns boolean True if there is a record for the specified folder.
       */
      boolean containsRecord( String folderAbsoluteName );
  }
  
      
  
  
  
  1.1                  jakarta-james/src/java/org/apache/james/imapserver/SimpleFolderRecord.java
  
  Index: SimpleFolderRecord.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.Serializable;
  import java.util.Map;
  import java.util.Set;
  import org.apache.avalon.framework.activity.Initializable;
  
  /**
   * Object representing the record of a folder in an IMAP on an IMAP Host.
   * 
   * @author <a href="mailto:charles@benett1.demon.co.uk">Charles Benett</a>
   * @version 0.1 on 14 Dec 2000
   */
  
  public class SimpleFolderRecord 
      implements FolderRecord, Serializable, Initializable {
  
      private final String fullName; 
      private final String owner;
      private final String absoluteName;
  
      private boolean nameInUse;
      private boolean deleted;
      private int uidValidity;
      private int highestUID;
      private Set usersWithLookupRights;
      private Set usersWithReadRights;
      private boolean marked;
      private boolean notSelectableByAnyone;
      private int exists;
      private int recent;
      private Map unseenByUser;
  
      /**
       * Constructor Records the full name, including namespace, of this mailbox
       * relative, to a specified user, and the absolute name.. 
       *
       * @param mailboxName String mailbox hierarchical name including namespace
       * @param user String a user. An empty user parameter indicates that the 
       * mailbox name is absolute.
       */
      public SimpleFolderRecord(String mailboxName, String user,
                                String absName) {
          fullName = mailboxName;
          owner = user;
          absoluteName = absName;
      }
  
      public void initialize() {
          nameInUse = true;
          deleted = false;
          uidValidity = 1; 
          highestUID = 1; 
      }
  
      public String getFullName() {
          return fullName;
      }
  
      public String getUser() {
          return owner;
      }
  
      public String getAbsoluteName() {
          return absoluteName;
      }
  
      public void setNameInUse(boolean state) {
          nameInUse = state;
      }
  
      public boolean isNameInUse() {
          return nameInUse;
      }
  
      public void setDeleted(boolean state) {
          deleted = state;
      }
  
      public boolean isDeleted() {
          return deleted;
      }
  
      public void setUidValidity(int uidV) {
          if (uidV > uidValidity) {
              uidValidity = uidV;
          }
      }
  
      public int getUidValidity() {
          return uidValidity;
      }
  
      public void setHighestUid(int uid) {
          highestUID = uid;
      }
  
      public int getHighestUid() {
          return highestUID;
      }
  
      public void setLookupRights(Set users) {
          usersWithLookupRights = users;
      }
  
      public boolean hasLookupRights(String user) {
          return usersWithLookupRights.contains(user) ;
      }
   
      public void setReadRights(Set users) {
          usersWithReadRights = users;
      }
  
      public boolean hasReadRights(String user) {
          return usersWithReadRights.contains(user) ;
      }
  
      public void setMarked(boolean mark) {
          marked = mark;
      }
  
      public boolean isMarked() {
          return marked;
      }
  
      public void setNotSelectableByAnyone(boolean state) {
          notSelectableByAnyone = state;
      }
  
      public boolean isNotSelectableByAnyone() {
          return notSelectableByAnyone;
      }
  
      public boolean isSelectable(String user) {
          return (!notSelectableByAnyone && hasReadRights(user));
      }
  
      public void setExists(int num) {
          exists = num;
      }
  
      public int getExists() {
          return exists;
      }
  
      public void setRecent(int num) {
          recent = num;
      }
  
      public int getRecent() {
          return recent;
      }
  
      public void setUnseenbyUser(Map unseen) {
          unseenByUser = unseen;
      }
  
      public int getUnseen(String user) {
          if (unseenByUser.containsKey(user)) {
              Integer unseen = ((Integer)unseenByUser.get(user));
              return unseen.intValue();
          } else {
              return exists;
          }
      }
  }
  
  
  
  1.1                  jakarta-james/src/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.james.core.EnhancedMimeMessage;
  import org.apache.james.util.RFC822DateFormat;
  import org.apache.log.LogKit;
  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
      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  = LogKit.getLoggerFor("james.JamesHost");
  
      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 reinitialize() {
          logger = LogKit.getLoggerFor("james.JamesHost");
      }
  
      void setAttributesFor(MimeMessage msg) throws MessagingException {
          size = msg.getSize();
  
          try {
              internalDate = msg.getSentDate();
              if (DEBUG) logger.debug("setAttributes - getSentDate: " + internalDate);
          } catch (MessagingException me) {
              if (DEBUG) logger.debug("Messaging Exception for getSentDate: " + me);
              internalDate = new Date();
          }
  
          if (DEBUG) {
              logger.debug("HeaderLines recieved were: ");
              Enumeration enum = msg.getAllHeaderLines();
              while(enum.hasMoreElements()) {
                  logger.debug((String)enum.nextElement());
              }
              //  logger.debug("Header objects available are:");
              //   Enumeration e = msg.getAllHeaders();
              //   while(e.hasMoreElements()) {
              //Header h = (Header) e.nextElement();
              //logger.debug("Name: " + h.getName());
              //logger.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) logger.debug("parseMessage - subject: " + subject);
              } catch (MessagingException me) {
                  if (DEBUG) logger.debug("Messaging Exception for getSubject: " + me);
              }
          }
          try {
              from = part.getHeader("From");
              if (DEBUG)  logger.debug("parseMessage - from: " + from);
          } catch (MessagingException me) {
              if (DEBUG) logger.debug("Messaging Exception for getHeader(From): " + me);
          }
          try {
              sender = part.getHeader("Sender");
              if (DEBUG) logger.debug("parseMessage - sender: " + sender);
          } catch (MessagingException me) {
              if (DEBUG) logger.debug("Messaging Exception for getHeader(Sender): " + me);
          }
          try {
              replyTo = part.getHeader("Reply To");
              if (DEBUG) logger.debug("parseMessage - ReplyTo: " + replyTo);
          } catch (MessagingException me) {
              if (DEBUG) logger.debug("Messaging Exception for getHeader(Reply To): " + me);
          }
          try {
              to = part.getHeader("To");
              if (DEBUG) logger.debug("parseMessage - To: " + to);
          } catch (MessagingException me) {
              if (DEBUG) logger.debug("Messaging Exception for getHeader(To): " + me);
          }
          try {
              cc = part.getHeader("Cc");
              if (DEBUG) logger.debug("parseMessage - cc: " + cc);
          } catch (MessagingException me) {
              if (DEBUG) logger.debug("Messaging Exception for getHeader(To): " + me);
          }
          try {
              bcc = part.getHeader("Bcc");
              if (DEBUG) logger.debug("parseMessage - bcc: " + bcc);
          } catch (MessagingException me) {
              if (DEBUG) logger.debug("Messaging Exception for getHeader(To): " + me);
          }
          try {
              inReplyTo = part.getHeader("In Reply To");
              if (DEBUG) logger.debug("parseMessage - In Reply To: " + inReplyTo);
          } catch (MessagingException me) {
              if (DEBUG) logger.debug("Messaging Exception for getHeader(In Reply To): " + me);
          }
          try {
              date = part.getHeader("Date");
              if (DEBUG) logger.debug("parseMessage - date: " + date);
          } catch (MessagingException me) {
              if (DEBUG) logger.debug("Messaging Exception for getHeader(Date): " + me);
          }
          try {
              messageID = part.getHeader("Message-ID");
              if (DEBUG) logger.debug("parseMessage - messageID: " + messageID);
          } catch (MessagingException me) {
              if (DEBUG) logger.debug("Messaging Exception for getHeader(messageID): " + me);
          }
          String contentTypeLine = null;
          try {
              contentTypeLine = part.getContentType();
              if (DEBUG) logger.debug("parseMessage - contentType: " + contentTypeLine);
          } catch (MessagingException me) {
              if (DEBUG) logger.debug("Messaging Exception for getContentType(): " + me);
          }
          if (contentTypeLine !=null ) {
              decodeContentType(contentTypeLine);
          }
          try {
              contentID = part.getContentID();
              if (DEBUG) logger.debug("parseMessage - contentID: " + contentID);
          } catch (MessagingException me) {
              if (DEBUG) logger.debug("Messaging Exception for getContentUD(): " + me);
          }
          try {
              contentDesc = part.getDescription();
              if (DEBUG) logger.debug("parseMessage - contentDesc: " + contentDesc);
          } catch (MessagingException me) {
              if (DEBUG) logger.debug("Messaging Exception for getDescription(): " + me);
          }
          try {
              contentEncoding = part.getEncoding();
              if (DEBUG) logger.debug("parseMessage - contentEncoding: " + contentEncoding);
          } catch (MessagingException me) {
              if (DEBUG) logger.debug("Messaging Exception for getEncoding(): " + me);
          }
          if (DEBUG) {
              try {
                  String contentDisposition = part.getDisposition();
                  logger.debug("parseMessage - contentDisposition: " + contentEncoding);
              } catch (MessagingException me) {
                  logger.debug("Messaging Exception for getEncoding(): " + me);
              }
          }
  
          try {
              lineCount = part.getLineCount();
              if (DEBUG) logger.debug("parseMessage - Line Count: " + lineCount);
          } catch (MessagingException me) {
              if (DEBUG) logger.debug("Messaging Exception for getLineCount(): " + me);
              if (DEBUG) logger.debug(me.getMessage());
          } catch (Exception e) {
              if (DEBUG) logger.debug("Exception for getLineCount(): " + e);
              if (DEBUG) logger.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();
                  logger.info("This part contains " + count + " parts.");
                  parts = new SimpleMessageAttributes[count];
                  for (int i = 0; i < count ; i ++) {
                      logger.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 {
                          logger.info("Found a non-Mime bodyPart");
                      }
                      logger.info("Finished with embedded part: " + i);
                  }
              } catch (Exception e) {
                  logger.debug("Messaging Exception for getContent(): " + e);
              }
          } else if (primaryType.equalsIgnoreCase("message")) {
              logger.info("This part contains an embedded message of subtype: " + secondaryType);
              logger.info("Uses java class: " + part.getClass().getName());
              if (secondaryType.equalsIgnoreCase("RFC822")) {
                  try {
  
                      EnhancedMimeMessage message = new EnhancedMimeMessage(Session.getDefaultInstance(System.getProperties(), null), part.getInputStream());
                      SimpleMessageAttributes msgAttrs = new SimpleMessageAttributes();
                      msgAttrs.setAttributesFor(message);
  
                      if (part instanceof MimeMessage) {
                          MimeMessage msg1 = (MimeMessage) part;
                          EnhancedMimeMessage message2 = new EnhancedMimeMessage(msg1);
                          SimpleMessageAttributes msgAttrs2 = new SimpleMessageAttributes();
                          msgAttrs.setAttributesFor(message2);
                      }
  
                      parts = new SimpleMessageAttributes[1];
                      parts[0] = msgAttrs;
                  } catch (Exception e) {
                      logger.error("Error interpreting a message/rfc822: " + e);
                      e.printStackTrace();
                  }
              } else {
                  logger.info("Unknown subtype of message encountered.");
              }
              logger.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) logger.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) {
          logger.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) logger.debug("decoding: " + rawLine);
          int slash = rawLine.indexOf("/");
          if( slash == -1){
              if (DEBUG) logger.debug("decoding ... no slash found");
              return;
          } else {
              primaryType = rawLine.substring(0, slash).trim();
          }
          int semicolon = rawLine.indexOf(";");
          if (semicolon == -1) {
              if (DEBUG) logger.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) logger.debug("decoding ... found another semicolon");
              String param = rawLine.substring(pos + 1, nextsemi);
              int esign = param.indexOf("=") ;
              if (esign == -1) {
                  if (DEBUG) logger.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) logger.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) logger.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) logger.debug("Found parameter: " + name + SP + value);
          }
      }
  
      String parseBodyFields() {
          logger.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() {
          logger.debug("Parsing bodyStructure.");
          try {
              String fields = parseBodyFields();
              StringBuffer buf = new StringBuffer();
              buf.append(LB);
              if (primaryType.equalsIgnoreCase("Text")) {
                  logger.debug("Assembling bodystrucuture for type TEXT.");
                  buf.append("\"Text\" \"" + secondaryType + "\" ");
                  buf.append(fields + " " + lineCount);
              } else if  (primaryType.equalsIgnoreCase(MESSAGE) && secondaryType.equalsIgnoreCase("rfc822")) {
                  logger.debug("Assembling bodyStructure for type MESSAGE/FRC822");
                  buf.append("\"MESSAGE\" \"RFC822\" ");
                  buf.append(fields + SP);
                  ((SimpleMessageAttributes)parts[0]).reinitialize(); // reset transient logger
                  buf.append(parts[0].getEnvelope() + SP);
                  buf.append(parts[0].getBodyStructure() + SP);
                  buf.append(lineCount);
              } else if (primaryType.equalsIgnoreCase(MULTIPART)) {
                  logger.debug("Assembling bodystructure for type MULTIPART");
                  for (int i=0; i<parts.length; i++) {
                      logger.debug("Parsing part: " + i);
                      ((SimpleMessageAttributes)parts[i]).reinitialize(); // reset transient logger
                      buf.append(parts[i].getBodyStructure());
                  }
                  buf.append(SP + secondaryType);
              }
              buf.append(RB);
              return buf.toString();
          } catch (Exception e) {
              logger.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/src/java/org/apache/james/imapserver/SimpleSystem.java
  
  Index: SimpleSystem.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.*;
  import org.apache.avalon.framework.activity.Initializable;
  import org.apache.avalon.framework.component.Component;
  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.james.AuthenticationException;
  
  /**
   * A simple, single-server, implementation of IMAPSystem.
   *
   * References: rfc 2060, rfc 2193, rfc 2221
   * @author <a href="mailto:charles@benett1.demon.co.uk">Charles Benett</a>
   * @version 0.1 on 14 Dec 2000
   */
  public class SimpleSystem
      implements IMAPSystem, Component, Initializable {
  
      private static final String namespaceToken = "#";
      private static final String hierarchySeperator = ".";
      private static final String namespace
          = "((\"#mail.\" \".\")) ((\"#users.\" \".\")) ((\"#shared.\" \".\"))";
  
      private static String singleServer;
      private Set servers = new HashSet();
      private Context context;
      private Configuration conf;
      private ComponentManager compMgr;
  
      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 initialize() throws Exception {
          // Derive namespace and namespaceToken from conf
          singleServer = (String) context.get("HostName");
          servers.add(singleServer);
      }
  
      /**
       * Returns the token indicating a namespace.  Implementation dependent but
       * by convention, '#'.
       * Example: #news.org.apache vs #mail.org.apache
       */
      public String getNamespaceToken() {
          return namespaceToken;
      }
  
      /**
       * Returns the home server (server with user's INBOX) for specified user.
       * Enables Login Referrals per RFC2221. (Ie user attempts to login to a
       * server which is not their Home Server.)  The returned string must comply
       * with RFC2192, IMAP URL Scheme.
       *
       * @param username String representation of a user
       * @returns String holding an IMAP URL for the user's home server
       * @throws AuthenticationException if this System does not recognise
       * the user.
       */
      public String getHomeServer(String username)
          throws AuthenticationException {
          return singleServer;
      }
  
      /**
       * Returns the character used as a mail hierarchy seperator in a given
       * namespace. A namespace must use the same seperator at all levels of
       * hierarchy.
       * <p>Recommendations (from rfc 2683) are period (US)/ full stop (Brit),
       * forward slash or backslash.
       *
       * @param namespace String identifying a namespace
       * @returns char, usually '.', '/', or '\'
       */
      public String getHierarchySeperator(String namespace) {
          return hierarchySeperator;
      }
  
      /**
       * Provides the set of namesapces a given user can access. Implementations
       * should but are not required to reveal all namespaces that a user can
       * access. Different namespaces may be handled by different
       * <code>IMAPHosts</code>
       *
       * @param username String identifying a user of this System
       * @returns String whose contents should be a space seperated triple
       * <personal namespaces(s)> space <other users' namespace(s)> space
       * <shared namespace(s)>, per RFC2342
       */
      public String getNamespaces(String username) {
          return namespace;
      }
  
      /**
       * Returns an iterator over the collection of servers on which this user
       * has access. The collection should be unmodifiable.
       * Enable Mailbox Referrals - RFC 2193.
       *
       * @param username String identifying a user
       * @return iterator over a collection of strings
       */
      public Iterator getAccessibleServers(String username) {
          return Collections.unmodifiableSet(servers).iterator();
      }
  }
  
  
  
  
  
  
  
  1.1                  jakarta-james/src/java/org/apache/james/imapserver/SingleThreadedConnectionHandler.java
  
  Index: SingleThreadedConnectionHandler.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.text.*;
  import java.util.*;
  import javax.mail.internet.*;
  import org.apache.avalon.framework.activity.Initializable;
  import org.apache.avalon.framework.activity.Disposable;
  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.context.Context;
  import org.apache.avalon.framework.context.ContextException;
  import org.apache.avalon.framework.context.Contextualizable;
  import org.apache.avalon.cornerstone.services.connection.ConnectionHandler;
  import org.apache.avalon.cornerstone.services.scheduler.PeriodicTimeTrigger;
  import org.apache.avalon.cornerstone.services.scheduler.Target;
  import org.apache.avalon.cornerstone.services.scheduler.TimeScheduler;
  import org.apache.james.AccessControlException;
  import org.apache.james.AuthenticationException;
  import org.apache.james.AuthorizationException;
  import org.apache.james.Constants;
  import org.apache.james.services.*;
  import org.apache.james.util.InternetPrintWriter;
  import org.apache.log.LogKit;
  import org.apache.log.Logger;
  
  /**
   * An IMAP Handler handles one IMAP connection. TBC - it may spawn worker
   * threads someday.
   *
   * <p> Based on SMTPHandler and POP3Handler by Federico Barbieri <sc...@systemy.it>
   *
   * @author  <a href="mailto:charles@benett1.demon.co.uk">Charles Benett</a>
   * @version 0.1 on 14 Dec 2000
   */
  public class SingleThreadedConnectionHandler
      extends BaseCommand
      implements ConnectionHandler, Composable, Configurable,
                 Initializable, Disposable, Target, MailboxEventListener {
  
      //mainly to switch on stack traces and catch responses;
      private static final boolean DEEP_DEBUG = true;
  
      // Connection states
      private static final int NON_AUTHENTICATED = 0;
      private static final int AUTHENTICATED = 1;
      private static final int SELECTED = 2;
      private static final int LOGOUT = 3;
  
      // Connection termination options
      private static final int NORMAL_CLOSE = 0;
      private static final int OK_BYE = 1;
      private static final int UNTAGGED_BYE = 2;
      private static final int TAGGED_NO = 3;
      private static final int NO_BYE = 4;
  
      // Basic response types
      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 static final String VERSION = "IMAP4rev1";
      private static final String CAPABILITY_RESPONSE = "CAPABILITY " + VERSION
          + " NAMESPACE" + " ACL"; //add as implemented
  
      private static final String LIST_WILD = "*";
      private static final String LIST_WILD_FLAT = "%";
      private static final char[] CTL = {};
      private static final String[] ATOM_SPECIALS
          = {"(", ")", "{", " ", LIST_WILD, LIST_WILD_FLAT,};
  
      private static final String AUTH_FAIL_MSG
          = "NO Command not authorized on this mailbox";
      private static final String BAD_LISTRIGHTS_MSG
          = "BAD Command should be <tag> <LISTRIGHTS> <mailbox> <identifier>";
      private static final String BAD_MYRIGHTS_MSG
          = "BAD Command should be <tag> <MYRIGHTS> <mailbox>";
      private static final String BAD_LIST_MSG
          = "BAD Command should be <tag> <LIST> <reference name> <mailbox>";
      private static final String BAD_LSUB_MSG
          = "BAD Command should be <tag> <LSUB> <reference name> <mailbox>";
      private static final String NO_NOTLOCAL_MSG
          = "NO Mailbox does not exist on this server";
  
      private Logger securityLogger = LogKit.getLoggerFor("james.Security");
      private MailServer mailServer;
      private UsersRepository users;
      private TimeScheduler scheduler;
  
      private Socket socket;
      private BufferedReader in;
      private PrintWriter out;
      private OutputStream outs;
      private String remoteHost;
      private String remoteIP;
      private String softwaretype  = "JAMES IMAP4rev1 Server " + Constants.SOFTWARE_VERSION;
      private int state;
      private String user;
  
      private IMAPSystem imapSystem;
      private Host imapHost;
      private String namespaceToken;
      private String currentNamespace = null;
      private String currentSeperator = null;
      private String commandRaw;
  
      //currentFolder holds the client-dependent absolute address of the current
      //folder, that is current Namespace and full mailbox hierarchy.
      private String currentFolder = null;
      private ACLMailbox currentMailbox = null;
      private boolean currentIsReadOnly = false;
      private boolean connectionClosed = false;
      private String tag;
      private boolean checkMailboxFlag = false;
      private int exists;
      private int recent;
      private List sequence;
  
      public void compose( final ComponentManager componentManager )
          throws ComponentException {
  
          mailServer = (MailServer)componentManager.
              lookup("org.apache.james.services.MailServer");
          users = (UsersRepository)componentManager.
              lookup("org.apache.james.services.UsersRepository");
          scheduler = (TimeScheduler)componentManager.
              lookup("org.apache.avalon.cornerstone.services.scheduler.TimeScheduler");
          imapSystem = (IMAPSystem)componentManager.
              lookup("org.apache.james.imapserver.IMAPSystem");
          imapHost = (Host)componentManager.
              lookup("org.apache.james.imapserver.Host");
      }
  
      public void initialize() throws Exception {
          getLogger().info("SingleThreadedConnectionHandler starting ...");
          namespaceToken = imapSystem.getNamespaceToken();
          getLogger().info("SingleThreadedConnectionHandler initialized");
      }
  
      /**
       * Handle a connection.
       * This handler is responsible for processing connections as they occur.
       *
       * @param connection the connection
       * @exception IOException if an error reading from socket occurs
       * @exception ProtocolException if an error handling connection occurs
       */
      public void handleConnection( final Socket connection )
          throws IOException {
  
          try {
              this.socket = connection;
              in = new BufferedReader(new
                  InputStreamReader(socket.getInputStream()));
              outs = socket.getOutputStream();
              out = new InternetPrintWriter(outs, true);
              remoteHost = socket.getInetAddress ().getHostName ();
              remoteIP = socket.getInetAddress ().getHostAddress ();
          } catch (Exception e) {
              getLogger().error("Cannot open connection from " + remoteHost + " ("
                                + remoteIP + "): " + e.getMessage());
          }
          getLogger().info("Connection from " + remoteHost + " (" + remoteIP + ")");
  
          try {
              final PeriodicTimeTrigger trigger = new PeriodicTimeTrigger( timeout, -1 );
              scheduler.addTrigger( this.toString(), trigger, this );
  
              if (false) { // arbitrary rejection of connection
                  // could screen connections by IP or host or implement
                  // connection pool management
                  connectionClosed = closeConnection(UNTAGGED_BYE,
                                                     " connection rejected.",
                                                     "");
              } else {
                  if (false) { // connection is pre-authenticated
                      out.println(UNTAGGED + SP + "PREAUTH" + SP + VERSION + SP
                                  + "server" + SP + this.helloName + SP
                                  + "logged in as" + SP + user);
                      state = AUTHENTICATED;
                      user = "preauth user";
                      securityLogger.info("Pre-authenticated connection from  "
                                          + remoteHost + "(" + remoteIP
                                          + ") received by SingleThreadedConnectionHandler");
                  } else {
                      out.println(UNTAGGED + SP + OK + SP + VERSION + SP
                                  + "server " + this.helloName + SP + "ready.");
                      state = NON_AUTHENTICATED;
                      user = "unknown";
                      securityLogger.info("Non-authenticated connection from  "
                                          + remoteHost + "(" + remoteIP
                                          + ") received by SingleThreadedConnectionHandler");
                  }
                  while (parseCommand(in.readLine())) {
                      scheduler.resetTrigger(this.toString());
                  }
              }
  
              if (!connectionClosed) {
                  connectionClosed
                      = closeConnection(UNTAGGED_BYE,
                                        "Server error, closing connection", "");
              }
  
          } catch (Exception e) {
              // This should never happen once code is debugged
              getLogger().error("Exception during connection from " + remoteHost
                                + " (" + remoteIP + ") : " + e.getMessage());
              e.printStackTrace();
              connectionClosed = closeConnection(UNTAGGED_BYE,
                                                 "Error processing command.", "");
          }
  
          scheduler.removeTrigger(this.toString());
      }
  
      public void targetTriggered( final String triggerName ) {
          getLogger().info("Connection timeout on socket");
          connectionClosed = closeConnection(UNTAGGED_BYE,
                                             "Autologout. Idle too long.", "");
      }
  
      private boolean closeConnection( int exitStatus,
                                       String message1,
                                       String message2 ) {
          scheduler.removeTrigger(this.toString());
          if (state == SELECTED) {
              currentMailbox.removeMailboxEventListener(this);
              imapHost.releaseMailbox(user, currentMailbox);
          }
  
          try {
              switch(exitStatus) {
              case 0 :
                  out.println(UNTAGGED + SP + "BYE" + SP + "server logging out");
                  out.println(tag + SP + OK + SP + "LOGOUT completed");
                  break;
              case 1 :
                  out.println(UNTAGGED + SP + "BYE" + SP + message1);
                  out.println(tag + SP + OK + SP + message2);
                  break;
              case 2:
                  out.println(UNTAGGED + SP + "BYE" + SP + message1);
                  break;
              case 3 :
                  out.println(tag + SP + NO + SP + message1);
                  break;
              case 4 :
                  out.println(UNTAGGED + SP + "BYE" + SP + message1);
                  out.println(tag + SP + NO + SP + message2);
                  break;
              }
              out.flush();
              socket.close();
              getLogger().info("Connection closed" + SP + exitStatus + SP + message1
                               +  SP + message2);
          } catch (IOException ioe) {
              getLogger().error("Exception while closing connection from " + remoteHost
                                + " (" + remoteIP + ") : " + ioe.getMessage());
              try {
                  socket.close();
              } catch (IOException ioe2) {
              }
          }
          return true;
      }
  
      private boolean parseCommand(String next) {
          commandRaw = next;
          String folder = null;
          String command = null;
          boolean subscribeOnly = false;
  
          if (commandRaw == null) return false;
          //        getLogger().debug("Command recieved: " + commandRaw + " from " + remoteHost
          //           + "(" + remoteIP  + ")");
          //String command = commandRaw.trim();
          StringTokenizer commandLine = new StringTokenizer(commandRaw.trim(), " ");
          int arguments = commandLine.countTokens();
          if (arguments == 0) {
              return true;
          } else {
              String test = commandLine.nextToken();
              if (test.length() < 10) {// this stops overlong junk.
                  // we should validate the tag contents
                  tag = test;
              } else {
                  out.println(UNTAGGED + SP + BAD + SP + "tag too long");
                  return true;
              }
          }
          if (arguments > 1) {
              String test = commandLine.nextToken();
              if (test.length() < 13) {// this stops overlong junk.
                  // we could validate the command contents,
                  // but may not be worth it
                  command = test;
              }
              else {
                  out.println(tag + SP + BAD + SP
                              + "overlong command attempted");
                  return true;
              }
          } else {
              out.println(UNTAGGED + SP + BAD + SP + "no command sent");
              return true;
          }
  
          // At this stage we have a tag and a string which may be a command
          // Start with commands that are valid in any state
          // CAPABILITY, NOOP, LOGOUT
  
          if (command.equalsIgnoreCase("CAPABILITY")) {
              out.println(UNTAGGED + SP + CAPABILITY_RESPONSE);
              if (state == SELECTED ) {
                  checkSize();
                  checkExpunge();
              }
              out.println(tag + SP + OK + SP + "CAPABILITY completed");
              getLogger().debug("Capability command completed for " + remoteHost
                                + "(" + remoteIP  + ")");
              return true;
  
          } else if (command.equalsIgnoreCase("NOOP")) {
              if (state == SELECTED ) {
                  checkSize();
                  checkExpunge();
              }
              // we could send optional untagged status responses as well
              out.println(tag + SP + OK + SP + "NOOP completed");
              getLogger().debug("Noop command completed for " + remoteHost
                                + "(" + remoteIP  + ")");
              return true;
  
          } else if (command.equalsIgnoreCase("LOGOUT")) {
              connectionClosed = closeConnection(NORMAL_CLOSE, "", "");
              return false;
  
          }
  
          // Commands only valid in NON_AUTHENTICATED state
          // AUTHENTICATE, LOGIN
  
          if (state == NON_AUTHENTICATED) {
              if (command.equalsIgnoreCase("AUTHENTICATE")) {
                  out.println(tag + SP + NO + SP + "Auth type not supported.");
                  getLogger().info("Attempt to use Authenticate command by "
                                   + remoteHost  + "(" + remoteIP  + ")");
                  securityLogger.info("Attempt to use Authenticate command by "
                                      + remoteHost  + "(" + remoteIP  + ")");
                  return true;
              } else if (command.equalsIgnoreCase("LOGIN")) {
                  if (arguments != 4) {
                      out.println(tag + SP + BAD + SP
                                  + "Command should be <tag> <LOGIN> <username> <password>");
                      getLogger().info("Wrong number of arguments for LOGIN command from "
                                       + remoteHost  + "(" + remoteIP  + ")");
                      return true;
                  }
                  user = decodeAstring(commandLine.nextToken());
                  String password = decodeAstring(commandLine.nextToken());
                  if (users.test(user, password)) {
                      securityLogger.info("Login successful for " + user + " from  "
                                          + remoteHost + "(" + remoteIP  + ")");
                      // four possibilites handled:
                      // private mail: isLocal, is Remote
                      // other mail (shared, news, etc.) is Local, is Remote
  
                      if (imapHost.isHomeServer(user)) {
                          out.println(tag + SP + OK + SP + "LOGIN completed");
                          state = AUTHENTICATED;
  
                      } else  {
                          String remoteServer = null;
                          try {
                              remoteServer
                                  = imapSystem.getHomeServer(user);
                          } catch (AuthenticationException ae) {
                              connectionClosed
                                  = closeConnection(TAGGED_NO,
                                                    " cannot find your inbox, closing connection",
                                                    "");
                              return false;
                          }
  
                          if (imapHost.hasLocalAccess(user)) {
                              out.println(tag + SP + OK + SP + "[REFERRAL "
                                          + remoteServer +"]" + SP
                                          + "Your home server is remote, other mailboxes available here");
                              state = AUTHENTICATED;
  
                          } else {
                              closeConnection(TAGGED_NO, " [REFERRAL" + SP
                                              + remoteServer +"]" + SP
                                              + "No mailboxes available here, try remote server", "");
                              return false;
                          }
                      }
                      currentNamespace = imapHost.getDefaultNamespace(user);
                      currentSeperator
                          = imapSystem.getHierarchySeperator(currentNamespace);
                      // position at root of default Namespace,
                      // which is not actually a folder
                      currentFolder = currentNamespace + currentSeperator + "";
                      getLogger().debug("Current folder for user "  + user + " from "
                                        + remoteHost  + "(" + remoteIP  + ") is "
                                        + currentFolder);
                      return true;
  
  
                  } // failed password test
                  // We should add ability to monitor attempts to login
                  out.println(tag + SP + NO + SP + "LOGIN failed");
                  securityLogger.error("Failed attempt to use Login command for account "
                                       + user + " from "  + remoteHost  + "(" + remoteIP
                                       + ")");
                  return true;
              }
              // bad client
              out.println(tag + SP + NO + SP + "Must authenticate first");
              return true;
          } // end of if (state == NON_AUTHENTICATED)
  
          // Commands not yet processed should be valid in either
          // Authenticated or Selected states.
          getLogger().debug("Command recieved: " + commandRaw + " from " + remoteHost
                            + "(" + remoteIP  + ")");
  
          // Create ImapRequest object here - is this the right stage?
          ImapRequest request = new ImapRequest(this);
          request.setCommandLine(commandLine);
          request.setUseUIDs(false);
          request.setCurrentMailbox(currentMailbox);
          request.setCommandRaw(commandRaw);
          request.setTag(tag);
          request.setCurrentFolder(currentFolder);
  
          // Commands valid in both Authenticated and Selected states
          // NAMESPACE, GETACL, SETACL, DELETEACL, LISTRIGHTS, MYRIGHTS, SELECT
          if (state == AUTHENTICATED || state == SELECTED) {
  
              // NAMESPACE capability ------------------------------
              if (command.equalsIgnoreCase("NAMESPACE")) {
                  String namespaces = imapSystem.getNamespaces(user);
                  out.println(UNTAGGED + SP + "NAMESPACE " + namespaces);
                  getLogger().info("Provided NAMESPACE: " + namespaces );
                  if (state == SELECTED ) {
                      checkSize();
                      checkExpunge();
                  }
                  out.println(tag + SP + OK + SP
                              + "NAMESPACE command completed");
                  return true;
  
                  // ACL Capability  ---------------------------------------
              } else if (command.equalsIgnoreCase("GETACL")) {
                  ACLMailbox target = null;
                  if (arguments != 3) {
                      out.println(tag + SP + BAD + SP +
                                  "Command should be <tag> <GETACL> <mailbox>");
                      return true;
                  }
                  folder =  getFullName(commandLine.nextToken());
                  if ( state == SELECTED && currentFolder.equals(folder) ) {
                      target = currentMailbox;
                  } else {
                      target = getBox(user, folder);
                      if (target == null) return true;
                  }
                  try {
                      out.println(UNTAGGED + SP + "ACL " + target.getName() + SP
                                  + target.getAllRights(user ));
                      getLogger().debug(UNTAGGED + SP + "ACL " + target.getName() + SP
                                        + target.getAllRights(user ));
                  } catch (AccessControlException ace) {
                      out.println(tag + SP + NO + SP + "Unknown mailbox");
                      logACE(ace);
                      return true;
                  } catch (AuthorizationException aze) {
                      out.println(tag + SP + AUTH_FAIL_MSG);
                      logAZE(aze);
                      return true;
                  }
                  if (state == SELECTED ) {
                      checkSize();
                      checkExpunge();
                  }
                  out.println(tag + SP + OK + SP
                              + "GetACL command completed");
                  return true;
  
              } else if (command.equalsIgnoreCase("SETACL")) {
                  ACLMailbox target = null;
                  if (arguments != 5) {
                      out.println(tag + SP + BAD + SP +
                                  "Command should be <tag> <SETACL> <mailbox> <identity> <rights modification>");
                      return true;
                  }
                  folder =  getFullName(commandLine.nextToken());
                  String identity = commandLine.nextToken();
                  String changes = commandLine.nextToken();
  
                  if ( (state == SELECTED && currentFolder.equals(folder))) {
                      target = currentMailbox;
                  } else {
                      target = getBox(user, folder);
                      if (target == null) return true;
                  }
  
                  try {
                      if (target.setRights(user, identity, changes)) {
                          out.println(tag + SP + OK + SP
                                      + "SetACL command completed");
                          securityLogger.info("ACL rights for "  + identity + " in "
                                              + folder  + " changed by " + user + " : "
                                              +  changes);
                      } else {
                          out.println(tag + SP + NO + SP
                                      + "SetACL command failed");
                          securityLogger.info("Failed attempt to change ACL rights for "
                                              + identity + " in " + folder  + " by "
                                              + user);
                      }
                  } catch (AccessControlException ace) {
                      out.println(tag + SP + NO + SP + "Unknown mailbox");
                      logACE(ace);
                      return true;
                  } catch (AuthorizationException aze) {
                      out.println(tag + SP + AUTH_FAIL_MSG);
                      logAZE(aze);
                      return true;
                  }
                  if (state == SELECTED ) {
                      checkSize();
                      checkExpunge();
                  }
                  return true;
  
  
              } else if (command.equalsIgnoreCase("DELETEACL")) {
                  ACLMailbox target = null;
                  if (arguments != 4) {
                      out.println(tag + SP + BAD + SP +
                                  "Command should be <tag> <DELETEACL> <mailbox> <identity>");
                      return true;
                  }
                  folder =  getFullName(commandLine.nextToken());
                  String identity = commandLine.nextToken();
                  String changes = "";
  
                  if ( (state == SELECTED && currentFolder.equals(folder))) {
                      target = currentMailbox;
                  } else {
                      target = getBox(user, folder);
                      if (target == null) return true;
                  }
  
                  try {
                      if (target.setRights(user, identity, changes)) {
                          out.println(tag + SP + OK + SP
                                      + "DeleteACL command completed");
                          securityLogger.info("ACL rights for "  + identity + " in "
                                              + folder + " deleted by " + user);
                      } else {
                          out.println(tag + SP + NO + SP
                                      + "SetACL command failed");
                          securityLogger.info("Failed attempt to change ACL rights for "
                                              + identity + " in " + folder  + " by "
                                              + user);
                      }
                  } catch (AccessControlException ace) {
                      out.println(tag + SP + NO + SP + "Unknown mailbox");
                      logACE(ace);
                      return true;
                  } catch (AuthorizationException aze) {
                      out.println(tag + SP + AUTH_FAIL_MSG);
                      logAZE(aze);
                      return true;
                  }
                  if (state == SELECTED ) {
                      checkSize();
                      checkExpunge();
                  }
                  return true;
  
              } else if (command.equalsIgnoreCase("LISTRIGHTS")) {
                  ACLMailbox target = null;
                  if (arguments != 4) {
                      out.println(tag + SP + BAD_LISTRIGHTS_MSG);
                      return true;
                  }
                  folder =  getFullName(commandLine.nextToken());
                  String identity = commandLine.nextToken();
                  if ( state == SELECTED && currentFolder.equals(folder) ) {
                      target = currentMailbox;
                  } else {
                      target = getBox(user, folder);
                      if (target == null) return true;
                  }
  
                  try {
                      out.println(UNTAGGED + SP + "LISTRIGHTS "
                                  + target.getName() + SP + identity + SP
                                  + target.getRequiredRights(user, identity)
                                  + SP
                                  + target.getOptionalRights(user, identity));
                      out.println(tag + SP + OK + SP
                                  + "ListRights command completed");
                  } catch (AccessControlException ace) {
                      out.println(tag + SP + NO + SP + "Unknown mailbox");
                      logACE(ace);
                      return true;
                  } catch (AuthorizationException aze) {
                      out.println(tag + SP + AUTH_FAIL_MSG);
                      logAZE(aze);
                      return true;
                  }
                  if (state == SELECTED ) {
                      checkSize();
                      checkExpunge();
                  }
                  return true;
  
              } else if (command.equalsIgnoreCase("MYRIGHTS")) {
                  ACLMailbox target = null;
                  if (arguments != 3) {
                      out.println(tag + SP + BAD_MYRIGHTS_MSG);
                      return true;
                  }
                  folder =  getFullName(commandLine.nextToken());
                  if ( state == SELECTED && currentFolder.equals(folder) ) {
                      target = currentMailbox;
                  } else {
                      target = getBox(user, folder);
                      if (target == null) return true;
                  }
  
                  try {
                      out.println(UNTAGGED + SP + "MYRIGHTS "
                                  + target.getName() + SP
                                  + target.getRights(user, user));
                      out.println(tag + SP + OK + SP
                                  + "MYRIGHTS command completed");
                  } catch (AccessControlException ace) {
                      out.println(tag + SP + NO + SP + "Unknown mailbox");
                      logACE(ace);
                      return true;
                  } catch (AuthorizationException aze) {
                      out.println(tag + SP + AUTH_FAIL_MSG);
                      logAZE(aze);
                      return true;
                  }
                  if (state == SELECTED ) {
                      checkSize();
                      checkExpunge();
                  }
                  return true;
  
  
                  // Standard IMAP commands --------------------------
  
              } else if (command.equalsIgnoreCase("SELECT")
                         || command.equalsIgnoreCase("EXAMINE")) {
                  // selecting a mailbox deselects current mailbox,
                  // even if this select fails
                  if (state == SELECTED) {
                      currentMailbox.removeMailboxEventListener(this);
                      imapHost.releaseMailbox(user, currentMailbox);
                      state = AUTHENTICATED;
                      currentMailbox = null;
                      currentIsReadOnly = false;
                  }
  
                  if (arguments != 3) {
                      if (command.equalsIgnoreCase("SELECT") ){
                          out.println(tag + SP + BAD + SP
                                      + "Command should be <tag> <SELECT> <mailbox>");
                      } else {
                          out.println(tag + SP + BAD + SP
                                      + "Command should be <tag> <EXAMINE> <mailbox>");
                      }
                      return true;
                  }
  
                  folder =  getFullName(commandLine.nextToken());
                  currentMailbox = getBox(user,  folder);
                  if (currentMailbox == null) {
                      return true;
                  }
                  try { // long tries clause against an AccessControlException
                      if (!currentMailbox.hasReadRights(user)) {
                          out.println(tag + SP + NO + SP
                                      + "Read access not granted." );
                          return true;
                      }
                      if (command.equalsIgnoreCase("SELECT") ){
                          if (!currentMailbox.isSelectable(user)) {
                              out.println(tag + SP + NO + SP
                                          + "Mailbox exists but is not selectable");
                              return true;
                          }
                      }
  
                      // Have mailbox with at least read rights. Server setup.
                      currentMailbox.addMailboxEventListener(this);
                      currentFolder = folder;
                      state = SELECTED;
                      exists = -1;
                      recent = -1;
                      getLogger().debug("Current folder for user "  + user + " from "
                                        + remoteHost  + "(" + remoteIP  + ") is "
                                        + currentFolder);
  
                      // Inform client
                      out.println(UNTAGGED + SP + "FLAGS ("
                                  + currentMailbox.getSupportedFlags() + ")" );
                      if (!currentMailbox.allFlags(user)) {
                          out.println(UNTAGGED + SP + OK + " [PERMANENTFLAGS ("
                                      + currentMailbox.getPermanentFlags(user)
                                      + ") ]");
                      }
                      checkSize();
                      out.println(UNTAGGED + SP + OK + " [UIDVALIDITY "
                                  + currentMailbox.getUIDValidity() + " ]");
                      int oldestUnseen = currentMailbox.getOldestUnseen(user);
                      if (oldestUnseen > 0 ) {
                          out.println(UNTAGGED + SP + OK + " [UNSEEN "
                                      + oldestUnseen + " ]");
                      } else {
                          out.println(UNTAGGED + SP + OK + " No unseen messages");
                      }
                      sequence = currentMailbox.listUIDs(user);
  
                      if (command.equalsIgnoreCase("EXAMINE")) {
                          currentIsReadOnly = true;
  
                          out.println(tag + SP + OK + SP
                                      + "[READ-ONLY] Examine completed");
                          return true;
  
                      } else if (currentMailbox.isReadOnly(user)) {
                          currentIsReadOnly = true;
                          out.println(tag + SP + OK + SP
                                      + "[READ-ONLY] Select completed");
                          return true;
                      }
                      out.println(tag + SP + OK + SP
                                  + "[READ-WRITE] Select completed");
                      return true;
                  } catch (AccessControlException ace) {
                      out.println(tag + SP + NO + SP + "No such mailbox.");
                      logACE(ace);
                      return true;
                  }
                  // End of SELECT || EXAMINE
              } else if (command.equalsIgnoreCase("CREATE")) {
                  if (arguments != 3) {
                      out.println(tag + SP + BAD + SP +
                                  "Command should be <tag> <CREATE> <mailbox>");
                      return true;
                  }
                  folder =  getFullName(commandLine.nextToken());
                  if(currentFolder == folder) {
                      out.println(tag + SP + NO + SP
                                  + "Folder exists and is selected." );
                      return true;
                  }
                  try {
                      ACLMailbox target = imapHost.createMailbox(user, folder);
                      out.println(tag + SP + OK + SP + "Create completed");
                      imapHost.releaseMailbox(user, target);
                  }  catch (AccessControlException ace) {
                      out.println(tag + SP + NO + SP
                                  + "No such mailbox. ");
                      logACE(ace);
                      return true;
                  } catch (MailboxException mbe) {
                      if (mbe.isRemote()) {
                          out.println(tag + SP + NO + SP + "[REFERRAL "
                                      + mbe.getRemoteServer() +"]"
                                      + SP + "Wrong server. Try remote." );
                      } else  {
                          out.println(tag + SP + NO + SP + mbe.getStatus() );
                      }
                      return true;
                  } catch (AuthorizationException aze) {
                      out.println(tag + SP + NO + SP
                                  + "You do not have the rights to create mailbox: "
                                  + folder);
                      return true;
                  }
                  if (state == SELECTED ) {
                      checkSize();
                      checkExpunge();
                  }
                  return true;
  
  
              } else if (command.equalsIgnoreCase("DELETE")) {
                  if (arguments != 3) {
                      out.println(tag + SP + BAD + SP +
                                  "Command should be <tag> <DELETE> <mailbox>");
                      return true;
                  }
                  folder =  getFullName(commandLine.nextToken());
                  if(currentFolder == folder) {
                      out.println(tag + SP + NO + SP
                                  + "You can't delete a folder while you have it selected." );
                      return true;
                  }
                  try {
                      if( imapHost.deleteMailbox(user, folder)) {
                          out.println(tag + SP + OK + SP + "Delete completed");
                      } else {
                          out.println(tag + SP + NO + SP
                                      + "Delete failed, unknown error");
                          getLogger().info("Attempt to delete mailbox " + folder
                                           + " by user " + user + " failed.");
                      }
                  } catch (MailboxException mbe) {
                      if (mbe.getStatus().equals(MailboxException.NOT_LOCAL)) {
                          out.println(tag + SP + NO_NOTLOCAL_MSG);
                      } else  {
                          out.println(tag + SP + NO + SP + mbe.getMessage() );
                      }
                      return true;
                  } catch (AuthorizationException aze) {
                      out.println(tag + SP + NO + SP
                                  + "You do not have the rights to delete mailbox: " + folder);
                      logAZE(aze);
                      return true;
                  }
                  if (state == SELECTED ) {
                      checkSize();
                      checkExpunge();
                  }
                  return true;
  
  
              } else if (command.equalsIgnoreCase("RENAME")) {
                  if (arguments != 4) {
                      out.println(tag + SP + BAD + SP +
                                  "Command should be <tag> <RENAME> <oldname> <newname>");
                      return true;
                  }
                  folder =  getFullName(commandLine.nextToken());
                  String newName = getFullName(commandLine.nextToken());
                  if(currentFolder == folder) {
                      out.println(tag + SP + NO + SP
                                  + "You can't rename a folder while you have it selected." );
                      return true;
                  }
                  try {
                      if(imapHost.renameMailbox(user, folder, newName)) {
                          out.println(tag + SP + OK + SP + "Rename completed");
                      } else {
                          out.println(tag + SP + NO + SP
                                      + "Rename failed, unknown error");
                          getLogger().info("Attempt to rename mailbox " + folder
                                           + " to " + newName
                                           + " by user " + user + " failed.");
                      }
                  } catch (MailboxException mbe) {
                      if (mbe.getStatus().equals(MailboxException.NOT_LOCAL)) {
                          out.println(tag + SP + NO_NOTLOCAL_MSG);
                      } else  {
                          out.println(tag + SP + NO + SP + mbe.getMessage() );
                      }
                      return true;
                  } catch (AuthorizationException aze) {
                      out.println(tag + SP + NO + SP
                                  + "You do not have the rights to delete mailbox: " + folder);
                      return true;
                  }
                  if (state == SELECTED ) {
                      checkSize();
                      checkExpunge();
                  }
                  return true;
  
  
              } else if (command.equalsIgnoreCase("SUBSCRIBE")) {
                  if (arguments != 3) {
                      out.println(tag + SP + BAD + SP +
                                  "Command should be <tag> <SUBSCRIBE> <mailbox>");
                      return true;
                  }
                  folder =  getFullName(commandLine.nextToken());
  
                  try {
                      if( imapHost.subscribe(user, folder) ) {
                          out.println(tag + SP + OK + SP
                                      + "Subscribe completed");
                      } else {
                          out.println(tag + SP + NO + SP + "Unknown error." );
                      }
                  } catch (MailboxException mbe) {
                      if (mbe.isRemote()) {
                          out.println(tag + SP + NO + SP + "[REFERRAL "
                                      + mbe.getRemoteServer() +"]"
                                      + SP + "Wrong server. Try remote." );
                      } else  {
                          out.println(tag + SP + NO + SP + "No such mailbox" );
                      }
                      return true;
                  } catch (AccessControlException ace) {
                      out.println(tag + SP + NO + SP + "No such mailbox");
                      logACE(ace);
                      return true;
                  }
                  if (state == SELECTED ) {
                      checkSize();
                      checkExpunge();
                  }
                  return true;
  
              } else if (command.equalsIgnoreCase("UNSUBSCRIBE")) {
                  if (arguments != 3) {
                      out.println(tag + SP + BAD + SP +
                                  "Command should be <tag> <UNSUBSCRIBE> <mailbox>");
                      return true;
                  }
                  folder =  getFullName(commandLine.nextToken());
  
                  try {
                      if( imapHost.unsubscribe(user, folder) ) {
                          out.println(tag + SP + OK + SP
                                      + "Unsubscribe completed");
                      } else {
                          out.println(tag + SP + NO + SP + "Unknown error." );
                      }
                  } catch (MailboxException mbe) {
                      if (mbe.isRemote()) {
                          out.println(tag + SP + NO + SP + "[REFERRAL "
                                      + mbe.getRemoteServer() +"]"
                                      + SP + "Wrong server. Try remote." );
                      } else  {
                          out.println(tag + SP + NO + SP + "No such mailbox" );
                      }
                      return true;
                  } catch (AccessControlException ace) {
                      out.println(tag + SP + NO + SP + "No such mailbox");
                      logACE(ace);
                      return true;
                  }
                  if (state == SELECTED ) {
                      checkSize();
                      checkExpunge();
                  }
                  return true;
  
              } else if (command.equalsIgnoreCase("LIST")
                         || command.equalsIgnoreCase("LSUB")) {
                  if (arguments != 4) {
                      if (command.equalsIgnoreCase("LIST")) {
                          out.println(tag + SP + BAD_LIST_MSG);
                      } else {
                          out.println(tag + SP + BAD_LSUB_MSG);
                      }
                      return true;
                  }
                  if (command.equalsIgnoreCase("LIST")) {
                      subscribeOnly =false;
                  } else {
                      subscribeOnly = true;
                  }
                  String reference = decodeAstring(commandLine.nextToken());
                  folder = decodeAstring(commandLine.nextToken());
  
                  if (reference.equals("")) {
                      reference = currentFolder;
                  } else {
                      reference = getFullName(reference);
                  }
                  Collection list = null;
                  try {
                      list = imapHost.listMailboxes(user, reference, folder,
                                                    subscribeOnly);
                      if (list == null) {
                          getLogger().debug(tag + SP + NO + SP + command
                                            + " unable to interpret mailbox");
                          out.println(tag + SP + NO + SP + command
                                      + " unable to interpret mailbox");
                      } else if (list.size() == 0) {
                          getLogger().debug("List request matches zero mailboxes: " + commandRaw);
                          out.println(tag + SP + OK + SP + command
                                      + " completed");
                      } else {
                          Iterator it = list.iterator();
                          while (it.hasNext()) {
                              String listResponse = (String)it.next();
                              out.println(UNTAGGED + SP + command.toUpperCase()
                                          + SP + listResponse);
                              getLogger().debug(UNTAGGED + SP + command.toUpperCase()
                                                + SP + listResponse);
                          }
                          out.println(tag + SP + OK + SP + command
                                      + " completed");
                      }
                  } catch (MailboxException mbe) {
                      if (mbe.isRemote()) {
                          out.println(tag + SP + NO + SP + "[REFERRAL "
                                      + mbe.getRemoteServer() +"]"
                                      + SP + "Wrong server. Try remote." );
                      } else  {
                          out.println(tag + SP + NO + SP
                                      + "No such mailbox" );
                      }
                      return true;
                  } catch (AccessControlException ace) {
                      out.println(tag + SP + NO + SP + "No such mailbox");
                      logACE(ace);
                      return true;
                  }
  
                  if (state == SELECTED ) {
                      checkSize();
                      checkExpunge();
                  }
                  return true;
  
              } else if (command.equalsIgnoreCase("STATUS")) {
                  if (arguments < 4) {
                      out.println(tag + SP + BAD + SP +
                                  "Command should be <tag> <STATUS> <mailboxname> (status data items)");
                      return true;
                  }
                  folder =  getFullName(commandLine.nextToken());
                  List dataNames = new ArrayList();
                  String  attr = commandLine.nextToken();
                  if (! attr.startsWith("(")) { //single attr
                      out.println(tag + SP + BAD + SP +
                                  "Command should be <tag> <STATUS> <mailboxname> (status data items)");
                      return true;
                  } else if (attr.endsWith(")")){ //single attr in paranthesis
                      dataNames.add(attr.substring(1, attr.length()-1 ));
                  } else { // multiple attrs
                      dataNames.add(attr.substring(1).trim());
                      while(commandLine.hasMoreTokens()) {
                          attr = commandLine.nextToken();
                          if (attr.endsWith(")")) {
                              dataNames.add(attr.substring(0, attr.length()-1 ));
                          } else {
                              dataNames.add(attr);
                          }
                      }
                  }
                  try {
                      String response = imapHost.getMailboxStatus(user, folder,
                                                                  dataNames);
                      out.println(UNTAGGED + " STATUS " + folder + " ("
                                  + response + ")");
                      out.println(tag + SP + OK + SP + "Status completed");
                  } catch (MailboxException mbe) {
                      if (mbe.isRemote()) {
                          out.println(tag + SP + NO + SP + "[REFERRAL "
                                      + mbe.getRemoteServer() +"]"
                                      + SP + "Wrong server. Try remote." );
                      } else  {
                          out.println(tag + SP + NO + SP
                                      + "No such mailbox" );
                      }
                      return true;
                  } catch (AccessControlException ace) {
                      out.println(tag + SP + NO + SP + "No such mailbox");
                      logACE(ace);
                      return true;
                  }
                  if (state == SELECTED ) {
                      checkSize();
                      checkExpunge();
                  }
                  return true;
  
              } else if (command.equalsIgnoreCase("APPEND")) {
                  if (true) {
                      out.println(tag + SP + BAD + SP +
                                  "Append Command not yet implemented.");
                      return true;
                  }
  
              }
  
          } // end of Auth & Selected
  
          // Commands valid only in Authenticated State
          // None
          if (state == AUTHENTICATED) {
              out.println(tag + SP + BAD + SP
                          + "Command not valid in this state");
              return true;
          }
  
          // Commands valid only in Selected state
          // CHECK
          if (state == SELECTED) {
              if (command.equalsIgnoreCase("CHECK")) {
                  if (currentMailbox.checkpoint()) {
                      out.println(tag + SP + OK + SP
                                  + "Check completed");
                      checkSize();
                      checkExpunge();
                      return true;
                  } else {
                      out.println(tag + SP + NO + SP
                                  + "Check failed");
                      return true;
                  }
              } else if (command.equalsIgnoreCase("CLOSE")) {
                  try {
                      currentMailbox.expunge(user);
                  } catch (Exception e) {
                      getLogger().error("Exception while expunging mailbox on CLOSE : " + e);
                  }
                  currentMailbox.removeMailboxEventListener(this);
                  imapHost.releaseMailbox(user, currentMailbox);
                  state = AUTHENTICATED;
                  currentMailbox = null;
                  currentIsReadOnly = false;
                  out.println(tag + SP + OK + SP
                              + "CLOSE completed");
                  return true;
              } else if (command.equalsIgnoreCase("COPY")) {
                  if (arguments < 4) {
                      out.println(tag + SP + BAD + SP +
                                  "Command should be <tag> <COPY> <message set> <mailbox name>");
                      return true;
                  }
                  List set = decodeSet(commandLine.nextToken(),
                                       currentMailbox.getExists());
                  getLogger().debug("Fetching message set of size: " + set.size());
                  String  targetFolder = getFullName(commandLine.nextToken());
  
                  ACLMailbox targetMailbox = getBox(user,  targetFolder);
                  if (targetMailbox == null) {
                      return true;
                  }
                  try { // long tries clause against an AccessControlException
                      if (!currentMailbox.hasInsertRights(user)) {
                          out.println(tag + SP + NO + SP
                                      + "Insert access not granted." );
                          return true;
                      }
                      for (int i = 0; i < set.size(); i++) {
                          int msn = ((Integer)set.get(i)).intValue();
                          MessageAttributes attrs = currentMailbox.getMessageAttributes(msn, user);
                      }
                  } catch (AccessControlException ace) {
                      out.println(tag + SP + NO + SP + "No such mailbox.");
                      logACE(ace);
                      return true;
                  } catch (AuthorizationException aze) {
                      out.println(tag + SP + NO + SP
                                  + "You do not have the rights to expunge mailbox: " + folder);
                      logAZE(aze);
                      return true;
                  }
  
                  out.println(tag + SP + OK + SP
                              + "CLOSE completed");
                  return true;
              } else if (command.equalsIgnoreCase("EXPUNGE")) {
                  try {
                      if(currentMailbox.expunge(user)) {
                          checkExpunge();
                          checkSize();
                          out.println(tag + SP + OK + SP
                                      + "EXPUNGE complete.");
                      } else {
                          out.println(tag + SP + NO + SP
                                      + "Unknown server error.");
                      }
                      return true;
                  } catch (AccessControlException ace) {
                      out.println(tag + SP + NO + SP + "No such mailbox");
                      logACE(ace);
                      return true;
                  } catch (AuthorizationException aze) {
                      out.println(tag + SP + NO + SP
                                  + "You do not have the rights to expunge mailbox: " + folder);
                      logAZE(aze);
                      return true;
                  } catch (Exception e) {
                      out.println(tag + SP + NO + SP
                                  + "Unknown server error.");
                      getLogger().error("Exception expunging mailbox " + folder + " by user " + user + " was : " + e);
                      if (DEEP_DEBUG) {e.printStackTrace();}
                      return true;
                  }
              } else if (command.equalsIgnoreCase("FETCH")) {
                  if (arguments < 4) {
                      out.println(tag + SP + BAD + SP +
                                  "Command should be <tag> <FETCH> <message set> <message data item names>");
                      return true;
                  }
                  CommandFetch fetcher = new CommandFetch();
                  fetcher.setLogger( getLogger() );
                  fetcher.setRequest(request);
                  fetcher.service();
                  return true;
                  // end of FETCH
              } else if (command.equalsIgnoreCase("STORE")) {
                  if (arguments < 5) {
                      out.println(tag + SP + BAD + SP +
                                  "Command should be <tag> <STORE> <message set> <message data item names> <value for message data item>");
                      return true;
                  }
                  //storeCommand(commandLine, false);
                  CommandStore storer = new CommandStore();
                  storer.setLogger( getLogger() );
                  storer.setRequest(request);
                  storer.service();
                  return true;
              } else if (command.equalsIgnoreCase("UID")) {
                  if (arguments < 4) {
                      out.println(tag + SP + BAD + SP +
                                  "Command should be <tag> <UID> <command> <command parameters>");
                      return true;
                  }
                  String uidCommand = commandLine.nextToken();
                  if (uidCommand.equalsIgnoreCase("STORE")) {
                      //storeCommand(commandLine, true);
                      CommandStore storer = new CommandStore();
                      storer.setLogger( getLogger() );
                      storer.setRequest(request);
                      storer.service();
                      return true;
                  } else if (uidCommand.equalsIgnoreCase("FETCH")) {
                      CommandFetch fetcher = new CommandFetch();
                      fetcher.setLogger( getLogger() );
                      fetcher.setRequest(request);
                      fetcher.service();
                      return true;
                  }
              } else {
                  // Other commands for selected state .....
                  out.println(tag + SP + BAD + SP + "Protocol error");
                  return true;
  
              } // end state SELECTED
          }
          // Shouldn't happen
          out.println(tag + SP + BAD + SP + "Protocol error");
          return true;
  
      } // end of parseCommand
  
      public void dispose() {
          // todo
          getLogger().error("Stop IMAPHandler");
      }
  
      public void receiveEvent(MailboxEvent me) {
          if (state == SELECTED) {
              checkMailboxFlag = true;
          }
      }
  
      private ACLMailbox getBox(String user, String mailboxName) {
          ACLMailbox tempMailbox = null;
          try {
              tempMailbox = imapHost.getMailbox(user, mailboxName);
          } catch (MailboxException me) {
              if (me.isRemote()) {
                  out.println(tag + SP + NO + SP + "[REFERRAL " + me.getRemoteServer() +"]" + SP + "Remote mailbox" );
              } else {
                  out.println(tag + SP + NO + SP + "Unknown mailbox" );
                  getLogger().info("MailboxException in method getBox for user: "
                                   + user + " mailboxName: " + mailboxName + " was "
                                   + me.getMessage());
              }
  
          } catch (AccessControlException e) {
              out.println(tag + SP + NO + SP + "Unknown mailbox" );
          }
          return tempMailbox;
      }
  
      private String getFullName(String name) {
          getLogger().debug("Method getFullName called for " + name);
          name = decodeAstring(name);
          if (name == null) {
              getLogger().error("Received null name");
              return null;
          }
          int inbox = name.toUpperCase().indexOf("INBOX");
          if (inbox == -1) {
              if (name.startsWith(namespaceToken)) {          //absolute reference
                  return name;
              } else if (name.startsWith(currentSeperator)) {//rooted relative ref
                  return currentNamespace + name;
              }else {                                        //unrooted relative ref
                  if (currentFolder.equals(currentNamespace + currentSeperator )) {
                      return currentFolder + name;
                  } else {
                      return currentFolder + currentSeperator + name;
                  }
              }
          } else {
              return ("#mail.INBOX");
  
          }
      }
  
      public void logACE(AccessControlException ace) {
          securityLogger.error("AccessControlException by user "  + user
                               + " from "  + remoteHost  + "(" + remoteIP
                               + ") with " + commandRaw + " was "
                               + ace.getMessage());
      }
  
      public void logAZE(AuthorizationException aze) {
          securityLogger.error("AuthorizationException by user "  + user
                               + " from "  + remoteHost  + "(" + remoteIP
                               + ") with " + commandRaw + " was "
                               + aze.getMessage());
      }
  
      public PrintWriter getPrintWriter() {
          return out;
      }
  
      public OutputStream getOutputStream() {
          return outs;
      }
  
      public String getUser() {
          return user;
      }
  
      private String decodeAstring(String rawAstring) {
  
          if (rawAstring.startsWith("\"")) {
              //quoted string
              if (rawAstring.endsWith("\"")) {
                  if (rawAstring.length() == 2) {
                      return new String(); //ie blank
                  } else {
                      return rawAstring.substring(1, rawAstring.length() - 1);
                  }
              } else {
                  getLogger().error("Quoted string with no closing quote.");
                  return null;
              }
          } else {
              //atom
              return rawAstring;
          }
      }
  
      public void checkSize() {
          int newExists = currentMailbox.getExists();
          if (newExists != exists) {
              out.println(UNTAGGED + SP + newExists + " EXISTS");
              exists = newExists;
          }
          int newRecent = currentMailbox.getRecent();
          if (newRecent != recent) {
              out.println(UNTAGGED + SP + newRecent + " RECENT");
              recent = newRecent;
          }
          return;
      }
  
      private void checkExpunge() {
          List newList = currentMailbox.listUIDs(user);
          for (int k = 0; k < newList.size(); k++) {
              getLogger().debug("New List msn " + (k+1) + " is uid "  + newList.get(k));
          }
          for (int i = sequence.size() -1; i > -1 ; i--) {
              Integer j = (Integer)sequence.get(i);
              getLogger().debug("Looking for old msn " + (i+1) + " was uid " + j);
              if (! newList.contains((Integer)sequence.get(i))) {
                  out.println(UNTAGGED + SP + (i+1) + " EXPUNGE");
              }
          }
          sequence = newList;
          //newList = null;
          return;
      }
  }
  
  
  

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