Author: norman
Date: Sun Jan 30 12:38:15 2011
New Revision: 1065257

URL: http://svn.apache.org/viewvc?rev=1065257&view=rev
Log:
Add better support for UIDPLUS. See IMAP-252. Thanks to Wojtek Strzalka for the 
patch which was applied with one change to remove dependency on commons-lang

Modified:
    
james/imap/trunk/api/src/main/java/org/apache/james/imap/api/message/IdRange.java
    
james/imap/trunk/api/src/main/java/org/apache/james/imap/api/message/response/StatusResponse.java
    
james/imap/trunk/message/src/main/java/org/apache/james/imap/encode/ImapResponseComposer.java
    
james/imap/trunk/message/src/main/java/org/apache/james/imap/encode/StatusResponseEncoder.java
    
james/imap/trunk/message/src/main/java/org/apache/james/imap/encode/base/ImapResponseComposerImpl.java
    
james/imap/trunk/message/src/test/java/org/apache/james/imap/encode/AbstractTestImapResponseComposer.java
    
james/imap/trunk/message/src/test/java/org/apache/james/imap/encode/base/ImapResponseComposerImplTest.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/AppendProcessor.java
    
james/imap/trunk/processor/src/main/java/org/apache/james/imap/processor/CopyProcessor.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=1065257&r1=1065256&r2=1065257&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
 Sun Jan 30 12:38:15 2011
@@ -113,6 +113,13 @@ public class IdRange {
         return retValue;
     }
     
