james-server-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From rdon...@apache.org
Subject svn commit: r617978 - in /james/server/trunk: core-library/src/main/java/org/apache/james/mailboxmanager/ core-library/src/test/java/org/apache/james/mailboxmanager/ experimental-seda-imap-function/src/main/java/org/apache/james/experimental/imapserver...
Date Sun, 03 Feb 2008 12:24:49 GMT
Author: rdonkin
Date: Sun Feb  3 04:24:45 2008
New Revision: 617978

URL: http://svn.apache.org/viewvc?rev=617978&view=rev
Log:
FETCH ENVELOPE efficient implementation

Added:
    james/server/trunk/experimental-seda-imap-function/src/test/resources/org/apache/james/test/functional/imap/scripts/FetchEnvelope.test
    james/server/trunk/imap-codec-library/src/test/java/org/apache/james/imapserver/codec/encode/imap4rev1/FetchResponseEncoderEnvelopeTest.java
Modified:
    james/server/trunk/core-library/src/main/java/org/apache/james/mailboxmanager/MessageResultUtils.java
    james/server/trunk/core-library/src/test/java/org/apache/james/mailboxmanager/MessageResultUtilsTest.java
    james/server/trunk/experimental-seda-imap-function/src/main/java/org/apache/james/experimental/imapserver/encode/writer/ChannelImapResponseWriter.java
    james/server/trunk/experimental-seda-imap-function/src/test/java/org/apache/james/test/functional/imap/AbstractTestFetch.java
    james/server/trunk/imap-codec-library/src/main/java/org/apache/james/imap/message/response/imap4rev1/FetchResponse.java
    james/server/trunk/imap-codec-library/src/main/java/org/apache/james/imapserver/codec/encode/ImapResponseComposer.java
    james/server/trunk/imap-codec-library/src/main/java/org/apache/james/imapserver/codec/encode/ImapResponseWriter.java
    james/server/trunk/imap-codec-library/src/main/java/org/apache/james/imapserver/codec/encode/base/ImapResponseComposerImpl.java
    james/server/trunk/imap-codec-library/src/main/java/org/apache/james/imapserver/codec/encode/imap4rev1/FetchResponseEncoder.java
    james/server/trunk/imap-codec-library/src/test/java/org/apache/james/imapserver/codec/encode/AbstractTestImapResponseComposer.java
    james/server/trunk/imap-codec-library/src/test/java/org/apache/james/imapserver/codec/encode/base/ByteImapResponseWriter.java
    james/server/trunk/imap-codec-library/src/test/java/org/apache/james/imapserver/codec/encode/imap4rev1/FetchResponseEncoderTest.java
    james/server/trunk/imap-codec-library/src/test/java/org/apache/james/imapserver/codec/encode/imap4rev1/legacy/MockImapResponseWriter.java
    james/server/trunk/imap-mailbox-processor-function/src/main/java/org/apache/james/imapserver/processor/base/SelectedMailboxSessionImpl.java
    james/server/trunk/imap-mailbox-processor-function/src/main/java/org/apache/james/imapserver/processor/imap4rev1/FetchProcessor.java
    james/server/trunk/imap-mailbox-processor-function/src/main/java/org/apache/james/imapserver/processor/imap4rev1/StoreProcessor.java
    james/server/trunk/imapmailbox-library/pom.xml
    james/server/trunk/imapserver-function/src/main/java/org/apache/james/imapserver/ImapConstants.java
    james/server/trunk/imapserver-function/src/main/java/org/apache/james/imapserver/commands/FetchCommand.java

Modified: james/server/trunk/core-library/src/main/java/org/apache/james/mailboxmanager/MessageResultUtils.java
URL: http://svn.apache.org/viewvc/james/server/trunk/core-library/src/main/java/org/apache/james/mailboxmanager/MessageResultUtils.java?rev=617978&r1=617977&r2=617978&view=diff
==============================================================================
--- james/server/trunk/core-library/src/main/java/org/apache/james/mailboxmanager/MessageResultUtils.java (original)
+++ james/server/trunk/core-library/src/main/java/org/apache/james/mailboxmanager/MessageResultUtils.java Sun Feb  3 04:24:45 2008
@@ -73,6 +73,31 @@
     }
     
     /**
+     * Gets a header matching the given name.
+     * The matching is case-insensitive.
+     * @param name name to be matched, not null
+     * @param iterator <code>Iterator</code> of <code>MessageResult.Header</code>'s,
+     * not null
+     * @return <code>MessageResult.Header</code>, 
+     * or null if the header does not exist
+     * @throws MessagingException 
+     */
+    public static MessageResult.Header getMatching(final String name, final Iterator iterator) throws MessagingException {
+        MessageResult.Header result = null;
+        if (name != null) {
+            while(iterator.hasNext()) {
+                MessageResult.Header header = (MessageResult.Header) iterator.next();
+                final String headerName = header.getName();
+                if (name.equalsIgnoreCase(headerName)) {
+                    result = header;
+                    break;
+                }
+            }
+        }
+        return result;
+    }
+
+    /**
      * Gets header lines whose header name fails to match (ignoring case)
      * all of the given names.
      * @param names header names, not null

Modified: james/server/trunk/core-library/src/test/java/org/apache/james/mailboxmanager/MessageResultUtilsTest.java
URL: http://svn.apache.org/viewvc/james/server/trunk/core-library/src/test/java/org/apache/james/mailboxmanager/MessageResultUtilsTest.java?rev=617978&r1=617977&r2=617978&view=diff
==============================================================================
--- james/server/trunk/core-library/src/test/java/org/apache/james/mailboxmanager/MessageResultUtilsTest.java (original)
+++ james/server/trunk/core-library/src/test/java/org/apache/james/mailboxmanager/MessageResultUtilsTest.java Sun Feb  3 04:24:45 2008
@@ -103,4 +103,11 @@
         assertEquals(1, results.size());
         assertEquals(headerTwo, results.get(0));
     }
+
+    public void testGetMatchingSingle() throws Exception {
+        assertEquals(headerOne, MessageResultUtils.getMatching("One", headers.iterator()));
+        assertEquals(headerThree, MessageResultUtils.getMatching("Three", headers.iterator()));
+        assertNull(MessageResultUtils.getMatching("Missing", headers.iterator()));
+    }
+
 }

Modified: james/server/trunk/experimental-seda-imap-function/src/main/java/org/apache/james/experimental/imapserver/encode/writer/ChannelImapResponseWriter.java
URL: http://svn.apache.org/viewvc/james/server/trunk/experimental-seda-imap-function/src/main/java/org/apache/james/experimental/imapserver/encode/writer/ChannelImapResponseWriter.java?rev=617978&r1=617977&r2=617978&view=diff
==============================================================================
--- james/server/trunk/experimental-seda-imap-function/src/main/java/org/apache/james/experimental/imapserver/encode/writer/ChannelImapResponseWriter.java (original)
+++ james/server/trunk/experimental-seda-imap-function/src/main/java/org/apache/james/experimental/imapserver/encode/writer/ChannelImapResponseWriter.java Sun Feb  3 04:24:45 2008
@@ -182,7 +182,7 @@
         skipNextSpace = false;
     }
     
-    private void skipNextSpace() {
+    public void skipNextSpace() {
         skipNextSpace = true;
     }
     

Modified: james/server/trunk/experimental-seda-imap-function/src/test/java/org/apache/james/test/functional/imap/AbstractTestFetch.java
URL: http://svn.apache.org/viewvc/james/server/trunk/experimental-seda-imap-function/src/test/java/org/apache/james/test/functional/imap/AbstractTestFetch.java?rev=617978&r1=617977&r2=617978&view=diff
==============================================================================
--- james/server/trunk/experimental-seda-imap-function/src/test/java/org/apache/james/test/functional/imap/AbstractTestFetch.java (original)
+++ james/server/trunk/experimental-seda-imap-function/src/test/java/org/apache/james/test/functional/imap/AbstractTestFetch.java Sun Feb  3 04:24:45 2008
@@ -27,6 +27,19 @@
         super(system);
     }
 
+    public void testFetchEnvelopeUS() throws Exception {
+        scriptTest("FetchEnvelope", Locale.US);
+    }
+
+
+    public void testFetchEnvelopeIT() throws Exception {
+        scriptTest("FetchEnvelope", Locale.ITALY);
+    }
+
+    public void testFetchEnvelopeKOREA() throws Exception {
+        scriptTest("FetchEnvelope", Locale.KOREA);
+    }
+
     public void testFetchTextUS() throws Exception {
         scriptTest("FetchText", Locale.US);
     }

Added: james/server/trunk/experimental-seda-imap-function/src/test/resources/org/apache/james/test/functional/imap/scripts/FetchEnvelope.test
URL: http://svn.apache.org/viewvc/james/server/trunk/experimental-seda-imap-function/src/test/resources/org/apache/james/test/functional/imap/scripts/FetchEnvelope.test?rev=617978&view=auto
==============================================================================
--- james/server/trunk/experimental-seda-imap-function/src/test/resources/org/apache/james/test/functional/imap/scripts/FetchEnvelope.test (added)
+++ james/server/trunk/experimental-seda-imap-function/src/test/resources/org/apache/james/test/functional/imap/scripts/FetchEnvelope.test Sun Feb  3 04:24:45 2008
@@ -0,0 +1,46 @@
+################################################################
+# 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.                                           #
+################################################################
+
+C: A007 APPEND selected {596+}
+C: Date: Mon, 10 Feb 1994 21:52:25 -0800 (PST)
+C: From: mooch@owatagu.siam.edu
+C: Sender: mooch@owatagu.siam.edu, Boss <boss@owatagu.siam.edu>
+C: Reply-to: Bin <dev.null@owatagu.siam.edu>, Thin Air <air@owatagu.siam.edu>
+C: Subject: Re: Test 05
+C: To: Fred Foobar <foobar@Blurdybloop.COM>, Sue <sue.foo@Blurdybloop.COM>
+C: Cc: Moo <moo@example.COM>, Hugh <hugh@example.COM>
+C: Bcc: Secret <spy@owatagu.siam.ed>, Audit <audit@owatagu.siam.ed>
+C: Message-Id: <12345677890-mooch@owatagu.siam.edu>
+C: In-reply-to: <B27397-0100000@Blurdybloop.COM>
+C: MIME-Version: 1.0
+C: Content-Type: TEXT/PLAIN; CHARSET=US-ASCII
+C:
+C: Works!
+C: 
+S: \* 5 EXISTS
+S: \* 1 RECENT
+S: A007 OK APPEND completed.
+
+C: f1 FETCH 1 (ENVELOPE)
+S: \* 1 FETCH \(ENVELOPE \(\"Mon, 7 Feb 1994 21:52:25 -0800 \(PST\)\" \"Test 01\" \(\(\"Fred Foobar\" NIL \"foobar\" \"Blurdybloop.COM\"\)\) \(\(\"Fred Foobar\" NIL \"foobar\" \"Blurdybloop.COM\"\)\) \(\(\"Fred Foobar\" NIL \"foobar\" \"Blurdybloop.COM\"\)\) \(\(NIL NIL \"mooch\" \"owatagu.siam.edu\"\)\) NIL NIL NIL \"<B27397-0100000@Blurdybloop.COM>\"\)\)
+S: f1 OK FETCH completed.
+
+C: f2 FETCH 5 (ENVELOPE)
+S: \* 5 FETCH \(ENVELOPE \(\"Mon, 10 Feb 1994 21:52:25 -0800 \(PST\)\" \"Re: Test 05\" \(\(NIL NIL \"mooch\" \"owatagu.siam.edu\"\)\) \(\(NIL NIL \"mooch\" \"owatagu.siam.edu\"\)\(\"Boss\" NIL \"boss\" \"owatagu.siam.edu\"\)\) \(\(\"Bin\" NIL \"dev.null\" \"owatagu.siam.edu\"\)\(\"Thin Air\" NIL \"air\" \"owatagu.siam.edu\"\)\) \(\(\"Fred Foobar\" NIL \"foobar\" \"Blurdybloop.COM\"\)\(\"Sue\" NIL \"sue.foo\" \"Blurdybloop.COM\"\)\) \(\(\"Moo\" NIL \"moo\" \"example.COM\"\)\(\"Hugh\" NIL \"hugh\" \"example.COM\"\)\) \(\(\"Secret\" NIL \"spy\" \"owatagu.siam.ed\"\)\(\"Audit\" NIL \"audit\" \"owatagu.siam.ed\"\)\) \"<B27397-0100000@Blurdybloop.COM>\" \"<12345677890-mooch@owatagu.siam.edu>\"\)\)
+S: f2 OK FETCH completed.
\ No newline at end of file

Modified: james/server/trunk/imap-codec-library/src/main/java/org/apache/james/imap/message/response/imap4rev1/FetchResponse.java
URL: http://svn.apache.org/viewvc/james/server/trunk/imap-codec-library/src/main/java/org/apache/james/imap/message/response/imap4rev1/FetchResponse.java?rev=617978&r1=617977&r2=617978&view=diff
==============================================================================
--- james/server/trunk/imap-codec-library/src/main/java/org/apache/james/imap/message/response/imap4rev1/FetchResponse.java (original)
+++ james/server/trunk/imap-codec-library/src/main/java/org/apache/james/imap/message/response/imap4rev1/FetchResponse.java Sun Feb  3 04:24:45 2008
@@ -36,15 +36,18 @@
     private final Integer size;
     private final StringBuffer misc;
     private final List elements;
-
+    private final Envelope envelope;
+    
     public FetchResponse(final int messageNumber, final Flags flags, final Long uid,
-            final Date internalDate, final Integer size, StringBuffer misc, List elements) {
+            final Date internalDate, final Integer size, final Envelope envelope,
+            StringBuffer misc, List elements) {
         super();
         this.messageNumber = messageNumber;
         this.flags = flags;
         this.uid = uid;
         this.internalDate = internalDate;
         this.size = size;
+        this.envelope = envelope;
         this.misc = misc;
         this.elements = elements;
     }
@@ -93,6 +96,15 @@
     public final Integer getSize() {
         return size;
     }
+    
+    /**
+     * Gets the envelope for the fetched message
+     * @return the envelope,
+     * or null if the <code>FETCH</code> did not include <code>ENVELOPE</code>
+     */
+    public final Envelope getEnvelope() {
+        return envelope;
+    }
 
     /**
      * TODO: replace
@@ -136,5 +148,119 @@
          * @throws IOException
          */
         public void writeTo(WritableByteChannel channel) throws IOException;
