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

cvs commit: jakarta-james/proposals/imap/java/org/apache/james/imapserver/commands FileStoreHighestUID.java FileMailbox.java CommandFetch.java HighestUID.java

danny       2002/08/27 06:57:29

  Added:       proposals/imap/java/org/apache/james/imapserver/commands
                        FileStoreHighestUID.java FileMailbox.java
                        CommandFetch.java HighestUID.java
  Log:
  latest changes from sascha
  
  Revision  Changes    Path
  1.1                  jakarta-james/proposals/imap/java/org/apache/james/imapserver/commands/FileStoreHighestUID.java
  
  Index: FileStoreHighestUID.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.*;
  
  /**
   * Reference: RFC 2060
   * @author <a href="mailto:s@rapi.com">Stephan Schiessling</a>
   * @version 0.1 on 15 Aug 2002
   */
  public class FileStoreHighestUID implements HighestUID {
  
    private int highestUID;
    private int whenToWrite;
    private static final int WRITE_STEP = 3;
    private File file;
    
    public FileStoreHighestUID(File f) {
      file = f;
      highestUID = 0;
      
      if (file.exists()) {
         ObjectInputStream is = null;
         try {
            is = new ObjectInputStream(new FileInputStream(file));
            Integer i = (Integer) is.readObject();
            highestUID = i.intValue();
            is.close();
            is = null;
         } catch (Exception ex) {
            // log here
            ex.printStackTrace();
            if (is != null) {
              try {
                is.close();
              } catch (Exception ignored) {}
            }
            throw new RuntimeException("Could not load highestUID!");
         }
         // maybe james was stopped without writing correct highestUID, therefore add
         // STEP_HIGHEST_UID, to ensure uniqeness of highestUID.
         highestUID += WRITE_STEP;
      }
      write();
      whenToWrite = highestUID+WRITE_STEP;
      System.out.println("Initialized highestUID="+highestUID);
    }
    
    public synchronized int get() {
      return highestUID;
    }
    
    public synchronized void increase() {
      highestUID++;
      if (highestUID >= whenToWrite) {
          // save this highestUID
          whenToWrite = highestUID+WRITE_STEP;
          // make it persistent
          write();
      }
    }
    
    private void write() {
      ObjectOutputStream os = null;
      try {
         os = new ObjectOutputStream( new FileOutputStream(file));
         os.writeObject(new Integer(highestUID));
         os.close();
         os = null;
      } catch (Exception ex) {
         // log here
         ex.printStackTrace();
         if (os != null) {
            try {
               os.close();
            } catch (Exception ignored) {}
         }
         throw new RuntimeException("Failed to save highestUID!");
      }
    }
  }
  
  
  
  1.1                  jakarta-james/proposals/imap/java/org/apache/james/imapserver/commands/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 org.apache.avalon.cornerstone.services.store.ObjectRepository;
  import org.apache.avalon.cornerstone.services.store.Store;
  import org.apache.avalon.framework.component.ComponentManager;
  import org.apache.avalon.framework.configuration.Configuration;
  import org.apache.avalon.framework.configuration.ConfigurationException;
  import org.apache.avalon.framework.context.Context;
  import org.apache.avalon.framework.logger.AbstractLogEnabled;
  import org.apache.avalon.phoenix.BlockContext;
  import org.apache.james.AccessControlException;
  import org.apache.james.AuthorizationException;
  import org.apache.james.core.MimeMessageWrapper;
  import org.apache.james.services.UsersRepository;
  import org.apache.james.services.UsersStore;
  import org.apache.james.util.Assert;
  
  import javax.mail.internet.InternetHeaders;
  import javax.mail.internet.MimeMessage;
  import javax.mail.MessagingException;
  import java.io.*;
  import java.util.*;
  
  /**
   * 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:sascha@kulawik.de">Sascha Kulawik</a>
   * @author <a href="mailto:charles@benett1.demon.co.uk">Charles Benett</a>
   * @version 0.2 on 04 Aug 2002
   */
  public class FileMailbox
      extends AbstractLogEnabled
      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 BlockContext context;
      private transient Configuration conf;
      private transient ComponentManager compMgr;
      private transient UsersRepository localUsers;
      private transient HashSet listeners;
  
      /* Serialized fileds */
      private String path; // does not end with File.separator
      private String rootPath; // 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 mailboxSize; // octets
      private boolean inferiorsAllowed;
      private boolean marked;
      private boolean notSelectableByAnyone;
      
      //Addon by s@rapi.com. Sets the UID for all Mailboxes
      private static HighestUID highestUID;
      
      // 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;
  
      // Set of subscribed users.
      private Set subscribedUsers = new HashSet(1000);
  
      public void configure(Configuration conf) throws ConfigurationException {
          this.conf = conf;
          highestUID = new FileStoreHighestUID(new File(conf.getChild("recordRepository").getValue()+File.separator+"#CONF.GLOBAL.uid"));
      }
  
      public void contextualize(Context context) {
          this.context = (BlockContext)context;
      }
  
      public void compose(ComponentManager comp) {
          compMgr = comp;
      }
  
      public void prepareMailbox(String user, String absName, String initialAdminUser, int uidValidity ) {
          Assert.isTrue( Assert.ON && user != null && user.length() > 0 );
          owner = user;
  
          Assert.isTrue( Assert.ON && absName != null && (absName.length() > 0));
          absoluteName = absName;
  
          Assert.isTrue( Assert.ON && initialAdminUser != null && initialAdminUser.length() > 0 );
          acl = new HashMap(7);
          acl.put(initialAdminUser, ALL_RIGHTS);
  
          Assert.isTrue( Assert.ON && uidValidity > 0 );
          this.uidValidity = uidValidity;
      }
  
      public void initialize() throws Exception {
          
          mailboxSize = 0;
          inferiorsAllowed = true;
          marked = false;
          notSelectableByAnyone = false;
          oldestUnseenMessage = new HashMap();
          listeners = new HashSet();
          sequence = new ArrayList();
          recentMessages = new HashSet();
          messagesForDeletion = new HashSet();
          getLogger().info("FileMailbox init for " + absoluteName);
          UsersStore usersStore = (UsersStore)compMgr.lookup( "org.apache.james.services.UsersStore" );
          localUsers = usersStore.getRepository("LocalUsers");
          rootPath = conf.getChild( "mailboxRepository" ).getValue();
          if (!rootPath.endsWith(File.separator)) {
              rootPath = rootPath + File.separator;
          }
          
          path = getPath( absoluteName, owner, rootPath );
          name = absoluteName.substring(absoluteName.lastIndexOf( JamesHost.HIERARCHY_SEPARATOR ) + 1);
          if (name.equals(owner)) {
              name = "";
          }
          //Check for writable directory
          getLogger().info("MailboxDir " + path);
          System.out.println("MailboxDir TO WRITE TO " + path);
          File mailboxDir = new File(path);
          if (mailboxDir.exists()) {
              throw new RuntimeException("Error: Attempt to overwrite mailbox directory at " + path);
          } else if (! mailboxDir.mkdir()){
              throw new RuntimeException("Error: Cannot create mailbox directory at " + path);
          } else if (!mailboxDir.canWrite()) {
              throw new RuntimeException("Error: Cannot write to directory at " + path);
          }
          writeMailbox();
          getLogger().info("FileMailbox init complete: " + absoluteName);
      }
  
  
  
      /**
       * 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 rootPath )
      {
          Assert.isTrue( Assert.ON &&
                         absoluteName.startsWith( JamesHost.NAMESPACE_TOKEN ) );
  
          // Remove the leading '#' and replace Hierarchy separators with file separators.
          String filePath = absoluteName.substring( JamesHost.NAMESPACE_TOKEN.length() );
          filePath = filePath.replace( JamesHost.HIERARCHY_SEPARATOR_CHAR, File.separatorChar );
          return rootPath + filePath;
      }
      
      /**
       * Re-initialises mailbox after reading from file-system. Cannot assume that this is the same instance of James that wrote it.
       *
       * <p> Contract is that re-init must be called after configure, contextualize, compose.
       */
      public void reinitialize() throws Exception {
          listeners = new HashSet();
          getLogger().info("FileMailbox reInit for " + absoluteName);
          UsersStore usersStore = (UsersStore)compMgr.lookup( "org.apache.james.services.UsersStore" );
          localUsers = usersStore.getRepository("LocalUsers");
          rootPath
              = conf.getChild("mailboxRepository").getValue();
          if (!rootPath.endsWith(File.separator)) {
              rootPath = rootPath + File.separator;
          }
          path = getPath( absoluteName, owner, rootPath );
      }
  
      /**
       * Call when host has finished with a mailbox.
       * This is really a stop rather than destroy.
       * Writes current mailbox object to disc.
       */
      public void dispose() {
          writeMailbox();
          getLogger().info("FileMailbox object destroyed: " + absoluteName);
      }
  
      /**
       * Permanently deletes the mailbox.
       */
      public void removeMailbox()
      {
          // First delete the mailbox file
          path = getPath( absoluteName, owner, rootPath );
          String mailboxRecordFile = path + File.separator + MAILBOX_FILE_NAME;
          File mailboxRecord = new File( mailboxRecordFile );
          Assert.isTrue( Assert.ON &&
                         mailboxRecord.exists() &&
                         mailboxRecord.isFile() );
          mailboxRecord.delete();
  
          // Check for empty directory before deleting.
          File mailboxDir = new File(path);
          Assert.isTrue( Assert.ON &&
                         mailboxDir.exists() );
          Assert.isTrue( Assert.ON &&
                         mailboxDir.isDirectory() );
          Assert.isTrue( Assert.ON &&
                         mailboxDir.list().length == 0 );
          mailboxDir.delete();
      }
      
      /**
       * Renames this Mailbox.
       * @param usernmae The Username who calles this Command.
       * @param newmailboxname The new name for this Mailbox.
       * @returns true if everythink was sucessfully, else false.
       * @throws MailboxException if mailbox does not exist locally.
       * @throws AuthorizationException if the user has no rights for changing the name of the Mailbox.
       */
      public boolean renameMailbox(String username, String newmailboxname) throws MailboxException, AuthorizationException {
          try {
              path = getPath( absoluteName, owner, rootPath );
              
              StringTokenizer strt = new StringTokenizer(newmailboxname,".");
              String lastnameofmailbox="";
              while(strt.hasMoreTokens())
                  lastnameofmailbox=strt.nextToken();
              
              File fle = new File(this.path);
              fle.renameTo(new File(fle.getParent(), lastnameofmailbox));
              
              this.path=fle.getParent()+File.separator+lastnameofmailbox;
              this.absoluteName = this.absoluteName.substring(0,this.absoluteName.length()-this.name.length())+lastnameofmailbox;
              this.name=lastnameofmailbox;
              
              
              this.writeMailbox();
              return true;
          }catch(Exception e) {
              e.printStackTrace();
              return false;
          }
      }
      
      /**
       * THIS IS AN INTERIM SOLUTION !
       *
       * @param usernmae The Username who calles this Command.
       * @param oldabsolutename The old name of the parent.
       * @param newabsolutename The new name for the parent.
       * @returns true if everythink was sucessfully, else false.
       * @throws MailboxException if mailbox does not exist locally.
       * @throws AuthorizationException if the user has no rights for changing the name of the Mailbox.
       */
      public boolean renameSubMailbox(String username, String oldabsolutename, String newname) {
          try {
              System.out.println("renameSubMailbox ABSOLUTE NAME "+this.absoluteName);
              StringTokenizer strt = new StringTokenizer(oldabsolutename,".");
              StringBuffer strbuff = new StringBuffer();
              
              for(int i=0;i<strt.countTokens();i++){
                  String token = strt.nextToken();
                  if(strbuff.length()>0) strbuff.append(".");
                  strbuff.append(token);
              }
              strbuff.append(".");
              strbuff.append(newname);
  
              this.absoluteName = strbuff.toString()+this.absoluteName.substring(oldabsolutename.length());
              System.out.println("renameSubMailbox TOKEN CONVERTED: "+this.absoluteName);            
              this.writeMailbox();
              return true;
          }catch(Exception e) {
              e.printStackTrace();
              return false;
          }
      }
      
      /**
       * Returns true once this Mailbox has been checkpointed.
       * This implementation just writes its mailbox record to disc.  Unless something is
       * broken all messages added, amended or removed from this mailbox will have been
       * handled by this object.
       * <br> This implementation always returns true.
       *
       * @returns true
       */
      public synchronized  boolean checkpoint() {
          writeMailbox();
          getLogger().info("FileMailbox: " + absoluteName + " checkpointed.");
          return true;
      }
  
      /**
       * Remove \Recent flag from all messages in mailbox. Should be called
       * whenever a user session finishes.
       */
      public synchronized void unsetRecent() {
          Iterator it = recentMessages.iterator();
          while(it.hasNext()) {
              Integer uidObj =(Integer)it.next();
              int uid = uidObj.intValue();
              Flags flags = readFlags(uid);
              if (flags != null) {
                  flags.setRecent(false);
                  writeFlags(uid, flags);
              }
          }
          recentMessages.clear();
      }
  
  
      // Methods that don't involve the ACL ------------------
  
      /**
       * Returns name of this mailbox relative to its parent in the mailbox
       * hierarchy.
       * Example: 'NewIdeas'
       *
       * @returns String name of mailbox relative to its immeadiate parent in
       * the mailbox hierarchy.
       */
      public String getName() {
          return name;
      }
  
      /**
       * Returns absolute, that is user-independent, hierarchical name of
       * mailbox (including namespace)
       * Example: '#mail.fred.flintstone.apache.James.NewIdeas'
       *
       * @returns String name of mailbox in absolute form
       */
      public String getAbsoluteName() {
          return absoluteName;
      }
  
      /** Returns namespace starting with namespace token.
       * Example: '#mail'
       *
       * @returns String containing user-independent namespace of this mailbox.
       */
      //  public String getNamespace();
  
      /** Returns true if the argument is the relative or absolute name of
       * this mailbox
       *
       * @param name possible name for this Mailbox
       * @returns true if name matches either getName() or getAbsoluteName()
       */
      public boolean matchesName(String testName) {
          return (name.equals(testName) || name.equals(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.get() + 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) + 1;
              }
          } else {
              if (sequence.size() > 0) {
                  response = 1;
                  oldestUnseenMessage.put(user, (Integer)sequence.get(0));
              } else {
                  oldestUnseenMessage.put(user, (new Integer(0)));
              }
          }
          return response;
      }
  
      /**
       * Indicates number of messages in folder
       *
       * @returns int number of messages
       */
      public  synchronized  int getExists() {
          return sequence.size();
      }
  
      /**
       * Indicates the number of  unseen messages for the specified user.
       *
       * @returns int number of messages without \Seen flag set for this User.
       */
      public int getUnseen(String user) {
          if (oldestUnseenMessage.containsKey(user)) {
              int response = 0; //indicates no unseen messages
              Integer uidObj = ((Integer)oldestUnseenMessage.get(user));
              int oldUID = uidObj.intValue();
              if (oldUID != 0) {
                  ListIterator lit
                      = sequence.listIterator(sequence.indexOf(uidObj));
                  while (lit.hasNext() ) {
                      int uid = ((Integer)lit.next()).intValue();
                      Flags flags = readFlags(uid);
                      if (!flags.isSeen(user)) {
                          response ++;
                      }
                  }
              }
              return response;
          } else { // user has never selected mailbox
              return sequence.size();
          }
      }
  
      /** Mailbox Events are used to inform registered listeners of events in the Mailbox.
       * E.g. if mail is delivered to an Inbox or if another user appends/ deletes a message.
       */
      public synchronized void addMailboxEventListener(MailboxEventListener mel) {
          listeners.add(mel);
      }
  
  
      public synchronized void removeMailboxEventListener(MailboxEventListener mel) {
          listeners.remove(mel);
      }
  
      /**
       * Mark this mailbox as not selectable by anyone.
       * Example folders at the roots of hierarchies, e. #mail for each user.
       *
       * @param state true if folder is not selectable by anyone
       */
      public void setNotSelectableByAnyone(boolean state) {
          notSelectableByAnyone = state;
      }
  
      public boolean isNotSelectableByAnyone() {
          return notSelectableByAnyone;
      }
  
      // Methods for the embedded ACL ------------------------
  
      /**
       * Store access rights for a given identity.
       * The setter is the user setting the rights, the identifier is the user
       * whose rights are affected.
       * The setter and identifier arguments must be non-null and non-empty.
       * The modification argument must be non-null and follow the syntax of the
       * third argument to a SETACL command.
       * If the modification argument is an empty string, that identifier is
       * removed from the ACL, if currently present.
       *
       * @param setter String representing user attempting to set rights, must
       * be non-null and non-empty
       * @param identity String representing user whose rights are being set,
       * must be non-null and non-empty
       * @param modification String representing the change in rights, following
       * the syntax specified in rfc 2086. Note a blank string means delete all
       * rights for given identity.
       * @returns true if requested modification succeeded. A return value of
       * false means an error other than an AccessControlException or
       * AuthorizationException.
       * @throws AccessControlException if setter does not have lookup rights for
       * this mailbox (ie they should not know this mailbox exists).
       * @throws AuthorizationException if specified setter does not have the
       * administer right (ie the right to write ACL rights), or if the result
       * of this method would leave no identities with admin rights.
       */
      public boolean setRights(String setter, String identifier,
                               String modification)
          throws AccessControlException, AuthorizationException {
  
          boolean[] settersRights = (boolean[]) acl.get(setter);
          if (settersRights == null
              || (settersRights[LOOKUP] == false)) {
              throw new AccessControlException(DENY_ACCESS);
          } else if (settersRights[ADMIN] == false) {
              throw new AuthorizationException(DENY_AUTH + setter);
          }
          boolean[] existingRights = (boolean[]) acl.get(identifier);
          char[] mods = modification.toCharArray();
          if (mods.length == 0) { // means delete all
              mods = DELETE_MODS;
          }
          if(existingRights == null) {
              if ( mods[0] == REMOVE_RIGHTS ) {
                  return false;
              } else {
                  existingRights = new boolean[NUMBER_OF_RIGHTS];
                  System.arraycopy(NO_RIGHTS, 0, existingRights, 0,
                                   NUMBER_OF_RIGHTS);
              }
          }
  
          boolean change;
          boolean[] rights = new boolean[NUMBER_OF_RIGHTS];
  
          if (mods[0] == ADD_RIGHTS) {
              change = true;
              System.arraycopy(existingRights, 0, rights, 0,
                               NUMBER_OF_RIGHTS);
          } else if (mods[0] == REMOVE_RIGHTS) {
              change = false;
              System.arraycopy(existingRights, 0, rights, 0,
                               NUMBER_OF_RIGHTS);
          } else {                                             // means replace
              System.arraycopy(NO_RIGHTS, 0, rights, 0,
                               NUMBER_OF_RIGHTS);
              char[] new_mods = new char[mods.length + 1];
              System.arraycopy(mods, 0, new_mods, 1, mods.length);
              mods = new_mods;
              change = true;
          }
  
          for (int i=1; i <mods.length; i++) {
              switch(mods[i]) {
              case LOOKUP_RIGHTS: rights[LOOKUP] = change;
                  break;
              case READ_RIGHTS: rights[READ] = change;
                  break;
              case KEEP_SEEN_RIGHTS: rights[KEEP_SEEN] = change;
                  break;
              case WRITE_RIGHTS: rights[WRITE] = change;
                  break;
              case INSERT_RIGHTS: rights[INSERT] = change;
                  break;
              case POST_RIGHTS: rights[POST] = change;
                  break;
              case CREATE_RIGHTS: rights[CREATE] = change;
                  break;
              case DELETE_RIGHTS: rights[DELETE] = change;
                  break;
              case ADMIN_RIGHTS: rights[ADMIN] = change;
                  break;
              default: return false;
              }
          }
  
          //  All rights above lookup require lookup
          if(rights[LOOKUP] == false  &&  !Arrays.equals(rights, NO_RIGHTS)) {
              return false;
          }
          // Each right requires all the rights before it.
          int count = 0;
          for (int i=1; i< NUMBER_OF_RIGHTS; i++) {
              if(rights[i-1] ^ rights[i]) {
                  count++;
              }
          }
          switch (count) {
          case 0:                              // now Admin or deleted
              if (rights[ADMIN]) {
                  acl.put(identifier, rights);
                  break;
              } else {
                  if (otherAdmin(identifier)) {
                      acl.remove(identifier);
                      break;
                  } else {
                      return false;
                  }
              }
          case 2:              // not allowed
              return false;
          case 1:             // not Admin, check there remains an Admin
              // Iterator namesIt = acl.keySet().iterator();
              //boolean otherAdmin = false;
              //while(namesIt.hasNext() && !otherAdmin) {
              //String name = (String)namesIt.next();
              //if (name != identifier) {
              //    boolean[] otherRights = (boolean[]) acl.get(name);
              //        otherAdmin = otherRights[ADMIN];
              //}
              //}
              if (otherAdmin(identifier)) {
                  acl.put(identifier, rights);
                  break;
              } else {
                  return false;
              }
          default:             // not allowed
              return false;
          }
          writeMailbox();
          return true;
      }
  
      /**
       * Check there is a person other than identifier who has Admin rights.
       */
      private boolean otherAdmin(String identifier) {
          Iterator namesIt = acl.keySet().iterator();
          boolean result = false;
          while(namesIt.hasNext() && !result) {
              String name = (String)namesIt.next();
              if (!name.equals(identifier)) {
                  boolean[] otherRights = (boolean[]) acl.get(name);
                  result = otherRights[ADMIN];
              }
          }
          return result;
      }
  
      /**
       * Retrieve access rights for a specific identity.
       *
       * @param getter String representing user attempting to get the rights,
       * must be non-null and non-empty
       * @param identity String representing user whose rights are being got,
       * must be non-null and non-empty
       * @returns String of rights usingrfc2086 syntax, empty if identity has no
       * rights in this mailbox.
       * @throws AccessControlException if getter does not have lookup rights for
       * this mailbox (ie they should not know this mailbox exists).
       * @throws AuthorizationException if implementation does not wish to expose
       * ACL for this identity to this getter.
       */
      public String getRights(String getter, String identity)
          throws AccessControlException, AuthorizationException {
          boolean[] gettersRights = (boolean[])  acl.get(getter);
          if (gettersRights == null
              || (gettersRights[LOOKUP] == false)) {
              throw new AccessControlException(DENY_ACCESS);
          } else if (!getter.equals(identity) && gettersRights[ADMIN] == false) {
              throw new AuthorizationException(DENY_AUTH + getter);
          }
          boolean[] rights = (boolean[]) acl.get(identity);
          if (rights == null) {
              return null;
          } else {
              StringBuffer buf = new StringBuffer(NUMBER_OF_RIGHTS);
              for (int i = 0; i<NUMBER_OF_RIGHTS; i++) {
                  if (rights[i]) {
                      buf.append(RIGHTS[i]);
                  }
              }
              return buf.toString();
          }
      }
  
      /**
       * Retrieves a String of one or more <identity space rights> who have
       * rights in this ACL
       *
       * @param getter String representing user attempting to get the rights,
       * must be non-null and non-empty
       * @returns String of rights sets usingrfc2086 syntax
       * @throws AccessControlException if getter does not have lookup rights for
       * this mailbox (ie they should not know this mailbox exists).
       * @throws AuthorizationException if implementation does not wish to expose
       * ACL to this getter.
       */
      public String getAllRights(String getter)
          throws AccessControlException, AuthorizationException {
          boolean[] gettersRights = (boolean[]) acl.get(getter);
          if (gettersRights == null
              || (gettersRights[LOOKUP] == false)) {
              throw new AccessControlException(DENY_ACCESS);
          } else if ( gettersRights[ADMIN] == false) {
              throw new AuthorizationException(DENY_AUTH + getter);
          }
          Iterator namesIt = acl.keySet().iterator();
          StringBuffer response = new StringBuffer(20*acl.size());
          while(namesIt.hasNext()) {
              String name = (String)namesIt.next();
              response.append("<" + name + " ");
              boolean[] rights = (boolean[]) acl.get(name);
              for (int i = 0; i<NUMBER_OF_RIGHTS; i++) {
                  if (rights[i]) {
                      response.append(RIGHTS[i]);
                  }
              }
              response.append("> ");
          }
  
          return response.toString();
      }
  
      /**
       * Retrieve rights which will always be granted to the specified identity.
       *
       * @param getter String representing user attempting to get the rights,
       * must be non-null and non-empty
       * @param identity String representing user whose rights are being got,
       * must be non-null and non-empty
       * @returns String of rights usingrfc2086 syntax, empty if identity has no
       * guaranteed rights in this mailbox.
       * @throws AccessControlException if getter does not have lookup rights for
       * this mailbox (ie they should not know this mailbox exists).
       * @throws AuthorizationException if implementation does not wish to expose
       * ACL for this identity to this getter.
       */
      public String getRequiredRights(String getter, String identity)
          throws AccessControlException, AuthorizationException {
          boolean[] gettersRights = (boolean[]) acl.get(getter);
          if (gettersRights == null
              || (gettersRights[LOOKUP] == false)) {
              throw new AccessControlException(DENY_ACCESS);
          } else if (!getter.equals(identity) && gettersRights[ADMIN] == false) {
              throw new AuthorizationException(DENY_AUTH + getter);
          }
  
          return "\"\"";
      }
  
      /**
       * Retrieve rights which may be granted to the specified identity.
       * @param getter String representing user attempting to get the rights,
       * must be non-null and non-empty
       * @param identity String representing user whose rights are being got,
       * must be non-null and non-empty
       * @returns String of rights usingrfc2086 syntax, empty if identity has no
       * guaranteed rights in this mailbox.
       * @throws AccessControlException if getter does not have lookup rights for
       * this mailbox (ie they should not know this mailbox exists).
       * @throws AuthorizationException if implementation does not wish to expose
       * ACL for this identity to this getter.
       */
      public String getOptionalRights(String getter, String identity)
          throws AccessControlException, AuthorizationException {
          boolean[] gettersRights = (boolean[]) acl.get(getter);
          if (gettersRights == null
              || (gettersRights[LOOKUP] == false)) {
              throw new AccessControlException(DENY_ACCESS);
          } else if (!getter.equals(identity) && gettersRights[ADMIN] == false) {
              throw new AuthorizationException(DENY_AUTH + getter);
          }
  
          return OPTIONAL_RIGHTS;
      }
  
      /**
       * Helper boolean methods.
       * Provided for cases where you need to check the ACL before selecting the
       * mailbox.
       *
       * @param username String representing user
       * @returns true if user has the requested right.
       * &throws AccessControlException if username does not have lookup rights.
       * (Except for hasLookupRights which just returns false.
       */
      public boolean hasLookupRights(String username) {
          boolean[] usersRights = (boolean[]) acl.get(username);
          return (( usersRights == null || (usersRights[LOOKUP] == false))
                  ? false : true);
      }
  
      public boolean hasReadRights(String username)
          throws AccessControlException {
          boolean[] usersRights = (boolean[]) acl.get(username);
          if (usersRights == null  || (usersRights[LOOKUP] == false)) {
              throw new AccessControlException(DENY_ACCESS);
          }
          return usersRights[READ];
      }
  
      public boolean hasKeepSeenRights(String username)
          throws AccessControlException {
          boolean[] usersRights = (boolean[]) acl.get(username);
          if (usersRights == null  || (usersRights[LOOKUP] == false)) {
              throw new AccessControlException(DENY_ACCESS);
          }
          return usersRights[KEEP_SEEN];
      }
  
      public boolean hasWriteRights(String username)
          throws AccessControlException {
          boolean[] usersRights = (boolean[]) acl.get(username);
          if (usersRights == null  || (usersRights[LOOKUP] == false)) {
              throw new AccessControlException(DENY_ACCESS);
          }
          return usersRights[WRITE];
      }
  
      public boolean hasInsertRights(String username)
          throws AccessControlException {
          boolean[] usersRights = (boolean[]) acl.get(username);
          if (usersRights == null  || (usersRights[LOOKUP] == false)) {
              throw new AccessControlException(DENY_ACCESS);
          }
          return usersRights[INSERT];
      }
  
      public boolean hasCreateRights(String username)
          throws AccessControlException {
          boolean[] usersRights = (boolean[]) acl.get(username);
          if (usersRights == null  || (usersRights[LOOKUP] == false)) {
              throw new AccessControlException(DENY_ACCESS);
          }
          return usersRights[CREATE];
      }
  
      public boolean hasDeleteRights(String username)
          throws AccessControlException {
          boolean[] usersRights = (boolean[]) acl.get(username);
          if (usersRights == null  || (usersRights[LOOKUP] == false)) {
              throw new AccessControlException(DENY_ACCESS);
          }
          return usersRights[DELETE];
      }
  
      public boolean hasAdminRights(String username)
          throws AccessControlException {
          boolean[] usersRights = (boolean[]) acl.get(username);
          if (usersRights == null  || (usersRights[LOOKUP] == false)) {
              throw new AccessControlException(DENY_ACCESS);
          }
          return usersRights[ADMIN];
      }
  
      // Mailbox methods using the ACL ---------------------------
  
      /**
       * Indicates if this folder may be selected by the specified user. Requires
       * user to have at least read rights. It does not indicate whether user
       * can write to mailbox
       *
       * @param username String represnting user
       * @returns boolean TRUE if specified user can Select mailbox.
       * @throws AccessControlException if username does not have lookup rights
       */
      public  synchronized  boolean isSelectable(String username)
          throws AccessControlException {
          return ( ! notSelectableByAnyone && hasReadRights(username) );
      }
  
      /**
       * Indicates if specified user can change any flag on a permanent basis,
       * except for \Recent which can never be changed by a user.
       *
       * @param username String represnting user
       * @returns true if specified user can change all flags permanently.
       */
      public synchronized boolean allFlags(String username)
          throws AccessControlException {
          // relies on implementation that each right implies those
          // before it in list:  l,r,s,w,i,p,c,d,a
          return hasDeleteRights(username);
      }
  
      /**
       * Indicates which flags this user can change permanently. If allFlags()
       * returns true for this user, then this method must have the same return
       * value as getSupportedFlags.
       *
       * @param username String represnting user
       * @returns String a space seperated list of message flags which this user
       * can set permanently
       */
      public  synchronized  String getPermanentFlags(String username)
          throws AccessControlException {
          if (hasDeleteRights(username)) {
              return SYSTEM_FLAGS;
          } else if (hasWriteRights(username)) {
              return "\\Seen \\Answered \\Flagged \\Draft";
          } else if (hasKeepSeenRights(username)) {
              return "\\Seen";
          } else {
              return "";
          }
      }
  
      /**
       * Provides a reference to the access control list for this mailbox.
       *
       * @returns the AccessControlList for this Mailbox
       */
      //   public ACL getACL();
  
      /**
       * Indicates state in which  the mailbox will be opened by specified user.
       * A return value of true indicates Read Only, false indicates Read-Write
       * and an AccessControlException is thrown if user does not have read
       * rights.
       * <p>Implementations decide if Read Only means only lookup and read
       * rights (lr) or lookup, read and keep seen rights (lrs). This may even
       * vary between mailboxes.
       *
       * @param username String represnting user
       * @returns true if specified user can only open the mailbox Read-Only.
       * @throws AccessControlException if the user can not open this mailbox
       * at least Read-Only.
       */
      public synchronized boolean isReadOnly(String username)
          throws AccessControlException {
          return (! hasWriteRights(username));
      }
  
      // Message handling methods ---------------------------
  
      /**
       * Stores a message in this mailbox. User must have insert rights.
       *
       * @param message the MimeMessage to be stored
       * @param username String represnting user
       * @returns boolean true if successful
       * @throws AccessControlException if username does not have lookup rights
       * for this mailbox.
       * @throws AuthorizationException if username has lookup rights but does
       * not have insert rights.
       */
      public synchronized boolean store(MimeMessage message, String username)
          throws AccessControlException, AuthorizationException,
                 IllegalArgumentException {
  
          if (message == null || username == null) {
              getLogger().error("Null argument received in store.");
              throw new IllegalArgumentException("Null argument received in store.");
          }
          if (!hasInsertRights(username)) { //throws AccessControlException
              throw new AuthorizationException("Not authorized to insert.");
          }
  
          SimpleMessageAttributes attrs = new SimpleMessageAttributes();
          try {
              setupLogger(attrs);
              attrs.setAttributesFor(message);
          } catch (javax.mail.MessagingException me) {
              throw new RuntimeException("Exception creating SimpleMessageAttributes: " + me);
          }
          Flags flags = new Flags();
          flags.initialize();
          return store(message, username, attrs, flags);
      }
  
      /**
       * Stores a message in this mailbox, using passed MessageAttributes and
       * Flags. User must have insert rights.
       * <br>Current implementation requires MessageAttributs to be of
       * class SimpleMessageAttributes
       *
       * @param mail the message to be stored
       * @param username String represnting user
       * @param msgAttrs non-null MessageAttributes for use with this Message
       * @returns boolean true if successful
       * @throws AccessControlException if username does not have lookup
       * rights for this mailbox.
       * @throws AuthorizationException if username has lookup rights but does
       * not have insert rights.
       */
      public boolean store(MimeMessage message, String username,
                           MessageAttributes msgAttrs, Flags flags)
          throws AccessControlException, AuthorizationException,
                 IllegalArgumentException {
  
          if (msgAttrs == null || message == null || username == null) {
              getLogger().error("Null argument received in store.");
              throw new IllegalArgumentException("Null argument received in store.");
          }
          if (! (msgAttrs instanceof SimpleMessageAttributes)) {
              getLogger().error("Wrong class for Attributes");
              throw new IllegalArgumentException("Wrong class for Attributes");
          }
          SimpleMessageAttributes attrs = (SimpleMessageAttributes)msgAttrs;
  
          highestUID.increase();
          int newUID = highestUID.get();
          
          attrs.setUID(newUID);
          sequence.add(new Integer(newUID));
          attrs.setMessageSequenceNumber(sequence.size());
  
          BufferedOutputStream outMsg = null;
          ObjectOutputStream outAttrs = null;
  
          try {
              // SK:UPDATE
              path = getPath( absoluteName, owner, rootPath );
              outMsg = new BufferedOutputStream( new FileOutputStream(path + File.separator + newUID + MESSAGE_EXTENSION));
              message.writeTo(outMsg);
              outMsg.close();
              outAttrs = new ObjectOutputStream( new FileOutputStream(path + File.separator + newUID + ATTRIBUTES_EXTENSION));
              outAttrs.writeObject(attrs);
              outAttrs.close();
          } catch(Exception e) {
              getLogger().error("Error writing message to disc: " + e);
              e.printStackTrace();
              throw new
                  RuntimeException("Exception caught while storing Mail: "
                                   + e);
          } finally {
              try {
                  outMsg.close();
                  outAttrs.close();
              } catch (IOException ie) {
                  getLogger().error("Error closing streams: " + ie);
              }
          }
          marked = true;
          if (flags.isRecent()) {
              recentMessages.add(new Integer(newUID));
          }
          if (flags.isDeleted()) {
              messagesForDeletion.add(new Integer(newUID));
          }
          //if (!flags.isSeen(username)) {
          //If a user had no unseen messages, they do, now.
          Iterator it = oldestUnseenMessage.keySet().iterator();
          while (it.hasNext()) {
              String user = (String)it.next();
              if ( ((Integer)oldestUnseenMessage.get(user)).intValue() == -1) {
                  oldestUnseenMessage.put(user, new Integer(newUID));
              }
          }
          //}
          writeFlags(newUID, flags);
          getLogger().info("Mail " + newUID + " written in " + absoluteName);
  
          return true;
      }
  
      /**
       * Retrieves a message given a message sequence number.
       *
       * @param msn the message sequence number
       * @param username String represnting user
       * @returns an  MimeMessageWrapper object containing the message, null if no message with
       * the given msn.
       * @throws AccessControlException if user does not have read rights for
       * this mailbox.
       * @throws AuthorizationException if user has lookup rights but does not
       * have read rights.
       */
      public synchronized MimeMessageWrapper retrieve(int msn, String user)
          throws AccessControlException, AuthorizationException {
          if (!hasReadRights(user)) { //throws AccessControlException
              throw new AuthorizationException("Not authorized to read.");
          }
  
          if (msn > sequence.size()) {
              return null;
          } else {
              int uid = ((Integer)sequence.get(msn - 1)).intValue();
              return retrieveUID(uid, user);
          }
      }
  
  
      /**
       * Retrieves a message given a unique identifier.
       *
       * @param uid the unique identifier of a message
       * @param username String represnting user
       * @returns an MimeMessageWrapper object containing the message, null if no message with
       * the given msn.
       * @throws AccessControlException if user does not have read rights for
       * this mailbox.
       * @throws AuthorizationException if user has lookup rights but does not
       * have read rights.
       */
      public synchronized MimeMessageWrapper retrieveUID(int uid, String user)
          throws AccessControlException, AuthorizationException {
          if (!hasReadRights(user)) { //throws AccessControlException
              throw new AuthorizationException("Not authorized to read.");
          }
          MimeMessageWrapper response = null;
          if (sequence.contains(new Integer(uid))) {
              //BufferedInputStream inMsg = null;
              try {
                  path = getPath( absoluteName, owner, rootPath );
  		MimeMessageFileSource source = new MimeMessageFileSource(path + File.separator + uid + MESSAGE_EXTENSION);
                  response = new MimeMessageWrapper(source);
               //   inMsg.close();
              } catch(Exception e) {
                  getLogger().error("Error reading message from disc: " + e);
                  e.printStackTrace();
                  throw new
                      RuntimeException("Exception caught while retrieving Mail: "
                                       + e);
              } //finally {
            //      try {
              //        inMsg.close();
            //      } catch (IOException ie) {
            //          getLogger().error("Error closing streams: " + ie);
            //      }
            //  }
              getLogger().info("MimeMessageWrapper " + uid + " read from " + absoluteName);
              return response;
          } else {
              return null;
          }
      }
  
      /**
       * Marks a message for deletion given a message sequence number.
       *
       * @param msn the message sequence number
       * @param username String represnting user
       * @returns boolean true if successful.
       * @throws AccessControlException if user does not have read rights for
       * this mailbox.
       * @throws AuthorizationException if user has lookup rights but does not
       * have delete rights.
       */
      public synchronized boolean markDeleted(int msn, String user)
          throws AccessControlException, AuthorizationException {
          if (!hasDeleteRights(user)) { //throws AccessControlException
              throw new AuthorizationException("Not authorized to delete.");
          }
  
          Assert.notImplemented();
          //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.");
          }
  
          Assert.notImplemented();
          //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.");
          }
          System.out.println("msn: "+msn);
          System.out.println("sequence.size: "+sequence.size());
          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.");
          }
          System.out.println("getMessageAttributesUID()");
          System.out.println("uid: "+uid);
          System.out.println("user: "+user);
          System.out.println("sequence.size: "+sequence.size());
          SimpleMessageAttributes response = null;
          if (sequence.contains(new Integer(uid))) {
                  System.out.println("reading from disk");
  
              ObjectInputStream inAttrs = null;
              try {
                  path = getPath( absoluteName, owner, rootPath );
                  System.out.println( "FileInputStream("+(path + File.separator + uid + ATTRIBUTES_EXTENSION));
                  inAttrs = new ObjectInputStream( new FileInputStream(path + File.separator + uid + ATTRIBUTES_EXTENSION));
                  System.out.println("inAttrs="+inAttrs);
                  response = (SimpleMessageAttributes)inAttrs.readObject();
                  System.out.println("response="+response);
                  if (response != null) {
                  System.out.println("response.parts="+response.parts);
                  if (response.parts != null) {
                  System.out.println("response.parts.len="+response.parts.length);
                  System.out.println("response.parts[0]="+response.parts[0]);
                  }
                  }
                  setupLogger(response);
              } catch(Exception e) {
                  getLogger().error("Error reading attributes from disc: " + e);
                  e.printStackTrace();
                  throw new
                      RuntimeException("Exception caught while retrieving Message attributes: "
                                       + e);
              } finally {
                  try {
                      inAttrs.close();
                  } catch (IOException ie) {
                      getLogger().error("Error closing streams: " + ie);
                  }
              }
              getLogger().info("MessageAttributes for " + uid + " read from " + absoluteName);
              return response;
          } else {
              return null;
          }
      }
  
      /**
       * Updates the attributes of a message.This may be incorporated into setFlags().
       *
       * @param MessageAttributes of a message already in this Mailbox
       * @throws AccessControlException if user does not have read rights for
       * this mailbox.
       * @throws AuthorizationException if user has lookup rights but does not
       * have delete rights.
       */
      public boolean updateMessageAttributes(MessageAttributes attrs, String user)
          throws AccessControlException, AuthorizationException {
          if (!hasKeepSeenRights(user)) { //throws AccessControlException
              throw new AuthorizationException("Not authorized to store flags.");
          }
          int uid = attrs.getUID();
          if (sequence.contains(new Integer(uid))) {
  
              // Really, we should check whether the exact change is authorized.
              ObjectOutputStream outAttrs = null;
              try {
                  path = getPath( absoluteName, owner, rootPath );
                  outAttrs = new ObjectOutputStream( new FileOutputStream(path + File.separator + uid + ATTRIBUTES_EXTENSION));
                  outAttrs.writeObject(attrs);
                  outAttrs.close();
              } catch(Exception e) {
                  getLogger().error("Error writing message to disc: " + e);
                  e.printStackTrace();
                  throw new
                      RuntimeException("Exception caught while storing Attributes: "
                                       + e);
              } finally {
                  try {
                      outAttrs.close();
                  } catch (IOException ie) {
                      getLogger().error("Error closing streams: " + ie);
                  }
              }
              getLogger().info("MessageAttributes for " + uid + " written in " + absoluteName);
  
              return true;
          } else {
              return false;
          }
      }
  
      /**
       * Get the IMAP-formatted String of flags for specified message.
       *
       * @param msn message sequence number for a message in this mailbox
       * @param username String represnting user
       * @returns flags for this message and user, null if no such message.
       * @throws AccessControlException if user does not have lookup rights for
       * this mailbox.
       * @throws AuthorizationException if user has lookup rights but does not
       * have read rights.
       */
      public synchronized  String getFlags(int msn, String user)
          throws AccessControlException, AuthorizationException {
          if (!hasReadRights(user)) { //throws AccessControlException
              throw new AuthorizationException("Not authorized to read.");
          }
          if (msn > sequence.size()) {
              return null;
          } else {
              int uid = ((Integer)sequence.get(msn - 1)).intValue();
              return getFlagsUID(uid, user);
          }
      }
  
      /**
       * Get the IMAP-formatted String of flags for specified message.
       *
       * @param uid UniqueIdentifier for a message in this mailbox
       * @param username String represnting user
       * @returns flags for this message and user, null if no such message.
       * @throws AccessControlException if user does not have lookup rights for
       * this mailbox.
       * @throws AuthorizationException if user has lookup rights but does not
       * have read rights.
       */
      public synchronized  String getFlagsUID(int uid, String user)
          throws AccessControlException, AuthorizationException {
          if (!hasReadRights(user)) { //throws AccessControlException
              throw new AuthorizationException("Not authorized to read.");
          }
          
          if (!sequence.contains(new Integer(uid))) {
              System.out.println("SEQUENCENOOO");
              return null;
          } else {
              System.out.println("FLAGSRETURNED");
              Flags flags = readFlags(uid);
              return flags.getFlags(user);
          }
      }
      /**
       * Updates the flags for a message.
       *
       * @param msn MessageSequenceNumber of a message already in this Mailbox
       * @param username String represnting user
       * @param request IMAP-formatted String representing requested change to
       * flags.
       * @returns true if succeeded, false otherwise, including no such message
       * @throws AccessControlException if user does not have read rights for
       * this mailbox.
       * @throws AuthorizationException if user has lookup rights but does not
       * have delete rights.
       */
      public synchronized  boolean setFlags(int msn, String user, String request)
          throws AccessControlException, AuthorizationException,
                 IllegalArgumentException {
          if (!hasKeepSeenRights(user)) { //throws AccessControlException
              throw new AuthorizationException("Not authorized to store any flags.");
          }
          if (msn > sequence.size()) {
              return false;
          } else {
              int uid = ((Integer)sequence.get(msn - 1)).intValue();
              return setFlagsUID(uid, user, request);
          }
      }
  
      /**
       * Updates the flags for a message.
       *
       * @param uid Unique Identifier of a message already in this Mailbox
       * @param username String represnting user
       * @param request IMAP-formatted String representing requested change to
       * flags.
       * @throws AccessControlException if user does not have read rights for
       * this mailbox.
       * @throws AuthorizationException if user has lookup rights but does not
       * have delete rights.
       */
      public synchronized boolean setFlagsUID(int uid, String user, String request)
          throws AccessControlException, AuthorizationException,
                 IllegalArgumentException {
          if (!hasKeepSeenRights(user)) { //throws AccessControlException
              throw new AuthorizationException("Not authorized to store any flags.");
          }
          if ((request.toUpperCase().indexOf("DELETED") != -1) && (!hasDeleteRights(user))) { //throws AccessControlException
              throw new AuthorizationException("Not authorized to delete.");
          }
          if (sequence.contains(new Integer(uid))) {
  
              Flags flags = readFlags(uid);
              boolean wasRecent = flags.isRecent();
              boolean wasDeleted = flags.isDeleted();
              boolean wasSeen = flags.isSeen(user);
  
              if  (flags.setFlags(request, user)) {
  
                  if (flags.isDeleted()) {
                      if (! wasDeleted) { messagesForDeletion.add(new Integer(uid)); }
                  }
                  if (flags.isSeen(user) != wasSeen) {
                      if (flags.isSeen(user)) {
                          int previousOld = ((Integer)oldestUnseenMessage.get(user)).intValue();
                          if (uid == previousOld) {
                              int newOld = findOldestUnseen(user, previousOld);
                              oldestUnseenMessage.put(user, (new Integer(newOld)));
                          }
                      } else { // seen flag unset
                          if (uid < ((Integer)oldestUnseenMessage.get(user)).intValue()) {
                              oldestUnseenMessage.put(user, (new Integer(uid)));
                          }
                      }
                  }
  
                  writeFlags(uid, flags);
                  getLogger().debug("Flags for message uid " + uid + " in " + absoluteName + " updated.");
                  return true;
              } else {
                  return false;
              }
          } else {
              return false;
          }
  
      }
  
      private int findOldestUnseen(String user, int previousOld)
          throws AccessControlException, AuthorizationException {
          int response = 0; //indicates no unseen messages
          try {
              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;
                  }
              }
          }catch(Exception e) { 
              // (because) BUG: Do nothing. Have investigated an error on fetching sequence.listIterator(previousOld);
              // with an error - but currently I don't know why :)
          }finally{
              return response;
          }
      }
  
      private Flags readFlags(int uid) {
          Flags response = null;
          if (sequence.contains(new Integer(uid))) {
              ObjectInputStream inFlags = null;
              try {
                  path = getPath( absoluteName, owner, rootPath );
                  inFlags = new ObjectInputStream( new FileInputStream(path + File.separator + uid + FLAGS_EXTENSION));
                  response = (Flags)inFlags.readObject();
              } catch(Exception e) {
                  getLogger().error("Error reading flags from disc: " + e);
                  e.printStackTrace();
                  throw new
                      RuntimeException("Exception caught while retrieving Message flags: "
                                       + e);
              } finally {
                  try {
                      inFlags.close();
                  } catch (IOException ie) {
                      getLogger().error("Error closing streams: " + ie);
                  }
              }
              getLogger().info("Flags for " + uid + " read from " + absoluteName);
          }
          return response;
      }
  
      private boolean writeFlags(int uid, Flags flags) {
          if (sequence.contains(new Integer(uid))) {
              ObjectOutputStream outFlags = null;
              try {
                  path = getPath( absoluteName, owner, rootPath );
                  outFlags = new ObjectOutputStream( new FileOutputStream(path + File.separator + uid + FLAGS_EXTENSION));
                  outFlags.writeObject(flags);
                  outFlags.close();
              } catch(Exception e) {
                  getLogger().error("Error writing message to disc: " + e);
                  e.printStackTrace();
                  throw new
                      RuntimeException("Exception caught while storing Flags: "
                                       + e);
              } finally {
                  try {
                      outFlags.close();
                  } catch (IOException ie) {
                      getLogger().error("Error closing streams: " + ie);
                  }
              }
              getLogger().info("Flags for " + uid + " written in " + absoluteName);
              writeMailbox();
              return true;
          } else {
              writeMailbox();
              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 for user '" + user + "'.");
          }
          Iterator it = messagesForDeletion.iterator();
  
          while (it.hasNext()) {
              Integer uidObj = (Integer)it.next();
              int uid = uidObj.intValue();
              if (sequence.contains(uidObj)) {
                  try  {
                      //SAM
                      try
                      {
                          MimeMessageWrapper message = retrieveUID(uid, user);
                          System.out.println("(before)decrementing mailboxSize ("+getName()+") = " + mailboxSize);
                          System.out.println("decre message.getMessageSize() = " + (int)message.getMessageSize());
                          mailboxSize -= (int)message.getMessageSize();
                          System.out.println("(after)decrementing mailboxSize ("+getName()+") = " + mailboxSize);
                      }
                      catch (MessagingException me)
                      {
                              //ignore
                      }
                      //END
                      path = getPath( absoluteName, owner, rootPath );
                      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();
                      //SAM
                      final File flagFile = new File(path + File.separator + uid + FLAGS_EXTENSION );
                      flagFile.delete();
                      //END
                      sequence.remove(uidObj);
                  } 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));
          }
          writeMailbox();
          return true;
      }
  
      private void writeMailbox() {
          String mailboxRecordFile = path + File.separator + MAILBOX_FILE_NAME;
          ObjectOutputStream out = null;
          FileOutputStream fout = null;
          try {
              fout = new FileOutputStream( mailboxRecordFile );
              out = new ObjectOutputStream( fout );
              out.writeObject(this);
              out.flush();
              out.close();
              fout.flush();
              fout.close();
          } catch(Exception e) {
              if (out != null) {
                  try {
                      out.close();
                  } catch (Exception ignored) {
                  }
              }
              if (fout != null) {
                  try {
                      fout.close();
                  } catch (Exception ignored) {
                  }
              }
              e.printStackTrace();
              throw new
                  RuntimeException("Exception caught while storing Mailbox: " + e);
          }
          getLogger().info("FileMailbox written: " + absoluteName);
      }
  
  
      /**
       * Lists uids of messages in mailbox indexed by MSN.
       *
       * @param username String represnting user
       * @returns List of Integers wrapping uids of message
       */
      public List listUIDs(String user) {
          return new ArrayList(Collections.unmodifiableList(sequence));
      }
  
      public Set getUsersWithLookupRights() {
          Set response = new  HashSet();
          Iterator it = acl.keySet().iterator();
          while (it.hasNext()) {
              String user = (String) it.next();
              boolean[] rights = (boolean[]) acl.get(user);
              if (rights[LOOKUP] == true) {
                  response.add(user);
              }
          }
          return response;
      }
  
      public Set getUsersWithReadRights() {
          Set response = new  HashSet();
          Iterator it = acl.keySet().iterator();
          while (it.hasNext()) {
              String user = (String) it.next();
              boolean[] rights = (boolean[]) acl.get(user);
              if (rights[READ] == true) {
                  response.add(user);
              }
          }
          return response;
      }
  
      public Map getUnseenByUser() {
          Map response = new HashMap();
          Iterator it = oldestUnseenMessage.keySet().iterator();
          while (it.hasNext()) {
              String user = (String) it.next();
              Integer uidObj = ((Integer)oldestUnseenMessage.get(user));
              int oldUID = uidObj.intValue();
              if (oldUID == 0) {
                  response.put(user, uidObj);
              } else {
                  int count = 0;
                  ListIterator lit
                      = sequence.listIterator(sequence.indexOf(uidObj));
                  while (lit.hasNext() ) {
                      int uid = ((Integer)lit.next()).intValue();
                      Flags flags = readFlags(uid);
                      if (!flags.isSeen(user)) {
                          count ++;
                      }
                  }
                  response.put(user, new Integer(count));
              }
          }
          return response;
      }
  
  
      public InternetHeaders getInternetHeaders(int msn, String user)
          throws AccessControlException, AuthorizationException {
          if (!hasReadRights(user)) { //throws AccessControlException
              throw new AuthorizationException("Not authorized to read.");
          }
          if (msn > sequence.size()) {
              return null;
          } else {
              int uid = ((Integer)sequence.get(msn - 1)).intValue();
              return getInternetHeadersUID(uid, user);
          }
      }
  
      public InternetHeaders getInternetHeadersUID(int uid, String user)
          throws AccessControlException, AuthorizationException {
          InternetHeaders response = null;
          if (sequence.contains(new Integer(uid))) {
              BufferedInputStream inMsg = null;
              try {
                  inMsg = new BufferedInputStream( new FileInputStream(path + File.separator + uid + MESSAGE_EXTENSION));
                  response = new InternetHeaders(inMsg);
                  inMsg.close();
              } catch(Exception e) {
                  getLogger().error("Error reading headers of message from disc: " + e);
                  e.printStackTrace();
                  throw new
                      RuntimeException("Exception caughtt while retrieving InternetHeaders: "    + e);
              } finally {
                  try {
                      inMsg.close();
                  } catch (IOException ie) {
                      getLogger().error("Error closing streams: " + ie);
                  }
              }
              getLogger().info("InternetHeaders for message " + uid + " read from "
                          + absoluteName);
              return response;
          } else {
              return null;
          }
      }
  
      /**
       * Returns true if the named user is subscribed to this Mailbox.
       */
      public boolean isSubscribed( String userName )
      {
          return subscribedUsers.contains( userName.toLowerCase() );
      }
  
      /**
       * Subscribes a user to this Mailbox.
       */
      public void subscribe( String userName )
      {
          subscribedUsers.add( userName.toLowerCase() );
      }
  
      /**
       * Unsubscrive a user from this Mailbox.
       */
      public void unsubscribe( String userName )
      {
          subscribedUsers.remove( userName.toLowerCase() );
      }
  }
  
  
  
  
  
  
  1.1                  jakarta-james/proposals/imap/java/org/apache/james/imapserver/commands/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 org.apache.james.AccessControlException;
  import org.apache.james.AuthorizationException;
  import org.apache.james.core.MimeMessageWrapper;
  import org.apache.james.imapserver.commands.ImapCommand;
  
  import javax.mail.MessagingException;
  import javax.mail.internet.InternetHeaders;
  import java.io.IOException;
  import java.io.OutputStream;
  import java.io.PrintWriter;
  import java.util.*;
  
  /**
   * Implements the IMAP FETCH command for a given ImapRequestImpl.
   *
   * References: rfc 2060, rfc 2193, rfc 2221
   * @author <a href="mailto:sascha@kulawik.de">Sascha Kulawik</a>
   * @author <a href="mailto:charles@benett1.demon.co.uk">Charles Benett</a>
   * @version 0.2 on 04 Aug 2002
   */
  public class CommandFetch
      extends BaseCommand implements ImapCommand
  {
  
      //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;
  
      public boolean validForState( ImapSessionState state )
      {
          return ( state == ImapSessionState.SELECTED );
      }
  
      public boolean process( ImapRequest request, ImapSession session )
      {
          setRequest( request );
  	StringTokenizer txt = request.getCommandLine();
  
  /*	System.out.println("CommandFetch.process: #args="+txt.countTokens());
          while (txt.hasMoreTokens()) {
              System.out.println("CommandFetch.process: arg='"+txt.nextToken()+"'");
          }
  */
     //     ((ImapRequestImpl)request).setCommandLine(new java.util.StringTokenizer(request.getCommandRaw()));
          if ( request.arguments() < 2 ) {
              session.badResponse( "#args="+request.arguments()+" '"+request.getCommandLine().nextToken()+"', '"+request.getCommandLine().nextToken()+"' Command should be <tag> <FETCH> <message set> <message data item names>" );
              return true;
          }
          service();
          return true;
      }
  
      /**
       * Debugging method - will probably disappear
       */
      public void setRequest(ImapRequest request) {
          commandLine = request.getCommandLine();
          useUIDs = request.useUIDs();
          currentMailbox = request.getCurrentMailbox();
          System.out.println("currentMailbox="+((currentMailbox!=null)?currentMailbox.getClass().getName():"null"));
          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 ImapRequestImpl.
       * This implementation attempts to satisfy the fetch command with the
       * smallest objects deserialized from storage.
       * <p>Warning - maybecome service(ImapRequestImpl 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) {
              System.out.println("Found fetchAttrsRaw: " + fetchAttrsRaw);
              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 && fetchTokens.hasMoreTokens()) {
                      String field = fetchTokens.nextToken();
                      attrWithFields.append(" " + field);
                      if (field.indexOf(")") != -1) {
                          endOfFields = true;
                      }
                  }
                  fetchAttrs.add(attrWithFields.toString());
              }
          }
  
          fetchAttrs = convertMacroCommands( fetchAttrs );
          try {
              for (int i = 0; i < set.size(); i++) {
                  Integer uidObject = null;
                  int uid = 0;
                  int msn = 0;
                  if (useUIDs) {
                      System.out.println("USE UIDS");
                      uidObject = (Integer)set.get(i);
                      uid = uidObject.intValue();
                      msn = uidsList.indexOf(uidObject) + 1;
                  } else {
                      msn = ((Integer)set.get(i)).intValue();
                  }
                  MessageAttributes  attrs = null;
                  String flags = null;
                  //EnhancedMimeMessage msg = null;
                  MimeMessageWrapper msg = null;
                  String response = UNTAGGED + SP + msn + SP + "FETCH (";
                  boolean responseAdded = false;
                  Iterator it = fetchAttrs.iterator();
                  while(it.hasNext()) {
                      String  arg = (String) it.next();
                      // commands that only need flags object
                      if (arg.equalsIgnoreCase("FLAGS")) {
                          if (flags == null) {
                              if (useUIDs) {
                                  System.out.println("TRIING UIDFLAGS"+uid);
                                  flags = currentMailbox.getFlagsUID(uid, user);
                              } else {
                                  System.out.println("TRIING MSNFLAGS"+msn);
                                  flags = currentMailbox.getFlags(msn, user);
                              }
                          }
                          if (flags == null) { // bad
                              //out.println(tag + SP + msn + SP + NO + SP + "Error retrieving message flags.");
                              //System.out.println(tag + SP + msn + SP + NO + SP + "Error retrieving message flags.");
                              //return;
                          }
                          if (responseAdded) {
                              response += SP + "FLAGS " + flags ;
                          } else {
                              response +=  "FLAGS " + flags ;
                              responseAdded = true;
                          }
                      }
                      // command that only need MessageAttributes object
                      else if (arg.equalsIgnoreCase("INTERNALDATE")) {
                          System.out.println("Starting 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 + SP + "Error retrieving message attributes.");
                              System.out.println(tag + SP + msn + SP + NO + SP + "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")) {
                          System.out.println("Starting 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 + SP + "Error retrieving message attributes.");
                              System.out.println(tag + SP + msn + SP + NO + SP + "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")) {
                          System.out.println("Starting 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 + SP + "Error retrieving message attributes.");
                              System.out.println(tag + SP + msn + SP + NO + SP + "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")) {
                         System.out.println("CommandFetch BODY start");
                         if (attrs == null) {
                          System.out.println("CommandFetch BODY fetching attrs");
                              if (useUIDs) {
                                  attrs = currentMailbox.getMessageAttributesUID(uid, user);
                                  System.out.println("currentMailbox.getMessageAttributesUID("+uid+","+user+")");
                              } else {
                                  attrs = currentMailbox.getMessageAttributes(msn, user);
                                  System.out.println("currentMailbox.getMessageAttributes("+msn+","+user+")");
                              }
                              System.out.println("attrs="+((attrs!=null)?attrs.getClass().getName():"null"));
  
                          }
                             System.out.println("CommandFetch BODY attrs="+attrs);
                              System.out.println("CommandFetch BODY attrs.getUID: "+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 + "BODY " + attrs.getBodyStructure();
                          } else {
                              response +=  "BODY " + attrs.getBodyStructure();
                              responseAdded = true;
                          }
                      } else if (arg.equalsIgnoreCase("BODYSTRUCTURE")) {
                          System.out.println("Starting 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")) {
                          System.out.println("CommandFetch UID start");
                          if (!useUIDs || fetchAttrs.size() == 1){
                              System.out.println("CommandFetch.!useUIDs");
                              if (attrs == null) {
                                  System.out.println("CommandFetch UID fetching attrs: "+attrs);
                                  attrs = currentMailbox.getMessageAttributes(msn, user);
                              }
                              uid = attrs.getUID();
                              System.out.println("CommandFetch UID attrs: "+attrs);
                              System.out.println("CommandFetch UID attrs.getUID: "+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;
                              }
                              System.out.println("CommandFetch UID printing UID: "+uid);
  
                              if (responseAdded) {
                                  response += SP + "UID "+ uid;
                              } else {
                                  response +=  "UID "+ uid;
                                  responseAdded = true;
                              }
                          } // don't duplicate on UID FETCH requests
                          System.out.println("CommandFetch UID end");
                      }
                      // 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]") 
                              || "RFC822.HEADER".equalsIgnoreCase(arg)) {
                          System.out.println("Starting BODY[HEADER]");
                          if (responseAdded) { // unlikely
                              if (useUIDs) {
                                  response += " UID " + uid + ")";
                              } else {
                                  response += ")";
                              }
                              System.out.println(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
                              System.out.println(tag + SP + msn + SP + NO + "Error retrieving message1.");
                              out.println(tag + SP + msn + SP + NO + "Error retrieving message1.");
                              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 (";
                          response += "BODY[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);
                          System.out.println(response);
                          getLogger().debug("Sending: " + response);
                          Iterator lit = lines.iterator();
                          while (lit.hasNext()) {
                              String line = (String)lit.next();
                              out.println(line);
                              System.out.println(line);
                              getLogger().debug("Sending: " + line);
                          }
                          out.println();
                          System.out.println();
                          getLogger().debug("Sending blank line");
                          if (useUIDs) {
                              out.println(  " UID " + uid + ")");
                              System.out.println(  " UID " + uid + ")");
                              getLogger().debug("Sending: UID " + uid + ")");
                          } else {
                              out.println( ")" );
                              System.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 +")");
                                          System.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 + ")");
                                          System.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")
                                 || arg.toUpperCase().startsWith("(BODY.PEEK[HEADER.FIELDS")){
                          System.out.println("Starting BODY[HEADER.FIELDS]");
                          if (responseAdded) {
                              if (useUIDs) {
                                  response += " UID " + uid + ")";
                              } else {
                                  response += ")";
                              }
                              out.println(response);
                              System.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 + SP + "Error retrieving message2.");
                              System.out.println(tag + SP + msn + SP + NO + SP + "Error retrieving message2.");
                              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);
                          System.out.println(response);
                          getLogger().debug("Sending: " + response);
                          Iterator lit = lines.iterator();
                          while (lit.hasNext()) {
                              String line = (String)lit.next();
                              out.println(line);
                              System.out.println(line);
                              getLogger().debug("Sending: " + line);
                          }
                          out.println();
                          System.out.println();
                          if (useUIDs) {
                              out.println(  " UID " + uid + ")");
                              System.out.println(  " UID " + uid + ")");
                          } else {
                              out.println(")");
                              System.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 +")");
                                          System.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 + ")");
                                          System.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[]")) {
                          System.out.println("Starting BODY[]");
                          if (responseAdded) { // unlikely
                              if (useUIDs) {
                                  response += " UID " + uid + ")";
                              } else {
                                  response += ")";
                              }
                              out.println(response);
                              System.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 message3.");
                              System.out.println(tag + SP + msn + SP + BAD + "Error retrieving message3.");
                              getLogger().error("Retrieved null message");
                              return;
                          }
                          try {
                              long size = msg.getMessageSize();
                              if (arg.equalsIgnoreCase("RFC822")) {
                                  out.println(UNTAGGED + SP + msn + SP + "FETCH ( RFC822 {" + size + "}");
                                  System.out.println(UNTAGGED + SP + msn + SP + "FETCH ( RFC822 {" + size + "}");
                              } else {
                                  out.println(UNTAGGED + SP + msn + SP + "FETCH ( BODY[] {" + size + "}");
                                  System.out.println(UNTAGGED + SP + msn + SP + "FETCH ( BODY[] {" + size + "}");
                              }
                              msg.writeTo(outs);
                              if (useUIDs) {
                                  out.println(" UID " + uid + ")");
                                  System.out.println(" UID " + uid + ")");
                              } else {
                                  out.println(")");
                                  System.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 +")");
                                          System.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 + ")");
                                          System.out.println(UNTAGGED + SP + msn + SP + "FETCH (FLAGS "
                                                      + newflags + ")");
                                      }
                                  }
                              }
                          } catch (MessagingException me) {
                              out.println(UNTAGGED + SP + NO + SP + "Error retrieving message4");
                              System.out.println(UNTAGGED + SP + NO + SP + "Error retrieving message4");
                              getLogger().error("Exception retrieving message: " + me);
                          } catch (IOException ioe) {
                              out.println(UNTAGGED + SP + NO + SP + "Error retrieving message5");
                              System.out.println(UNTAGGED + SP + NO + SP + "Error retrieving message5");
                              getLogger().error("Exception sending message: " + ioe);
                          } catch (Exception e) {
                              out.println(UNTAGGED + SP + NO + SP + "Error retrieving message6");
                              System.out.println(UNTAGGED + SP + NO + SP + "Error retrieving message6");
                              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]")) {
                          System.out.println("Starting BODY[TEXT]");
                          if (responseAdded) { // unlikely
                              if (useUIDs) {
                                  response += " UID " + uid + ")";
                              } else {
                                  response += ")";
                              }
                              out.println(response);
                              System.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 message7.");
                              System.out.println(tag + SP + msn + SP + NO + "Error retrieving message7.");
                              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 + "}");
                                  System.out.println(UNTAGGED + SP + msn + SP + "FETCH ( RFC822.TEXT {" + size + "}");
                              } else {
                                  out.println(UNTAGGED + SP + msn + SP + "FETCH ( BODY[TEXT] {" + size + "}");
                                  System.out.println(UNTAGGED + SP + msn + SP + "FETCH ( BODY[TEXT] {" + size + "}");
                              }
                              msg.writeContentTo(outs);
                              if (useUIDs) {
                                  out.println(  " UID " + uid + ")");
                                  System.out.println(  " UID " + uid + ")");
                              } else {
                                  out.println(")");
                                  System.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 +")");
                                          System.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 + ")");
                                          System.out.println(UNTAGGED + SP + msn + SP + "FETCH (FLAGS "
                                                      + newflags + ")");
                                      }
                                  }
                              }
                          } catch (MessagingException me) {
                              out.println(UNTAGGED + SP + NO + SP + "Error retrieving message8:"+me);
                              System.out.println(UNTAGGED + SP + NO + SP + "Error retrieving message8:"+me);
                              me.printStackTrace();
                              getLogger().error("Exception retrieving message: " + me);
                          } catch (IOException ioe) {
                              out.println(UNTAGGED + SP + NO + SP + "Error retrieving message9");
                              System.out.println(UNTAGGED + SP + NO + SP + "Error retrieving message9");
                              getLogger().error("Exception sending message: " + ioe);
                          } catch (Exception e) {
                              out.println(UNTAGGED + SP + NO + SP + "Error retrieving message10");
                              System.out.println(UNTAGGED + SP + NO + SP + "Error retrieving message10");
                              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);
                              System.out.println(response);
                          }
                          out.println(tag + SP + NO + SP
                                      + "FETCH attribute not recognized");
                          System.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);
                      System.out.println(response);
                  }
              } // end for loop
  
              out.println(tag + SP + OK + SP + "FETCH completed");
              System.out.println(tag + SP + OK + SP + "FETCH completed");
              caller.checkSize();
              return;
          } catch (AccessControlException ace) {
              out.println(tag + SP + NO + SP + "No such mailbox");
              System.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);
              System.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.");
              System.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;
          }
      }
  
      private List convertMacroCommands( List fetchAttributes )
      {
          List convertedAttributes = new ArrayList();
  
          // convert macro fetch commands to basic commands
          Iterator iter = fetchAttributes.iterator();
          while ( iter.hasNext() ) {
              String arg = (String)iter.next();
              if (arg.equalsIgnoreCase("FAST")) {
                  convertedAttributes.add("FLAGS");
                  convertedAttributes.add("INTERNALDATE");
                  convertedAttributes.add("RFC822.SIZE");
              } else if (arg.equalsIgnoreCase("ALL")) {
                  convertedAttributes.add("FLAGS");
                  convertedAttributes.add("INTERNALDATE");
                  convertedAttributes.add("RFC822.SIZE");
                  convertedAttributes.add("ENVELOPE");
              } else if (arg.equalsIgnoreCase("FULL")) {
                  convertedAttributes.add("FLAGS");
                  convertedAttributes.add("INTERNALDATE");
                  convertedAttributes.add("RFC822.SIZE");
                  convertedAttributes.add("ENVELOPE");
                  convertedAttributes.add("BODY");
              }
              else {
                  convertedAttributes.add( arg );
              }
              getLogger().debug("Found convertedAttributes: " + arg);
          }
  
          return convertedAttributes;
      }
  }
  
  
  
  1.1                  jakarta-james/proposals/imap/java/org/apache/james/imapserver/commands/HighestUID.java
  
  Index: HighestUID.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;
  
  /**
   * Reference: RFC 2060
   * @author <a href="mailto:s@rapi.com">Stephan Schiessling</a>
   * @version 0.1 on 15 Aug 2002
   */
  public interface HighestUID {
  
     int get();
     void increase();
  
  }
  
  
  

--
To unsubscribe, e-mail:   <ma...@jakarta.apache.org>
For additional commands, e-mail: <ma...@jakarta.apache.org>