+    public String getFormattedString() {
+       if(this._lowVal == this._highVal)
+               return Long.toString(this._lowVal);
+       else
+               return this._lowVal+":"+this._highVal;
+    }
+    
     /**
      * Utility method which will copy the given {@link List} and try to merge 
the
      * {@link IdRange} in the copy before return it.

Modified: 
james/imap/trunk/api/src/main/java/org/apache/james/imap/api/message/response/StatusResponse.java
URL: 
http://svn.apache.org/viewvc/james/imap/trunk/api/src/main/java/org/apache/james/imap/api/message/response/StatusResponse.java?rev=1065257&r1=1065256&r2=1065257&view=diff
==============================================================================
--- 
james/imap/trunk/api/src/main/java/org/apache/james/imap/api/message/response/StatusResponse.java
 (original)
+++ 
james/imap/trunk/api/src/main/java/org/apache/james/imap/api/message/response/StatusResponse.java
 Sun Jan 30 12:38:15 2011
@@ -19,6 +19,7 @@
 
 package org.apache.james.imap.api.message.response;
 
+import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
 
@@ -26,6 +27,7 @@ import javax.mail.Flags;
 
 import org.apache.james.imap.api.ImapCommand;
 import org.apache.james.imap.api.display.HumanReadableText;
+import org.apache.james.imap.api.message.IdRange;
 import org.apache.james.imap.api.message.MessageFlags;
 
 /**
@@ -127,6 +129,33 @@ public interface StatusResponse extends 
         private static final ResponseCode TRYCREATE = new ResponseCode(
                 "TRYCREATE");
 
+        /** RFC4315 <code>APPENDUID</code> response code */
+        public static final ResponseCode appendUid(long uidValidity, IdRange[] 
uids) {
+            String uidParam = formatRanges(uids);
+            return new ResponseCode("APPENDUID", Arrays.asList(uidParam), 
uidValidity, false);
+        }
+
+        /** RFC4315 <code>COPYUID</code> response code */
+        public static final ResponseCode copyUid(long uidValidity, IdRange[] 
sourceRanges, IdRange[] targetRanges) {
+            String source = formatRanges(sourceRanges);
+            String target = formatRanges(targetRanges);
+
+            return new ResponseCode("COPYUID", Arrays.asList(new String[] { 
source, target }), uidValidity, false);
+        }
+
+        private static String formatRanges(IdRange[] ranges) {
+            if (ranges == null || ranges.length == 0)
+                return "*";
+            StringBuilder rangeBuilder = new StringBuilder();
+            for (int i = 0; i < ranges.length; i++) {
+                rangeBuilder.append(ranges[i].getFormattedString());
+                if (i + 1 < ranges.length) {
+                    rangeBuilder.append(",");
+                }
+            }
+            return rangeBuilder.toString();
+        }
+
         /**
          * Creates a RFC2060 <code>ALERT</code> response code.
          * 
@@ -251,24 +280,27 @@ public interface StatusResponse extends 
         private final Collection<String> parameters;
 
         private final long number;
+        
+        private final boolean useParens;
 
         @SuppressWarnings ("unchecked")
         private ResponseCode(final String code) {
-            this(code, Collections.EMPTY_LIST, 0);
+            this(code, Collections.EMPTY_LIST, 0, true);
         }
 
         @SuppressWarnings ("unchecked")
         private ResponseCode(final String code, final long number) {
-            this(code, Collections.EMPTY_LIST, number);
+            this(code, Collections.EMPTY_LIST, number, true);
         }
 
         private ResponseCode(final String code, final Collection<String> 
parameters) {
-            this(code, parameters, 0);
+            this(code, parameters, 0, true);
         }
 
         private ResponseCode(final String code, final Collection<String> 
parameters,
-                final long number) {
+                final long number, final boolean useParens) {
             super();
+            this.useParens = useParens;
             this.code = code;
             this.parameters = parameters;
             this.number = number;
@@ -287,6 +319,10 @@ public interface StatusResponse extends 
             return number;
         }
 
+        public final boolean useParens() {
+            return useParens;
+        }
+        
         /**
          * Gets parameters for this code.
          * 

Modified: 
james/imap/trunk/message/src/main/java/org/apache/james/imap/encode/ImapResponseComposer.java
URL: 
http://svn.apache.org/viewvc/james/imap/trunk/message/src/main/java/org/apache/james/imap/encode/ImapResponseComposer.java?rev=1065257&r1=1065256&r2=1065257&view=diff
==============================================================================
--- 
james/imap/trunk/message/src/main/java/org/apache/james/imap/encode/ImapResponseComposer.java
 (original)
+++ 
james/imap/trunk/message/src/main/java/org/apache/james/imap/encode/ImapResponseComposer.java
 Sun Jan 30 12:38:15 2011
@@ -201,7 +201,7 @@ public interface ImapResponseComposer {
 
     public abstract void statusResponse(String tag, ImapCommand command,
             String type, String responseCode, Collection<String> parameters,
-            long number, String text) throws IOException;
+            boolean useParens, long number, String text) throws IOException;
 
     public abstract void statusResponse(Long messages, Long recent,
             Long uidNext, Long uidValidity, Long unseen, String mailboxName)

Modified: 
james/imap/trunk/message/src/main/java/org/apache/james/imap/encode/StatusResponseEncoder.java
URL: 
http://svn.apache.org/viewvc/james/imap/trunk/message/src/main/java/org/apache/james/imap/encode/StatusResponseEncoder.java?rev=1065257&r1=1065256&r2=1065257&view=diff
==============================================================================
--- 
james/imap/trunk/message/src/main/java/org/apache/james/imap/encode/StatusResponseEncoder.java
 (original)
+++ 
james/imap/trunk/message/src/main/java/org/apache/james/imap/encode/StatusResponseEncoder.java
 Sun Jan 30 12:38:15 2011
@@ -57,14 +57,17 @@ public class StatusResponseEncoder exten
         final String text = asString(textKey, session);
         final Collection<String> parameters;
         final long number;
+        final boolean useParens;
         if (responseCode == null) {
             parameters = null;
             number = 0;
+            useParens = false;
         } else {
             parameters = responseCode.getParameters();
             number = responseCode.getNumber();
+            useParens = responseCode.useParens();
         }
-        composer.statusResponse(tag, command, type, code, parameters, number,
+        composer.statusResponse(tag, command, type, code, parameters, 
useParens, number,
                 text);
     }
 

Modified: 
james/imap/trunk/message/src/main/java/org/apache/james/imap/encode/base/ImapResponseComposerImpl.java
URL: 
http://svn.apache.org/viewvc/james/imap/trunk/message/src/main/java/org/apache/james/imap/encode/base/ImapResponseComposerImpl.java?rev=1065257&r1=1065256&r2=1065257&view=diff
==============================================================================
--- 
james/imap/trunk/message/src/main/java/org/apache/james/imap/encode/base/ImapResponseComposerImpl.java
 (original)
+++ 
james/imap/trunk/message/src/main/java/org/apache/james/imap/encode/base/ImapResponseComposerImpl.java
 Sun Jan 30 12:38:15 2011
@@ -267,7 +267,7 @@ public class ImapResponseComposerImpl im
      *      java.lang.String, Collection, long, java.lang.String)
      */
     public void statusResponse(String tag, ImapCommand command, String type,
-            String responseCode, Collection<String> parameters, long number, 
String text)
+            String responseCode, Collection<String> parameters, boolean 
useParens, long number, String text)
             throws IOException {
         if (tag == null) {
             untagged();
@@ -278,16 +278,18 @@ public class ImapResponseComposerImpl im
         if (responseCode != null) {
             openSquareBracket();
             message(responseCode);
+            if (number > 0) {
+                message(number);
+            }
             if (parameters != null && !parameters.isEmpty()) {
-                openParen();
+               if(useParens)
+                       openParen();
                 for (Iterator<String> it = parameters.iterator(); 
it.hasNext();) {
                     final String parameter = it.next();
                     message(parameter);
                 }
-                closeParen();
-            }
-            if (number > 0) {
-                message(number);
+                if(useParens)
+                       closeParen();
             }
             closeSquareBracket();
         }

Modified: 
james/imap/trunk/message/src/test/java/org/apache/james/imap/encode/AbstractTestImapResponseComposer.java
URL: 
http://svn.apache.org/viewvc/james/imap/trunk/message/src/test/java/org/apache/james/imap/encode/AbstractTestImapResponseComposer.java?rev=1065257&r1=1065256&r2=1065257&view=diff
==============================================================================
--- 
james/imap/trunk/message/src/test/java/org/apache/james/imap/encode/AbstractTestImapResponseComposer.java
 (original)
+++ 
james/imap/trunk/message/src/test/java/org/apache/james/imap/encode/AbstractTestImapResponseComposer.java
 Sun Jan 30 12:38:15 2011
@@ -155,11 +155,11 @@ public abstract class AbstractTestImapRe
     @Test
     public void testShouldEncodeUnparameterisedStatus() throws Exception {
         checkStatusResponseEncode("A1 NO [ALERT] APPEND failed\r\n", "A1",
-                command("APPEND"), "NO", "ALERT", new ArrayList<String>(), 0,
+                command("APPEND"), "NO", "ALERT", new ArrayList<String>(), 
true, 0,
                 "failed");
         checkStatusResponseEncode("A1 BAD [TRYCREATE] SELECT whatever\r\n",
                 "A1", command("SELECT"), "BAD", "TRYCREATE",
-                new ArrayList<String>(), 0, "whatever");
+                new ArrayList<String>(), true, 0, "whatever");
     }
 
     @Test
@@ -170,15 +170,31 @@ public abstract class AbstractTestImapRe
         parameters.add("THREE");
         checkStatusResponseEncode(
                 "A1 NO [BADCHARSET (ONE TWO THREE)] APPEND failed\r\n", "A1",
-                command("APPEND"), "NO", "BADCHARSET", parameters, 0, 
"failed");
+                command("APPEND"), "NO", "BADCHARSET", parameters, true, 0, 
"failed");
     }
 
     @Test
     public void testShouldEncodeNumberParameterStatus() throws Exception {
         checkStatusResponseEncode("A1 NO [UIDNEXT 10] APPEND failed\r\n", "A1",
-                command("APPEND"), "NO", "UIDNEXT", null, 10, "failed");
+                command("APPEND"), "NO", "UIDNEXT", null, true, 10, "failed");
     }
 
