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 jo...@apache.org on 2006/10/09 19:15:42 UTC

svn commit: r454432 [3/14] - in /james/server/sandbox/imap-integration: ./ lib/ src/java/ src/java/org/apache/james/imapserver/ src/java/org/apache/james/imapserver/commands/ src/java/org/apache/james/imapserver/debug/ src/java/org/apache/james/imapser...

Added: james/server/sandbox/imap-integration/src/java/org/apache/james/imapserver/commands/CopyCommand.java
URL: http://svn.apache.org/viewvc/james/server/sandbox/imap-integration/src/java/org/apache/james/imapserver/commands/CopyCommand.java?view=auto&rev=454432
==============================================================================
--- james/server/sandbox/imap-integration/src/java/org/apache/james/imapserver/commands/CopyCommand.java (added)
+++ james/server/sandbox/imap-integration/src/java/org/apache/james/imapserver/commands/CopyCommand.java Mon Oct  9 10:15:30 2006
@@ -0,0 +1,96 @@
+/***********************************************************************
+ * Copyright (c) 2000-2004 The Apache Software Foundation.             *
+ * All rights reserved.                                                *
+ * ------------------------------------------------------------------- *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you *
+ * may not use this file except in compliance with the License. You    *
+ * may obtain a copy of the License at:                                *
+ *                                                                     *
+ *     http://www.apache.org/licenses/LICENSE-2.0                      *
+ *                                                                     *
+ * Unless required by applicable law or agreed to in writing, software *
+ * distributed under the License is distributed on an "AS IS" BASIS,   *
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or     *
+ * implied.  See the License for the specific language governing       *
+ * permissions and limitations under the License.                      *
+ ***********************************************************************/
+
+package org.apache.james.imapserver.commands;
+
+import org.apache.james.imapserver.ImapRequestLineReader;
+import org.apache.james.imapserver.ImapResponse;
+import org.apache.james.imapserver.ImapSession;
+import org.apache.james.imapserver.ProtocolException;
+import org.apache.james.imapserver.store.MailboxException;
+import org.apache.james.mailboxmanager.GeneralMessageSet;
+import org.apache.james.mailboxmanager.MailboxManagerException;
+import org.apache.james.mailboxmanager.impl.GeneralMessageSetImpl;
+import org.apache.james.mailboxmanager.mailbox.ImapMailboxSession;
+
+/**
+ * Handles processeing for the COPY imap command.
+ *
+ * @author  Darrell DeBoer <da...@apache.org>
+ *
+ * @version $Revision: 109034 $
+ */
+class CopyCommand extends SelectedStateCommand implements UidEnabledCommand
+{
+    public static final String NAME = "COPY";
+    public static final String ARGS = "<message-set> <mailbox>";
+
+    /** @see CommandTemplate#doProcess */
+    protected void doProcess( ImapRequestLineReader request,
+                              ImapResponse response,
+                              ImapSession session )
+        throws ProtocolException, MailboxException
+    {
+        doProcess( request, response, session, false );
+    }
+
+    public void doProcess( ImapRequestLineReader request,
+                              ImapResponse response,
+                              ImapSession session,
+                              boolean useUids)
+            throws ProtocolException, MailboxException
+    {
+        IdRange[] idSet = parser.parseIdRange( request );
+        String mailboxName = parser.mailbox( request );
+        parser.endLine( request );
+
+        ImapMailboxSession currentMailbox = session.getSelected().getMailbox();
+        
+        
+        try {
+            mailboxName=session.buildFullName(mailboxName);
+            if (!session.getMailboxManager().existsMailbox(mailboxName)) {
+                MailboxException e=new MailboxException("Mailbox does not exists");
+                e.setResponseCode( "TRYCREATE" );
+                throw e;
+            }
+            for (int i = 0; i < idSet.length; i++) {
+                GeneralMessageSet messageSet=GeneralMessageSetImpl.range(idSet[i].getLowVal(),idSet[i].getHighVal(),useUids);
+                session.getMailboxManager().copyMessages(currentMailbox,messageSet,mailboxName);
+            }
+        } catch (MailboxManagerException e) {
+            throw new MailboxException(e);
+        } 
+        
+
+
+        session.unsolicitedResponses( response, useUids);
+        response.commandComplete( this );
+    }
+
+    /** @see ImapCommand#getName */
+    public String getName()
+    {
+        return NAME;
+    }
+
+    /** @see CommandTemplate#getArgSyntax */
+    public String getArgSyntax()
+    {
+        return ARGS;
+    }
+}

Added: james/server/sandbox/imap-integration/src/java/org/apache/james/imapserver/commands/CreateCommand.java
URL: http://svn.apache.org/viewvc/james/server/sandbox/imap-integration/src/java/org/apache/james/imapserver/commands/CreateCommand.java?view=auto&rev=454432
==============================================================================
--- james/server/sandbox/imap-integration/src/java/org/apache/james/imapserver/commands/CreateCommand.java (added)
+++ james/server/sandbox/imap-integration/src/java/org/apache/james/imapserver/commands/CreateCommand.java Mon Oct  9 10:15:30 2006
@@ -0,0 +1,123 @@
+/***********************************************************************
+ * Copyright (c) 2000-2004 The Apache Software Foundation.             *
+ * All rights reserved.                                                *
+ * ------------------------------------------------------------------- *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you *
+ * may not use this file except in compliance with the License. You    *
+ * may obtain a copy of the License at:                                *
+ *                                                                     *
+ *     http://www.apache.org/licenses/LICENSE-2.0                      *
+ *                                                                     *
+ * Unless required by applicable law or agreed to in writing, software *
+ * distributed under the License is distributed on an "AS IS" BASIS,   *
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or     *
+ * implied.  See the License for the specific language governing       *
+ * permissions and limitations under the License.                      *
+ ***********************************************************************/
+
+package org.apache.james.imapserver.commands;
+
+import org.apache.james.imapserver.AuthorizationException;
+import org.apache.james.imapserver.ImapRequestLineReader;
+import org.apache.james.imapserver.ImapResponse;
+import org.apache.james.imapserver.ImapSession;
+import org.apache.james.imapserver.ProtocolException;
+import org.apache.james.imapserver.store.MailboxException;
+import org.apache.james.mailboxmanager.MailboxManagerException;
+
+/**
+ * Handles processeing for the CREATE imap command.
+ *
+ * @author  Darrell DeBoer <da...@apache.org>
+ *
+ * @version $Revision: 109034 $
+ */
+class CreateCommand extends AuthenticatedStateCommand
+{
+    public static final String NAME = "CREATE";
+    public static final String ARGS = "<mailbox>";
+
+    /** @see CommandTemplate#doProcess */
+    protected void doProcess( ImapRequestLineReader request,
+                              ImapResponse response,
+                              ImapSession session )
+            throws ProtocolException, MailboxException, AuthorizationException
+    {
+        String mailboxName = parser.mailbox( request );
+        parser.endLine( request );
+
+
+        try {
+
+            mailboxName=session.buildFullName(mailboxName);
+            session.getMailboxManager().createMailbox(mailboxName );
+        } catch (MailboxManagerException e) {
+           throw new MailboxException(e);
+        }
+        session.unsolicitedResponses( response, false );
+        response.commandComplete( this );
+    }
+
+    /** @see ImapCommand#getName */
+    public String getName()
+    {
+        return NAME;
+    }
+
+    /** @see CommandTemplate#getArgSyntax */
+    public String getArgSyntax()
+    {
+        return ARGS;
+    }
+
+}
+
+/*
+6.3.3.  CREATE Command
+
+   Arguments:  mailbox name
+
+   Responses:  no specific responses for this command
+
+   Result:     OK - create completed
+               NO - create failure: can't create mailbox with that name
+               BAD - command unknown or arguments invalid
+
+      The CREATE command creates a mailbox with the given name.  An OK
+      response is returned only if a new mailbox with that name has been
+      created.  It is an error to attempt to create INBOX or a mailbox
+      with a name that refers to an extant mailbox.  Any error in
+      creation will return a tagged NO response.
+
+      If the mailbox name is suffixed with the server's hierarchy
+      separator character (as returned from the server by a LIST
+      command), this is a declaration that the client intends to create
+      mailbox names under this name in the hierarchy.  Server
+      implementations that do not require this declaration MUST ignore
+      it.
+
+      If the server's hierarchy separator character appears elsewhere in
+      the name, the server SHOULD create any superior hierarchical names
+      that are needed for the CREATE command to complete successfully.
+      In other words, an attempt to create "foo/bar/zap" on a server in
+      which "/" is the hierarchy separator character SHOULD create foo/
+      and foo/bar/ if they do not already exist.
+
+      If a new mailbox is created with the same name as a mailbox which
+      was deleted, its unique identifiers MUST be greater than any
+      unique identifiers used in the previous incarnation of the mailbox
+      UNLESS the new incarnation has a different unique identifier
+      validity value.  See the description of the UID command for more
+      detail.
+
+   Example:    C: A003 CREATE owatagusiam/
+               S: A003 OK CREATE completed
+               C: A004 CREATE owatagusiam/blurdybloop
+               S: A004 OK CREATE completed
+
+      Note: the interpretation of this example depends on whether "/"
+      was returned as the hierarchy separator from LIST.  If "/" is the
+      hierarchy separator, a new level of hierarchy named "owatagusiam"
+      with a member called "blurdybloop" is created.  Otherwise, two
+      mailboxes at the same hierarchy level are created.
+*/

