james-server-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From nor...@apache.org
Subject svn commit: r1147281 - in /james/imap/trunk: api/src/main/java/org/apache/james/imap/api/message/ message/src/main/java/org/apache/james/imap/decode/parser/ message/src/main/java/org/apache/james/imap/encode/ message/src/main/java/org/apache/james/imap...
Date Fri, 15 Jul 2011 19:01:31 GMT
Author: norman
Date: Fri Jul 15 19:01:28 2011
New Revision: 1147281

URL: http://svn.apache.org/viewvc?rev=1147281&view=rev
Log:
More work on QRESYNC extension. Still work in progress as some tests of the imaptest suite (dovecot) still fail. Part of IMAP-307

Modified:
    james/imap/trunk/api/src/main/java/org/apache/james/imap/api/message/IdRange.java
    james/imap/trunk/message/src/main/java/org/apache/james/imap/decode/parser/AbstractSelectionCommandParser.java
    james/imap/trunk/message/src/main/java/org/apache/james/imap/decode/parser/EnableCommandParser.java
    james/imap/trunk/message/src/main/java/org/apache/james/imap/decode/parser/StoreCommandParser.java
    james/imap/trunk/message/src/main/java/org/apache/james/imap/encode/CapabilityResponseEncoder.java
    james/imap/trunk/message/src/main/java/org/apache/james/imap/encode/main/DefaultImapEncoderFactory.java
    james/imap/trunk/message/src/main/java/org/apache/james/imap/message/request/EnableRequest.java
    james/imap/trunk/message/src/main/java/org/apache/james/imap/message/response/CapabilityResponse.java
    james/imap/trunk/message/src/main/java/org/apache/james/imap/message/response/EnableResponse.java
    james/imap/trunk/processor/src/main/java/org/apache/james/imap/processor/AbstractMailboxProcessor.java
    james/imap/trunk/processor/src/main/java/org/apache/james/imap/processor/AbstractSelectionProcessor.java
    james/imap/trunk/processor/src/main/java/org/apache/james/imap/processor/CapabilityProcessor.java
    james/imap/trunk/processor/src/main/java/org/apache/james/imap/processor/DefaultProcessorChain.java
    james/imap/trunk/processor/src/main/java/org/apache/james/imap/processor/EnableProcessor.java
    james/imap/trunk/processor/src/main/java/org/apache/james/imap/processor/ExpungeProcessor.java
    james/imap/trunk/processor/src/main/java/org/apache/james/imap/processor/PermitEnableCapabilityProcessor.java
    james/imap/trunk/processor/src/main/java/org/apache/james/imap/processor/StoreProcessor.java
    james/imap/trunk/processor/src/main/java/org/apache/james/imap/processor/fetch/FetchProcessor.java
    james/imap/trunk/processor/src/test/java/org/apache/james/imap/processor/NamespaceProcessorTest.java

Modified: james/imap/trunk/api/src/main/java/org/apache/james/imap/api/message/IdRange.java
URL: http://svn.apache.org/viewvc/james/imap/trunk/api/src/main/java/org/apache/james/imap/api/message/IdRange.java?rev=1147281&r1=1147280&r2=1147281&view=diff
==============================================================================
--- james/imap/trunk/api/src/main/java/org/apache/james/imap/api/message/IdRange.java (original)
+++ james/imap/trunk/api/src/main/java/org/apache/james/imap/api/message/IdRange.java Fri Jul 15 19:01:28 2011
@@ -21,13 +21,14 @@ package org.apache.james.imap.api.messag
 
 import java.util.ArrayList;
 import java.util.Collections;
-import java.util.Comparator;
+import java.util.Iterator;
 import java.util.List;
+import java.util.NoSuchElementException;
 
 /**
  * Represents a range of UID or MSN values.
  */
