Index: api/pom.xml
===================================================================
--- api/pom.xml (revision 1065029)
+++ api/pom.xml (working copy)
@@ -43,6 +43,10 @@
commons-logging
commons-logging
+
+ commons-lang
+ commons-lang
+
junit
junit-dep
Index: api/src/main/java/org/apache/james/imap/api/message/IdRange.java
===================================================================
--- api/src/main/java/org/apache/james/imap/api/message/IdRange.java (revision 1065029)
+++ api/src/main/java/org/apache/james/imap/api/message/IdRange.java (working copy)
@@ -113,6 +113,13 @@
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.
Index: api/src/main/java/org/apache/james/imap/api/message/response/StatusResponse.java
===================================================================
--- api/src/main/java/org/apache/james/imap/api/message/response/StatusResponse.java (revision 1065029)
+++ api/src/main/java/org/apache/james/imap/api/message/response/StatusResponse.java (working copy)
@@ -19,13 +19,18 @@
package org.apache.james.imap.api.message.response;
+import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
+import java.util.List;
import javax.mail.Flags;
+import org.apache.commons.lang.StringUtils;
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 +132,29 @@
private static final ResponseCode TRYCREATE = new ResponseCode(
"TRYCREATE");
+ /** RFC4315 APPENDUID 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 COPYUID 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 "*";
+ List textRanges = new ArrayList(ranges.length);
+ for(IdRange range : ranges)
+ textRanges.add(range.getFormattedString());
+ return StringUtils.join(textRanges, ",");
+ }
+
/**
* Creates a RFC2060 ALERT response code.
*
@@ -251,24 +279,27 @@
private final Collection 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 parameters) {
- this(code, parameters, 0);
+ this(code, parameters, 0, true);
}
private ResponseCode(final String code, final Collection 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 +318,10 @@
return number;
}
+ public final boolean useParens() {
+ return useParens;
+ }
+
/**
* Gets parameters for this code.
*
Index: message/src/main/java/org/apache/james/imap/encode/base/ImapResponseComposerImpl.java
===================================================================
--- message/src/main/java/org/apache/james/imap/encode/base/ImapResponseComposerImpl.java (revision 1065029)
+++ message/src/main/java/org/apache/james/imap/encode/base/ImapResponseComposerImpl.java (working copy)
@@ -267,7 +267,7 @@
* java.lang.String, Collection, long, java.lang.String)
*/
public void statusResponse(String tag, ImapCommand command, String type,
- String responseCode, Collection parameters, long number, String text)
+ String responseCode, Collection parameters, boolean useParens, long number, String text)
throws IOException {
if (tag == null) {
untagged();
@@ -278,17 +278,19 @@
if (responseCode != null) {
openSquareBracket();
message(responseCode);
+ if (number > 0) {
+ message(number);
+ }
if (parameters != null && !parameters.isEmpty()) {
- openParen();
+ if(useParens)
+ openParen();
for (Iterator it = parameters.iterator(); it.hasNext();) {
final String parameter = it.next();
message(parameter);
}
- closeParen();
+ if(useParens)
+ closeParen();
}
- if (number > 0) {
- message(number);
- }
closeSquareBracket();
}
if (command != null) {
Index: message/src/main/java/org/apache/james/imap/encode/ImapResponseComposer.java
===================================================================
--- message/src/main/java/org/apache/james/imap/encode/ImapResponseComposer.java (revision 1065029)
+++ message/src/main/java/org/apache/james/imap/encode/ImapResponseComposer.java (working copy)
@@ -201,7 +201,7 @@
public abstract void statusResponse(String tag, ImapCommand command,
String type, String responseCode, Collection 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)
Index: message/src/main/java/org/apache/james/imap/encode/StatusResponseEncoder.java
===================================================================
--- message/src/main/java/org/apache/james/imap/encode/StatusResponseEncoder.java (revision 1065029)
+++ message/src/main/java/org/apache/james/imap/encode/StatusResponseEncoder.java (working copy)
@@ -57,14 +57,17 @@
final String text = asString(textKey, session);
final Collection 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);
}
Index: message/src/test/java/org/apache/james/imap/encode/AbstractTestImapResponseComposer.java
===================================================================
--- message/src/test/java/org/apache/james/imap/encode/AbstractTestImapResponseComposer.java (revision 1065029)
+++ message/src/test/java/org/apache/james/imap/encode/AbstractTestImapResponseComposer.java (working copy)
@@ -155,11 +155,11 @@
@Test
public void testShouldEncodeUnparameterisedStatus() throws Exception {
checkStatusResponseEncode("A1 NO [ALERT] APPEND failed\r\n", "A1",
- command("APPEND"), "NO", "ALERT", new ArrayList(), 0,
+ command("APPEND"), "NO", "ALERT", new ArrayList(), true, 0,
"failed");
checkStatusResponseEncode("A1 BAD [TRYCREATE] SELECT whatever\r\n",
"A1", command("SELECT"), "BAD", "TRYCREATE",
- new ArrayList(), 0, "whatever");
+ new ArrayList(), true, 0, "whatever");
}
@Test
@@ -170,15 +170,31 @@
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 parameters = new ArrayList();
+ 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 @@
protected abstract byte[] encodeStatusResponse(String tag,
ImapCommand command, String type, String responseCode,
- Collection parameters, int number, String text) throws Exception;
+ Collection parameters, boolean useParens, int number, String text) throws Exception;
private void checkStatusResponseEncode(String expected, String tag,
ImapCommand command, String type, String responseCode,
- Collection parameters, int number, String text) throws Exception {
+ Collection 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]);
}
Index: message/src/test/java/org/apache/james/imap/encode/base/ImapResponseComposerImplTest.java
===================================================================
--- message/src/test/java/org/apache/james/imap/encode/base/ImapResponseComposerImplTest.java (revision 1065029)
+++ message/src/test/java/org/apache/james/imap/encode/base/ImapResponseComposerImplTest.java (working copy)
@@ -71,10 +71,10 @@
}
protected byte[] encodeStatusResponse(String tag, ImapCommand command,
- String type, String responseCode, Collection parameters,
- int number, String text) throws Exception {
+ String type, String responseCode, Collection parameters,
+ boolean useParens, int number, String text) throws Exception {
composer.statusResponse(tag, command, type, responseCode, parameters,
- number, text);
+ useParens, number, text);
return writer.getBytes();
}
Index: processor/src/main/java/org/apache/james/imap/processor/AbstractMailboxProcessor.java
===================================================================
--- processor/src/main/java/org/apache/james/imap/processor/AbstractMailboxProcessor.java (revision 1065029)
+++ processor/src/main/java/org/apache/james/imap/processor/AbstractMailboxProcessor.java (working copy)
@@ -31,6 +31,7 @@
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 @@
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,
Index: processor/src/main/java/org/apache/james/imap/processor/AppendProcessor.java
===================================================================
--- processor/src/main/java/org/apache/james/imap/processor/AppendProcessor.java (revision 1065029)
+++ processor/src/main/java/org/apache/james/imap/processor/AppendProcessor.java (working copy)
@@ -29,8 +29,10 @@
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.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 {
@@ -69,7 +72,7 @@
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 @@
} 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 @@
}
- private void cosume(InputStream in) {
+ private void consume(InputStream in) {
try {
while(in.read() != -1);
} catch (IOException e1) {
@@ -121,6 +124,7 @@
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 @@
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
Index: processor/src/main/java/org/apache/james/imap/processor/CopyProcessor.java
===================================================================
--- processor/src/main/java/org/apache/james/imap/processor/CopyProcessor.java (revision 1065029)
+++ processor/src/main/java/org/apache/james/imap/processor/CopyProcessor.java (working copy)
@@ -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.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 {
@@ -62,15 +68,23 @@
no(command, tag, responder, HumanReadableText.FAILURE_NO_SUCH_MAILBOX,
ResponseCode.tryCreate());
} else {
+ List resultRanges=new ArrayList();
for (int i = 0; i < idSet.length; i++) {
-
MessageRange messageSet = messageRange(currentMailbox, idSet[i], useUids);
- mailboxManager.copyMessages(messageSet, currentMailbox.getPath(),
- targetMailbox, mailboxSession);
+ List 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);