+    }
+    
+    /**
+     * ENVELOPE content.
+     */
+    public interface Envelope {
+        
+        /**
+         * Gets the envelope <code>date</code>.
+         * This is the value of the RFC822 <code>date</code> header.
+         * @return envelope Date
+         * or null if this attribute is <code>NIL</code>
+         */
+        public String getDate();
+        
+        /**
+         * Gets the envelope <code>subject</code>.
+         * This is the value of the RFC822 <code>subject</code> header.
+         * @return subject,
+         * or null if this attribute is <code>NIL</code>
+         */
+        public String getSubject();
+        
+        /**
+         * Gets the envelope <code>from</code> addresses.
+         * 
+         * @return from addresses, not null
+         */
+        public Address[] getFrom();
+        
+        /**
+         * Gets the envelope <code>sender</code> addresses.
+         * @return <code>sender</code> addresses, not null
+         */
+        public Address[] getSender();
+     
+        /**
+         * Gets the envelope <code>reply-to</code> addresses.
+         * @return <code>reply-to</code>, not null
+         */
+        public Address[] getReplyTo();
+        
+        /**
+         * Gets the envelope <code>to</code> addresses.
+         * @return <code>to</code>,
+         * or null if <code>NIL</code>
+         */
+        public Address[] getTo();
+        
+        /**
+         * Gets the envelope <code>cc</code> addresses.
+         * @return <code>cc</code>, 
+         * or null if <code>NIL</code>
+         */
+        public Address[] getCc();
+        
+        /**
+         * Gets the envelope <code>bcc</code> addresses.
+         * @return <code>bcc</code>,
+         * or null if <code>NIL</code>
+         */
+        public Address[] getBcc();
+        
+        /**
+         * Gets the envelope <code>in-reply-to</code>.
+         * @return <code>in-reply-to</code>
+         * or null if <code>NIL</code>
+         */
+        public String getInReplyTo();
+        
+        /**
+         * Gets the envelope <code>message
+         * @return
+         */
+        public String getMessageId();
+        
+        /**
+         * Values an envelope address.
+         */
+        public interface Address {
+            
+            /** Empty array */
+            public static final Address[] EMPTY = {};
+            
+            /**
+             * Gets the personal name.
+             * @return personal name, 
+             * or null if the personal name is <code>NIL</code>
+             */
+            public String getPersonalName();
+            
+            /**
+             * Gets the SMTP source route.
+             * @return SMTP at-domain-list, 
+             * or null if the list if <code>NIL</code>
+             */
+            public String getAtDomainList();
+            
+            /**
+             * Gets the mailbox name.
+             * @return the mailbox name 
+             * or the group name when {@link #getHostName()}
+             * is null
+             */
+            public String getMailboxName();
+            
+            /**
+             * Gets the host name.
+             * @return the host name,
+             * or null when this address marks the start
+             * or end of a group
+             */
+            public String getHostName();
+        }
     }
 }

Modified: james/server/trunk/imap-codec-library/src/main/java/org/apache/james/imapserver/codec/encode/ImapResponseComposer.java
URL: http://svn.apache.org/viewvc/james/server/trunk/imap-codec-library/src/main/java/org/apache/james/imapserver/codec/encode/ImapResponseComposer.java?rev=617978&r1=617977&r2=617978&view=diff
==============================================================================
--- james/server/trunk/imap-codec-library/src/main/java/org/apache/james/imapserver/codec/encode/ImapResponseComposer.java (original)
+++ james/server/trunk/imap-codec-library/src/main/java/org/apache/james/imapserver/codec/encode/ImapResponseComposer.java Sun Feb  3 04:24:45 2008
@@ -175,6 +175,51 @@
      */
     public abstract void closeFetchResponse() throws IOException;
     
+    /**
+     * Starts a <code>FETCH ENVELOPE</code> production.
+     * @param date envelope date, or null for <code>NIL</code>
+     * @param subject envelope subject, or null for <code>NIL</code>
+     * @throws IOException
+     * @see {@link #endEnvelope(String, String)} must be called
+     */
+    public abstract void startEnvelope(String date, String subject) throws IOException;
+    
+    /**
+     * Starts a list of addresses.
+     * @throws IOException
+     */
+    public abstract void startAddresses() throws IOException;
+    
+    /**
+     * Composes an address.
+     * @param name personal name, or null for <code>NIL</code>
+     * @param domainList route address list, or null for <code>NIL</code>
+     * @param mailbox mailbox name, or null for <code>NIL</code>
+     * @param host host name, or null for <code>NIL</code>
+     * @throws IOException
+     */
+    public abstract void address(String name, String domainList, String mailbox, String host) throws IOException;
+    
+    /**
+     * Ends a list of addresses.
+     * @throws IOException
+     */
+    public abstract void endAddresses() throws IOException;
+    
+    /**
+     * Ends a <code>FETCH ENVELOPE</code> production.
+     * @param inReplyTo envelope in-reply-to, or null for <code>NIL</code>
+     * @param messageId envelope message-id, or null for <code>NIL</code>
+     * @throws IOException
+     */
+    public abstract void endEnvelope(String inReplyTo, String messageId) throws IOException;
+    
+    /**
+     * Composes a <code>NIL</code>.
+     * @throws IOException
+     */
+    public abstract void nil() throws IOException;
+    
     public abstract void commandResponse(ImapCommand command, String message) throws IOException;
 
     /**

Modified: james/server/trunk/imap-codec-library/src/main/java/org/apache/james/imapserver/codec/encode/ImapResponseWriter.java
URL: http://svn.apache.org/viewvc/james/server/trunk/imap-codec-library/src/main/java/org/apache/james/imapserver/codec/encode/ImapResponseWriter.java?rev=617978&r1=617977&r2=617978&view=diff
==============================================================================
--- james/server/trunk/imap-codec-library/src/main/java/org/apache/james/imapserver/codec/encode/ImapResponseWriter.java (original)
+++ james/server/trunk/imap-codec-library/src/main/java/org/apache/james/imapserver/codec/encode/ImapResponseWriter.java Sun Feb  3 04:24:45 2008
@@ -73,6 +73,12 @@
     void openParen() throws IOException;
     
     /**
+     * Do not write a space before the next production.
+     * @throws IOException
+     */
+    public void skipNextSpace() throws IOException;
+    
+    /**
      * Closes a parenthesis - writes a <code>)</code>.
      */
     void closeParen() throws IOException;