Added: james/server/sandbox/imap-integration/src/java/org/apache/james/imapserver/commands/DeleteCommand.java
URL: http://svn.apache.org/viewvc/james/server/sandbox/imap-integration/src/java/org/apache/james/imapserver/commands/DeleteCommand.java?view=auto&rev=454432
==============================================================================
--- james/server/sandbox/imap-integration/src/java/org/apache/james/imapserver/commands/DeleteCommand.java (added)
+++ james/server/sandbox/imap-integration/src/java/org/apache/james/imapserver/commands/DeleteCommand.java Mon Oct  9 10:15:30 2006
@@ -0,0 +1,151 @@
+/***********************************************************************
+ * Copyright (c) 2000-2004 The Apache Software Foundation.             *
+ * All rights reserved.                                                *
+ * ------------------------------------------------------------------- *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you *
+ * may not use this file except in compliance with the License. You    *
+ * may obtain a copy of the License at:                                *
+ *                                                                     *
+ *     http://www.apache.org/licenses/LICENSE-2.0                      *
+ *                                                                     *
+ * Unless required by applicable law or agreed to in writing, software *
+ * distributed under the License is distributed on an "AS IS" BASIS,   *
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or     *
+ * implied.  See the License for the specific language governing       *
+ * permissions and limitations under the License.                      *
+ ***********************************************************************/
+
+package org.apache.james.imapserver.commands;
+
+import org.apache.james.imapserver.AuthorizationException;
+import org.apache.james.imapserver.ImapRequestLineReader;
+import org.apache.james.imapserver.ImapResponse;
+import org.apache.james.imapserver.ImapSession;
+import org.apache.james.imapserver.ProtocolException;
+import org.apache.james.imapserver.store.MailboxException;
+import org.apache.james.mailboxmanager.MailboxManagerException;
+import org.apache.james.mailboxmanager.mailbox.ImapMailboxSession;
+
+/**
+ * Handles processeing for the DELETE imap command.
+ *
+ * @author  Darrell DeBoer <da...@apache.org>
+ *
+ * @version $Revision: 109034 $
+ */
+class DeleteCommand extends AuthenticatedStateCommand
+{
+    public static final String NAME = "DELETE";
+    public static final String ARGS = "<mailbox>";
+
+    /** @see CommandTemplate#doProcess */
+    protected void doProcess( ImapRequestLineReader request,
+                              ImapResponse response,
+                              ImapSession session )
+            throws ProtocolException, MailboxException, AuthorizationException
+    {
+
+        String mailboxName = parser.mailbox( request );
+        parser.endLine( request );
+
+        try {
+            mailboxName = session.buildFullName(mailboxName);
+            if (session.getSelected() != null) {
+                if (session.getSelected().getMailbox().getName().equals(
+                        mailboxName)) {
+                    session.deselect();
+                }
+            }
+            session.getMailboxManager().deleteMailbox(mailboxName);
+        } catch (MailboxManagerException e) {
+            throw new MailboxException(e);
+        }
+
+        session.unsolicitedResponses( response, false );
+        response.commandComplete( this );
+    }
+
+    /** @see ImapCommand#getName */
+    public String getName()
+    {
+        return NAME;
+    }
+
+    /** @see CommandTemplate#getArgSyntax */
+    public String getArgSyntax()
+    {
+        return ARGS;
+    }
+}
+
+/*
+6.3.4.  DELETE Command
+
+   Arguments:  mailbox name
+
+   Responses:  no specific responses for this command
+
+   Result:     OK - delete completed
+               NO - delete failure: can't delete mailbox with that name
+               BAD - command unknown or arguments invalid
+
+      The DELETE command permanently removes the mailbox with the given
+      name.  A tagged OK response is returned only if the mailbox has
+      been deleted.  It is an error to attempt to delete INBOX or a
+      mailbox name that does not exist.
+
+      The DELETE command MUST NOT remove inferior hierarchical names.
+      For example, if a mailbox "foo" has an inferior "foo.bar"
+      (assuming "." is the hierarchy delimiter character), removing
+      "foo" MUST NOT remove "foo.bar".  It is an error to attempt to
+      delete a name that has inferior hierarchical names and also has
+      the \Noselect mailbox name attribute (see the description of the
+      LIST response for more details).
+
+      It is permitted to delete a name that has inferior hierarchical
+      names and does not have the \Noselect mailbox name attribute.  In
+      this case, all messages in that mailbox are removed, and the name
+      will acquire the \Noselect mailbox name attribute.
+
+      The value of the highest-used unique identifier of the deleted
+      mailbox MUST be preserved so that a new mailbox created with the
+      same name will not reuse the identifiers of the former
+      incarnation, UNLESS the new incarnation has a different unique
+      identifier validity value.  See the description of the UID command
+      for more detail.
+
+
+   Examples:   C: A682 LIST "" *
+               S: * LIST () "/" blurdybloop
+               S: * LIST (\Noselect) "/" foo
+               S: * LIST () "/" foo/bar
+               S: A682 OK LIST completed
+               C: A683 DELETE blurdybloop
+               S: A683 OK DELETE completed
+               C: A684 DELETE foo
+               S: A684 NO Name "foo" has inferior hierarchical names
+               C: A685 DELETE foo/bar
+               S: A685 OK DELETE Completed
+               C: A686 LIST "" *
+               S: * LIST (\Noselect) "/" foo
+               S: A686 OK LIST completed
+               C: A687 DELETE foo
+               S: A687 OK DELETE Completed
+
+
+               C: A82 LIST "" *
+               S: * LIST () "." blurdybloop
+               S: * LIST () "." foo
+               S: * LIST () "." foo.bar
+               S: A82 OK LIST completed
+               C: A83 DELETE blurdybloop
+               S: A83 OK DELETE completed
+               C: A84 DELETE foo
+               S: A84 OK DELETE Completed
+               C: A85 LIST "" *
+               S: * LIST () "." foo.bar
+               S: A85 OK LIST completed
+               C: A86 LIST "" %
+               S: * LIST (\Noselect) "." foo
+               S: A86 OK LIST completed
+*/

Added: james/server/sandbox/imap-integration/src/java/org/apache/james/imapserver/commands/ExamineCommand.java
URL: http://svn.apache.org/viewvc/james/server/sandbox/imap-integration/src/java/org/apache/james/imapserver/commands/ExamineCommand.java?view=auto&rev=454432
==============================================================================
--- james/server/sandbox/imap-integration/src/java/org/apache/james/imapserver/commands/ExamineCommand.java (added)
+++ james/server/sandbox/imap-integration/src/java/org/apache/james/imapserver/commands/ExamineCommand.java Mon Oct  9 10:15:30 2006
@@ -0,0 +1,34 @@
+/***********************************************************************
+ * Copyright (c) 2000-2004 The Apache Software Foundation.             *
+ * All rights reserved.                                                *
+ * ------------------------------------------------------------------- *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you *
+ * may not use this file except in compliance with the License. You    *
+ * may obtain a copy of the License at:                                *
+ *                                                                     *
+ *     http://www.apache.org/licenses/LICENSE-2.0                      *
+ *                                                                     *
+ * Unless required by applicable law or agreed to in writing, software *
+ * distributed under the License is distributed on an "AS IS" BASIS,   *
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or     *
+ * implied.  See the License for the specific language governing       *
+ * permissions and limitations under the License.                      *
+ ***********************************************************************/
+
+package org.apache.james.imapserver.commands;
+
+/**
+ *
+ * @author  Darrell DeBoer <da...@apache.org>
+ *
+ * @version $Revision: 109034 $
+ */
+class ExamineCommand extends SelectCommand
+{
+    public static final String NAME = "EXAMINE";
+
+    public String getName()
+    {
+        return NAME;
+    }
+}

