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 ba...@apache.org on 2006/10/10 10:35:02 UTC

svn commit: r454662 [6/15] - in /james/server/sandbox/imap-integration: ./ 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/imapserver/store/ src/...

Modified: 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=diff&rev=454662&r1=454661&r2=454662
==============================================================================
--- james/server/sandbox/imap-integration/src/java/org/apache/james/imapserver/commands/FetchCommand.java (original)
+++ james/server/sandbox/imap-integration/src/java/org/apache/james/imapserver/commands/FetchCommand.java Tue Oct 10 01:34:56 2006
@@ -1,948 +1,948 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you 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)
-
-*/
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you 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)
+
+*/

Propchange: james/server/sandbox/imap-integration/src/java/org/apache/james/imapserver/commands/FetchCommand.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: 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=diff&rev=454662&r1=454661&r2=454662
==============================================================================
--- james/server/sandbox/imap-integration/src/java/org/apache/james/imapserver/commands/IdRange.java (original)
+++ james/server/sandbox/imap-integration/src/java/org/apache/james/imapserver/commands/IdRange.java Tue Oct 10 01:34:56 2006
@@ -1,52 +1,52 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you 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;
-    }
-
-}
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you 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;
+    }
+
+}

Propchange: james/server/sandbox/imap-integration/src/java/org/apache/james/imapserver/commands/IdRange.java
------------------------------------------------------------------------------
    svn:eol-style = native



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