Modified: james/server/trunk/imap-codec-library/src/main/java/org/apache/james/imapserver/codec/encode/base/ImapResponseComposerImpl.java
URL: http://svn.apache.org/viewvc/james/server/trunk/imap-codec-library/src/main/java/org/apache/james/imapserver/codec/encode/base/ImapResponseComposerImpl.java?rev=617978&r1=617977&r2=617978&view=diff
==============================================================================
--- james/server/trunk/imap-codec-library/src/main/java/org/apache/james/imapserver/codec/encode/base/ImapResponseComposerImpl.java (original)
+++ james/server/trunk/imap-codec-library/src/main/java/org/apache/james/imapserver/codec/encode/base/ImapResponseComposerImpl.java Sun Feb  3 04:24:45 2008
@@ -41,6 +41,8 @@
 public class ImapResponseComposerImpl extends AbstractLogEnabled implements
         ImapConstants, ImapResponseWriter, ImapResponseComposer {
 
+    public static final String ENVELOPE = "ENVELOPE";
+    
     public static final String FETCH = "FETCH";
 
     public static final String EXPUNGE = "EXPUNGE";
@@ -499,4 +501,50 @@
         writer.literal(literal);
     }
 
+    public void address(String name, String domainList, String mailbox, String host) throws IOException {
+        skipNextSpace();
+        openParen();
+        nillableQuote(name);
+        nillableQuote(domainList);
+        nillableQuote(mailbox);
+        nillableQuote(host);
+        closeParen();
+    }
+
+    public void endAddresses() throws IOException {
+        closeParen();
+    }
+
+    public void endEnvelope(String inReplyTo, String messageId) throws IOException {
+        nillableQuote(inReplyTo);
+        nillableQuote(messageId);
+        closeParen();
+    }
+
+    public void nil() throws IOException {
+        message(NIL);
+    }
+
+    public void startAddresses() throws IOException {
+        openParen();
+    }
+
+    public void startEnvelope(String date, String subject) throws IOException {
+        message(ENVELOPE);
+        openParen();
+        nillableQuote(date);
+        nillableQuote(subject);
+    }
+
+    public void nillableQuote(String message) throws IOException {
+        if (message == null) {
+            nil();
+        } else {
+            quote(message);
+        }
+    }
+
+    public void skipNextSpace() throws IOException {
+        writer.skipNextSpace();
+    }
 }

Modified: james/server/trunk/imap-codec-library/src/main/java/org/apache/james/imapserver/codec/encode/imap4rev1/FetchResponseEncoder.java
URL: http://svn.apache.org/viewvc/james/server/trunk/imap-codec-library/src/main/java/org/apache/james/imapserver/codec/encode/imap4rev1/FetchResponseEncoder.java?rev=617978&r1=617977&r2=617978&view=diff
==============================================================================
--- james/server/trunk/imap-codec-library/src/main/java/org/apache/james/imapserver/codec/encode/imap4rev1/FetchResponseEncoder.java (original)
+++ james/server/trunk/imap-codec-library/src/main/java/org/apache/james/imapserver/codec/encode/imap4rev1/FetchResponseEncoder.java Sun Feb  3 04:24:45 2008
@@ -52,6 +52,7 @@
             encodeFlags(composer, fetchResponse);
             encodeInternalDate(composer, fetchResponse);
             encodeSize(composer, fetchResponse);
+            encodeEnvelope(composer, fetchResponse);
             encode(composer, fetchResponse.getMisc());
             encodeUid(composer, fetchResponse);
             encodeBodyElements(composer, fetchResponse.getElements());
@@ -108,4 +109,50 @@
         }
     }
 
+    private void encodeEnvelope(final ImapResponseComposer composer, final FetchResponse fetchResponse) throws IOException {
+        final FetchResponse.Envelope envelope = fetchResponse.getEnvelope();
+        if (envelope != null) {
+            final String date = envelope.getDate();
+            final String subject = envelope.getSubject();
+            final FetchResponse.Envelope.Address[] from = envelope.getFrom();
+            final FetchResponse.Envelope.Address[] sender = envelope.getSender();
+            final FetchResponse.Envelope.Address[] replyTo = envelope.getReplyTo();
+            final FetchResponse.Envelope.Address[] to = envelope.getTo();
+            final FetchResponse.Envelope.Address[] cc = envelope.getCc();
+            final FetchResponse.Envelope.Address[] bcc = envelope.getBcc();
+            final String inReplyTo = envelope.getInReplyTo();
+            final String messageId = envelope.getMessageId();
+            
+            composer.startEnvelope(date, subject);
+            encodeAddresses(composer, from);
+            encodeAddresses(composer, sender);
+            encodeAddresses(composer, replyTo);
+            encodeAddresses(composer, to);
+            encodeAddresses(composer, cc);
+            encodeAddresses(composer, bcc);
+            composer.endEnvelope(inReplyTo, messageId);
+        }
+    }
+    
+    private void encodeAddresses(final ImapResponseComposer composer, final FetchResponse.Envelope.Address[] addresses) throws IOException {
+        if (addresses == null || addresses.length == 0) {
+            composer.nil();
+        } else {
+            composer.startAddresses();
+            final int length = addresses.length;
+            for (int i=0;i<length;i++) {
+                final FetchResponse.Envelope.Address address = addresses[i];
+                encodeAddress(composer, address);
+            }
+            composer.endAddresses();
+        }
+    }
+
+    private void encodeAddress(final ImapResponseComposer composer, final FetchResponse.Envelope.Address address) throws IOException {
+        final String name = address.getPersonalName();
+        final String domainList = address.getAtDomainList();
+        final String mailbox = address.getMailboxName();
+        final String host = address.getHostName();
+        composer.address(name, domainList, mailbox, host);
+    }
 }

Modified: james/server/trunk/imap-codec-library/src/test/java/org/apache/james/imapserver/codec/encode/AbstractTestImapResponseComposer.java
URL: http://svn.apache.org/viewvc/james/server/trunk/imap-codec-library/src/test/java/org/apache/james/imapserver/codec/encode/AbstractTestImapResponseComposer.java?rev=617978&r1=617977&r2=617978&view=diff
==============================================================================
--- james/server/trunk/imap-codec-library/src/test/java/org/apache/james/imapserver/codec/encode/AbstractTestImapResponseComposer.java (original)
+++ james/server/trunk/imap-codec-library/src/test/java/org/apache/james/imapserver/codec/encode/AbstractTestImapResponseComposer.java Sun Feb  3 04:24:45 2008
@@ -110,6 +110,7 @@
         checkFlagsEncode(" FLAGS (\\Answered \\Deleted \\Draft \\Flagged \\Seen)", flags);
     }
     
+    
     private void checkFlagsEncode(String expected, Flags flags) throws Exception {
         StringBuffer buffer = new StringBuffer();
         byte[] output = encodeFlagsResponse(flags);

Modified: james/server/trunk/imap-codec-library/src/test/java/org/apache/james/imapserver/codec/encode/base/ByteImapResponseWriter.java
URL: http://svn.apache.org/viewvc/james/server/trunk/imap-codec-library/src/test/java/org/apache/james/imapserver/codec/encode/base/ByteImapResponseWriter.java?rev=617978&r1=617977&r2=617978&view=diff
==============================================================================
--- james/server/trunk/imap-codec-library/src/test/java/org/apache/james/imapserver/codec/encode/base/ByteImapResponseWriter.java (original)
+++ james/server/trunk/imap-codec-library/src/test/java/org/apache/james/imapserver/codec/encode/base/ByteImapResponseWriter.java Sun Feb  3 04:24:45 2008
@@ -147,7 +147,7 @@
         skipNextSpace = false;
     }
     