-public class IdRange {
+public final class IdRange implements Iterable<Long>, Comparable<IdRange>{
 
     private long _lowVal;
 
@@ -132,7 +133,7 @@ public class IdRange {
      */
     public static List<IdRange> mergeRanges(final List<IdRange> ranges) {
         List<IdRange> copy = new ArrayList<IdRange>(ranges);
-        Collections.sort(copy, IdRangeComperator.INSTANCE);
+        Collections.sort(copy);
 
         boolean lastUid = false;
 
@@ -164,25 +165,70 @@ public class IdRange {
 
     }
 
-    private static class IdRangeComperator implements Comparator<IdRange> {
 
-        private static IdRangeComperator INSTANCE = new IdRangeComperator();
+    /**
+     * Return a read-only {@link Iterator} which contains all msn/uid which fail in the specified range.
+     * 
+     * @return rangeIt
+     */
+    @Override
+    public Iterator<Long> iterator() {
+        long from = getLowVal();
+        if (from == Long.MAX_VALUE) {
+            from = 1;
+        }
+        long to = getHighVal();
+        return new RangeIterator(from, to);
+    }
+    
+    /**
+     * {@link Iterator} of a range of msn/uid
+     *
+     */
+    private final class RangeIterator implements Iterator<Long> {
 
-        public int compare(IdRange range1, IdRange range2) {
+        private long to;
+        private long current;
+        
+        public RangeIterator(long from, long to) {
+            this.to = to;
+            this.current = from;
+        }
+        
+        @Override
+        public boolean hasNext() {
+            return current <= to;
+        }
 
-            // Correctly sort and respect "*" and "*:*" ranges. See IMAP-289
-            if (range1.getLowVal() == Long.MAX_VALUE && range1.getHighVal() == Long.MAX_VALUE && range2.getLowVal() == Long.MAX_VALUE && range2.getHighVal() == Long.MAX_VALUE) {
-                return 0;
-            }
-            if (range1.getLowVal() == Long.MAX_VALUE && range1.getHighVal() == Long.MAX_VALUE) {
-                return 1;
-            } else if (range2.getLowVal() == Long.MAX_VALUE && range2.getHighVal() == Long.MAX_VALUE) {
-                return -1;
+        @Override
+        public Long next() {
+            if (hasNext()) {
+                return current++;
             } else {
-                return (int) (range1.getLowVal() - range2.getLowVal());
+                throw new NoSuchElementException("Highest id of " + to + " was reached before");
             }
         }
 
+        @Override
+        public void remove() {
+            throw new UnsupportedOperationException("Read-Only");
+        }
+        
+    }
+
+    @Override
+    public int compareTo(IdRange range2) {
+        // Correctly sort and respect "*" and "*:*" ranges. See IMAP-289
+        if (getLowVal() == Long.MAX_VALUE && getHighVal() == Long.MAX_VALUE && range2.getLowVal() == Long.MAX_VALUE && range2.getHighVal() == Long.MAX_VALUE) {
+            return 0;
+        }
+        if (getLowVal() == Long.MAX_VALUE && getHighVal() == Long.MAX_VALUE) {
+            return 1;
+        } else if (range2.getLowVal() == Long.MAX_VALUE && range2.getHighVal() == Long.MAX_VALUE) {
+            return -1;
+        } else {
+            return (int) (getLowVal() - range2.getLowVal());
+        }
     }
 
 }

Modified: james/imap/trunk/message/src/main/java/org/apache/james/imap/decode/parser/AbstractSelectionCommandParser.java
URL: http://svn.apache.org/viewvc/james/imap/trunk/message/src/main/java/org/apache/james/imap/decode/parser/AbstractSelectionCommandParser.java?rev=1147281&r1=1147280&r2=1147281&view=diff
==============================================================================
--- james/imap/trunk/message/src/main/java/org/apache/james/imap/decode/parser/AbstractSelectionCommandParser.java (original)
+++ james/imap/trunk/message/src/main/java/org/apache/james/imap/decode/parser/AbstractSelectionCommandParser.java Fri Jul 15 19:01:28 2011
@@ -19,6 +19,7 @@
 package org.apache.james.imap.decode.parser;
 
 import org.apache.james.imap.api.ImapCommand;
+import org.apache.james.imap.api.ImapConstants;
 import org.apache.james.imap.api.ImapMessage;
 import org.apache.james.imap.api.display.HumanReadableText;
 import org.apache.james.imap.api.message.IdRange;
@@ -30,8 +31,8 @@ import org.apache.james.imap.decode.base
 import org.apache.james.imap.message.request.AbstractMailboxSelectionRequest;
 
 public abstract class AbstractSelectionCommandParser extends AbstractImapCommandParser{
-    private final static byte[] CONDSTORE = "CONDSTORE".getBytes();
-    private final static byte[] QRESYNC = "QRESYNC".getBytes();
+    private final static byte[] CONDSTORE = ImapConstants.SUPPORTS_CONDSTORE.getBytes();
+    private final static byte[] QRESYNC = ImapConstants.SUPPORTS_QRESYNC.getBytes();
 
     public AbstractSelectionCommandParser(ImapCommand command) {
         super(command);
@@ -78,7 +79,7 @@ public abstract class AbstractSelectionC
 
                     @Override
                     public boolean isValid(char chr) {
-                        if (pos > CONDSTORE.length) {
+                        if (pos >= CONDSTORE.length) {
                             return false;
                         } else {
                             return ImapRequestLineReader.cap(chr) == CONDSTORE[pos++];
@@ -94,7 +95,7 @@ public abstract class AbstractSelectionC
 
                     @Override
                     public boolean isValid(char chr) {
-                        if (pos > QRESYNC.length) {
+                        if (pos >= QRESYNC.length) {
                             return false;
                         } else {
                             return ImapRequestLineReader.cap(chr) == QRESYNC[pos++];
@@ -104,6 +105,9 @@ public abstract class AbstractSelectionC
                 
                 // Consume the SP
                 request.consumeChar(' ');
+                
+                // Consume enclosing paren
+                request.consumeChar('(');
                 lastKnownUidValidity = request.number();
                 
                 // Consume the SP
@@ -118,8 +122,8 @@ public abstract class AbstractSelectionC
                     request.consumeChar(' ');
                     uidSet = request.parseIdRange();
                     
-                    // Check for * and check if its in ascending order
-                    checkIdRanges(uidSet);
+                    // Check for *
+                    checkIdRanges(uidSet, false);
                     
                     nc = request.nextChar();
                     if (nc == ' ')  {
@@ -132,13 +136,15 @@ public abstract class AbstractSelectionC
                         knownUidSet = request.parseIdRange();
                        
                         // Check for * and check if its in ascending order
-                        checkIdRanges(knownSequenceSet);
-                        checkIdRanges(knownUidSet);
+                        checkIdRanges(knownSequenceSet, true);
+                        checkIdRanges(knownUidSet, true);
                         
                         // This is enclosed in () so remove )
                         request.consumeChar(')');
                     }
                 }
+                request.consumeChar(')');
+
                 break;
             default:
                 throw new DecodingException(HumanReadableText.ILLEGAL_ARGUMENTS, "Unknown option");
@@ -153,7 +159,30 @@ public abstract class AbstractSelectionC
         return result;
     }
     
-    private void checkIdRanges(IdRange[] ranges) throws DecodingException {
+    /**
+     * Check if the {@link IdRange}'s are formatted like stated in the QRESYNC RFC.
+     * 
+     * From RFC5162:
+     * 
+     *  known-uids             =  sequence-set
+     *                          ;; sequence of UIDs, "*" is not allowed
+     *
+     *  known-sequence-set     =  sequence-set
+     *                          ;; set of message numbers corresponding to
+     *                          ;; the UIDs in known-uid-set, in ascending order.
+     *                          ;; * is not allowed.
+     *                          
+     *  known-uid-set       =  sequence-set
+     *                          ;; set of UIDs corresponding to the messages in
+     *                          ;; known-sequence-set, in ascending order.
+     *                          ;; * is not allowed.
+     * 
+     * 
+     * @param ranges
+     * @param checkOrder
+     * @throws DecodingException
+     */
+    private void checkIdRanges(IdRange[] ranges, boolean checkOrder) throws DecodingException {
         long last = 0;
         for (int i = 0; i < ranges.length; i++ ) {
             
@@ -163,13 +192,29 @@ public abstract class AbstractSelectionC
             if (low == Long.MAX_VALUE || high == Long.MAX_VALUE) {
                 throw new DecodingException(HumanReadableText.INVALID_MESSAGESET, "* is not allowed in the sequence-set");
             }
-            if (low < last) {
-                throw new DecodingException(HumanReadableText.INVALID_MESSAGESET, "Sequence-set must be in ascending order");
-            } else {
-                last = high;
+            if (checkOrder) {
+                if (low < last) {
+                    throw new DecodingException(HumanReadableText.INVALID_MESSAGESET, "Sequence-set must be in ascending order");
+                } else {
+                    last = high;
+                }
             }
         }
     }
     
+    /**
+     * Create a new {@link AbstractMailboxSelectionRequest} for the given arguments
+     * 
+     * @param command
+     * @param mailboxName
+     * @param condstore
+     * @param lastKnownUidValidity
+     * @param knownModSeq
+     * @param uidSet
+     * @param knownUidSet
+     * @param knownSequenceSet
+     * @param tag
+     * @return request
+     */
     protected abstract AbstractMailboxSelectionRequest createRequest(ImapCommand command, String mailboxName, boolean condstore, Long lastKnownUidValidity, Long knownModSeq, IdRange[] uidSet, IdRange[] knownUidSet, IdRange[] knownSequenceSet, String tag);
 }

Modified: james/imap/trunk/message/src/main/java/org/apache/james/imap/decode/parser/EnableCommandParser.java
URL: http://svn.apache.org/viewvc/james/imap/trunk/message/src/main/java/org/apache/james/imap/decode/parser/EnableCommandParser.java?rev=1147281&r1=1147280&r2=1147281&view=diff
==============================================================================
--- james/imap/trunk/message/src/main/java/org/apache/james/imap/decode/parser/EnableCommandParser.java (original)
+++ james/imap/trunk/message/src/main/java/org/apache/james/imap/decode/parser/EnableCommandParser.java Fri Jul 15 19:01:28 2011
@@ -20,6 +20,7 @@ package org.apache.james.imap.decode.par
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Locale;
 
 import org.apache.james.imap.api.ImapCommand;
 import org.apache.james.imap.api.ImapConstants;
@@ -40,11 +41,11 @@ public class EnableCommandParser extends
     protected ImapMessage decode(ImapCommand command, ImapRequestLineReader request, String tag, ImapSession session) throws DecodingException {
         List<String> caps = new ArrayList<String>();
         String cap = request.astring();
-        caps.add(cap);
+        caps.add(cap.toUpperCase(Locale.UK));
         while (request.nextChar() == ' ') {
             request.consume();
             cap = request.astring();
-            caps.add(cap);
+            caps.add(cap.toUpperCase(Locale.UK));
         }
         request.eol();
         return new EnableRequest(tag, command, caps);

Modified: james/imap/trunk/message/src/main/java/org/apache/james/imap/decode/parser/StoreCommandParser.java
URL: http://svn.apache.org/viewvc/james/imap/trunk/message/src/main/java/org/apache/james/imap/decode/parser/StoreCommandParser.java?rev=1147281&r1=1147280&r2=1147281&view=diff
==============================================================================
--- james/imap/trunk/message/src/main/java/org/apache/james/imap/decode/parser/StoreCommandParser.java (original)
+++ james/imap/trunk/message/src/main/java/org/apache/james/imap/decode/parser/StoreCommandParser.java Fri Jul 15 19:01:28 2011
@@ -64,7 +64,7 @@ public class StoreCommandParser extends 
             request.consumeWord(new CharacterValidator() {
                 private int pos = 0;
                 public boolean isValid(char chr) {
-                    if (pos > UNCHANGEDSINCE.length) {
+                    if (pos >= UNCHANGEDSINCE.length) {
                         return false;
                     } else {
                         return ImapRequestLineReader.cap(chr) == UNCHANGEDSINCE[pos++];

Modified: james/imap/trunk/message/src/main/java/org/apache/james/imap/encode/CapabilityResponseEncoder.java
URL: http://svn.apache.org/viewvc/james/imap/trunk/message/src/main/java/org/apache/james/imap/encode/CapabilityResponseEncoder.java?rev=1147281&r1=1147280&r2=1147281&view=diff
==============================================================================
--- james/imap/trunk/message/src/main/java/org/apache/james/imap/encode/CapabilityResponseEncoder.java (original)
+++ james/imap/trunk/message/src/main/java/org/apache/james/imap/encode/CapabilityResponseEncoder.java Fri Jul 15 19:01:28 2011
@@ -19,6 +19,7 @@
 package org.apache.james.imap.encode;
 
 import java.io.IOException;
+import java.util.ArrayList;
 
 import org.apache.james.imap.api.ImapMessage;
 import org.apache.james.imap.api.process.ImapSession;
@@ -47,7 +48,7 @@ public class CapabilityResponseEncoder e
      */
     protected void doEncode(ImapMessage acceptableMessage, ImapResponseComposer composer, ImapSession session) throws IOException {
         final CapabilityResponse response = (CapabilityResponse) acceptableMessage;
-        composer.capabilities(response.getCapabilities());
+        composer.capabilities(new ArrayList<String>(response.getCapabilities()));
     }
 
     /*

Modified: james/imap/trunk/message/src/main/java/org/apache/james/imap/encode/main/DefaultImapEncoderFactory.java
URL: http://svn.apache.org/viewvc/james/imap/trunk/message/src/main/java/org/apache/james/imap/encode/main/DefaultImapEncoderFactory.java?rev=1147281&r1=1147280&r2=1147281&view=diff
==============================================================================
--- james/imap/trunk/message/src/main/java/org/apache/james/imap/encode/main/DefaultImapEncoderFactory.java (original)
+++ james/imap/trunk/message/src/main/java/org/apache/james/imap/encode/main/DefaultImapEncoderFactory.java Fri Jul 15 19:01:28 2011
@@ -38,6 +38,7 @@ import org.apache.james.imap.encode.Rece
 import org.apache.james.imap.encode.MailboxStatusResponseEncoder;
 import org.apache.james.imap.encode.SearchResponseEncoder;
 import org.apache.james.imap.encode.StatusResponseEncoder;
+import org.apache.james.imap.encode.VanishedResponseEncoder;
 import org.apache.james.imap.encode.XListResponseEncoder;
 import org.apache.james.imap.encode.base.EndImapEncoder;
 
@@ -75,7 +76,8 @@ public class DefaultImapEncoderFactory i
         final ContinuationResponseEncoder continuationResponseEncoder = new ContinuationResponseEncoder(enableResponseEncoder, localizer);
         final AuthenticateResponseEncoder authResponseEncoder = new AuthenticateResponseEncoder(continuationResponseEncoder);
         final ESearchResponseEncoder esearchResponseEncoder = new ESearchResponseEncoder(authResponseEncoder);
-        return esearchResponseEncoder;
+        final VanishedResponseEncoder vanishedResponseEncoder = new VanishedResponseEncoder(esearchResponseEncoder);
+        return vanishedResponseEncoder;
     }
 
     private final Localizer localizer;

Modified: james/imap/trunk/message/src/main/java/org/apache/james/imap/message/request/EnableRequest.java
URL: http://svn.apache.org/viewvc/james/imap/trunk/message/src/main/java/org/apache/james/imap/message/request/EnableRequest.java?rev=1147281&r1=1147280&r2=1147281&view=diff
==============================================================================
--- james/imap/trunk/message/src/main/java/org/apache/james/imap/message/request/EnableRequest.java (original)
+++ james/imap/trunk/message/src/main/java/org/apache/james/imap/message/request/EnableRequest.java Fri Jul 15 19:01:28 2011
@@ -31,6 +31,11 @@ public class EnableRequest extends Abstr
         this.capabilities = capabilities;
     }
     
+    /**
+     * Return a List of <code>CAPABILITIES</code>. All these must be uppercase
+     * 
+     * @return caps
+     */
     public List<String> getCapabilities() {
         return capabilities;
     }

Modified: james/imap/trunk/message/src/main/java/org/apache/james/imap/message/response/CapabilityResponse.java
URL: http://svn.apache.org/viewvc/james/imap/trunk/message/src/main/java/org/apache/james/imap/message/response/CapabilityResponse.java?rev=1147281&r1=1147280&r2=1147281&view=diff
==============================================================================
--- james/imap/trunk/message/src/main/java/org/apache/james/imap/message/response/CapabilityResponse.java (original)
+++ james/imap/trunk/message/src/main/java/org/apache/james/imap/message/response/CapabilityResponse.java Fri Jul 15 19:01:28 2011
@@ -18,7 +18,7 @@
  ****************************************************************/
 package org.apache.james.imap.message.response;
 
-import java.util.List;
+import java.util.Set;
 
 import org.apache.james.imap.api.message.response.ImapResponseMessage;
 
@@ -29,7 +29,7 @@ import org.apache.james.imap.api.message
  */
 public class CapabilityResponse implements ImapResponseMessage {
 
-    private final List<String> capabilities;
+    private final Set<String> capabilities;
 
     /**
      * Constructs a response based on the given capabilities.
@@ -37,17 +37,17 @@ public class CapabilityResponse implemen
      * @param capabilities
      *            not null
      */
-    public CapabilityResponse(final List<String> capabilities) {
+    public CapabilityResponse(final Set<String> capabilities) {
         super();
         this.capabilities = capabilities;
     }
 
     /**
-     * Gets a list containing the capabilities of this server.
+     * Gets a {@link Set} containing the capabilities of this server.
      * 
      * @return not null
      */
-    public List<String> getCapabilities() {
+    public Set<String> getCapabilities() {
         return capabilities;
     }
 

Modified: james/imap/trunk/message/src/main/java/org/apache/james/imap/message/response/EnableResponse.java
URL: http://svn.apache.org/viewvc/james/imap/trunk/message/src/main/java/org/apache/james/imap/message/response/EnableResponse.java?rev=1147281&r1=1147280&r2=1147281&view=diff
==============================================================================
--- james/imap/trunk/message/src/main/java/org/apache/james/imap/message/response/EnableResponse.java (original)
+++ james/imap/trunk/message/src/main/java/org/apache/james/imap/message/response/EnableResponse.java Fri Jul 15 19:01:28 2011
@@ -18,11 +18,11 @@
  ****************************************************************/
 package org.apache.james.imap.message.response;
 
-import java.util.List;
+import java.util.Set;
 
 public class EnableResponse extends CapabilityResponse{
 
-    public EnableResponse(List<String> capabilities) {
+    public EnableResponse(Set<String> capabilities) {
         super(capabilities);
     }
 

Modified: james/imap/trunk/processor/src/main/java/org/apache/james/imap/processor/AbstractMailboxProcessor.java
URL: http://svn.apache.org/viewvc/james/imap/trunk/processor/src/main/java/org/apache/james/imap/processor/AbstractMailboxProcessor.java?rev=1147281&r1=1147280&r2=1147281&view=diff
==============================================================================
--- james/imap/trunk/processor/src/main/java/org/apache/james/imap/processor/AbstractMailboxProcessor.java (original)
+++ james/imap/trunk/processor/src/main/java/org/apache/james/imap/processor/AbstractMailboxProcessor.java Fri Jul 15 19:01:28 2011
@@ -19,8 +19,10 @@
 package org.apache.james.imap.processor;
 
 import java.util.Collection;
+import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Set;
 
 import javax.mail.Flags;
 
@@ -54,7 +56,10 @@ import org.apache.james.mailbox.MessageM
 import org.apache.james.mailbox.MessageRange;
 import org.apache.james.mailbox.MessageRangeException;
 import org.apache.james.mailbox.MessageResult;
+import org.apache.james.mailbox.SearchQuery;
+import org.apache.james.mailbox.MessageManager.MetaData;
 import org.apache.james.mailbox.MessageRange.Type;
+import org.apache.james.mailbox.SearchQuery.NumericRange;
 
 abstract public class AbstractMailboxProcessor<M extends ImapRequest> extends AbstractChainedProcessor<M> {
 
@@ -140,15 +145,17 @@ abstract public class AbstractMailboxPro
         }
         // Expunged messages
         if (!omitExpunged) {
-            // Check if QRESYNC was enabled. If so we MUST use VANISHED responses
-            if (EnableProcessor.getEnabledCapabilities(session).contains(ImapConstants.SUPPORTS_QRESYNC)) {
-                addVanishedResponse(selected, responder);
-            } else {
-                addExpungedResponses(selected, responder);
+            final Collection<Long> expungedUids = selected.expungedUids();
+            if (!expungedUids.isEmpty()) {
+                // Check if QRESYNC was enabled. If so we MUST use VANISHED responses
+                if (EnableProcessor.getEnabledCapabilities(session).contains(ImapConstants.SUPPORTS_QRESYNC)) {
+                    addVanishedResponse(selected, expungedUids, responder);
+                } else {
+                    addExpungedResponses(selected, expungedUids, responder);
+                }
+                // Only reset the events if we send the EXPUNGE or VANISHED responses. See IMAP-286
+                selected.resetExpungedUids();
             }
-            
-            // Only reset the events if we send the EXPUNGE or VANISHED responses. See IMAP-286
-            selected.resetExpungedUids();
 
         }
         if (sizeChanged || (selected.isRecentUidRemoved() && !omitExpunged)) {
@@ -162,8 +169,7 @@ abstract public class AbstractMailboxPro
         selected.resetEvents();
     }
 
-    private void addExpungedResponses(final SelectedMailbox selected, final ImapProcessor.Responder responder) {
-        final Collection<Long> expungedUids = selected.expungedUids();
+    private void addExpungedResponses(SelectedMailbox selected, Collection<Long> expungedUids, final ImapProcessor.Responder responder) {
         for (final Long uid : expungedUids) {
             final long uidValue = uid.longValue();
 
@@ -176,18 +182,12 @@ abstract public class AbstractMailboxPro
         }
     }
     
-    private void addVanishedResponse(final SelectedMailbox selected, final ImapProcessor.Responder responder) {
-        final Collection<Long> expungedUids = selected.expungedUids();
-        List<MessageRange> ranges = MessageRange.toRanges(expungedUids);
-        IdRange[] uidRange = new IdRange[ranges.size()];
-        for (int i = 0 ; i < ranges.size(); i++) {
-            MessageRange r = ranges.get(i);
-            if (r.getType() == Type.ONE) {
-                uidRange[i] = new IdRange(r.getUidFrom());
-            } else {
-                uidRange[i] = new IdRange(r.getUidFrom(), r.getUidTo());
-            }
+    private void addVanishedResponse(SelectedMailbox selected, Collection<Long> expungedUids, final ImapProcessor.Responder responder) {
+        for (final Long uid : expungedUids) {
+            final long uidValue = uid.longValue();
+            selected.remove(uidValue);
         }
+        IdRange[] uidRange = idRanges(MessageRange.toRanges(expungedUids));
         responder.respond(new VanishedResponse(uidRange, false));
     }
     
@@ -230,9 +230,10 @@ abstract public class AbstractMailboxPro
             if (msn == SelectedMailbox.NO_SUCH_MESSAGE)
                 throw new MailboxException("No message found with uid " + uid);
 
+            boolean qresyncEnabled = EnableProcessor.getEnabledCapabilities(session).contains(ImapConstants.SUPPORTS_QRESYNC);
             final Flags flags = mr.getFlags();
             final Long uidOut;
-            if (useUid) {
+            if (useUid || qresyncEnabled) {
                 uidOut = uid;
             } else {
                 uidOut = null;
@@ -246,7 +247,7 @@ abstract public class AbstractMailboxPro
             
             // Check if we also need to return the MODSEQ in the response. This is true if the mailbox was selected with the CONSTORE option or
             // if QRESYNC was enabled
-            if (selected.getCondstore() || EnableProcessor.getEnabledCapabilities(session).contains(ImapConstants.SUPPORTS_QRESYNC)) {
+            if (selected.getCondstore() || qresyncEnabled) {
                 response = new FetchResponse(msn, flags, uidOut, mr.getModSeq(), null, null, null, null, null, null);
             } else {
                 response = new FetchResponse(msn, flags, uidOut, null, null, null, null, null, null, null);
@@ -512,5 +513,80 @@ abstract public class AbstractMailboxPro
             throw new MessageRangeException("Unknown message range type: " + rangeType);
         }
     }
+    
+    
+    /**
+     * Send VANISHED responses if needed. 
+     * 
+     * @param session
+     * @param mailbox
+     * @param ranges
+     * @param changedSince
+     * @param metaData
+     * @param responder
+     * @throws MailboxException
+     */
+    protected void respondVanished(MailboxSession session, MessageManager mailbox, List<MessageRange> ranges, long changedSince, MetaData metaData, Responder responder) throws MailboxException {
+        // RFC5162 4.2. Server Implementations Storing Minimal State
+        //  
+        //      A server that stores the HIGHESTMODSEQ value at the time of the last
+        //      EXPUNGE can omit the VANISHED response when a client provides a
+        //      MODSEQ value that is equal to, or higher than, the current value of
+        //      this datum, that is, when there have been no EXPUNGEs.
+        //
+        //      A client providing message sequence match data can reduce the scope
+        //      as above.  In the case where there have been no expunges, the server
+        //      can ignore this data.
+        if (metaData.getHighestModSeq() > changedSince) {
+            SearchQuery searchQuery = new SearchQuery();
+            NumericRange[] nRanges = new NumericRange[ranges.size()];
+            Set<Long> vanishedUids = new HashSet<Long>();
+            for (int i = 0; i < ranges.size(); i++) {
+                MessageRange r = ranges.get(i);
+                NumericRange nr;
+                if (r.getType() == Type.ONE) {
+                    nr = new NumericRange(r.getUidFrom());
+                } else {
+                    nr = new NumericRange(r.getUidFrom(), r.getUidTo());
+                }
+                long from = nr.getLowValue();
+                long to = nr.getHighValue();
+                while(from <= to) {
+                    vanishedUids.add(from++);
+                }
+                nRanges[i] = nr;
+                
+            }
+            searchQuery.andCriteria(SearchQuery.uid(nRanges));
+            searchQuery.andCriteria(SearchQuery.modSeqGreaterThan(changedSince));
+            Iterator<Long> uids = mailbox.search(searchQuery, session);
+            while(uids.hasNext()) {
+                vanishedUids.remove(uids.next());
+            }
+            IdRange[] vanishedIdRanges = idRanges(MessageRange.toRanges(vanishedUids));
+            responder.respond(new VanishedResponse(vanishedIdRanges, true));
+        }
+        
+        
+    }
+    
+    
+    // TODO: Do we need to handle wildcards here ?
+    protected IdRange[] idRanges(Collection<MessageRange> mRanges) {
+        IdRange[] idRanges = new IdRange[mRanges.size()];
+        Iterator<MessageRange> mIt = mRanges.iterator();
+        int i = 0;
+        while(mIt.hasNext()) {
+            MessageRange mr = mIt.next();
+            IdRange ir;
+            if (mr.getType() == Type.ONE) {
+                ir = new IdRange(mr.getUidFrom());
+            } else {
+                ir = new IdRange(mr.getUidFrom(), mr.getUidTo());
+            }
+            idRanges[i++] = ir;
+        }
+        return idRanges;
+    }
 
 }

Modified: james/imap/trunk/processor/src/main/java/org/apache/james/imap/processor/AbstractSelectionProcessor.java
URL: http://svn.apache.org/viewvc/james/imap/trunk/processor/src/main/java/org/apache/james/imap/processor/AbstractSelectionProcessor.java?rev=1147281&r1=1147280&r2=1147281&view=diff
==============================================================================
--- james/imap/trunk/processor/src/main/java/org/apache/james/imap/processor/AbstractSelectionProcessor.java (original)
+++ james/imap/trunk/processor/src/main/java/org/apache/james/imap/processor/AbstractSelectionProcessor.java Fri Jul 15 19:01:28 2011
@@ -20,6 +20,8 @@
 package org.apache.james.imap.processor;
 
 import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
 import java.util.Iterator;
 import java.util.List;
 
@@ -27,6 +29,7 @@ import javax.mail.Flags;
 
 import org.apache.james.imap.api.ImapCommand;
 import org.apache.james.imap.api.ImapConstants;
+import org.apache.james.imap.api.ImapMessage;
 import org.apache.james.imap.api.ImapSessionUtils;
 import org.apache.james.imap.api.display.HumanReadableText;
 import org.apache.james.imap.api.message.IdRange;
@@ -40,7 +43,6 @@ import org.apache.james.imap.api.process
 import org.apache.james.imap.message.request.AbstractMailboxSelectionRequest;
 import org.apache.james.imap.message.response.ExistsResponse;
 import org.apache.james.imap.message.response.RecentResponse;
-import org.apache.james.imap.message.response.VanishedResponse;
 import org.apache.james.imap.processor.base.FetchGroupImpl;
 import org.apache.james.imap.processor.base.SelectedMailboxImpl;
 import org.apache.james.mailbox.MailboxException;
@@ -49,21 +51,23 @@ import org.apache.james.mailbox.MailboxN
 import org.apache.james.mailbox.MailboxPath;
 import org.apache.james.mailbox.MailboxSession;
 import org.apache.james.mailbox.MessageManager;
-import org.apache.james.mailbox.MessageRange.Type;
+import org.apache.james.mailbox.MessageManager.MetaData.FetchGroup;
+import org.apache.james.mailbox.MessageRangeException;
 import org.apache.james.mailbox.SearchQuery;
 import org.apache.james.mailbox.MessageManager.MetaData;
-import org.apache.james.mailbox.SearchQuery.NumericRange;
 import org.apache.james.mailbox.MessageRange;
 import org.apache.james.mailbox.MessageResult;
 
-abstract class AbstractSelectionProcessor<M extends AbstractMailboxSelectionRequest> extends AbstractMailboxProcessor<M> {
+abstract class AbstractSelectionProcessor<M extends AbstractMailboxSelectionRequest> extends AbstractMailboxProcessor<M> implements PermitEnableCapabilityProcessor {
 
     private final Flags flags = new Flags();
 
     final StatusResponseFactory statusResponseFactory;
 
     private final boolean openReadOnly;
+    private final List<String> CAPS = Collections.unmodifiableList(Arrays.asList(ImapConstants.SUPPORTS_QRESYNC, ImapConstants.SUPPORTS_CONDSTORE));
 
+    
     public AbstractSelectionProcessor(final Class<M> acceptableClass, final ImapProcessor next, final MailboxManager mailboxManager, final StatusResponseFactory statusResponseFactory, final boolean openReadOnly) {
         super(acceptableClass, next, mailboxManager, statusResponseFactory);
         this.statusResponseFactory = statusResponseFactory;
@@ -99,12 +103,20 @@ abstract class AbstractSelectionProcesso
         } catch (MailboxException e) {
             session.getLog().debug("Select failed", e);
             no(command, tag, responder, HumanReadableText.SELECT);
+        } catch (MessageRangeException e) {
+            session.getLog().debug("Select failed", e);
+            no(command, tag, responder, HumanReadableText.SELECT);
+            
         }
     }
 
-    private void respond(String tag, ImapCommand command, ImapSession session, MailboxPath fullMailboxPath, AbstractMailboxSelectionRequest request, Responder responder) throws MailboxException {
+    private void respond(String tag, ImapCommand command, ImapSession session, MailboxPath fullMailboxPath, AbstractMailboxSelectionRequest request, Responder responder) throws MailboxException, MessageRangeException {
         
         Long lastKnownUidValidity = request.getLastKnownUidValidity();
+        Long modSeq = request.getKnownModSeq();
+        IdRange[] knownSequences = request.getKnownSequenceSet();
+        IdRange[] knownUids = request.getKnownUidSet();
+
         // Check if a QRESYNC parameter was used and if so if QRESYNC was enabled before.
         // If it was not enabled before its needed to return a BAD response
         //
@@ -156,70 +168,142 @@ abstract class AbstractSelectionProcesso
 
                 if (uidSet == null) {
                     // See mailbox had some messages stored before, if not we don't need to query at all
-                    if (metaData.getUidNext() != 1) {
-                        uidSet = new IdRange[] {new IdRange(1, selected.getLastUid())};
+                    long uidNext = metaData.getUidNext();
+                    if ( uidNext != 1) {
+                        // Use UIDNEXT -1 as max uid as stated in the QRESYNC RFC
+                        uidSet = new IdRange[] {new IdRange(1, uidNext -1)};
                     }
                 }
-                // Check if the know uid set was provided. If so we only need to get the uids of these messages that matched here
+                
                 if (uidSet != null) {
-                    NumericRange[] nranges = new NumericRange[uidSet.length];
-
-                    for (int i = 0 ; i < uidSet.length; i++) {
-                        IdRange r = uidSet[i];
-                        long low = r.getLowVal();
-                        long high = r.getHighVal();
-                        if (low == high) {
-                            nranges[i] = new NumericRange(low);
-                        } else {
-                            nranges[i] = new NumericRange(low, high);
+                    // RFC5162 3.1. QRESYNC Parameter to SELECT/EXAMINE
+                    //
+                    // Message sequence match data:
+                    //
+                    //      A client MAY provide a parenthesized list of a message sequence set
+                    //      and the corresponding UID sets.  Both MUST be provided in ascending
+                    //      order.  The server uses this data to restrict the range for which it
+                    //      provides expunged message information.
+                    //
+                    //
+                    //      Conceptually, the client provides a small sample of sequence numbers
+                    //      for which it knows the corresponding UIDs.  The server then compares
+                    //      each sequence number and UID pair the client provides with the
+                    //      current state of the mailbox.  If a pair matches, then the client
+                    //      knows of any expunges up to, and including, the message, and thus
+                    //      will not include that range in the VANISHED response, even if the
+                    //      "mod-sequence-value" provided by the client is too old for the server
+                    //      to have data of when those messages were expunged.
+                    //
+                    //      Thus, if the Nth message number in the first set in the list is 4,
+                    //      and the Nth UID in the second set in the list is 8, and the mailbox's
+                    //      fourth message has UID 8, then no UIDs equal to or less than 8 are
+                    //      present in the VANISHED response.  If the (N+1)th message number is
+                    //      12, and the (N+1)th UID is 24, and the (N+1)th message in the mailbox
+                    //      has UID 25, then the lowest UID included in the VANISHED response
+                    //      would be 9.
+                    if (knownSequences != null && knownUids != null) {
+                        
+                        // Add all uids which are contained in the knownuidsset to a List so we can later access them via the index
+                        List<Long> knownUidsList = new ArrayList<Long>();
+                        for (int a = 0; a < knownUids.length; a++) {
+                            Iterator<Long> it =  knownUids[a].iterator();
+                            
+                            while(it.hasNext()) {
+                                knownUidsList.add(it.next());
+                            }
                         }
-                    
-                    }
-                    sq.andCriteria(SearchQuery.uid(nranges));
-                }
-
-                Iterator<Long> uidsIt = mailbox.search(sq, mailboxSession);
-                List<Long> uids = new ArrayList<Long>();
-                while(uidsIt.hasNext()) {
-                    uids.add(uidsIt.next());
-                }
-                if (uids.isEmpty() == false) {
-                    if (uidSet != null) {
-                        List<Long> vanished = new ArrayList<Long>();
-                        for (int i = 0; i < uids.size(); i++) {
-                            long uid = uids.get(i);
-                            boolean match = false;
-                            for (int a = 0; a < uidSet.length; a++) {
-                                if (uidSet[a].includes(uid)) {
-                                    match = true;
+                       
+                        
+                        
+                        // loop over the known sequences and check the UID for MSN X again the known UID X 
+                        long firstUid = 1;
+                        int index = 0;
+                        for (int a = 0; a < knownSequences.length; a++) {
+                            boolean done = false;
+                            Iterator<Long> it =  knownSequences[a].iterator();
+                            while(it.hasNext()) {
+                                
+                                // Check if we have uids left to check against
+                                if (knownUidsList.size() > index++) {
+                                    int msn = it.next().intValue();
+                                    long knownUid = knownUidsList.get(index);
+                                    
+                                    // Check if the uid mathc if not we are done here
+                                    if (selected.uid(msn) != knownUid) {
+                                        done = true;
+                                        break;
+                                    } else {
+                                        firstUid = knownUid;
+                                    }
+                                    
+                                } else {
+                                    done = true;
                                     break;
                                 }
+
                             }
-                            if (!match) {
-                                vanished.add(uid);
-                            }
-                        }
-                        if (!vanished.isEmpty()) {
-                            List<MessageRange> ranges = MessageRange.toRanges(vanished);
-                            IdRange[] idRanges  = new IdRange[ranges.size()];
-                            for (int i = 0; i < ranges.size(); i++) {
-                                MessageRange r = ranges.get(i);
-                                if (r.getType() == Type.ONE) {
-                                    idRanges[i] = new IdRange(r.getUidFrom());
-                                } else {
-                                    idRanges[i] = new IdRange(r.getUidFrom(), r.getUidTo());
+                            
+                            // We found the first uid to start with 
+                            if (done) {
+                                firstUid++;
+                                
+                                // Ok now its time to filter out the IdRanges which we are not interested in
+                                List<IdRange> filteredUidSet = new ArrayList<IdRange>();
+                                for ( int i = 0; i < uidSet.length; i++) {
+                                    IdRange r = uidSet[i];
+                                    if (r.getLowVal() < firstUid) {
+                                        if (r.getHighVal() > firstUid) {
+                                            r.setLowVal(firstUid);
+                                            filteredUidSet.add(r);
+                                        }
+                                    } else {
+                                        filteredUidSet.add(r);
+                                    }
                                 }
+                                uidSet = filteredUidSet.toArray(new IdRange[0]);
                                 
+                                break;
                             }
-                            //TODO: Send also VANISHED responses as stated in QRESYNC RFC
-                            responder.respond(new VanishedResponse(idRanges, true));
                         }
-                      
+                        
+                        
                     }
-                    List<MessageRange> ranges = MessageRange.toRanges(uids);
-                    for (int i = 0; i < ranges.size(); i++) {
-                        addFlagsResponses(session, selected, responder, true, ranges.get(i), mailbox, mailboxSession);
+                    
+                    List<MessageRange> ranges = new ArrayList<MessageRange>();
+                    for (int i = 0; i < uidSet.length; i++) {
+                        MessageRange messageSet = messageRange(session.getSelected(), uidSet[i], true);
+                        if (messageSet != null) {
+                            MessageRange normalizedMessageSet = normalizeMessageRange(session.getSelected(), messageSet);
+                            ranges.add(normalizedMessageSet);
+                        }
                     }
+                    
+                    
+                    
+                    // TODO: Reconsider if we can do something to make the handling better. Maybe at least cache the triplets for the expunged
+                    //       while have the server running. This could maybe allow us to not return every expunged message all the time
+                    //  
+                    //      As we don't store the <<MSN, UID>, <MODSEQ>> in a permanent way its the best to just ignore it here.
+                    //
+                    //      From RFC5162 4.1. Server Implementations That Don't Store Extra State
+                    //
+                    //
+                    //          Strictly speaking, a server implementation that doesn't remember mod-
+                    //          sequences associated with expunged messages can be considered
+                    //          compliant with this specification.  Such implementations return all
+                    //          expunged messages specified in the UID set of the UID FETCH
+                    //          (VANISHED) command every time, without paying attention to the
+                    //          specified CHANGEDSINCE mod-sequence.  Such implementations are
+                    //          discouraged, as they can end up returning VANISHED responses that are
+                    //          bigger than the result of a UID SEARCH command for the same UID set.
+                    //
+                    //          Clients that use the message sequence match data can reduce the scope
+                    //          of this VANISHED response substantially in the typical case where
+                    //          expunges have not happened, or happen only toward the end of the
+                    //          mailbox.
+                    //
+                    respondVanished(mailboxSession, mailbox, ranges, modSeq, metaData , responder);
                 }
                 taggedOk(responder, tag, command, metaData, HumanReadableText.SELECT);
             } else {
@@ -229,15 +313,13 @@ abstract class AbstractSelectionProcesso
         } else {
             taggedOk(responder, tag, command, metaData, HumanReadableText.SELECT);
         }
-        
-        
-        
-        
+
         // Reset the saved sequence-set after successful SELECT / EXAMINE
         // See RFC 5812 2.1. Normative Description of the SEARCHRES Extension
         SearchResUtil.resetSavedSequenceSet(session);
     }
 
+
     private void highestModSeq(Responder responder, MetaData metaData, SelectedMailbox selected) {
         final StatusResponse untaggedOk;
         if (metaData.isModSeqPermanent()) {
@@ -317,6 +399,9 @@ abstract class AbstractSelectionProcesso
             if (currentMailbox != null) {
                 getStatusResponseFactory().untaggedOk(HumanReadableText.QRESYNC_CLOSED, ResponseCode.closed());
             }
+            if (condstore == false) {
+                condstore = EnableProcessor.getEnabledCapabilities(session).contains(ImapConstants.SUPPORTS_CONDSTORE);
+            }
             sessionMailbox = createNewSelectedMailbox(mailbox, mailboxSession, session, mailboxPath, condstore);
             
         } else {
@@ -355,4 +440,39 @@ abstract class AbstractSelectionProcesso
             sessionMailbox.addRecent(uid);
         }
     }
+
+    @Override
+    public List<String> getImplementedCapabilities(ImapSession session) {        
+        return CAPS;
+    }
+
+    @Override
+    public List<String> getPermitEnableCapabilities(ImapSession session) {
+        return CAPS;
+    }
+
+    @Override
+    public void enable(ImapMessage message, Responder responder, ImapSession session, String capability) throws EnableException {
+
+        if (EnableProcessor.getEnabledCapabilities(session).contains(capability) == false) {
+            SelectedMailbox sm = session.getSelected();
+            // Send a HIGHESTMODSEQ response if the there was a select mailbox before and the client just enabled
+            // QRESYNC or CONDSTORE
+            //
+            // See http://www.dovecot.org/list/dovecot/2008-March/029561.html
+            if (sm != null) {
+                if (capability.equalsIgnoreCase(ImapConstants.SUPPORTS_CONDSTORE)|| capability.equalsIgnoreCase(ImapConstants.SUPPORTS_QRESYNC)) {
+                    try {
+                        MessageManager mailbox = getSelectedMailbox(session);
+                        MetaData metaData = mailbox.getMetaData(false, ImapSessionUtils.getMailboxSession(session), FetchGroup.NO_COUNT);
+                        highestModSeq(responder, metaData, sm);
+                    } catch (MailboxException e) {
+                        throw new EnableException("Unable to enable " + capability, e);
+                    }
+                }
+            }
+        }
+    }
+    
+    
 }

Modified: james/imap/trunk/processor/src/main/java/org/apache/james/imap/processor/CapabilityProcessor.java
URL: http://svn.apache.org/viewvc/james/imap/trunk/processor/src/main/java/org/apache/james/imap/processor/CapabilityProcessor.java?rev=1147281&r1=1147280&r2=1147281&view=diff
==============================================================================
--- james/imap/trunk/processor/src/main/java/org/apache/james/imap/processor/CapabilityProcessor.java (original)
+++ james/imap/trunk/processor/src/main/java/org/apache/james/imap/processor/CapabilityProcessor.java Fri Jul 15 19:01:28 2011
@@ -27,11 +27,12 @@ import static org.apache.james.imap.api.
 import static org.apache.james.imap.api.ImapConstants.SUPPORTS_CONDSTORE;
 
 import java.util.ArrayList;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
 
 import org.apache.james.imap.api.ImapCommand;
 import org.apache.james.imap.api.display.CharsetUtil;
-import org.apache.james.imap.api.message.response.ImapResponseMessage;
 import org.apache.james.imap.api.message.response.StatusResponseFactory;
 import org.apache.james.imap.api.process.ImapProcessor;
 import org.apache.james.imap.api.process.ImapSession;
@@ -41,18 +42,17 @@ import org.apache.james.mailbox.MailboxM
 
 public class CapabilityProcessor extends AbstractMailboxProcessor<CapabilityRequest> implements CapabilityImplementingProcessor {
 
-    private final List<CapabilityImplementingProcessor> capabilities = new ArrayList<CapabilityImplementingProcessor>();
+    private static final List<CapabilityImplementingProcessor> capabilities = new ArrayList<CapabilityImplementingProcessor>();
 
     public CapabilityProcessor(final ImapProcessor next, final MailboxManager mailboxManager, final StatusResponseFactory factory, final List<CapabilityImplementingProcessor> capabilities) {
         this(next, mailboxManager, factory);
-        this.capabilities.addAll(capabilities);
+        CapabilityProcessor.capabilities.addAll(capabilities);
 
     }
 
     public CapabilityProcessor(final ImapProcessor next, final MailboxManager mailboxManager, final StatusResponseFactory factory) {
         super(CapabilityRequest.class, next, mailboxManager, factory);
-        this.capabilities.add(this);
-
+        capabilities.add(this);
     }
 
     /*
@@ -66,21 +66,12 @@ public class CapabilityProcessor extends
      * org.apache.james.imap.api.process.ImapProcessor.Responder)
      */
     protected void doProcess(CapabilityRequest request, ImapSession session, String tag, ImapCommand command, Responder responder) {
-        final ImapResponseMessage result = doProcess(request, session, tag, command);
+        final CapabilityResponse result = new CapabilityResponse(getSupportedCapabilities(session));        
         responder.respond(result);
         unsolicitedResponses(session, responder, false);
         okComplete(command, tag, responder);
     }
 
-    private ImapResponseMessage doProcess(CapabilityRequest request, ImapSession session, String tag, ImapCommand command) {
-        List<String> caps = new ArrayList<String>();
-        for (int i = 0; i < capabilities.size(); i++) {
-            caps.addAll(capabilities.get(i).getImplementedCapabilities(session));
-        }
-        final CapabilityResponse result = new CapabilityResponse(caps);
-        return result;
-    }
-
     /**
      * Add a {@link CapabilityImplementor} which will get queried for
      * implemented capabilities
@@ -110,5 +101,20 @@ public class CapabilityProcessor extends
         capabilities.add(SUPPORTS_CONDSTORE);
         return capabilities;
     }
+    
+    /**
+     * Return all supported <code>CAPABILITIES</code> for this {@link ImapSession}
+     * 
+     * @param session
+     * @return supported
+     */
+    public static Set<String> getSupportedCapabilities(ImapSession session) {
+        Set<String> caps = new HashSet<String>();
+        for (int i = 0; i < capabilities.size(); i++) {
+            caps.addAll(capabilities.get(i).getImplementedCapabilities(session));
+        }
+        return caps;
+    }
+    
 
 }

Modified: james/imap/trunk/processor/src/main/java/org/apache/james/imap/processor/DefaultProcessorChain.java
URL: http://svn.apache.org/viewvc/james/imap/trunk/processor/src/main/java/org/apache/james/imap/processor/DefaultProcessorChain.java?rev=1147281&r1=1147280&r2=1147281&view=diff
==============================================================================
--- james/imap/trunk/processor/src/main/java/org/apache/james/imap/processor/DefaultProcessorChain.java (original)
+++ james/imap/trunk/processor/src/main/java/org/apache/james/imap/processor/DefaultProcessorChain.java Fri Jul 15 19:01:28 2011
@@ -75,7 +75,8 @@ public class DefaultProcessorChain {
 
         final CompressProcessor compressProcessor = new CompressProcessor(unselectProcessor, statusResponseFactory);
         final EnableProcessor enableProcessor = new EnableProcessor(compressProcessor, mailboxManager, statusResponseFactory);
-        
+        // add for QRESYNC
+        enableProcessor.addProcessor(selectProcessor);
         
         capabilityProcessor.addProcessor(startTLSProcessor);
         capabilityProcessor.addProcessor(idleProcessor);
@@ -95,6 +96,9 @@ public class DefaultProcessorChain {
         // add to announnce ENABLE
         capabilityProcessor.addProcessor(enableProcessor);
         
+        // Add to announce QRESYNC
+        capabilityProcessor.addProcessor(selectProcessor);
+
         return enableProcessor;
 
     }

Modified: james/imap/trunk/processor/src/main/java/org/apache/james/imap/processor/EnableProcessor.java
URL: http://svn.apache.org/viewvc/james/imap/trunk/processor/src/main/java/org/apache/james/imap/processor/EnableProcessor.java?rev=1147281&r1=1147280&r2=1147281&view=diff
==============================================================================
--- james/imap/trunk/processor/src/main/java/org/apache/james/imap/processor/EnableProcessor.java (original)
+++ james/imap/trunk/processor/src/main/java/org/apache/james/imap/processor/EnableProcessor.java Fri Jul 15 19:01:28 2011
@@ -27,17 +27,19 @@ import java.util.List;
 import java.util.Set;
 
 import org.apache.james.imap.api.ImapCommand;
+import org.apache.james.imap.api.display.HumanReadableText;
 import org.apache.james.imap.api.message.response.StatusResponseFactory;
 import org.apache.james.imap.api.process.ImapProcessor;
 import org.apache.james.imap.api.process.ImapSession;
 import org.apache.james.imap.message.request.EnableRequest;
 import org.apache.james.imap.message.response.EnableResponse;
+import org.apache.james.imap.processor.PermitEnableCapabilityProcessor.EnableException;
 import org.apache.james.mailbox.MailboxManager;
 
 public class EnableProcessor extends AbstractMailboxProcessor<EnableRequest> implements CapabilityImplementingProcessor {
 
     private final List<PermitEnableCapabilityProcessor> capabilities = new ArrayList<PermitEnableCapabilityProcessor>();
-    private final static String ENABLED_CAPABILITIES = "ENABLED_CAPABILITIES";
+    public final static String ENABLED_CAPABILITIES = "ENABLED_CAPABILITIES";
     
     public EnableProcessor(final ImapProcessor next, final MailboxManager mailboxManager, final StatusResponseFactory factory, final List<PermitEnableCapabilityProcessor> capabilities) {
         this(next, mailboxManager, factory);
@@ -55,24 +57,33 @@ public class EnableProcessor extends Abs
      * @see org.apache.james.imap.processor.AbstractMailboxProcessor#doProcess(org.apache.james.imap.api.message.request.ImapRequest, org.apache.james.imap.api.process.ImapSession, java.lang.String, org.apache.james.imap.api.ImapCommand, org.apache.james.imap.api.process.ImapProcessor.Responder)
      */
     protected void doProcess(EnableRequest request, ImapSession session, String tag, ImapCommand command, Responder responder) {
-        List<String> caps = request.getCapabilities();
-        Set<String> enabledCaps = new HashSet<String>();
-        for (int i = 0; i < caps.size(); i++) {
-            String cap = caps.get(i);
-            for (int a = 0; a < capabilities.size(); a++) {
-                if (capabilities.get(a).getPermitEnableCapabilities(session).contains(cap)) {
-                    enabledCaps.add(cap);
+        try {
+
+            List<String> caps = request.getCapabilities();
+            Set<String> enabledCaps = new HashSet<String>();
+            for (int i = 0; i < caps.size(); i++) {
+                String cap = caps.get(i);
+                // Check if the CAPABILITY is supported at all
+                if (CapabilityProcessor.getSupportedCapabilities(session).contains(cap)) {
+                    for (int a = 0; a < capabilities.size(); a++) {
+                        PermitEnableCapabilityProcessor enableProcessor = capabilities.get(a);
+                        if (enableProcessor.getPermitEnableCapabilities(session).contains(cap)) {
+                            enableProcessor.enable(request, responder, session, cap);
+                            enabledCaps.add(cap);
+                        }
+                    }
                 }
             }
+            getEnabledCapabilities(session).addAll(enabledCaps);
+
+            responder.respond(new EnableResponse(enabledCaps));
+
+            unsolicitedResponses(session, responder, false);
+            okComplete(command, tag, responder);
+        } catch (EnableException e) {
+            taggedBad(command, tag, responder, HumanReadableText.FAILED);
         }
-        getEnabledCapabilities(session).addAll(enabledCaps);
-        
-        responder.respond(new EnableResponse(new ArrayList<String>(enabledCaps)));
-        
-        unsolicitedResponses(session, responder, false);
-        okComplete(command, tag, responder);
     }
-
    
     /**
      * Add a {@link PermitEnableCapabilityProcessor} which can be enabled
@@ -83,6 +94,12 @@ public class EnableProcessor extends Abs
         capabilities.add(implementor);
     }
 
+    /**
+     * Return all enabled <code>CAPABILITIES</code> for this {@link ImapSession}
+     * 
+     * @param session
+     * @return enabled
+     */
     @SuppressWarnings("unchecked")
     public static Set<String> getEnabledCapabilities(ImapSession session) {
         Set<String> caps = (Set<String>) session.getAttribute(ENABLED_CAPABILITIES);

Modified: james/imap/trunk/processor/src/main/java/org/apache/james/imap/processor/ExpungeProcessor.java
URL: http://svn.apache.org/viewvc/james/imap/trunk/processor/src/main/java/org/apache/james/imap/processor/ExpungeProcessor.java?rev=1147281&r1=1147280&r2=1147281&view=diff
==============================================================================
--- james/imap/trunk/processor/src/main/java/org/apache/james/imap/processor/ExpungeProcessor.java (original)
+++ james/imap/trunk/processor/src/main/java/org/apache/james/imap/processor/ExpungeProcessor.java Fri Jul 15 19:01:28 2011
@@ -56,19 +56,20 @@ public class ExpungeProcessor extends Ab
             final MessageManager mailbox = getSelectedMailbox(session);
             final MailboxSession mailboxSession = ImapSessionUtils.getMailboxSession(session);
 
+            int expunged = 0;
             if (!mailbox.isWriteable(mailboxSession)) {
                 no(command, tag, responder, HumanReadableText.MAILBOX_IS_READ_ONLY);
             } else {
                 IdRange[] ranges = request.getUidSet();
                 if (ranges == null) {
-                    expunge(mailbox, MessageRange.all(), session, mailboxSession);
+                   expunged = expunge(mailbox, MessageRange.all(), session, mailboxSession);
                 } else {
                     // Handle UID EXPUNGE which is part of UIDPLUS
                     // See http://tools.ietf.org/html/rfc4315
                     for (int i = 0; i < ranges.length; i++) {
                         MessageRange mRange = messageRange(session.getSelected(), ranges[i], true);
                         if (mRange != null) {
-                            expunge(mailbox, mRange, session, mailboxSession);
+                            expunged += expunge(mailbox, mRange, session, mailboxSession);
                         }
 
                     }
@@ -77,10 +78,10 @@ public class ExpungeProcessor extends Ab
                 unsolicitedResponses(session, responder, false);
                 
                 
-                // Check if UID EXPUNGE was used and QRESYNC was enabled. If so we need to respond with an OK response that contain the HIGHESTMODSEQ
+                // Check if QRESYNC was enabled and at least one message was expunged. If so we need to respond with an OK response that contain the HIGHESTMODSEQ
                 //
-                // See RFC5162 3.5. UID EXPUNGE Command
-                if (ranges != null && EnableProcessor.getEnabledCapabilities(session).contains(ImapConstants.SUPPORTS_QRESYNC)) {
+                // See RFC5162 3.3 EXPUNGE Command 3.5. UID EXPUNGE Command
+                if (EnableProcessor.getEnabledCapabilities(session).contains(ImapConstants.SUPPORTS_QRESYNC)  && expunged > 0) {
                     MetaData mdata = mailbox.getMetaData(false, mailboxSession, FetchGroup.NO_COUNT);
                     okComplete(command, tag, ResponseCode.highestModSeq(mdata.getHighestModSeq()), responder);
                 } else {
@@ -96,15 +97,18 @@ public class ExpungeProcessor extends Ab
         }
     }
 
-    private void expunge(MessageManager mailbox, MessageRange range, ImapSession session, MailboxSession mailboxSession) throws MailboxException {
+    private int expunge(MessageManager mailbox, MessageRange range, ImapSession session, MailboxSession mailboxSession) throws MailboxException {
         final Iterator<Long> it = mailbox.expunge(range, mailboxSession);
         final SelectedMailbox selected = session.getSelected();
+        int expunged = 0;
         if (mailboxSession != null) {
             while (it.hasNext()) {
                 final long uid = it.next();
                 selected.removeRecent(uid);
+                expunged++;
             }
         }
+        return expunged;
     }
 
     /*

Modified: james/imap/trunk/processor/src/main/java/org/apache/james/imap/processor/PermitEnableCapabilityProcessor.java
URL: http://svn.apache.org/viewvc/james/imap/trunk/processor/src/main/java/org/apache/james/imap/processor/PermitEnableCapabilityProcessor.java?rev=1147281&r1=1147280&r2=1147281&view=diff
==============================================================================
--- james/imap/trunk/processor/src/main/java/org/apache/james/imap/processor/PermitEnableCapabilityProcessor.java (original)
+++ james/imap/trunk/processor/src/main/java/org/apache/james/imap/processor/PermitEnableCapabilityProcessor.java Fri Jul 15 19:01:28 2011
@@ -20,6 +20,7 @@ package org.apache.james.imap.processor;
 
 import java.util.List;
 
+import org.apache.james.imap.api.ImapMessage;
 import org.apache.james.imap.api.process.ImapSession;
 
 /**
@@ -36,5 +37,35 @@ public interface PermitEnableCapabilityP
      * @return permitCaps
      */
     public List<String> getPermitEnableCapabilities(ImapSession session);
+    
+    /**
+     * Callback which is used when a ENABLED command was used to enable on of the CAPABILITIES which is managed by this implementation
+     * 
+     * @param message
+     * @param responder
+     * @param session
+     * @param capability
+     * @throws EnableException
+     */
+    public void enable(ImapMessage message, Responder responder, ImapSession session, String capability) throws EnableException;
+
+    /**
+     * Exception which should get thrown if for whatever reason its not possible to enable a capability
+     * 
+     *
+     */
+    public final static class EnableException extends Exception {
+        /**
+         * 
+         */
+        private static final long serialVersionUID = -4456052968041000753L;
 
+        public EnableException(String msg, Throwable e) {
+            super(msg, e);
+        }
+        
+        public EnableException(Throwable e) {
+            super(e);
+        }
+    }
 }

Modified: james/imap/trunk/processor/src/main/java/org/apache/james/imap/processor/StoreProcessor.java
URL: http://svn.apache.org/viewvc/james/imap/trunk/processor/src/main/java/org/apache/james/imap/processor/StoreProcessor.java?rev=1147281&r1=1147280&r2=1147281&view=diff
==============================================================================
--- james/imap/trunk/processor/src/main/java/org/apache/james/imap/processor/StoreProcessor.java (original)
+++ james/imap/trunk/processor/src/main/java/org/apache/james/imap/processor/StoreProcessor.java Fri Jul 15 19:01:28 2011
@@ -25,10 +25,12 @@ import java.util.HashMap;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 
 import javax.mail.Flags;
 
 import org.apache.james.imap.api.ImapCommand;
+import org.apache.james.imap.api.ImapConstants;
 import org.apache.james.imap.api.ImapSessionUtils;
 import org.apache.james.imap.api.display.HumanReadableText;
 import org.apache.james.imap.api.message.IdRange;
@@ -152,10 +154,10 @@ public class StoreProcessor extends Abst
                         }
                         List<MessageRange> mRanges = MessageRange.toRanges(uids);
                         for (int a = 0 ; a < mRanges.size(); a++) {
-                            setFlags(request, mailboxSession, mailbox, mRanges.get(a), selected, tag, imapCommand, responder);
+                            setFlags(request, mailboxSession, mailbox, mRanges.get(a), session, tag, imapCommand, responder);
                         }
                     } else {
-                        setFlags(request, mailboxSession, mailbox, messageSet, selected, tag, imapCommand, responder);
+                        setFlags(request, mailboxSession, mailbox, messageSet, session, tag, imapCommand, responder);
                     }
                     
                 }
@@ -210,7 +212,7 @@ public class StoreProcessor extends Abst
      * @param responder
      * @throws MailboxException
      */
-    private void setFlags(StoreRequest request, MailboxSession mailboxSession, MessageManager mailbox, MessageRange messageSet, SelectedMailbox selected, String tag, ImapCommand command, Responder responder) throws MailboxException {
+    private void setFlags(StoreRequest request, MailboxSession mailboxSession, MessageManager mailbox, MessageRange messageSet, ImapSession session, String tag, ImapCommand command, Responder responder) throws MailboxException {
         
         final Flags flags = request.getFlags();
         final boolean useUids = request.isUseUids();
@@ -231,6 +233,7 @@ public class StoreProcessor extends Abst
             value = true;
         }
         
+        SelectedMailbox selected = session.getSelected();
         final Map<Long, Flags> flagsByUid = mailbox.setFlags(flags, value, replace, messageSet, mailboxSession);
         // As the STORE command is allowed to create a new "flag/keyword", we need to send a FLAGS and PERMANENTFLAGS response before the FETCH response
         // if some new flag/keyword was used
@@ -241,11 +244,21 @@ public class StoreProcessor extends Abst
             selected.resetNewApplicableFlags();
         }
         
-        if (!silent || unchangedSince != -1) {
+        Set<String> enabled = EnableProcessor.getEnabledCapabilities(session);
+        boolean qresyncEnabled = enabled.contains(ImapConstants.SUPPORTS_QRESYNC);
+        boolean condstoreEnabled = enabled.contains(ImapConstants.SUPPORTS_CONDSTORE);
+        
+        if (!silent || unchangedSince != -1 || qresyncEnabled || condstoreEnabled) {
             final Map<Long, Long> modSeqs = new HashMap<Long, Long>();
            
             // Check if we need to also send the the mod-sequences back to the client
-            if (unchangedSince != -1) {
+            //
+            // This is the case if one of these is true:
+            //      - UNCHANGEDSINCE was used
+            //      - CONDSTORE was enabled via ENABLE CONDSTORE
+            //      - QRESYNC was enabled via ENABLE QRESYNC
+            //
+            if (unchangedSince != -1 || qresyncEnabled || condstoreEnabled) {
                 Iterator<MessageResult> results = mailbox.getMessages(messageSet, FetchGroupImpl.MINIMAL, mailboxSession);
                 while(results.hasNext()) {
                     MessageResult r = results.next();
@@ -263,7 +276,13 @@ public class StoreProcessor extends Abst
 
                 final Flags resultFlags = entry.getValue();
                 final Long resultUid;
-                if (useUids) {
+                
+                // Check if we need to include the uid. T
+                //
+                // This is the case if one of these is true:
+                //      - FETCH (UID...)  was used
+                //      - QRESYNC was enabled via ENABLE QRESYNC
+                if (useUids || qresyncEnabled) {
                     resultUid = uid;
                 } else {
                     resultUid = null;
@@ -278,10 +297,10 @@ public class StoreProcessor extends Abst
                 // For more informations related to the FETCH response see
                 //
                 // RFC4551 3.2. STORE and UID STORE Commands
-                if (silent && unchangedSince != -1) {
+                if (silent && (unchangedSince != -1 || qresyncEnabled || condstoreEnabled)) {
                     // We need to return an FETCH response which contains the mod-sequence of the message even if FLAGS.SILENT was used
                     response = new FetchResponse(msn, null, resultUid, modSeqs.get(resultUid), null, null, null, null, null, null);
-                } else if (!silent && unchangedSince != -1){
+                } else if (!silent && (unchangedSince != -1 || qresyncEnabled || condstoreEnabled)){
                     //
                     // Use a FETCH response which contains the mod-sequence and the flags
                     response = new FetchResponse(msn, resultFlags, resultUid, modSeqs.get(resultUid), null, null, null, null, null, null);

Modified: james/imap/trunk/processor/src/main/java/org/apache/james/imap/processor/fetch/FetchProcessor.java
URL: http://svn.apache.org/viewvc/james/imap/trunk/processor/src/main/java/org/apache/james/imap/processor/fetch/FetchProcessor.java?rev=1147281&r1=1147280&r2=1147281&view=diff
==============================================================================
--- james/imap/trunk/processor/src/main/java/org/apache/james/imap/processor/fetch/FetchProcessor.java (original)
+++ james/imap/trunk/processor/src/main/java/org/apache/james/imap/processor/fetch/FetchProcessor.java Fri Jul 15 19:01:28 2011
@@ -36,7 +36,6 @@ import org.apache.james.imap.api.process
 import org.apache.james.imap.api.process.ImapSession;
 import org.apache.james.imap.message.request.FetchRequest;
 import org.apache.james.imap.message.response.FetchResponse;
-import org.apache.james.imap.message.response.VanishedResponse;
 import org.apache.james.imap.processor.AbstractMailboxProcessor;
 import org.apache.james.imap.processor.EnableProcessor;
 import org.apache.james.imap.processor.base.FetchGroupImpl;
@@ -77,6 +76,8 @@ public class FetchProcessor extends Abst
         final FetchData fetch = request.getFetch();
         
         try {
+            final Long changedSince = fetch.getChangedSince();
+
             final MessageManager mailbox = getSelectedMailbox(session);
 
             if (mailbox == null) {
@@ -89,7 +90,7 @@ public class FetchProcessor extends Abst
                 return;
             }
            
-            if (vanished && fetch.getChangedSince() == -1) {
+            if (vanished && changedSince == -1) {
                 taggedBad(command, tag, responder, HumanReadableText.QRESYNC_VANISHED_WITHOUT_CHANGEDSINCE);
                 return;
             }
@@ -105,9 +106,10 @@ public class FetchProcessor extends Abst
                 }
             }
 
-            if (vanished) {
-                IdRange[] vanishedRanges = getNotIncluded(ranges, idSet);
-                responder.respond(new VanishedResponse(vanishedRanges, true));
+            if (vanished ) {
+                // TODO: From the QRESYNC RFC it seems ok to send the VANISHED responses after the FETCH Responses. 
+                //       If we do so we could prolly save one mailbox access which should give use some more speed up
+                respondVanished(mailboxSession, mailbox, ranges, changedSince, mailbox.getMetaData(false, mailboxSession, org.apache.james.mailbox.MessageManager.MetaData.FetchGroup.NO_COUNT), responder);
             }
             processMessageRanges(session, mailbox, ranges, fetch, useUids, mailboxSession, responder);
 
@@ -127,16 +129,8 @@ public class FetchProcessor extends Abst
         }
     }
 
-    /**
-     * TODO: implement me
-     * 
-     * @param ranges
-     * @param ids
-     * @return vanished
-     */
-    private IdRange[] getNotIncluded(List<MessageRange> ranges, IdRange[] ids) {
-        return new IdRange[0];
-    }
+
+    
     /**
      * Process the given message ranges by fetch them and pass them to the
      * {@link Responder}

Modified: james/imap/trunk/processor/src/test/java/org/apache/james/imap/processor/NamespaceProcessorTest.java
URL: http://svn.apache.org/viewvc/james/imap/trunk/processor/src/test/java/org/apache/james/imap/processor/NamespaceProcessorTest.java?rev=1147281&r1=1147280&r2=1147281&view=diff
==============================================================================
--- james/imap/trunk/processor/src/test/java/org/apache/james/imap/processor/NamespaceProcessorTest.java (original)
+++ james/imap/trunk/processor/src/test/java/org/apache/james/imap/processor/NamespaceProcessorTest.java Fri Jul 15 19:01:28 2011
@@ -86,6 +86,9 @@ public class NamespaceProcessorTest {
         mockery.checking (new Expectations() {{
             allowing(imapSessionStub).supportMultipleNamespaces(); will(returnValue(true));
             allowing(imapSessionStub).getAttribute(ImapSessionUtils.MAILBOX_SESSION_ATTRIBUTE_SESSION_KEY); will(returnValue(mailboxSessionStub));
+            allowing(imapSessionStub).getAttribute(EnableProcessor.ENABLED_CAPABILITIES); will(returnValue(null));
+            allowing(any(ImapSession.class)).method("setAttribute");
+
             allowing(mailboxSessionStub).getPersonalSpace(); will(returnValue(PERSONAL_PREFIX));
             allowing(mailboxSessionStub).getOtherUsersSpace(); will(returnValue(USERS_PREFIX));
             allowing(mailboxSessionStub).getSharedSpaces();will(returnValue(new ArrayList<String>()));
@@ -112,6 +115,8 @@ public class NamespaceProcessorTest {
         mockery.checking (new Expectations() {{
             allowing(imapSessionStub).supportMultipleNamespaces(); will(returnValue(true));
             allowing(imapSessionStub).getAttribute(ImapSessionUtils.MAILBOX_SESSION_ATTRIBUTE_SESSION_KEY); will(returnValue(mailboxSessionStub));
+            allowing(imapSessionStub).getAttribute(EnableProcessor.ENABLED_CAPABILITIES); will(returnValue(null));
+            allowing(any(ImapSession.class)).method("setAttribute");
             allowing(mailboxSessionStub).getPersonalSpace(); will(returnValue(PERSONAL_PREFIX));
             allowing(mailboxSessionStub).getOtherUsersSpace(); will(returnValue(USERS_PREFIX));
             allowing(mailboxSessionStub).getSharedSpaces();will(returnValue(Arrays.asList(SHARED_PREFIX)));



---------------------------------------------------------------------
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