Added: james/server/sandbox/imap-integration/src/java/org/apache/james/imapserver/commands/ExpungeCommand.java
URL: http://svn.apache.org/viewvc/james/server/sandbox/imap-integration/src/java/org/apache/james/imapserver/commands/ExpungeCommand.java?view=auto&rev=454432
==============================================================================
--- james/server/sandbox/imap-integration/src/java/org/apache/james/imapserver/commands/ExpungeCommand.java (added)
+++ james/server/sandbox/imap-integration/src/java/org/apache/james/imapserver/commands/ExpungeCommand.java Mon Oct  9 10:15:30 2006
@@ -0,0 +1,105 @@
+/***********************************************************************
+ * Copyright (c) 2000-2004 The Apache Software Foundation.             *
+ * All rights reserved.                                                *
+ * ------------------------------------------------------------------- *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you *
+ * may not use this file except in compliance with the License. You    *
+ * may obtain a copy of the License at:                                *
+ *                                                                     *
+ *     http://www.apache.org/licenses/LICENSE-2.0                      *
+ *                                                                     *
+ * Unless required by applicable law or agreed to in writing, software *
+ * distributed under the License is distributed on an "AS IS" BASIS,   *
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or     *
+ * implied.  See the License for the specific language governing       *
+ * permissions and limitations under the License.                      *
+ ***********************************************************************/
+
+package org.apache.james.imapserver.commands;
+
+import org.apache.james.imapserver.ImapRequestLineReader;
+import org.apache.james.imapserver.ImapResponse;
+import org.apache.james.imapserver.ImapSession;
+import org.apache.james.imapserver.ProtocolException;
+import org.apache.james.imapserver.store.MailboxException;
+import org.apache.james.mailboxmanager.GeneralMessageSet;
+import org.apache.james.mailboxmanager.MailboxManagerException;
+import org.apache.james.mailboxmanager.impl.GeneralMessageSetImpl;
+import org.apache.james.mailboxmanager.mailbox.ImapMailboxSession;
+
+/**
+ * Handles processeing for the EXPUNGE imap command.
+ *
+ * @author  Darrell DeBoer <da...@apache.org>
+ *
+ * @version $Revision: 109034 $
+ */
+class ExpungeCommand extends SelectedStateCommand
+{
+    public static final String NAME = "EXPUNGE";
+    public static final String ARGS = null;
+
+    /** @see CommandTemplate#doProcess */
+    protected void doProcess( ImapRequestLineReader request,
+                              ImapResponse response,
+                              ImapSession session )
+            throws ProtocolException, MailboxException
+    {
+        parser.endLine( request );
+
+        ImapMailboxSession mailbox = session.getSelected().getMailbox();
+        if (!mailbox.isWriteable()) {
+            response.commandFailed( this, "Mailbox selected read only." );
+        }
+       
+        try {
+            mailbox.expunge(GeneralMessageSetImpl.all(),GeneralMessageSet.TYPE_NONE);
+        } catch (MailboxManagerException e) {
+            throw new MailboxException(e);
+        }
+
+        session.unsolicitedResponses( response, false );
+        response.commandComplete( this );
+    }
+
+    /** @see ImapCommand#getName */
+    public String getName()
+    {
+        return NAME;
+    }
+
+    /** @see CommandTemplate#getArgSyntax */
+    public String getArgSyntax()
+    {
+        return ARGS;
+    }
+}
+/*
+6.4.3.  EXPUNGE Command
+
+   Arguments:  none
+
+   Responses:  untagged responses: EXPUNGE
+
+   Result:     OK - expunge completed
+               NO - expunge failure: can't expunge (e.g. permission
+                    denied)
+               BAD - command unknown or arguments invalid
+
+      The EXPUNGE command permanently removes from the currently
+      selected mailbox all messages that have the \Deleted flag set.
+      Before returning an OK to the client, an untagged EXPUNGE response
+      is sent for each message that is removed.
+
+   Example:    C: A202 EXPUNGE
+               S: * 3 EXPUNGE
+               S: * 3 EXPUNGE
+               S: * 5 EXPUNGE
+               S: * 8 EXPUNGE
+               S: A202 OK EXPUNGE completed
+
+      Note: in this example, messages 3, 4, 7, and 11 had the
+      \Deleted flag set.  See the description of the EXPUNGE
+      response for further explanation.
+*/
+