-    private void skipNextSpace() {
+    public void skipNextSpace() {
         skipNextSpace = true;
     }
     

Added: james/server/trunk/imap-codec-library/src/test/java/org/apache/james/imapserver/codec/encode/imap4rev1/FetchResponseEncoderEnvelopeTest.java
URL: http://svn.apache.org/viewvc/james/server/trunk/imap-codec-library/src/test/java/org/apache/james/imapserver/codec/encode/imap4rev1/FetchResponseEncoderEnvelopeTest.java?rev=617978&view=auto
==============================================================================
--- james/server/trunk/imap-codec-library/src/test/java/org/apache/james/imapserver/codec/encode/imap4rev1/FetchResponseEncoderEnvelopeTest.java (added)
+++ james/server/trunk/imap-codec-library/src/test/java/org/apache/james/imapserver/codec/encode/imap4rev1/FetchResponseEncoderEnvelopeTest.java Sun Feb  3 04:24:45 2008
@@ -0,0 +1,383 @@
+/****************************************************************
+ * 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.codec.encode.imap4rev1;
+
+import javax.mail.Flags;
+
+import org.apache.james.api.imap.ImapCommand;
+import org.apache.james.imap.message.response.imap4rev1.FetchResponse;
+import org.apache.james.imap.message.response.imap4rev1.FetchResponse.Envelope.Address;
+import org.apache.james.imapserver.codec.encode.ImapEncoder;
+import org.apache.james.imapserver.codec.encode.ImapResponseComposer;
+import org.jmock.Mock;
+import org.jmock.MockObjectTestCase;
+
+public class FetchResponseEncoderEnvelopeTest extends MockObjectTestCase {
+
+    private static final String ADDRESS_ONE_HOST = "HOST";
+    private static final String ADDRESS_ONE_MAILBOX = "MAILBOX";
+    private static final String ADDRESS_ONE_DOMAIN_LIST = "DOMAIN LIST";
+    private static final String ADDRESS_ONE_NAME = "NAME";
+    private static final String ADDRESS_TWO_HOST = "2HOST";
+    private static final String ADDRESS_TWO_MAILBOX = "2MAILBOX";
+    private static final String ADDRESS_TWO_DOMAIN_LIST = "2DOMAIN LIST";
+    private static final String ADDRESS_TWO_NAME = "2NAME";
+    
+    private static final int MSN = 100;
+    Flags flags;
+    ImapResponseComposer composer;
+    Mock mockComposer;
+    Mock mockNextEncoder;
+    FetchResponseEncoder encoder;
+    Mock mockCommand;
+    FetchResponse message;
+    Mock envelope;
+    Address[] bcc;
+    Address[] cc;
+    String date;
+    Address[] from;
+    String inReplyTo;
+    String messageId;
+    Address[] replyTo;
+    Address[] sender;
+    String subject;                  
+    Address[] to;
+    
+    protected void setUp() throws Exception {
+        super.setUp();
+        envelope = mock(FetchResponse.Envelope.class);
+        
+        bcc = null;
+        cc = null;
+        date = null;
+        from = null;
+        inReplyTo = null;
+        messageId = null;
+        replyTo = null;
+        sender = null;
+        subject = null;                  
+        to = null;
+    
+        
+        message = new FetchResponse(MSN, null, null, null, null, 
+                (FetchResponse.Envelope) envelope.proxy(), null, null);
+        mockComposer = mock(ImapResponseComposer.class);
+        composer = (ImapResponseComposer) mockComposer.proxy();
+        mockNextEncoder = mock(ImapEncoder.class);
+        encoder = new FetchResponseEncoder((ImapEncoder) mockNextEncoder.proxy());
+        mockCommand = mock(ImapCommand.class);
+        flags = new Flags(Flags.Flag.DELETED);
+    }
+    
+    private Address[] mockOneAddress()
+    {
+        Address[] one = {mockAddress(ADDRESS_ONE_NAME, ADDRESS_ONE_DOMAIN_LIST, ADDRESS_ONE_MAILBOX, ADDRESS_ONE_HOST)};
+        return one;
+    }
+    
+    private Address[] mockManyAddresses()
+    {
+        Address[] many = {mockAddress(ADDRESS_ONE_NAME, ADDRESS_ONE_DOMAIN_LIST, ADDRESS_ONE_MAILBOX, ADDRESS_ONE_HOST),
+                mockAddress(ADDRESS_TWO_NAME, ADDRESS_TWO_DOMAIN_LIST, ADDRESS_TWO_MAILBOX, ADDRESS_TWO_HOST)};
+        return many;
+    }
+    
+    private Address mockAddress(final String name, final String domainList, final String mailbox, final String host)
+    {
+        Mock address = mock(Address.class);
+        address.expects(once()).method("getPersonalName").will(returnValue(name));
+        address.expects(once()).method("getAtDomainList").will(returnValue(domainList));
+        address.expects(once()).method("getMailboxName").will(returnValue(mailbox));
+        address.expects(once()).method("getHostName").will(returnValue(host));
+        return (Address) address.proxy();
+    }
+
+    private void envelopExpects() {
+        envelope.expects(once()).method("getBcc").will(returnValue(bcc)).id("bcc");
+        envelope.expects(once()).method("getCc").will(returnValue(cc)).id("cc");
+        envelope.expects(once()).method("getDate").will(returnValue(date)).id("date");
+        envelope.expects(once()).method("getFrom").will(returnValue(from)).id("from");
+        envelope.expects(once()).method("getInReplyTo").will(returnValue(inReplyTo)).id("inreplyto");
+        envelope.expects(once()).method("getMessageId").will(returnValue(messageId)).id("messageid");
+        envelope.expects(once()).method("getReplyTo").will(returnValue(replyTo)).id("replyto");
+        envelope.expects(once()).method("getSender").will(returnValue(sender)).id("sender");
+        envelope.expects(once()).method("getSubject").will(returnValue(subject)).id("subject");
+        envelope.expects(once()).method("getTo").will(returnValue(to)).id("to");
+    }
+
+    protected void tearDown() throws Exception {
+        super.tearDown();
+    }
+
+    public void testShouldNilAllNullProperties() throws Exception {
+        envelopExpects();
+        mockComposer.expects(once()).method("openFetchResponse").with(eq((long)MSN)).id("open");
+        mockComposer.expects(once()).method("startEnvelope").with(NULL, NULL).after("open").id("start");
+        mockComposer.expects(exactly(6)).method("nil").after("start").id("last");
+        mockComposer.expects(once()).method("endEnvelope").with(NULL, NULL).after("last").id("end");
+        mockComposer.expects(once()).method("closeFetchResponse").after("end");
+        
+        encoder.doEncode(message, composer);
+    }
+    
+    public void testShouldComposeDate() throws Exception {
+        date = "a date";
+        envelopExpects();
+        mockComposer.expects(once()).method("openFetchResponse").with(eq((long)MSN)).id("open");
+        mockComposer.expects(once()).method("startEnvelope").with(eq(date), NULL).after("open").id("start");
+        mockComposer.expects(exactly(6)).method("nil").after("start").id("last");
+        mockComposer.expects(once()).method("endEnvelope").with(NULL, NULL).after("last").id("end");
+        mockComposer.expects(once()).method("closeFetchResponse").after("end");
+        
+        encoder.doEncode(message, composer);
+    }
+    
+    public void testShouldComposeSubject() throws Exception {
+        subject = "some subject";
+        envelopExpects();
+        mockComposer.expects(once()).method("openFetchResponse").with(eq((long)MSN)).id("open");
+        mockComposer.expects(once()).method("startEnvelope").with(NULL, eq(subject)).after("open").id("start");
+        mockComposer.expects(exactly(6)).method("nil").after("start").id("last");
+        mockComposer.expects(once()).method("endEnvelope").with(NULL, NULL).after("last").id("end");
+        mockComposer.expects(once()).method("closeFetchResponse").after("end");
+        
+        encoder.doEncode(message, composer);
+    }
+    
+    public void testShouldComposeInReplyTo() throws Exception {
+        inReplyTo = "some reply to";
+        envelopExpects();
+        mockComposer.expects(once()).method("openFetchResponse").with(eq((long)MSN)).id("open");
+        mockComposer.expects(once()).method("startEnvelope").with(NULL, NULL).after("open").id("start");
+        mockComposer.expects(exactly(6)).method("nil").after("start").id("last");
+        mockComposer.expects(once()).method("endEnvelope").with(eq(inReplyTo), NULL).after("last").id("end");
+        mockComposer.expects(once()).method("closeFetchResponse").after("end");
+        
+        encoder.doEncode(message, composer);
+    }
+    
+    public void testShouldComposeMessageId() throws Exception {
+        messageId = "some message id";
+        envelopExpects();
+        mockComposer.expects(once()).method("openFetchResponse").with(eq((long)MSN)).id("open");
+        mockComposer.expects(once()).method("startEnvelope").with(NULL, NULL).after("open").id("start");
+        mockComposer.expects(exactly(6)).method("nil").after("start").id("last");
+        mockComposer.expects(once()).method("endEnvelope").with(NULL, eq(messageId)).after("last").id("end");
+        mockComposer.expects(once()).method("closeFetchResponse").after("end");
+        
+        encoder.doEncode(message, composer);
+    }
+    
+    public void testShouldComposeOneFromAddress() throws Exception {
+        from = mockOneAddress();
+        envelopExpects();
+        mockComposer.expects(once()).method("openFetchResponse").with(eq((long)MSN)).id("open");
+        mockComposer.expects(once()).method("startEnvelope").with(NULL, NULL).after("open").id("start");
+        mockComposer.expects(once()).method("startAddresses").after("start").id("start-from");
+        mockComposer.expects(once()).method("address").with(eq(ADDRESS_ONE_NAME), eq(ADDRESS_ONE_DOMAIN_LIST), eq(ADDRESS_ONE_MAILBOX), eq(ADDRESS_ONE_HOST)).after("start-from").id("address-one");
+        mockComposer.expects(once()).method("endAddresses").after("address-one").id("from");
+        mockComposer.expects(exactly(5)).method("nil").after("from").id("last");
+        mockComposer.expects(once()).method("endEnvelope").with(NULL, eq(messageId)).after("last").id("end");
+        mockComposer.expects(once()).method("closeFetchResponse").after("end");
+        
+        encoder.doEncode(message, composer);
+    }
+    
+    public void testShouldComposeManyFromAddress() throws Exception {
+        from = mockManyAddresses();
+        envelopExpects();
+        mockComposer.expects(once()).method("openFetchResponse").with(eq((long)MSN)).id("open");
+        mockComposer.expects(once()).method("startEnvelope").with(NULL, NULL).after("open").id("start");
+        mockComposer.expects(once()).method("startAddresses").after("start").id("start-from");
+        mockComposer.expects(once()).method("address").with(eq(ADDRESS_ONE_NAME), eq(ADDRESS_ONE_DOMAIN_LIST), eq(ADDRESS_ONE_MAILBOX), eq(ADDRESS_ONE_HOST)).after("start-from").id("address-one");
+        mockComposer.expects(once()).method("address").with(eq(ADDRESS_TWO_NAME), eq(ADDRESS_TWO_DOMAIN_LIST), eq(ADDRESS_TWO_MAILBOX), eq(ADDRESS_TWO_HOST)).after("address-one").id("address-two");
+        mockComposer.expects(once()).method("endAddresses").after("address-two").id("from");
+        mockComposer.expects(exactly(5)).method("nil").after("from").id("last");
+        mockComposer.expects(once()).method("endEnvelope").with(NULL, eq(messageId)).after("last").id("end");
+        mockComposer.expects(once()).method("closeFetchResponse").after("end");
+        
+        encoder.doEncode(message, composer);
+    }
+
+    public void testShouldComposeOneSenderAddress() throws Exception {
+        sender = mockOneAddress();
+        envelopExpects();
+        mockComposer.expects(once()).method("openFetchResponse").with(eq((long)MSN)).id("open");
+        mockComposer.expects(once()).method("startEnvelope").with(NULL, NULL).after("open").id("start");
+        mockComposer.expects(exactly(1)).method("nil").after("start").id("before");
+        mockComposer.expects(once()).method("startAddresses").after("before").id("start-addresses");
+        mockComposer.expects(once()).method("address").with(eq(ADDRESS_ONE_NAME), eq(ADDRESS_ONE_DOMAIN_LIST), eq(ADDRESS_ONE_MAILBOX), eq(ADDRESS_ONE_HOST)).after("start-addresses").id("address-one");
+        mockComposer.expects(once()).method("endAddresses").after("address-one").id("end-addresses");
+        mockComposer.expects(exactly(4)).method("nil").after("end-addresses").id("last");
+        mockComposer.expects(once()).method("endEnvelope").with(NULL, eq(messageId)).after("last").id("end");
+        mockComposer.expects(once()).method("closeFetchResponse").after("end");
+        
+        encoder.doEncode(message, composer);
+    }
+    
+    public void testShouldComposeManySenderAddress() throws Exception {
+        sender = mockManyAddresses();
+        envelopExpects();
+        mockComposer.expects(once()).method("openFetchResponse").with(eq((long)MSN)).id("open");
+        mockComposer.expects(once()).method("startEnvelope").with(NULL, NULL).after("open").id("start");
+        mockComposer.expects(exactly(1)).method("nil").after("start").id("before");
+        mockComposer.expects(once()).method("startAddresses").after("before").id("start-addresses");
+        mockComposer.expects(once()).method("address").with(eq(ADDRESS_ONE_NAME), eq(ADDRESS_ONE_DOMAIN_LIST), eq(ADDRESS_ONE_MAILBOX), eq(ADDRESS_ONE_HOST)).after("start-addresses").id("address-one");
+        mockComposer.expects(once()).method("address").with(eq(ADDRESS_TWO_NAME), eq(ADDRESS_TWO_DOMAIN_LIST), eq(ADDRESS_TWO_MAILBOX), eq(ADDRESS_TWO_HOST)).after("address-one").id("address-two");
+        mockComposer.expects(once()).method("endAddresses").after("address-two").id("end-addresses");
+        mockComposer.expects(exactly(4)).method("nil").after("end-addresses").id("last");
+        mockComposer.expects(once()).method("endEnvelope").with(NULL, eq(messageId)).after("last").id("end");
+        mockComposer.expects(once()).method("closeFetchResponse").after("end");
+        
+        encoder.doEncode(message, composer);
+    }
+    
+
+    public void testShouldComposeOneReplyToAddress() throws Exception {
+        replyTo = mockOneAddress();
+        envelopExpects();
+        mockComposer.expects(once()).method("openFetchResponse").with(eq((long)MSN)).id("open");
+        mockComposer.expects(once()).method("startEnvelope").with(NULL, NULL).after("open").id("start");
+        mockComposer.expects(exactly(2)).method("nil").after("start").id("before");
+        mockComposer.expects(once()).method("startAddresses").after("before").id("start-addresses");
+        mockComposer.expects(once()).method("address").with(eq(ADDRESS_ONE_NAME), eq(ADDRESS_ONE_DOMAIN_LIST), eq(ADDRESS_ONE_MAILBOX), eq(ADDRESS_ONE_HOST)).after("start-addresses").id("address-one");
+        mockComposer.expects(once()).method("endAddresses").after("address-one").id("end-addresses");
+        mockComposer.expects(exactly(3)).method("nil").after("end-addresses").id("last");
+        mockComposer.expects(once()).method("endEnvelope").with(NULL, eq(messageId)).after("last").id("end");
+        mockComposer.expects(once()).method("closeFetchResponse").after("end");
+        
+        encoder.doEncode(message, composer);
+    }
+    
+    public void testShouldComposeManyReplyToAddress() throws Exception {
+        replyTo = mockManyAddresses();
+        envelopExpects();
+        mockComposer.expects(once()).method("openFetchResponse").with(eq((long)MSN)).id("open");
+        mockComposer.expects(once()).method("startEnvelope").with(NULL, NULL).after("open").id("start");
+        mockComposer.expects(exactly(2)).method("nil").after("start").id("before");
+        mockComposer.expects(once()).method("startAddresses").after("before").id("start-addresses");
+        mockComposer.expects(once()).method("address").with(eq(ADDRESS_ONE_NAME), eq(ADDRESS_ONE_DOMAIN_LIST), eq(ADDRESS_ONE_MAILBOX), eq(ADDRESS_ONE_HOST)).after("start-addresses").id("address-one");
+        mockComposer.expects(once()).method("address").with(eq(ADDRESS_TWO_NAME), eq(ADDRESS_TWO_DOMAIN_LIST), eq(ADDRESS_TWO_MAILBOX), eq(ADDRESS_TWO_HOST)).after("address-one").id("address-two");
+        mockComposer.expects(once()).method("endAddresses").after("address-two").id("end-addresses");
+        mockComposer.expects(exactly(3)).method("nil").after("end-addresses").id("last");
+        mockComposer.expects(once()).method("endEnvelope").with(NULL, eq(messageId)).after("last").id("end");
+        mockComposer.expects(once()).method("closeFetchResponse").after("end");
+        
+        encoder.doEncode(message, composer);
+    }
+
+    public void testShouldComposeOneToAddress() throws Exception {
+        to = mockOneAddress();
+        envelopExpects();
+        mockComposer.expects(once()).method("openFetchResponse").with(eq((long)MSN)).id("open");
+        mockComposer.expects(once()).method("startEnvelope").with(NULL, NULL).after("open").id("start");
+        mockComposer.expects(exactly(3)).method("nil").after("start").id("before");
+        mockComposer.expects(once()).method("startAddresses").after("before").id("start-addresses");
+        mockComposer.expects(once()).method("address").with(eq(ADDRESS_ONE_NAME), eq(ADDRESS_ONE_DOMAIN_LIST), eq(ADDRESS_ONE_MAILBOX), eq(ADDRESS_ONE_HOST)).after("start-addresses").id("address-one");
+        mockComposer.expects(once()).method("endAddresses").after("address-one").id("end-addresses");
+        mockComposer.expects(exactly(2)).method("nil").after("end-addresses").id("last");
+        mockComposer.expects(once()).method("endEnvelope").with(NULL, eq(messageId)).after("last").id("end");
+        mockComposer.expects(once()).method("closeFetchResponse").after("end");
+        
+        encoder.doEncode(message, composer);
+    }
+    
+    public void testShouldComposeManyToAddress() throws Exception {
+        to = mockManyAddresses();
+        envelopExpects();
+        mockComposer.expects(once()).method("openFetchResponse").with(eq((long)MSN)).id("open");
+        mockComposer.expects(once()).method("startEnvelope").with(NULL, NULL).after("open").id("start");
+        mockComposer.expects(exactly(3)).method("nil").after("start").id("before");
+        mockComposer.expects(once()).method("startAddresses").after("before").id("start-addresses");
+        mockComposer.expects(once()).method("address").with(eq(ADDRESS_ONE_NAME), eq(ADDRESS_ONE_DOMAIN_LIST), eq(ADDRESS_ONE_MAILBOX), eq(ADDRESS_ONE_HOST)).after("start-addresses").id("address-one");
+        mockComposer.expects(once()).method("address").with(eq(ADDRESS_TWO_NAME), eq(ADDRESS_TWO_DOMAIN_LIST), eq(ADDRESS_TWO_MAILBOX), eq(ADDRESS_TWO_HOST)).after("address-one").id("address-two");
+        mockComposer.expects(once()).method("endAddresses").after("address-two").id("end-addresses");
+        mockComposer.expects(exactly(2)).method("nil").after("end-addresses").id("last");
+        mockComposer.expects(once()).method("endEnvelope").with(NULL, eq(messageId)).after("last").id("end");
+        mockComposer.expects(once()).method("closeFetchResponse").after("end");
+        
+        encoder.doEncode(message, composer);
+    }
+
+
+    public void testShouldComposeOneCcAddress() throws Exception {
+        cc = mockOneAddress();
+        envelopExpects();
+        mockComposer.expects(once()).method("openFetchResponse").with(eq((long)MSN)).id("open");
+        mockComposer.expects(once()).method("startEnvelope").with(NULL, NULL).after("open").id("start");
+        mockComposer.expects(exactly(4)).method("nil").after("start").id("before");
+        mockComposer.expects(once()).method("startAddresses").after("before").id("start-addresses");
+        mockComposer.expects(once()).method("address").with(eq(ADDRESS_ONE_NAME), eq(ADDRESS_ONE_DOMAIN_LIST), eq(ADDRESS_ONE_MAILBOX), eq(ADDRESS_ONE_HOST)).after("start-addresses").id("address-one");
+        mockComposer.expects(once()).method("endAddresses").after("address-one").id("end-addresses");
+        mockComposer.expects(exactly(1)).method("nil").after("end-addresses").id("last");
+        mockComposer.expects(once()).method("endEnvelope").with(NULL, eq(messageId)).after("last").id("end");
+        mockComposer.expects(once()).method("closeFetchResponse").after("end");
+        
+        encoder.doEncode(message, composer);
+    }
+    
+    public void testShouldComposeManyCcAddress() throws Exception {
+        cc = mockManyAddresses();
+        envelopExpects();
+        mockComposer.expects(once()).method("openFetchResponse").with(eq((long)MSN)).id("open");
+        mockComposer.expects(once()).method("startEnvelope").with(NULL, NULL).after("open").id("start");
+        mockComposer.expects(exactly(4)).method("nil").after("start").id("before");
+        mockComposer.expects(once()).method("startAddresses").after("before").id("start-addresses");
+        mockComposer.expects(once()).method("address").with(eq(ADDRESS_ONE_NAME), eq(ADDRESS_ONE_DOMAIN_LIST), eq(ADDRESS_ONE_MAILBOX), eq(ADDRESS_ONE_HOST)).after("start-addresses").id("address-one");
+        mockComposer.expects(once()).method("address").with(eq(ADDRESS_TWO_NAME), eq(ADDRESS_TWO_DOMAIN_LIST), eq(ADDRESS_TWO_MAILBOX), eq(ADDRESS_TWO_HOST)).after("address-one").id("address-two");
+        mockComposer.expects(once()).method("endAddresses").after("address-two").id("end-addresses");
+        mockComposer.expects(exactly(1)).method("nil").after("end-addresses").id("last");
+        mockComposer.expects(once()).method("endEnvelope").with(NULL, eq(messageId)).after("last").id("end");
+        mockComposer.expects(once()).method("closeFetchResponse").after("end");
+        
+        encoder.doEncode(message, composer);
+    }
+    
+    public void testShouldComposeOneBccAddress() throws Exception {
+        bcc = mockOneAddress();
+        envelopExpects();
+        mockComposer.expects(once()).method("openFetchResponse").with(eq((long)MSN)).id("open");
+        mockComposer.expects(once()).method("startEnvelope").with(NULL, NULL).after("open").id("start");
+        mockComposer.expects(exactly(5)).method("nil").after("start").id("before");
+        mockComposer.expects(once()).method("startAddresses").after("before").id("start-addresses");
+        mockComposer.expects(once()).method("address").with(eq(ADDRESS_ONE_NAME), eq(ADDRESS_ONE_DOMAIN_LIST), eq(ADDRESS_ONE_MAILBOX), eq(ADDRESS_ONE_HOST)).after("start-addresses").id("address-one");
+        mockComposer.expects(once()).method("endAddresses").after("address-one").id("end-addresses");
+        mockComposer.expects(once()).method("endEnvelope").with(NULL, eq(messageId)).after("end-addresses").id("end");
+        mockComposer.expects(once()).method("closeFetchResponse").after("end");
+        
+        encoder.doEncode(message, composer);
+    }
+    
+    public void testShouldComposeManyBccAddress() throws Exception {
+        bcc = mockManyAddresses();
+        envelopExpects();
+        mockComposer.expects(once()).method("openFetchResponse").with(eq((long)MSN)).id("open");
+        mockComposer.expects(once()).method("startEnvelope").with(NULL, NULL).after("open").id("start");
+        mockComposer.expects(exactly(5)).method("nil").after("start").id("before");
+        mockComposer.expects(once()).method("startAddresses").after("before").id("start-addresses");
+        mockComposer.expects(once()).method("address").with(eq(ADDRESS_ONE_NAME), eq(ADDRESS_ONE_DOMAIN_LIST), eq(ADDRESS_ONE_MAILBOX), eq(ADDRESS_ONE_HOST)).after("start-addresses").id("address-one");
+        mockComposer.expects(once()).method("address").with(eq(ADDRESS_TWO_NAME), eq(ADDRESS_TWO_DOMAIN_LIST), eq(ADDRESS_TWO_MAILBOX), eq(ADDRESS_TWO_HOST)).after("address-one").id("address-two");
+        mockComposer.expects(once()).method("endAddresses").after("address-two").id("end-addresses");
+        mockComposer.expects(once()).method("endEnvelope").with(NULL, eq(messageId)).after("end-addresses").id("end");
+        mockComposer.expects(once()).method("closeFetchResponse").after("end");
+        
+        encoder.doEncode(message, composer);
+    }
+}

Modified: james/server/trunk/imap-codec-library/src/test/java/org/apache/james/imapserver/codec/encode/imap4rev1/FetchResponseEncoderTest.java
URL: http://svn.apache.org/viewvc/james/server/trunk/imap-codec-library/src/test/java/org/apache/james/imapserver/codec/encode/imap4rev1/FetchResponseEncoderTest.java?rev=617978&r1=617977&r2=617978&view=diff
==============================================================================
--- james/server/trunk/imap-codec-library/src/test/java/org/apache/james/imapserver/codec/encode/imap4rev1/FetchResponseEncoderTest.java (original)
+++ james/server/trunk/imap-codec-library/src/test/java/org/apache/james/imapserver/codec/encode/imap4rev1/FetchResponseEncoderTest.java Sun Feb  3 04:24:45 2008
@@ -57,11 +57,11 @@
     }
     
     public void testShouldAcceptFetchResponse() throws Exception {
-        assertTrue(encoder.isAcceptable(new FetchResponse(11, null, null, null, null, null, null)));
+        assertTrue(encoder.isAcceptable(new FetchResponse(11, null, null, null, null, null, null, null)));
     }
     
     public void testShouldEncodeFlagsResponse() throws Exception {
-        FetchResponse message = new FetchResponse(100, flags, null, null, null, null, null);
+        FetchResponse message = new FetchResponse(100, flags, null, null, null, null, null, null);
         mockComposer.expects(once()).method("openFetchResponse").with(eq(100L));
         mockComposer.expects(once()).method("flags").with(eq(flags));
         mockComposer.expects(once()).method("closeFetchResponse");
@@ -69,7 +69,7 @@
     }
     
     public void testShouldEncodeUidResponse() throws Exception {
-        FetchResponse message = new FetchResponse(100, null, new Long(72), null, null, null, null);
+        FetchResponse message = new FetchResponse(100, null, new Long(72), null, null, null, null, null);
         mockComposer.expects(once()).method("openFetchResponse").with(eq(100L));
         mockComposer.expects(once()).method("message").with(eq("UID"));
         mockComposer.expects(once()).method("message").with(eq(72L));
@@ -78,7 +78,7 @@
     }
     
     public void testShouldEncodeAllResponse() throws Exception {
-        FetchResponse message = new FetchResponse(100, flags, new Long(72), null, null, null, null);
+        FetchResponse message = new FetchResponse(100, flags, new Long(72), null, null, null, null, null);
         mockComposer.expects(once()).method("openFetchResponse").with(eq(100L));
         mockComposer.expects(once()).method("flags").with(eq(flags));
         mockComposer.expects(once()).method("message").with(eq("UID"));

Modified: james/server/trunk/imap-codec-library/src/test/java/org/apache/james/imapserver/codec/encode/imap4rev1/legacy/MockImapResponseWriter.java
URL: http://svn.apache.org/viewvc/james/server/trunk/imap-codec-library/src/test/java/org/apache/james/imapserver/codec/encode/imap4rev1/legacy/MockImapResponseWriter.java?rev=617978&r1=617977&r2=617978&view=diff
==============================================================================
--- james/server/trunk/imap-codec-library/src/test/java/org/apache/james/imapserver/codec/encode/imap4rev1/legacy/MockImapResponseWriter.java (original)
+++ james/server/trunk/imap-codec-library/src/test/java/org/apache/james/imapserver/codec/encode/imap4rev1/legacy/MockImapResponseWriter.java Sun Feb  3 04:24:45 2008
@@ -353,5 +353,8 @@
         
         
     }
+
+    public void skipNextSpace() throws IOException {
+    }
     
 }

Modified: james/server/trunk/imap-mailbox-processor-function/src/main/java/org/apache/james/imapserver/processor/base/SelectedMailboxSessionImpl.java
URL: http://svn.apache.org/viewvc/james/server/trunk/imap-mailbox-processor-function/src/main/java/org/apache/james/imapserver/processor/base/SelectedMailboxSessionImpl.java?rev=617978&r1=617977&r2=617978&view=diff
==============================================================================
--- james/server/trunk/imap-mailbox-processor-function/src/main/java/org/apache/james/imapserver/processor/base/SelectedMailboxSessionImpl.java (original)
+++ james/server/trunk/imap-mailbox-processor-function/src/main/java/org/apache/james/imapserver/processor/base/SelectedMailboxSessionImpl.java Sun Feb  3 04:24:45 2008
@@ -143,7 +143,7 @@
                     } else {
                         uidOut = null;
                     }
-                    FetchResponse response = new FetchResponse(msn, flags, uidOut, null, null, null, null);
+                    FetchResponse response = new FetchResponse(msn, flags, uidOut, null, null, null, null, null);
                     responses.add(response);
                 }
             }

Modified: james/server/trunk/imap-mailbox-processor-function/src/main/java/org/apache/james/imapserver/processor/imap4rev1/FetchProcessor.java
URL: http://svn.apache.org/viewvc/james/server/trunk/imap-mailbox-processor-function/src/main/java/org/apache/james/imapserver/processor/imap4rev1/FetchProcessor.java?rev=617978&r1=617977&r2=617978&view=diff
==============================================================================
--- james/server/trunk/imap-mailbox-processor-function/src/main/java/org/apache/james/imapserver/processor/imap4rev1/FetchProcessor.java (original)
+++ james/server/trunk/imap-mailbox-processor-function/src/main/java/org/apache/james/imapserver/processor/imap4rev1/FetchProcessor.java Sun Feb  3 04:24:45 2008
@@ -61,6 +61,15 @@
 import org.apache.james.mailboxmanager.MessageResult.Content;
 import org.apache.james.mailboxmanager.impl.GeneralMessageSetImpl;
 import org.apache.james.mailboxmanager.mailbox.ImapMailbox;
+import org.apache.james.mime4j.field.address.Address;
+import org.apache.james.mime4j.field.address.AddressList;
+import org.apache.james.mime4j.field.address.DomainList;
+import org.apache.james.mime4j.field.address.Group;
+import org.apache.james.mime4j.field.address.Mailbox;
+import org.apache.james.mime4j.field.address.MailboxList;
+import org.apache.james.mime4j.field.address.NamedMailbox;
+import org.apache.james.mime4j.field.address.parser.ParseException;
+import org.apache.mailet.RFC2822Headers;
 
 public class FetchProcessor extends AbstractImapRequestProcessor {
 
@@ -131,9 +140,11 @@
         if (fetch.isSize()) {
             result |= MessageResult.SIZE;
         }
-        if (fetch.isEnvelope() || fetch.isBody() || fetch.isBodyStructure()) {
+        if (fetch.isEnvelope()) {
+            result |= MessageResult.HEADERS;
+        }
+        if (fetch.isBody() || fetch.isBodyStructure()) {
             // TODO: structure
-            // result |= MessageResult.ENVELOPE;
             result |= MessageResult.MIME_MESSAGE;
         }
 
@@ -174,6 +185,7 @@
         private Integer size;
         private StringBuffer misc;
         private List elements;
+        private FetchResponse.Envelope envelope;
         
         public FetchResponseBuilder(final Logger logger) {
             super();
@@ -200,7 +212,7 @@
         
         public FetchResponse build() {
             final FetchResponse result = new FetchResponse(msn, flags, uid, internalDate, 
-                    size, misc, elements);
+                    size, envelope, misc, elements);
             return result;
         }
 
@@ -245,19 +257,17 @@
                     setSize(result.getSize());
                 }
 
+                if (fetch.isEnvelope()) {
+                    this.envelope = buildEnvelope(result);
+                }
+                
                 // Only create when needed
-                if (fetch.isEnvelope() || fetch.isBody() || fetch.isBodyStructure()) {
+                if (fetch.isBody() || fetch.isBodyStructure()) {
                     misc = new StringBuffer();
                     // TODO: replace SimpleMessageAttributes
                     final SimpleMessageAttributes attrs = new SimpleMessageAttributes(
                             result.getMimeMessage(), logger);
 
-                    // ENVELOPE response
-                    if (fetch.isEnvelope()) {
-                        misc.append(" ENVELOPE ");
-                        misc.append(attrs.getEnvelope());
-                    }
-
                     // BODY response
                     if (fetch.isBody()) {
                         misc.append(" BODY ");
@@ -293,9 +303,153 @@
                 throw new MailboxException(mme);
             } catch (MessagingException me) {
                 throw new MailboxException(me);
+            } catch (ParseException e) {
+                logger.debug("Cannot parse header address", e);
+                throw new MailboxException("Cannot parse address");
+            }
+        }
+
+        private FetchResponse.Envelope buildEnvelope(final MessageResult messageResult) throws MessagingException, ParseException {
+            final String date = headerValue(messageResult, RFC2822Headers.DATE);
+            final String subject = headerValue(messageResult, RFC2822Headers.SUBJECT);
+            final FetchResponse.Envelope.Address[] fromAddresses 
+                        = buildAddresses(messageResult, RFC2822Headers.FROM);
+            final FetchResponse.Envelope.Address[] senderAddresses
+                        = buildAddresses(messageResult, RFC2822Headers.SENDER, fromAddresses);
+            final FetchResponse.Envelope.Address[] replyToAddresses
+                        = buildAddresses(messageResult, RFC2822Headers.REPLY_TO, fromAddresses);
+            final FetchResponse.Envelope.Address[] toAddresses 
+                        = buildAddresses(messageResult, RFC2822Headers.TO);
+            final FetchResponse.Envelope.Address[] ccAddresses 
+                        = buildAddresses(messageResult, RFC2822Headers.CC);
+            final FetchResponse.Envelope.Address[] bccAddresses 
+                        = buildAddresses(messageResult, RFC2822Headers.BCC);
+            final String inReplyTo = headerValue(messageResult, RFC2822Headers.IN_REPLY_TO);
+            final String messageId = headerValue(messageResult, RFC2822Headers.MESSAGE_ID);
+            final FetchResponse.Envelope envelope = new EnvelopeImpl(date, subject, fromAddresses, senderAddresses, 
+                    replyToAddresses, toAddresses, ccAddresses, bccAddresses, inReplyTo, messageId);
+            return envelope;
+        }
+        
+        private String headerValue(final MessageResult message, final String headerName) throws MessagingException, MailboxManagerException {
+            final MessageResult.Header header 
+                = MessageResultUtils.getMatching(headerName, message.iterateHeaders());
+            final String result;
+            if (header == null) {
+                result = null;
+            } else {
+                final String value = header.getValue();
+                if (value == null || "".equals(value)) {
+                    result = null;
+                } else {
+                    result = value;
+                }
             }
+            return result;
         }
 
+        private FetchResponse.Envelope.Address[] buildAddresses(final MessageResult message, final String headerName, 
+                final FetchResponse.Envelope.Address[] defaults) throws ParseException, MessagingException {
+            final FetchResponse.Envelope.Address[] results;
+            final FetchResponse.Envelope.Address[] addresses = buildAddresses(message, headerName);
+            if (addresses == null) {
+                results = defaults;
+            } else {
+                results = addresses;
+            }
+            return results;
+        }
+        
+        private FetchResponse.Envelope.Address[] buildAddresses(final MessageResult message, final String headerName) 
+                    throws ParseException, MessagingException {
+            final MessageResult.Header header = MessageResultUtils.getMatching(headerName, message.iterateHeaders());
+            final FetchResponse.Envelope.Address[] results;
+            if (header == null) {
+                results = null;
+            } else {
+                final String value = header.getValue();
+                if ("".equals(value.trim())) {
+                    results  = null;
+                } else {
+                    final AddressList addressList = AddressList.parse(value);
+                    final int size = addressList.size();
+                    final List addresses = new ArrayList(size);
+                    for (int i=0;i<size;i++) {
+                        final Address address = addressList.get(i);
+                        if (address instanceof Group) {
+                            final Group group = (Group) address;
+                            addAddresses(group, addresses);
+
+                        } else if (address instanceof Mailbox) {
+                            final Mailbox mailbox = (Mailbox) address;
+                            final FetchResponse.Envelope.Address mailboxAddress = buildMailboxAddress(mailbox);
+                            addresses.add(mailboxAddress);
+
+                        } else {
+                            logger.warn("Unknown address type");
+                        }
+                    }
+
+                    results = (FetchResponse.Envelope.Address[]) 
+                    addresses.toArray(FetchResponse.Envelope.Address.EMPTY);
+                }
+            }
+            return results;
+        }
+        
+        private FetchResponse.Envelope.Address buildMailboxAddress(final Mailbox mailbox) {
+            final String name;
+            if (mailbox instanceof NamedMailbox) {
+                final NamedMailbox namedMailbox = (NamedMailbox) mailbox;
+                name = namedMailbox.getName();
+            } else {
+                name = null;
+            }
+            final String domain = mailbox.getDomain();
+            final DomainList route = mailbox.getRoute();
+            final String atDomainList;
+            if (route == null) {
+                atDomainList = null;
+            } else {
+                atDomainList = route.toRouteString();
+            }
+            final String localPart = mailbox.getLocalPart();
+            final FetchResponse.Envelope.Address result = buildMailboxAddress(name, atDomainList, localPart, domain);
+            return result;
+        }
+        
+        private void addAddresses(final Group group, final List addresses) {
+            final String groupName = group.getName();
+            final FetchResponse.Envelope.Address start = startGroup(groupName);
+            addresses.add(start);
+            final MailboxList mailboxList = group.getMailboxes();
+            for (int i=0;i<mailboxList.size();i++) {
+                final Mailbox mailbox = mailboxList.get(i);
+                final FetchResponse.Envelope.Address address = buildMailboxAddress(mailbox);
+                addresses.add(address);
+            }
+            final FetchResponse.Envelope.Address end = endGroup();
+        }
+        
+        private FetchResponse.Envelope.Address startGroup(String groupName) {
+            final FetchResponse.Envelope.Address result 
+            = new AddressImpl(null, null, groupName, null);
+            return result;
+        }
+        
+        private FetchResponse.Envelope.Address endGroup() {
+            final FetchResponse.Envelope.Address result 
+            = new AddressImpl(null, null, null, null);
+            return result;
+        }
+                    
+        private FetchResponse.Envelope.Address buildMailboxAddress(String name, String atDomainList, 
+                                                            String mailbox, String domain) {
+            final FetchResponse.Envelope.Address result 
+                = new AddressImpl(atDomainList, domain, mailbox, name);
+            return result;
+        }
+        
         private void setSize(int size) {
             this.size = new Integer(size);
         }
@@ -315,22 +469,26 @@
             if (sectionSpecifier.length() == 0) {
                 final MessageResult.Content fullMessage = messageResult.getFullMessage();
                 result = new ContentBodyElement(name, fullMessage);
+                
             } else if (sectionSpecifier.equalsIgnoreCase("HEADER")) {
                 final Iterator headers = messageResult.iterateHeaders();
                 List lines = MessageResultUtils.getAll(headers);
                 result = new HeaderBodyElement(name, lines);
+                
             } else if (sectionSpecifier.startsWith("HEADER.FIELDS.NOT ")) {
                 String[] excludeNames = extractHeaderList(sectionSpecifier,
                         "HEADER.FIELDS.NOT ".length());
                 final Iterator headers = messageResult.iterateHeaders();
                 List lines = MessageResultUtils.getNotMatching(excludeNames, headers);
                 result = new HeaderBodyElement(name, lines);
+                
             } else if (sectionSpecifier.startsWith("HEADER.FIELDS ")) {
                 String[] includeNames = extractHeaderList(sectionSpecifier,
                         "HEADER.FIELDS ".length());
                 final Iterator headers = messageResult.iterateHeaders();
                 List lines = MessageResultUtils.getMatching(includeNames, headers);
                 result = new HeaderBodyElement(name, lines);
+                
             } else if (sectionSpecifier.equalsIgnoreCase("MIME")) {
                 // TODO implement
                 throw new MailboxManagerException("MIME not yet implemented.");
@@ -388,6 +546,109 @@
             return (String[]) strings.toArray(new String[0]);
         }
     }
+    
+    private static final class EnvelopeImpl implements FetchResponse.Envelope {
+
+        private final Address[] bcc;
+        private final Address[] cc;
+        private final String date;
+        private final Address[] from;
+        private final String inReplyTo;
+        private final String messageId;
+        private final Address[] replyTo;
+        private final Address[] sender;
+        private final String subject;                  
+        private final Address[] to;
+        
+        public EnvelopeImpl(final String date, final String subject, final Address[] from, 
+                final Address[] sender, final Address[] replyTo, final Address[] to, 
+                final Address[] cc, final Address[] bcc, final String inReplyTo, 
+                final String messageId) {
+            super();
+            this.bcc = bcc;
+            this.cc = cc;
+            this.date = date;
+            this.from = from;
+            this.inReplyTo = inReplyTo;
+            this.messageId = messageId;
+            this.replyTo = replyTo;
+            this.sender = sender;
+            this.subject = subject;
+            this.to = to;
+        }
+
+        public Address[] getBcc() {
+            return bcc;
+        }
+
+        public Address[] getCc() {
+            return cc;
+        }
+
+        public String getDate() {
+            return date;
+        }
+
+        public Address[] getFrom() {
+            return from;
+        }
+
+        public String getInReplyTo() {
+            return inReplyTo;
+        }
+
+        public String getMessageId() {
+            return messageId;
+        }
+
+        public Address[] getReplyTo() {
+            return replyTo;
+        }
+
+        public Address[] getSender() {
+            return sender;
+        }
+
+        public String getSubject() {
+            return subject;
+        }
+
+        public Address[] getTo() {
+            return to;
+        }
+    }
+    
+    private static final class AddressImpl implements FetchResponse.Envelope.Address {
+        private final String atDomainList;
+        private final String hostName;
+        private final String mailboxName;
+        private final String personalName;
+
+        public AddressImpl(final String atDomainList, final String hostName, final String mailboxName, final String personalName) {
+            super();
+            this.atDomainList = atDomainList;
+            this.hostName = hostName;
+            this.mailboxName = mailboxName;
+            this.personalName = personalName;
+        }
+
+        public String getAtDomainList() {
+            return atDomainList;
+        }
+
+        public String getHostName() {
+            return hostName;
+        }
+
+        public String getMailboxName() {
+            return mailboxName;
+        }
+
+        public String getPersonalName() {
+            return personalName;
+        }
+    }
+     
     
     private static final class HeaderBodyElement implements BodyElement {
         private final String name;

Modified: james/server/trunk/imap-mailbox-processor-function/src/main/java/org/apache/james/imapserver/processor/imap4rev1/StoreProcessor.java
URL: http://svn.apache.org/viewvc/james/server/trunk/imap-mailbox-processor-function/src/main/java/org/apache/james/imapserver/processor/imap4rev1/StoreProcessor.java?rev=617978&r1=617977&r2=617978&view=diff
==============================================================================
--- james/server/trunk/imap-mailbox-processor-function/src/main/java/org/apache/james/imapserver/processor/imap4rev1/StoreProcessor.java (original)
+++ james/server/trunk/imap-mailbox-processor-function/src/main/java/org/apache/james/imapserver/processor/imap4rev1/StoreProcessor.java Sun Feb  3 04:24:45 2008
@@ -105,7 +105,8 @@
                         } else {
                             resultUid = null;
                         }
-                        final FetchResponse response = new FetchResponse(msn, resultFlags, resultUid, null, null, null, null);
+                        final FetchResponse response 
+                            = new FetchResponse(msn, resultFlags, resultUid, null, null, null, null, null);
                         responder.respond(response);
                     }
                 }

Modified: james/server/trunk/imapmailbox-library/pom.xml
URL: http://svn.apache.org/viewvc/james/server/trunk/imapmailbox-library/pom.xml?rev=617978&r1=617977&r2=617978&view=diff
==============================================================================
--- james/server/trunk/imapmailbox-library/pom.xml (original)
+++ james/server/trunk/imapmailbox-library/pom.xml Sun Feb  3 04:24:45 2008
@@ -45,8 +45,12 @@
       <groupId>org.apache.james</groupId>
       <artifactId>james-server-core-library</artifactId>
     </dependency>
-     -->
-    
+     -->
+    <dependency>
+      <groupId>org.apache.james</groupId>
+      <artifactId>apache-mime4j</artifactId>
+      <version>0.4-SNAPSHOT</version>
+    </dependency>
     <dependency>
       <groupId>junit</groupId>
       <artifactId>junit</artifactId>

Modified: james/server/trunk/imapserver-function/src/main/java/org/apache/james/imapserver/ImapConstants.java
URL: http://svn.apache.org/viewvc/james/server/trunk/imapserver-function/src/main/java/org/apache/james/imapserver/ImapConstants.java?rev=617978&r1=617977&r2=617978&view=diff
==============================================================================
--- james/server/trunk/imapserver-function/src/main/java/org/apache/james/imapserver/ImapConstants.java (original)
+++ james/server/trunk/imapserver-function/src/main/java/org/apache/james/imapserver/ImapConstants.java Sun Feb  3 04:24:45 2008
@@ -40,4 +40,7 @@
     final String NAMESPACE_PREFIX = String.valueOf( NAMESPACE_PREFIX_CHAR );
 
     String INBOX_NAME = "INBOX";
+    
+    public static final char SP_CHAR = ' ';
+    public static final String NIL = "NIL";
 }

Modified: james/server/trunk/imapserver-function/src/main/java/org/apache/james/imapserver/commands/FetchCommand.java
URL: http://svn.apache.org/viewvc/james/server/trunk/imapserver-function/src/main/java/org/apache/james/imapserver/commands/FetchCommand.java?rev=617978&r1=617977&r2=617978&view=diff
==============================================================================
--- james/server/trunk/imapserver-function/src/main/java/org/apache/james/imapserver/commands/FetchCommand.java (original)
+++ james/server/trunk/imapserver-function/src/main/java/org/apache/james/imapserver/commands/FetchCommand.java Sun Feb  3 04:24:45 2008
@@ -29,7 +29,9 @@
 import javax.mail.Flags;
 import javax.mail.MessagingException;
 
+import org.apache.avalon.framework.logger.Logger;
 import org.apache.james.api.imap.message.MessageFlags;
+import org.apache.james.imapserver.ImapConstants;
 import org.apache.james.imapserver.ImapRequestLineReader;
 import org.apache.james.imapserver.ImapResponse;
 import org.apache.james.imapserver.ImapSession;
@@ -45,8 +47,17 @@
 import org.apache.james.mailboxmanager.MessageResult.Content;
 import org.apache.james.mailboxmanager.impl.GeneralMessageSetImpl;
 import org.apache.james.mailboxmanager.mailbox.ImapMailbox;
+import org.apache.james.mime4j.field.address.Address;
+import org.apache.james.mime4j.field.address.AddressList;
+import org.apache.james.mime4j.field.address.Group;
+import org.apache.james.mime4j.field.address.Mailbox;
+import org.apache.james.mime4j.field.address.MailboxList;
+import org.apache.james.mime4j.field.address.NamedMailbox;
+import org.apache.james.mime4j.field.address.parser.ParseException;
+import org.apache.mailet.RFC2822Headers;
 import org.apache.mailet.dates.RFC822DateFormat;
 
+
 /**
  * Handles processeing for the FETCH imap command.
  *
@@ -158,8 +169,16 @@
                 response.append(" RFC822.SIZE ");
                 response.append(result.getSize());
             }
+            
+            // ENVELOPE response
+            if (fetch.envelope) {
+                response.append(" ENVELOPE ");
+                final Iterator iterator = result.iterateHeaders();
+                List headers = MessageResultUtils.getAll(iterator);
+                outputEnvelope(headers, response);
+            }
 
-            if (fetch.envelope || fetch.body || fetch.bodyStructure) {
+            if (fetch.body || fetch.bodyStructure) {
                 SimpleMessageAttributes attrs = new SimpleMessageAttributes(result
                         .getMimeMessage(), getLogger());
     
@@ -329,6 +348,124 @@
         response.append("\r\n");
     }
 
+
+    private void outputEnvelope(List headers, StringBuffer response) throws MessagingException {
+        MessageResult.Header dateHeader = MessageResultUtils.getMatching(RFC2822Headers.DATE, headers.iterator());
+        outputEnvelope(dateHeader, response);
+        MessageResult.Header subjectHeader = MessageResultUtils.getMatching(RFC2822Headers.SUBJECT, headers.iterator());
+        outputEnvelope(subjectHeader, response);
+        MessageResult.Header fromHeader = MessageResultUtils.getMatching(RFC2822Headers.FROM, headers.iterator());
+        outputAddressEnvelope(fromHeader, response);
+        MessageResult.Header senderHeader = MessageResultUtils.getMatching(RFC2822Headers.SENDER, headers.iterator());
+        if (senderHeader == null) {
+            outputAddressEnvelope(fromHeader, response);
+        } else {
+            outputAddressEnvelope(senderHeader, response);
+        }
+        MessageResult.Header replyToHeader = MessageResultUtils.getMatching(RFC2822Headers.REPLY_TO, headers.iterator());
+        if (replyToHeader == null) {
+            outputAddressEnvelope(fromHeader, response);
+        } else {
+            outputAddressEnvelope(replyToHeader, response);
+        }
+        MessageResult.Header toHeader = MessageResultUtils.getMatching(RFC2822Headers.TO, headers.iterator());
+        outputAddressEnvelope(toHeader, response);
+        MessageResult.Header ccHeader = MessageResultUtils.getMatching(RFC2822Headers.CC, headers.iterator());
+        outputAddressEnvelope(ccHeader, response);
+        MessageResult.Header bccHeader = MessageResultUtils.getMatching(RFC2822Headers.BCC, headers.iterator());
+        outputAddressEnvelope(bccHeader, response);
+        MessageResult.Header inReplyToHeader = MessageResultUtils.getMatching(RFC2822Headers.IN_REPLY_TO, headers.iterator());
+        outputEnvelope(inReplyToHeader, response);
+        MessageResult.Header messageIdHeader = MessageResultUtils.getMatching(RFC2822Headers.MESSAGE_ID, headers.iterator());
+        outputEnvelope(messageIdHeader, response);
+    }
+
+    private void outputAddressEnvelope(final MessageResult.Header header, final StringBuffer response) throws MessagingException {
+        if (header == null) {
+            response.append(ImapConstants.NIL);
+        } else {
+            final String value = header.getValue();
+            try {
+                boolean first = true;
+                final AddressList addressList = AddressList.parse(value);
+                final int size = addressList.size();
+                for (int i=0;i<size;i++) {
+                    final Address address = addressList.get(i);
+                    if (address instanceof Group) {
+                        final Group group = (Group) address;
+                        first = outputGroup(response, first, group);
+                    } else if (address instanceof Mailbox) {
+                        final Mailbox mailbox = (Mailbox) address;
+                        first = outputMailbox(response, first, mailbox);
+                    } else {
+                        getLogger().warn("Unknown address type");
+                    }
+                }
+            } catch (ParseException e) {
+                final Logger logger = getLogger();
+                if (logger.isDebugEnabled()) {
+                    logger.debug("Cannot parse field " + header.getName(), e);
+                }
+                throw new MessagingException("Failed to parse field " + header.getName(), e);
+            }
+        }
+    }
+    
+    private boolean outputMailbox(StringBuffer response, boolean first, Mailbox mailbox) {
+        final String name;
+        if (mailbox instanceof NamedMailbox) {
+            final NamedMailbox namedMailbox = (NamedMailbox) mailbox;
+            name = namedMailbox.getName();
+        } else {
+            name = null;
+        }
+        final String domain = mailbox.getDomain();
+        final String atDomainList = mailbox.getRoute().toRouteString();
+        final String localPart = mailbox.getLocalPart();
+        first = writeAddress(first, name, atDomainList, localPart, domain, response);
+        return first;
+    }
+
+    private boolean outputGroup(final StringBuffer response, boolean first, final Group group) {
+        final String groupName = group.getName();
+        first = writeAddress(first, null, null, groupName, null, response);
+        final MailboxList mailboxList = group.getMailboxes();
+        for (int i=0;i<mailboxList.size();i++) {
+            final Mailbox mailbox = mailboxList.get(i);
+            first = outputMailbox(response, first, mailbox);
+        }
+        first = writeAddress(first, null, null, null, null, response);
+        return first;
+    }
+
+    private boolean writeAddress(boolean first, String name, String atDomainList, String mailbox, String domain, StringBuffer response) {
+        if (first) {
+            response.append(',');
+            response.append(ImapConstants.SP_CHAR);
+        } 
+        outputNullable(name, response);
+        outputNullable(atDomainList, response);
+        outputNullable(mailbox, response);
+        outputNullable(domain, response);
+        return false;
+    }
+
+    private void outputNullable(String value, StringBuffer response) {
+        if (value == null) {
+            response.append(ImapConstants.NIL);
+        }
+    }
+
+    private void outputEnvelope(final MessageResult.Header header, final StringBuffer response) throws MessagingException {
+        if (header == null) {
+            response.append(ImapConstants.NIL);
+        } else {
+            final String value = header.getValue();
+            response.append(value);
+        }
+    }
+
+
     /** @see ImapCommand#getName */
     public String getName()
     {
@@ -537,9 +674,11 @@
             if (mailFetchElement) {
                 result |= MessageResult.MIME_MESSAGE;
             }
-            if (body || bodyStructure || envelope) {
+            if (envelope) {
+                result |= MessageResult.HEADERS;
+            }
+            if (body || bodyStructure) {
                 // TODO: structure
-                //result |= MessageResult.ENVELOPE;
                 result |= MessageResult.MIME_MESSAGE;
             }
             if (headerFetchElement || mailFetchElement) {



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


Mime
View raw message