+    @Test
+    public void testShouldEncodeNumberAndListParameterStatus() throws 
Exception {
+        Collection<String> parameters = new ArrayList<String>();
+        parameters.add("1:2");
+        parameters.add("3:4");
+        
+        checkStatusResponseEncode("A1 OK [COPYUID 10 1:2 3:4] COPY 
completed\r\n", "A1",
+                command("COPY"), "OK", "COPYUID", parameters, false, 10, 
"completed");
+        
+        parameters.clear();
+        parameters.add("3");
+        
+        checkStatusResponseEncode("A1 OK [APPENDUID 10 3] APPEND 
completed\r\n", "A1",
+                command("APPEND"), "OK", "APPENDUID", parameters, false, 10, 
"completed");
+    }
+    
     private void checkFlagsEncode(String expected, Flags flags)
             throws Exception {
         StringBuffer buffer = new StringBuffer();
@@ -243,14 +259,14 @@ public abstract class AbstractTestImapRe
 
     protected abstract byte[] encodeStatusResponse(String tag,
             ImapCommand command, String type, String responseCode,
-            Collection<String> parameters, int number, String text) throws 
Exception;
+            Collection<String> parameters, boolean useParens, int number, 
String text) throws Exception;
 
     private void checkStatusResponseEncode(String expected, String tag,
             ImapCommand command, String type, String responseCode,
-            Collection<String> parameters, int number, String text) throws 
Exception {
+            Collection<String> parameters, boolean useParens, int number, 
String text) throws Exception {
         StringBuffer buffer = new StringBuffer();
         byte[] output = encodeStatusResponse(tag, command, type, responseCode,
-                parameters, number, text);
+                parameters, useParens, number, text);
         for (int i = 0; i < output.length; i++) {
             buffer.append((char) output[i]);
         }

