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