Added: james/server/sandbox/imap-integration/src/java/org/apache/james/imapserver/commands/FetchCommand.java
URL: http://svn.apache.org/viewvc/james/server/sandbox/imap-integration/src/java/org/apache/james/imapserver/commands/FetchCommand.java?view=auto&rev=454432
==============================================================================
--- james/server/sandbox/imap-integration/src/java/org/apache/james/imapserver/commands/FetchCommand.java (added)
+++ james/server/sandbox/imap-integration/src/java/org/apache/james/imapserver/commands/FetchCommand.java Mon Oct  9 10:15:30 2006
@@ -0,0 +1,946 @@
+/***********************************************************************
+ * Copyright (c) 2000-2004 The Apache Software Foundation.             *
+ * All rights reserved.                                                *
+ * ------------------------------------------------------------------- *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you *
+ * may not use this file except in compliance with the License. You    *
+ * may obtain a copy of the License at:                                *
+ *                                                                     *
+ *     http://www.apache.org/licenses/LICENSE-2.0                      *
+ *                                                                     *
+ * Unless required by applicable law or agreed to in writing, software *
+ * distributed under the License is distributed on an "AS IS" BASIS,   *
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or     *
+ * implied.  See the License for the specific language governing       *
+ * permissions and limitations under the License.                      *
+ ***********************************************************************/
+
+package org.apache.james.imapserver.commands;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+
+import javax.mail.Flags;
+import javax.mail.MessagingException;
+import javax.mail.internet.MimeMessage;
+
+import org.apache.james.core.MimeMessageWrapper;
+import org.apache.james.imapserver.ImapRequestLineReader;
+import org.apache.james.imapserver.ImapResponse;
+import org.apache.james.imapserver.ImapSession;
+import org.apache.james.imapserver.ProtocolException;
+import org.apache.james.imapserver.store.MailboxException;
+import org.apache.james.imapserver.store.MessageFlags;
+import org.apache.james.imapserver.store.SimpleMessageAttributes;
+import org.apache.james.mailboxmanager.GeneralMessageSet;
+import org.apache.james.mailboxmanager.MailboxManagerException;
+import org.apache.james.mailboxmanager.MessageResult;
+import org.apache.james.mailboxmanager.impl.GeneralMessageSetImpl;
+import org.apache.james.mailboxmanager.mailbox.ImapMailboxSession;
+import org.apache.mailet.dates.RFC822DateFormat;
+
+import com.sun.mail.util.CRLFOutputStream;
+
+/**
+ * Handles processeing for the FETCH imap command.
+ *
+ * @author  Darrell DeBoer <da...@apache.org>
+ *
+ * @version $Revision: 109034 $
+ */
+class FetchCommand extends SelectedStateCommand implements UidEnabledCommand
+{
+    public static final String NAME = "FETCH";
+    public static final String ARGS = "<message-set> <fetch-profile>";
+
+    private FetchCommandParser parser = new FetchCommandParser();
+
+    /** @see CommandTemplate#doProcess */
+    protected void doProcess( ImapRequestLineReader request,
+                              ImapResponse response,
+                              ImapSession session )
+            throws ProtocolException, MailboxException
+    {
+        doProcess( request, response, session, false );
+    }
+
+    public void doProcess( ImapRequestLineReader request,
+                              ImapResponse response,
+                              ImapSession session,
+                              boolean useUids )
+            throws ProtocolException, MailboxException
+    {
+        IdRange[] idSet = parser.parseIdRange( request );
+        FetchRequest fetch = parser.fetchRequest( request );
+        parser.endLine( request );
+        
+        if (useUids) {
+            fetch.uid = true;
+        }
+
+        // TODO only fetch needed results
+        int resultToFetch = MessageResult.FLAGS | MessageResult.MIME_MESSAGE
+                | MessageResult.INTERNAL_DATE | MessageResult.MSN
+                | MessageResult.SIZE;
+        ImapMailboxSession mailbox = session.getSelected().getMailbox();
+        for (int i = 0; i < idSet.length; i++) {
+            GeneralMessageSet messageSet=GeneralMessageSetImpl.range(idSet[i].getLowVal(),idSet[i].getHighVal(),useUids);
+            MessageResult[] result;
+            try {
+                result = mailbox.getMessages(messageSet,resultToFetch);
+            } catch (MailboxManagerException e) {
+                throw new MailboxException(e);
+            }
+            for (int j = 0; j < result.length; j++) {
+                String msgData = outputMessage( fetch, result[j], mailbox, useUids );
+                response.fetchResponse( result[j].getMsn(), msgData );
+
+            }
+        }
+
+        boolean omitExpunged = (!useUids);
+        session.unsolicitedResponses( response, omitExpunged , useUids);
+        response.commandComplete( this );
+    }
+
+    private String outputMessage(FetchRequest fetch, MessageResult result,
+            ImapMailboxSession mailbox, boolean useUids)
+            throws MailboxException, ProtocolException {
+        // Check if this fetch will cause the "SEEN" flag to be set on this
+        // message
+        // If so, update the flags, and ensure that a flags response is included
+        // in the response.
+        try {
+            boolean ensureFlagsResponse = false;
+            if (fetch.isSetSeen()
+                    && !result.getFlags().contains(Flags.Flag.SEEN)) {
+                mailbox.setFlags(new Flags(Flags.Flag.SEEN), true, false,
+                        GeneralMessageSetImpl.oneUid(result.getUid()), null);
+                result.getFlags().add(Flags.Flag.SEEN);
+                ensureFlagsResponse = true;
+            }
+
+            StringBuffer response = new StringBuffer();
+
+            // FLAGS response
+            if (fetch.flags || ensureFlagsResponse) {
+                response.append(" FLAGS ");
+                response.append(MessageFlags.format(result.getFlags()));
+            }
+
+            // INTERNALDATE response
+            if (fetch.internalDate) {
+                response.append(" INTERNALDATE \"");
+                // TODO format properly
+                response.append(RFC822DateFormat.toString(result
+                        .getInternalDate())); // not right format
+                response.append("\"");
+
+            }
+
+            // RFC822.SIZE response
+            if (fetch.size) {
+                response.append(" RFC822.SIZE ");
+                response.append(result.getSize());
+            }
+
+            SimpleMessageAttributes attrs = new SimpleMessageAttributes(result
+                    .getMimeMessage());
+
+            // ENVELOPE response
+            if (fetch.envelope) {
+                response.append(" ENVELOPE ");
+                response.append(attrs.getEnvelope());
+            }
+
+            // BODY response
+            if (fetch.body) {
+                response.append(" BODY ");
+                response.append(attrs.getBodyStructure(false));
+            }
+
+            // BODYSTRUCTURE response
+            if (fetch.bodyStructure) {
+                response.append(" BODYSTRUCTURE ");
+                response.append(attrs.getBodyStructure(true));
+            }
+
+            // UID response
+            if (fetch.uid) {
+                response.append(" UID ");
+                response.append(result.getUid());
+            }
+
+            // BODY part responses.
+            Collection elements = fetch.getBodyElements();
+            for (Iterator iterator = elements.iterator(); iterator.hasNext();) {
+                BodyFetchElement fetchElement = (BodyFetchElement) iterator
+                        .next();
+                response.append(SP);
+                response.append(fetchElement.getResponseName());
+                response.append(SP);
+
+                // Various mechanisms for returning message body.
+                String sectionSpecifier = fetchElement.getParameters();
+
+                MimeMessage mimeMessage = result.getMimeMessage();
+                try {
+                    handleBodyFetch(mimeMessage, sectionSpecifier, response);
+                } catch (MessagingException e) {
+                    // TODO chain exceptions
+                    throw new MailboxException(e.getMessage());
+                }
+            }
+
+            if (response.length() > 0) {
+                // Remove the leading " ".
+                return response.substring(1);
+            } else {
+                return "";
+            }
+        } catch (MailboxManagerException mme) {
+            throw new MailboxException(mme);
+        } catch (MessagingException me) {
+            throw new MailboxException(me);       
+        }
+    }
+
+
+    private void handleBodyFetch( MimeMessage mimeMessage,
+                                  String sectionSpecifier,
+                                  StringBuffer response )
+            throws ProtocolException, MessagingException
+    {
+        if ( sectionSpecifier.length() == 0 ) {
+            // TODO - need to use an InputStream from the response here.
+            ByteArrayOutputStream bout = new ByteArrayOutputStream();
+            try {
+                mimeMessage.writeTo(new CRLFOutputStream(bout));
+            }
+            catch ( IOException e ) {
+                throw new ProtocolException( "Error reading message source" );
+            }
+            byte[] bytes = bout.toByteArray();
+            addLiteral( bytes, response );
+            // TODO JD maybe we've to add CRLF here
+            
+        }
+        else if ( sectionSpecifier.equalsIgnoreCase( "HEADER" ) ) {
+            Enumeration enum = mimeMessage.getAllHeaderLines();
+            addHeaders( enum, response );
+        }
+        else if ( sectionSpecifier.startsWith( "HEADER.FIELDS.NOT " ) ) {
+            String[] excludeNames = extractHeaderList( sectionSpecifier, "HEADER.FIELDS.NOT ".length() );
+            Enumeration enum = mimeMessage.getNonMatchingHeaderLines( excludeNames );
+            addHeaders( enum, response );
+        }
+        else if ( sectionSpecifier.startsWith( "HEADER.FIELDS " ) ) {
+            String[] includeNames = extractHeaderList( sectionSpecifier, "HEADER.FIELDS ".length() );
+            Enumeration enum = mimeMessage.getMatchingHeaderLines( includeNames );
+            addHeaders( enum, response );
+        }
+        else if ( sectionSpecifier.equalsIgnoreCase( "MIME" ) ) {
+            // TODO implement
+            throw new ProtocolException( "MIME not yet implemented." );
+        }
+        else if ( sectionSpecifier.equalsIgnoreCase( "TEXT" ) ) {
+            // TODO - need to use an InputStream from the response here.
+            // TODO - this is a hack. To get just the body content, I'm using a null
+            // input stream to take the headers. Need to have a way of ignoring headers.
+            ByteArrayOutputStream headerOut = new ByteArrayOutputStream();
+            ByteArrayOutputStream bodyOut = new ByteArrayOutputStream();
+            try {
+            	// TODO James Trunk : Is this okay?
+            	MimeMessageWrapper mmw=new MimeMessageWrapper(mimeMessage);
+            	
+            	mmw.writeTo(headerOut, bodyOut );
+                byte[] bytes = bodyOut.toByteArray();
+
+                addLiteral( bytes, response );
+
+            }
+            catch ( IOException e ) {
+                throw new ProtocolException( "Error reading message source" );
+            }
+        }
+        else {
+            // Should be a part specifier followed by a section specifier.
+            // See if there's a leading part specifier.
+            // If so, get the number, get the part, and call this recursively.
+            int dotPos = sectionSpecifier.indexOf( '.' );
+            if ( dotPos == -1 ) {
+                throw new ProtocolException( "Malformed fetch attribute: " + sectionSpecifier );
+            }
+            int partNumber = Integer.parseInt( sectionSpecifier.substring( 0, dotPos ) );
+            String partSectionSpecifier = sectionSpecifier.substring( dotPos + 1 );
+
+            // TODO - get the MimePart of the mimeMessage, and call this method
+            // with the new partSectionSpecifier.
+//        MimeMessage part;
+//        handleBodyFetch( part, partSectionSpecifier, response );
+            throw new ProtocolException( "Mime parts not yet implemented for fetch." );
+        }
+
+    }
+
+    private void addLiteral( byte[] bytes, StringBuffer response )
+    {
+        response.append('{' );
+        response.append( bytes.length ); // TODO JD addLiteral: why was it  bytes.length +1 here?
+        response.append( '}' );
+        response.append( "\r\n" );
+
+        for ( int i = 0; i < bytes.length; i++ ) {
+            byte b = bytes[i];
+            response.append((char)b);
+        }
+    }
+
+    // TODO should do this at parse time.
+    private String[] extractHeaderList( String headerList, int prefixLen )
+    {
+        // Remove the trailing and leading ')('
+        String tmp = headerList.substring( prefixLen + 1, headerList.length() - 1 );
+        String[] headerNames = split( tmp, " " );
+        return headerNames;
+    }
+    
+    private String[] split(String value, String delimiter) {
+        ArrayList strings = new ArrayList();
+        int startPos = 0;
+        int delimPos;
+        while ( (delimPos = value.indexOf(delimiter, startPos) ) != -1) {
+            String sub = value.substring(startPos, delimPos);
+            strings.add(sub);
+            startPos = delimPos + 1;
+        }
+        String sub = value.substring(startPos);
+        strings.add(sub);
+        
+        return (String[]) strings.toArray(new String[0]);
+    }
+
+    private void addHeaders( Enumeration enum, StringBuffer response )
+    {
+        List lines = new ArrayList();
+        int count = 0;
+        while (enum.hasMoreElements()) {
+            String line = (String)enum.nextElement();
+            count += line.length() + 2;
+            lines.add(line);
+        }
+        response.append( '{' );
+        response.append( count + 2 );
+        response.append( '}' );
+        response.append("\r\n");
+
+        Iterator lit = lines.iterator();
+        while (lit.hasNext()) {
+            String line = (String)lit.next();
+            response.append( line );
+            response.append( "\r\n" );
+        }
+        response.append("\r\n");
+    }
+
+    /** @see ImapCommand#getName */
+    public String getName()
+    {
+        return NAME;
+    }
+
+    /** @see CommandTemplate#getArgSyntax */
+    public String getArgSyntax()
+    {
+        return ARGS;
+    }
+
+    private class FetchCommandParser extends CommandParser
+    {
+
+
+        public FetchRequest fetchRequest( ImapRequestLineReader request )
+                throws ProtocolException
+        {
+            FetchRequest fetch = new FetchRequest();
+
+            char next = nextNonSpaceChar( request );
+            consumeChar( request, '(' );
+
+            next = nextNonSpaceChar( request );
+            while ( next != ')' ) {
+                addNextElement( request, fetch );
+                next = nextNonSpaceChar( request );
+            }
+
+            consumeChar(request, ')');
+
+            return fetch;
+        }
+
+        private void addNextElement( ImapRequestLineReader command, FetchRequest fetch)
+                throws ProtocolException
+        {
+            char next = nextCharInLine( command );
+                StringBuffer element = new StringBuffer();
+                while ( next != ' ' && next != '[' && next != ')' ) {
+                    element.append(next);
+                    command.consume();
+                    next = nextCharInLine( command );
+                }
+                String name = element.toString();
+                // Simple elements with no '[]' parameters.
+                if ( next == ' ' || next == ')' ) {
+                    if ( "FAST".equalsIgnoreCase( name ) ) {
+                        fetch.flags = true;
+                        fetch.internalDate = true;
+                        fetch.size = true;
+                    } else if ("FULL".equalsIgnoreCase(name)) {
+                        fetch.flags = true;
+                        fetch.internalDate = true;
+                        fetch.size = true;
+                        fetch.envelope = true;
+                        fetch.body = true;
+                    } else if ("ALL".equalsIgnoreCase(name)) {
+                        fetch.flags = true;
+                        fetch.internalDate = true;
+                        fetch.size = true;
+                        fetch.envelope = true;
+                    } else if ("FLAGS".equalsIgnoreCase(name)) {
+                        fetch.flags = true;
+                    } else if ("RFC822.SIZE".equalsIgnoreCase(name)) {
+                        fetch.size = true;
+                    } else if ("ENVELOPE".equalsIgnoreCase(name)) {
+                        fetch.envelope = true;
+                    } else if ("INTERNALDATE".equalsIgnoreCase(name)) {
+                        fetch.internalDate = true;
+                    } else if ("BODY".equalsIgnoreCase(name)) {
+                        fetch.body = true;
+                    } else if ("BODYSTRUCTURE".equalsIgnoreCase(name)) {
+                        fetch.bodyStructure = true;
+                    } else if ("UID".equalsIgnoreCase(name)) {
+                        fetch.uid = true;
+                    } else if ("RFC822".equalsIgnoreCase(name)) {
+                        fetch.add(new BodyFetchElement("RFC822", ""), false);
+                    } else if ("RFC822.HEADER".equalsIgnoreCase(name)) {
+                        fetch.add(new BodyFetchElement("RFC822.HEADER", "HEADER"), true);
+                    } else if ("RFC822.TEXT".equalsIgnoreCase(name)) {
+                        fetch.add(new BodyFetchElement("RFC822.TEXT", "TEXT"), false);
+                    } else {
+                        throw new ProtocolException( "Invalid fetch attribute: " + name );
+                    }
+                }
+                else {
+                    consumeChar( command, '[' );
+
+                    StringBuffer sectionIdentifier = new StringBuffer();
+                    next = nextCharInLine( command );
+                    while ( next != ']' ) {
+                        sectionIdentifier.append( next );
+                        command.consume();
+                        next = nextCharInLine(command);
+                    }
+                    consumeChar( command, ']' );
+
+                    String parameter = sectionIdentifier.toString();
+
+                    if ( "BODY".equalsIgnoreCase( name ) ) {
+                        fetch.add(new BodyFetchElement("BODY[" + parameter + "]", parameter), false);
+                    } else if ( "BODY.PEEK".equalsIgnoreCase( name ) ) {
+                        fetch.add(new BodyFetchElement("BODY[" + parameter + "]", parameter), true);
+                    } else {
+                        throw new ProtocolException( "Invalid fetch attibute: " + name + "[]" );
+                    }
+                }
+            }
+
+        private char nextCharInLine( ImapRequestLineReader request )
+                throws ProtocolException
+        {
+            char next = request.nextChar();
+            if ( next == '\r' || next == '\n' ) {
+                throw new ProtocolException( "Unexpected end of line." );
+            }
+            return next;
+        }
+
+        private char nextNonSpaceChar( ImapRequestLineReader request )
+                throws ProtocolException
+        {
+            char next = request.nextChar();
+            while ( next == ' ' ) {
+                request.consume();
+                next = request.nextChar();
+            }
+            return next;
+        }
+
+    }
+
+    private static class FetchRequest
+    {
+        boolean flags;
+        boolean uid;
+        boolean internalDate;
+        boolean size;
+        boolean envelope;
+        boolean body;
+        boolean bodyStructure;
+        
+        private boolean setSeen = false;
+        
+        private Set bodyElements = new HashSet();
+        
+        public Collection getBodyElements() {
+            return bodyElements;
+        }
+
+        public boolean isSetSeen() {
+            return setSeen;
+        }
+
+        public void add( BodyFetchElement element, boolean peek )
+        {
+            if (!peek) {
+                setSeen = true;
+            }
+            bodyElements.add(element);
+        }
+    }
+
+    private class BodyFetchElement
+    {
+        private String name;
+        private String sectionIdentifier;
+
+        public BodyFetchElement( String name, String sectionIdentifier)
+        {
+            this.name = name;
+            this.sectionIdentifier = sectionIdentifier;
+        }
+
+        public String getParameters()
+        {
+            return this.sectionIdentifier;
+        }
+
+        public String getResponseName() {
+            return this.name;
+        }
+    }
+
+}
+/*
+6.4.5.  FETCH Command
+
+   Arguments:  message set
+               message data item names
+
+   Responses:  untagged responses: FETCH
+
+   Result:     OK - fetch completed
+               NO - fetch error: can't fetch that data
+               BAD - command unknown or arguments invalid
+
+      The FETCH command retrieves data associated with a message in the
+      mailbox.  The data items to be fetched can be either a single atom
+      or a parenthesized list.
+
+      The currently defined data items that can be fetched are:
+
+      ALL            Macro equivalent to: (FLAGS INTERNALDATE
+                     RFC822.SIZE ENVELOPE)
+
+      BODY           Non-extensible form of BODYSTRUCTURE.
+
+      BODY[<section>]<<partial>>
+                     The text of a particular body section.  The section
+                     specification is a set of zero or more part
+                     specifiers delimited by periods.  A part specifier
+                     is either a part number or one of the following:
+                     HEADER, HEADER.FIELDS, HEADER.FIELDS.NOT, MIME, and
+                     TEXT.  An empty section specification refers to the
+                     entire message, including the header.
+
+                     Every message has at least one part number.
+                     Non-[MIME-IMB] messages, and non-multipart
+                     [MIME-IMB] messages with no encapsulated message,
+                     only have a part 1.
+
+                     Multipart messages are assigned consecutive part
+                     numbers, as they occur in the message.  If a
+                     particular part is of type message or multipart,
+                     its parts MUST be indicated by a period followed by
+                     the part number within that nested multipart part.
+
+                     A part of type MESSAGE/RFC822 also has nested part
+                     numbers, referring to parts of the MESSAGE part's
+                     body.
+
+                     The HEADER, HEADER.FIELDS, HEADER.FIELDS.NOT, and
+                     TEXT part specifiers can be the sole part specifier
+                     or can be prefixed by one or more numeric part
+                     specifiers, provided that the numeric part
+                     specifier refers to a part of type MESSAGE/RFC822.
+                     The MIME part specifier MUST be prefixed by one or
+                     more numeric part specifiers.
+
+                     The HEADER, HEADER.FIELDS, and HEADER.FIELDS.NOT
+                     part specifiers refer to the [RFC-822] header of
+                     the message or of an encapsulated [MIME-IMT]
+                     MESSAGE/RFC822 message.  HEADER.FIELDS and
+                     HEADER.FIELDS.NOT are followed by a list of
+                     field-name (as defined in [RFC-822]) names, and
+                     return a subset of the header.  The subset returned
+                     by HEADER.FIELDS contains only those header fields
+                     with a field-name that matches one of the names in
+                     the list; similarly, the subset returned by
+                     HEADER.FIELDS.NOT contains only the header fields
+                     with a non-matching field-name.  The field-matching
+                     is case-insensitive but otherwise exact.  In all
+                     cases, the delimiting blank line between the header
+                     and the body is always included.
+
+                     The MIME part specifier refers to the [MIME-IMB]
+                     header for this part.
+
+                     The TEXT part specifier refers to the text body of
+                     the message, omitting the [RFC-822] header.
+
+
+                       Here is an example of a complex message
+                       with some of its part specifiers:
+
+                        HEADER     ([RFC-822] header of the message)
+                        TEXT       MULTIPART/MIXED
+                        1          TEXT/PLAIN
+                        2          APPLICATION/OCTET-STREAM
+                        3          MESSAGE/RFC822
+                        3.HEADER   ([RFC-822] header of the message)
+                        3.TEXT     ([RFC-822] text body of the message)
+                        3.1        TEXT/PLAIN
+                        3.2        APPLICATION/OCTET-STREAM
+                        4          MULTIPART/MIXED
+                        4.1        IMAGE/GIF
+                        4.1.MIME   ([MIME-IMB] header for the IMAGE/GIF)
+                        4.2        MESSAGE/RFC822
+                        4.2.HEADER ([RFC-822] header of the message)
+                        4.2.TEXT   ([RFC-822] text body of the message)
+                        4.2.1      TEXT/PLAIN
+                        4.2.2      MULTIPART/ALTERNATIVE
+                        4.2.2.1    TEXT/PLAIN
+                        4.2.2.2    TEXT/RICHTEXT
+
+
+                     It is possible to fetch a substring of the
+                     designated text.  This is done by appending an open
+                     angle bracket ("<"), the octet position of the
+                     first desired octet, a period, the maximum number
+                     of octets desired, and a close angle bracket (">")
+                     to the part specifier.  If the starting octet is
+                     beyond the end of the text, an empty string is
+                     returned.
+
+                     Any partial fetch that attempts to read beyond the
+                     end of the text is truncated as appropriate.  A
+                     partial fetch that starts at octet 0 is returned as
+                     a partial fetch, even if this truncation happened.
+
+                          Note: this means that BODY[]<0.2048> of a
+                          1500-octet message will return BODY[]<0>
+                          with a literal of size 1500, not BODY[].
+
+                          Note: a substring fetch of a
+                          HEADER.FIELDS or HEADER.FIELDS.NOT part
+                          specifier is calculated after subsetting
+                          the header.
+
+
+                     The \Seen flag is implicitly set; if this causes
+                     the flags to change they SHOULD be included as part
+                     of the FETCH responses.
+
+      BODY.PEEK[<section>]<<partial>>
+                     An alternate form of BODY[<section>] that does not
+                     implicitly set the \Seen flag.
+
+      BODYSTRUCTURE  The [MIME-IMB] body structure of the message.  This
+                     is computed by the server by parsing the [MIME-IMB]
+                     header fields in the [RFC-822] header and
+                     [MIME-IMB] headers.
+
+      ENVELOPE       The envelope structure of the message.  This is
+                     computed by the server by parsing the [RFC-822]
+                     header into the component parts, defaulting various
+                     fields as necessary.
+
+      FAST           Macro equivalent to: (FLAGS INTERNALDATE
+                     RFC822.SIZE)
+
+      FLAGS          The flags that are set for this message.
+
+      FULL           Macro equivalent to: (FLAGS INTERNALDATE
+                     RFC822.SIZE ENVELOPE BODY)
+
+      INTERNALDATE   The internal date of the message.
+
+      RFC822         Functionally equivalent to BODY[], differing in the
+                     syntax of the resulting untagged FETCH data (RFC822
+                     is returned).
+
+      RFC822.HEADER  Functionally equivalent to BODY.PEEK[HEADER],
+                     differing in the syntax of the resulting untagged
+                     FETCH data (RFC822.HEADER is returned).
+
+      RFC822.SIZE    The [RFC-822] size of the message.
+
+      RFC822.TEXT    Functionally equivalent to BODY[TEXT], differing in
+                     the syntax of the resulting untagged FETCH data
+                     (RFC822.TEXT is returned).
+
+      UID            The unique identifier for the message.
+
+   Example:    C: A654 FETCH 2:4 (FLAGS BODY[HEADER.FIELDS (DATE FROM)])
+               S: * 2 FETCH ....
+               S: * 3 FETCH ....
+               S: * 4 FETCH ....
+               S: A654 OK FETCH completed
+
+
+7.4.2.  FETCH Response
+
+   Contents:   message data
+
+      The FETCH response returns data about a message to the client.
+      The data are pairs of data item names and their values in
+      parentheses.  This response occurs as the result of a FETCH or
+      STORE command, as well as by unilateral server decision (e.g. flag
+      updates).
+
+      The current data items are:
+
+      BODY           A form of BODYSTRUCTURE without extension data.
+
+      BODY[<section>]<<origin_octet>>
+                     A string expressing the body contents of the
+                     specified section.  The string SHOULD be
+                     interpreted by the client according to the content
+                     transfer encoding, body type, and subtype.
+
+                     If the origin octet is specified, this string is a
+                     substring of the entire body contents, starting at
+                     that origin octet.  This means that BODY[]<0> MAY
+                     be truncated, but BODY[] is NEVER truncated.
+
+                     8-bit textual data is permitted if a [CHARSET]
+                     identifier is part of the body parameter
+                     parenthesized list for this section.  Note that
+                     headers (part specifiers HEADER or MIME, or the
+                     header portion of a MESSAGE/RFC822 part), MUST be
+                     7-bit; 8-bit characters are not permitted in
+                     headers.  Note also that the blank line at the end
+                     of the header is always included in header data.
+
+                     Non-textual data such as binary data MUST be
+                     transfer encoded into a textual form such as BASE64
+                     prior to being sent to the client.  To derive the
+                     original binary data, the client MUST decode the
+                     transfer encoded string.
+
+      BODYSTRUCTURE  A parenthesized list that describes the [MIME-IMB]
+                     body structure of a message.  This is computed by
+                     the server by parsing the [MIME-IMB] header fields,
+                     defaulting various fields as necessary.
+
+                     For example, a simple text message of 48 lines and
+                     2279 octets can have a body structure of: ("TEXT"
+                     "PLAIN" ("CHARSET" "US-ASCII") NIL NIL "7BIT" 2279
+                     48)
+
+                     Multiple parts are indicated by parenthesis
+                     nesting.  Instead of a body type as the first
+                     element of the parenthesized list there is a nested
+                     body.  The second element of the parenthesized list
+                     is the multipart subtype (mixed, digest, parallel,
+                     alternative, etc.).
+
+                     For example, a two part message consisting of a
+                     text and a BASE645-encoded text attachment can have
+                     a body structure of: (("TEXT" "PLAIN" ("CHARSET"
+                     "US-ASCII") NIL NIL "7BIT" 1152 23)("TEXT" "PLAIN"
+                     ("CHARSET" "US-ASCII" "NAME" "cc.diff")
+                     "<96...@cac.washington.edu>"
+                     "Compiler diff" "BASE64" 4554 73) "MIXED"))
+
+                     Extension data follows the multipart subtype.
+                     Extension data is never returned with the BODY
+                     fetch, but can be returned with a BODYSTRUCTURE
+                     fetch.  Extension data, if present, MUST be in the
+                     defined order.
+
+                     The extension data of a multipart body part are in
+                     the following order:
+
+                     body parameter parenthesized list
+                        A parenthesized list of attribute/value pairs
+                        [e.g. ("foo" "bar" "baz" "rag") where "bar" is
+                        the value of "foo" and "rag" is the value of
+                        "baz"] as defined in [MIME-IMB].
+
+                     body disposition
+                        A parenthesized list, consisting of a
+                        disposition type string followed by a
+                        parenthesized list of disposition
+                        attribute/value pairs.  The disposition type and
+                        attribute names will be defined in a future
+                        standards-track revision to [DISPOSITION].
+
+                     body language
+                        A string or parenthesized list giving the body
+                        language value as defined in [LANGUAGE-TAGS].
+
+                     Any following extension data are not yet defined in
+                     this version of the protocol.  Such extension data
+                     can consist of zero or more NILs, strings, numbers,
+                     or potentially nested parenthesized lists of such
+                     data.  Client implementations that do a
+                     BODYSTRUCTURE fetch MUST be prepared to accept such
+                     extension data.  Server implementations MUST NOT
+                     send such extension data until it has been defined
+                     by a revision of this protocol.
+
+                     The basic fields of a non-multipart body part are
+                     in the following order:
+
+                     body type
+                        A string giving the content media type name as
+                        defined in [MIME-IMB].
+
+                     body subtype
+                        A string giving the content subtype name as
+                        defined in [MIME-IMB].
+
+                     body parameter parenthesized list
+                        A parenthesized list of attribute/value pairs
+                        [e.g. ("foo" "bar" "baz" "rag") where "bar" is
+                        the value of "foo" and "rag" is the value of
+                        "baz"] as defined in [MIME-IMB].
+
+                     body id
+                        A string giving the content id as defined in
+                        [MIME-IMB].
+
+                     body description
+                        A string giving the content description as
+                        defined in [MIME-IMB].
+
+                     body encoding
+                        A string giving the content transfer encoding as
+                        defined in [MIME-IMB].
+
+                     body size
+                        A number giving the size of the body in octets.
+                        Note that this size is the size in its transfer
+                        encoding and not the resulting size after any
+                        decoding.
+
+                     A body type of type MESSAGE and subtype RFC822
+                     contains, immediately after the basic fields, the
+                     envelope structure, body structure, and size in
+                     text lines of the encapsulated message.
+
+                     A body type of type TEXT contains, immediately
+                     after the basic fields, the size of the body in
+                     text lines.  Note that this size is the size in its
+                     content transfer encoding and not the resulting
+                     size after any decoding.
+
+                     Extension data follows the basic fields and the
+                     type-specific fields listed above.  Extension data
+                     is never returned with the BODY fetch, but can be
+                     returned with a BODYSTRUCTURE fetch.  Extension
+                     data, if present, MUST be in the defined order.
+
+                     The extension data of a non-multipart body part are
+                     in the following order:
+
+                     body MD5
+                        A string giving the body MD5 value as defined in
+                        [MD5].
+
+                     body disposition
+                        A parenthesized list with the same content and
+                        function as the body disposition for a multipart
+                        body part.
+
+                     body language
+                        A string or parenthesized list giving the body
+                        language value as defined in [LANGUAGE-TAGS].
+
+                     Any following extension data are not yet defined in
+                     this version of the protocol, and would be as
+                     described above under multipart extension data.
+
+      ENVELOPE       A parenthesized list that describes the envelope
+                     structure of a message.  This is computed by the
+                     server by parsing the [RFC-822] header into the
+                     component parts, defaulting various fields as
+                     necessary.
+
+                     The fields of the envelope structure are in the
+                     following order: date, subject, from, sender,
+                     reply-to, to, cc, bcc, in-reply-to, and message-id.
+                     The date, subject, in-reply-to, and message-id
+                     fields are strings.  The from, sender, reply-to,
+                     to, cc, and bcc fields are parenthesized lists of
+                     address structures.
+
+                     An address structure is a parenthesized list that
+                     describes an electronic mail address.  The fields
+                     of an address structure are in the following order:
+                     personal name, [SMTP] at-domain-list (source
+                     route), mailbox name, and host name.
+
+                     [RFC-822] group syntax is indicated by a special
+                     form of address structure in which the host name
+                     field is NIL.  If the mailbox name field is also
+                     NIL, this is an end of group marker (semi-colon in
+                     RFC 822 syntax).  If the mailbox name field is
+                     non-NIL, this is a start of group marker, and the
+                     mailbox name field holds the group name phrase.
+
+                     Any field of an envelope or address structure that
+                     is not applicable is presented as NIL.  Note that
+                     the server MUST default the reply-to and sender
+                     fields from the from field; a client is not
+                     expected to know to do this.
+
+      FLAGS          A parenthesized list of flags that are set for this
+                     message.
+
+      INTERNALDATE   A string representing the internal date of the
+                     message.
+
+      RFC822         Equivalent to BODY[].
+
+      RFC822.HEADER  Equivalent to BODY.PEEK[HEADER].
+
+      RFC822.SIZE    A number expressing the [RFC-822] size of the
+                     message.
+
+      RFC822.TEXT    Equivalent to BODY[TEXT].
+
+      UID            A number expressing the unique identifier of the
+                     message.
+
+
+   Example:    S: * 23 FETCH (FLAGS (\Seen) RFC822.SIZE 44827)
+
+*/