Modified: 
james/imap/trunk/message/src/test/java/org/apache/james/imap/encode/base/ImapResponseComposerImplTest.java
URL: 
http://svn.apache.org/viewvc/james/imap/trunk/message/src/test/java/org/apache/james/imap/encode/base/ImapResponseComposerImplTest.java?rev=1065257&r1=1065256&r2=1065257&view=diff
==============================================================================
--- 
james/imap/trunk/message/src/test/java/org/apache/james/imap/encode/base/ImapResponseComposerImplTest.java
 (original)
+++ 
james/imap/trunk/message/src/test/java/org/apache/james/imap/encode/base/ImapResponseComposerImplTest.java
 Sun Jan 30 12:38:15 2011
@@ -71,10 +71,10 @@ public class ImapResponseComposerImplTes
     }
 
     protected byte[] encodeStatusResponse(String tag, ImapCommand command,
-            String type, String responseCode, Collection<String> parameters,
-            int number, String text) throws Exception {
+            String type, String responseCode, Collection<String> parameters, 
+            boolean useParens, int number, String text) throws Exception {
         composer.statusResponse(tag, command, type, responseCode, parameters,
-                number, text);
+                useParens, number, text);
         return writer.getBytes();
     }
 

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=1065257&r1=1065256&r2=1065257&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
 Sun Jan 30 12:38:15 2011
@@ -31,6 +31,7 @@ import org.apache.james.imap.api.message
 import org.apache.james.imap.api.message.response.ImapResponseMessage;
 import org.apache.james.imap.api.message.response.StatusResponse;
 import org.apache.james.imap.api.message.response.StatusResponseFactory;
+import org.apache.james.imap.api.message.response.StatusResponse.ResponseCode;
 import org.apache.james.imap.api.process.ImapProcessor;
 import org.apache.james.imap.api.process.ImapSession;
 import org.apache.james.imap.api.process.SelectedMailbox;
@@ -241,6 +242,13 @@ abstract public class AbstractMailboxPro
                 HumanReadableText.COMPLETED);
         responder.respond(response);
     }