Added: james/server/sandbox/imap-integration/src/java/org/apache/james/imapserver/commands/IdRange.java
URL: http://svn.apache.org/viewvc/james/server/sandbox/imap-integration/src/java/org/apache/james/imapserver/commands/IdRange.java?view=auto&rev=454432
==============================================================================
--- james/server/sandbox/imap-integration/src/java/org/apache/james/imapserver/commands/IdRange.java (added)
+++ james/server/sandbox/imap-integration/src/java/org/apache/james/imapserver/commands/IdRange.java Mon Oct  9 10:15:30 2006
@@ -0,0 +1,50 @@
+/***********************************************************************
+ * Copyright (c) 2000-2004 The Apache Software Foundation.             *
+ * All rights reserved.                                                *
+ * ------------------------------------------------------------------- *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you *
+ * may not use this file except in compliance with the License. You    *
+ * may obtain a copy of the License at:                                *
+ *                                                                     *
+ *     http://www.apache.org/licenses/LICENSE-2.0                      *
+ *                                                                     *
+ * Unless required by applicable law or agreed to in writing, software *
+ * distributed under the License is distributed on an "AS IS" BASIS,   *
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or     *
+ * implied.  See the License for the specific language governing       *
+ * permissions and limitations under the License.                      *
+ ***********************************************************************/
+
+package org.apache.james.imapserver.commands;
+
+/**
+ * Represents a range of UID values.
+ */
+public class IdRange {
+
+    private long _lowVal;
+    private long _highVal;
+
+    public IdRange(long singleVal) {
+        _lowVal = singleVal;
+        _highVal = singleVal;
+    }
+
+    public IdRange(long lowVal, long highVal) {
+        _lowVal = lowVal;
+        _highVal = highVal;
+    }
+
+    public long getLowVal() {
+        return _lowVal;
+    }
+
+    public long getHighVal() {
+        return _highVal;
+    }
+
+    public boolean includes(long uid) {
+        return _lowVal <= uid && uid <= _highVal;
+    }
+
+}

Added: james/server/sandbox/imap-integration/src/java/org/apache/james/imapserver/commands/IdSet.java
URL: http://svn.apache.org/viewvc/james/server/sandbox/imap-integration/src/java/org/apache/james/imapserver/commands/IdSet.java?view=auto&rev=454432
==============================================================================
--- james/server/sandbox/imap-integration/src/java/org/apache/james/imapserver/commands/IdSet.java (added)
+++ james/server/sandbox/imap-integration/src/java/org/apache/james/imapserver/commands/IdSet.java Mon Oct  9 10:15:30 2006
@@ -0,0 +1,29 @@
+/***********************************************************************
+ * Copyright (c) 2000-2004 The Apache Software Foundation.             *
+ * All rights reserved.                                                *
+ * ------------------------------------------------------------------- *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you *
+ * may not use this file except in compliance with the License. You    *
+ * may obtain a copy of the License at:                                *
+ *                                                                     *
+ *     http://www.apache.org/licenses/LICENSE-2.0                      *
+ *                                                                     *
+ * Unless required by applicable law or agreed to in writing, software *
+ * distributed under the License is distributed on an "AS IS" BASIS,   *
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or     *
+ * implied.  See the License for the specific language governing       *
+ * permissions and limitations under the License.                      *
+ ***********************************************************************/
+
+package org.apache.james.imapserver.commands;
+
+/**
+ *
+ * @author  Darrell DeBoer <da...@apache.org>
+ *
+ * @version $Revision: 109034 $
+ */
+public interface IdSet
+{
+    boolean includes( long value );
+}