+    
+    protected void okComplete(final ImapCommand command, final String tag, 
+               final ResponseCode code, final ImapProcessor.Responder 
responder) {
+        final StatusResponse response = factory.taggedOk(tag, command,
+                HumanReadableText.COMPLETED, code);
+        responder.respond(response);
+    }
 
     protected void no(final ImapCommand command, final String tag,
             final ImapProcessor.Responder responder,

Modified: 
james/imap/trunk/processor/src/main/java/org/apache/james/imap/processor/AppendProcessor.java
URL: 
http://svn.apache.org/viewvc/james/imap/trunk/processor/src/main/java/org/apache/james/imap/processor/AppendProcessor.java?rev=1065257&r1=1065256&r2=1065257&view=diff
==============================================================================
--- 
james/imap/trunk/processor/src/main/java/org/apache/james/imap/processor/AppendProcessor.java
 (original)
+++ 
james/imap/trunk/processor/src/main/java/org/apache/james/imap/processor/AppendProcessor.java
 Sun Jan 30 12:38:15 2011
@@ -29,8 +29,10 @@ import org.apache.commons.logging.Log;
 import org.apache.james.imap.api.ImapCommand;
 import org.apache.james.imap.api.ImapSessionUtils;
 import org.apache.james.imap.api.display.HumanReadableText;
+import org.apache.james.imap.api.message.IdRange;
 import org.apache.james.imap.api.message.response.StatusResponse;
 import org.apache.james.imap.api.message.response.StatusResponseFactory;
+import org.apache.james.imap.api.message.response.StatusResponse.ResponseCode;
 import org.apache.james.imap.api.process.ImapProcessor;
 import org.apache.james.imap.api.process.ImapSession;
 import org.apache.james.imap.api.process.SelectedMailbox;
@@ -41,6 +43,7 @@ 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.MessageManager.MetaData.FetchGroup;
 
 public class AppendProcessor extends AbstractMailboxProcessor<AppendRequest> {
 
@@ -69,7 +72,7 @@ public class AppendProcessor extends Abs
                     command, mailbox, responder, mailboxPath);
         } catch (MailboxNotFoundException e) {
             // consume message on exception
-            cosume(messageIn);
+            consume(messageIn);
             
 //          Indicates that the mailbox does not exist
 //          So TRY CREATE
@@ -77,7 +80,7 @@ public class AppendProcessor extends Abs
             
         } catch (MailboxException e) {
             // consume message on exception
-            cosume(messageIn);
+            consume(messageIn);
             
 //          Some other issue
             no(command, tag, responder, 
HumanReadableText.GENERIC_FAILURE_DURING_PROCESSING);
@@ -86,7 +89,7 @@ public class AppendProcessor extends Abs
 
     }
     