Added: james/server/sandbox/imap-integration/src/java/org/apache/james/imapserver/commands/ImapCommand.java
URL: http://svn.apache.org/viewvc/james/server/sandbox/imap-integration/src/java/org/apache/james/imapserver/commands/ImapCommand.java?view=auto&rev=454432
==============================================================================
--- james/server/sandbox/imap-integration/src/java/org/apache/james/imapserver/commands/ImapCommand.java (added)
+++ james/server/sandbox/imap-integration/src/java/org/apache/james/imapserver/commands/ImapCommand.java Mon Oct  9 10:15:30 2006
@@ -0,0 +1,58 @@
+/***********************************************************************
+ * Copyright (c) 2000-2004 The Apache Software Foundation.             *
+ * All rights reserved.                                                *
+ * ------------------------------------------------------------------- *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you *
+ * may not use this file except in compliance with the License. You    *
+ * may obtain a copy of the License at:                                *
+ *                                                                     *
+ *     http://www.apache.org/licenses/LICENSE-2.0                      *
+ *                                                                     *
+ * Unless required by applicable law or agreed to in writing, software *
+ * distributed under the License is distributed on an "AS IS" BASIS,   *
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or     *
+ * implied.  See the License for the specific language governing       *
+ * permissions and limitations under the License.                      *
+ ***********************************************************************/
+
+package org.apache.james.imapserver.commands;
+
+import org.apache.james.imapserver.ImapRequestLineReader;
+import org.apache.james.imapserver.ImapResponse;
+import org.apache.james.imapserver.ImapSession;
+import org.apache.james.imapserver.ImapSessionState;
+
+/**
+ * Represents a processor for a particular Imap command. Implementations of this
+ * interface should encpasulate all command specific processing.
+ *
+ * @author  Darrell DeBoer <da...@apache.org>
+ *
+ * @version $Revision: 109034 $
+ */
+public interface ImapCommand
+{
+    /**
+     * @return the name of the command, as specified in rfc2060.
+     */
+    String getName();
+
+    /**
+     * Specifies if this command is valid for the given session state.
+     * @param state The current {@link org.apache.james.imapserver.ImapSessionState state} of the {@link org.apache.james.imapserver.ImapSession}
+     * @return <code>true</code> if the command is valid in this state.
+     */
+    boolean validForState( ImapSessionState state );
+
+    /**
+     * Performs all processing of the current Imap request. Reads command
+     * arguments from the request, performs processing, and writes responses
+     * back to the request object, which are sent to the client.
+     * @param request The current client request
+     * @param response The current server response
+     * @param session The current session
+     */
+    void process( ImapRequestLineReader request,
+                  ImapResponse response,
+                  ImapSession session );
+}

Added: james/server/sandbox/imap-integration/src/java/org/apache/james/imapserver/commands/ImapCommandFactory.java
URL: http://svn.apache.org/viewvc/james/server/sandbox/imap-integration/src/java/org/apache/james/imapserver/commands/ImapCommandFactory.java?view=auto&rev=454432
==============================================================================
--- james/server/sandbox/imap-integration/src/java/org/apache/james/imapserver/commands/ImapCommandFactory.java (added)
+++ james/server/sandbox/imap-integration/src/java/org/apache/james/imapserver/commands/ImapCommandFactory.java Mon Oct  9 10:15:30 2006
@@ -0,0 +1,121 @@
+/***********************************************************************
+ * Copyright (c) 2000-2004 The Apache Software Foundation.             *
+ * All rights reserved.                                                *
+ * ------------------------------------------------------------------- *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you *
+ * may not use this file except in compliance with the License. You    *
+ * may obtain a copy of the License at:                                *
+ *                                                                     *
+ *     http://www.apache.org/licenses/LICENSE-2.0                      *
+ *                                                                     *
+ * Unless required by applicable law or agreed to in writing, software *
+ * distributed under the License is distributed on an "AS IS" BASIS,   *
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or     *
+ * implied.  See the License for the specific language governing       *
+ * permissions and limitations under the License.                      *
+ ***********************************************************************/
+
+package org.apache.james.imapserver.commands;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.avalon.framework.CascadingRuntimeException;
+import org.apache.avalon.framework.logger.AbstractLogEnabled;
+import org.apache.avalon.framework.logger.LogEnabled;
+
+/**
+ * A factory for ImapCommand instances, provided based on the command name.
+ * Command instances are created on demand, when first accessed.
+ *
+ * @author  Darrell DeBoer <da...@apache.org>
+ *
+ * @version $Revision: 109034 $
+ */
+public class ImapCommandFactory
+        extends AbstractLogEnabled
+{
+    private Map _imapCommands;
+
+    public ImapCommandFactory()
+    {
+        _imapCommands = new HashMap();
+
+        // Commands valid in any state
+        // CAPABILITY, NOOP, and LOGOUT
+        _imapCommands.put( CapabilityCommand.NAME, CapabilityCommand.class );
+        _imapCommands.put( NoopCommand.NAME, NoopCommand.class );
+        _imapCommands.put( LogoutCommand.NAME, LogoutCommand.class );
+
+        // Commands valid in NON_AUTHENTICATED state.
+        // AUTHENTICATE and LOGIN
+        _imapCommands.put( AuthenticateCommand.NAME, AuthenticateCommand.class );
+        _imapCommands.put( LoginCommand.NAME, LoginCommand.class );
+
+        // Commands valid in AUTHENTICATED or SELECTED state.
+        // RFC2060: SELECT, EXAMINE, CREATE, DELETE, RENAME, SUBSCRIBE, UNSUBSCRIBE, LIST, LSUB, STATUS, and APPEND
+        _imapCommands.put( SelectCommand.NAME, SelectCommand.class );
+        _imapCommands.put( ExamineCommand.NAME, ExamineCommand.class );
+        _imapCommands.put( CreateCommand.NAME, CreateCommand.class );
+        _imapCommands.put( DeleteCommand.NAME, DeleteCommand.class );
+        _imapCommands.put( RenameCommand.NAME, RenameCommand.class );
+        _imapCommands.put( SubscribeCommand.NAME, SubscribeCommand.class );
+        _imapCommands.put( UnsubscribeCommand.NAME, UnsubscribeCommand.class );
+        _imapCommands.put( ListCommand.NAME, ListCommand.class );
+        _imapCommands.put( LsubCommand.NAME, LsubCommand.class );
+        _imapCommands.put( StatusCommand.NAME, StatusCommand.class );
+        _imapCommands.put( AppendCommand.NAME, AppendCommand.class );
+
+//        // RFC2342 NAMESPACE
+//        _imapCommands.put( "NAMESPACE", NamespaceCommand.class );
+
+        // RFC2086 GETACL, SETACL, DELETEACL, LISTRIGHTS, MYRIGHTS
+//        _imapCommands.put( "GETACL", GetAclCommand.class );
+//        _imapCommands.put( "SETACL", SetAclCommand.class );
+//        _imapCommands.put( "DELETEACL", DeleteAclCommand.class );
+//        _imapCommands.put( "LISTRIGHTS", ListRightsCommand.class );
+//        _imapCommands.put( "MYRIGHTS", MyRightsCommand.class );
+
+
+        // Commands only valid in SELECTED state.
+        // CHECK, CLOSE, EXPUNGE, SEARCH, FETCH, STORE, COPY, and UID
+        _imapCommands.put( CheckCommand.NAME, CheckCommand.class );
+        _imapCommands.put( CloseCommand.NAME, CloseCommand.class );
+        _imapCommands.put( ExpungeCommand.NAME, ExpungeCommand.class );
+        _imapCommands.put( CopyCommand.NAME, CopyCommand.class );
+        _imapCommands.put( SearchCommand.NAME, SearchCommand.class );
+        _imapCommands.put( FetchCommand.NAME, FetchCommand.class );
+        _imapCommands.put( StoreCommand.NAME, StoreCommand.class );
+        _imapCommands.put( UidCommand.NAME, UidCommand.class );
+    }
+
+    public ImapCommand getCommand( String commandName )
+    {
+        Class cmdClass = ( Class ) _imapCommands.get( commandName.toUpperCase() );
+
+        if ( cmdClass == null ) {
+            return null;
+        }
+        else {
+            return createCommand( cmdClass );
+        }
+    }
+
+    private ImapCommand createCommand( Class commandClass )
+    {
+        try {
+            ImapCommand cmd = ( ImapCommand ) commandClass.newInstance();
+            if ( cmd instanceof LogEnabled ) {
+                ( ( LogEnabled ) cmd ).enableLogging( getLogger() );
+            }
+            if ( cmd instanceof UidCommand ) {
+                ( ( UidCommand) cmd ).setCommandFactory( this );
+            }
+            return cmd;
+        }
+        catch ( Exception e ) {
+            throw new CascadingRuntimeException( "Could not create command instance: " + commandClass.getName(), e );
+        }
+    }
+
+}



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