-    private void cosume(InputStream in) {
+    private void consume(InputStream in) {
         try {
             while(in.read() != -1);
         } catch (IOException e1) {
@@ -121,6 +124,7 @@ public class AppendProcessor extends Abs
         try {
             final MailboxSession mailboxSession = 
ImapSessionUtils.getMailboxSession(session);
             final SelectedMailbox selectedMailbox = session.getSelected();
+            final MailboxManager mailboxManager = getMailboxManager();
             final boolean isSelectedMailbox = selectedMailbox != null
                     && selectedMailbox.getPath().equals(mailboxPath);
             final long uid  = mailbox.appendMessage(message, datetime, 
mailboxSession, 
@@ -128,8 +132,14 @@ public class AppendProcessor extends Abs
             if (isSelectedMailbox) {
                 selectedMailbox.addRecent(uid);
             }
+            
+            // get folder UIDVALIDITY
+            Long uidValidity = mailboxManager.getMailbox(mailboxPath, 
mailboxSession).getMetaData(false, mailboxSession, 
FetchGroup.NO_UNSEEN).getUidValidity();
+
             unsolicitedResponses(session, responder, false);
-            okComplete(command, tag, responder);
+
+            // in case of MULTIAPPEND support we will push more then one UID 
here
+            okComplete(command, tag, ResponseCode.appendUid(uidValidity, new 
IdRange[] { new IdRange(uid) }), responder);
         } catch (MailboxNotFoundException e) {
 //          Indicates that the mailbox does not exist
 //          So TRY CREATE

Modified: 
james/imap/trunk/processor/src/main/java/org/apache/james/imap/processor/CopyProcessor.java
URL: 
http://svn.apache.org/viewvc/james/imap/trunk/processor/src/main/java/org/apache/james/imap/processor/CopyProcessor.java?rev=1065257&r1=1065256&r2=1065257&view=diff
==============================================================================
--- 
james/imap/trunk/processor/src/main/java/org/apache/james/imap/processor/CopyProcessor.java
 (original)
+++ 
james/imap/trunk/processor/src/main/java/org/apache/james/imap/processor/CopyProcessor.java
 Sun Jan 30 12:38:15 2011
@@ -19,10 +19,15 @@
 
 package org.apache.james.imap.processor;
 
+import java.util.ArrayList;
+import java.util.List;
 import org.apache.james.imap.api.ImapCommand;
 import org.apache.james.imap.api.ImapSessionUtils;
 import org.apache.james.imap.api.display.HumanReadableText;
 import org.apache.james.imap.api.message.IdRange;
+import org.apache.james.imap.api.message.request.ImapRequest;
+import org.apache.james.imap.api.message.response.StatusResponse;
+import org.apache.james.imap.api.message.response.StatusResponseFactory;
 import org.apache.james.imap.api.message.response.StatusResponse.ResponseCode;
 import org.apache.james.imap.api.message.response.StatusResponseFactory;
 import org.apache.james.imap.api.process.ImapProcessor;
@@ -35,6 +40,7 @@ import org.apache.james.mailbox.MailboxM
 import org.apache.james.mailbox.MailboxPath;
 import org.apache.james.mailbox.MailboxSession;
 import org.apache.james.mailbox.MessageRange;
+import org.apache.james.mailbox.MessageManager.MetaData.FetchGroup;
 
 public class CopyProcessor extends AbstractMailboxProcessor<CopyRequest> {
 
@@ -62,15 +68,23 @@ public class CopyProcessor extends Abstr
                 no(command, tag, responder, 
HumanReadableText.FAILURE_NO_SUCH_MAILBOX,
                         ResponseCode.tryCreate());
             } else {
+                List<IdRange> resultRanges=new ArrayList<IdRange>(); 
                 for (int i = 0; i < idSet.length; i++) {
-                    
                     MessageRange messageSet = messageRange(currentMailbox, 
idSet[i], useUids);
 
-                    mailboxManager.copyMessages(messageSet, 
currentMailbox.getPath(),
-                                                targetMailbox, mailboxSession);
+                    List<MessageRange> copiedUids = 
mailboxManager.copyMessages(messageSet, currentMailbox.getPath(), 
targetMailbox, mailboxSession);
+                    
+                    for (MessageRange mr : copiedUids) {
+                       resultRanges.add(new IdRange(mr.getUidFrom(), 
mr.getUidTo())); 
+                    }
                 }
+                IdRange[] resultUids = 
IdRange.mergeRanges(resultRanges).toArray(new IdRange[0]);
+
+                // get folder UIDVALIDITY
+                Long uidValidity = mailboxManager.getMailbox(targetMailbox, 
mailboxSession).getMetaData(false, mailboxSession, 
FetchGroup.NO_UNSEEN).getUidValidity();
+                
                 unsolicitedResponses(session, responder, useUids);
-                okComplete(command, tag, responder);
+                okComplete(command, tag, ResponseCode.copyUid(uidValidity, 
idSet, resultUids), responder);
             }
         } catch (MessageRangeException e) {
             taggedBad(command, tag, responder, 
HumanReadableText.INVALID_MESSAGESET);



---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to