You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@james.apache.org by bt...@apache.org on 2022/11/18 01:53:27 UTC
[james-project] branch master updated: JAMES-3856 RFC-9208 Implement IMAP QUOTA revision (#1311)
This is an automated email from the ASF dual-hosted git repository.
btellier pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/james-project.git
The following commit(s) were added to refs/heads/master by this push:
new 44d26e878d JAMES-3856 RFC-9208 Implement IMAP QUOTA revision (#1311)
44d26e878d is described below
commit 44d26e878d02828172a083543a052987194b3976
Author: Benoit TELLIER <bt...@linagora.com>
AuthorDate: Fri Nov 18 08:53:22 2022 +0700
JAMES-3856 RFC-9208 Implement IMAP QUOTA revision (#1311)
- Adds QUOTA=RES-MESSAGE and QUOTA=RES-STORAGE capabilities
- Adds support for DELETED status item (also mandated by RFC-9051 IMAP4Rev2)
- Adds support for DELETED_STORAGE status item
- Adds the optional OVERQUOTA response code for append commands
- Document support for this specification
---
.../james/mpt/imapmailbox/suite/QuotaTest.java | 2 +-
.../org/apache/james/imap/scripts/Quota.test | 32 ++++++++++--
.../org/apache/james/imap/scripts/Status.test | 59 ++++++++++++++++++++++
.../org/apache/james/imap/api/ImapConstants.java | 6 +++
.../james/imap/api/display/HumanReadableText.java | 2 +
.../james/imap/api/message/StatusDataItems.java | 17 ++++++-
.../imap/api/message/response/StatusResponse.java | 5 ++
.../imap/decode/parser/StatusCommandParser.java | 26 ++++++++++
.../imap/encode/MailboxStatusResponseEncoder.java | 12 +++++
.../message/response/MailboxStatusResponse.java | 14 ++++-
.../james/imap/processor/AppendProcessor.java | 8 +++
.../imap/processor/GetQuotaRootProcessor.java | 2 +-
.../james/imap/processor/StatusProcessor.java | 53 ++++++++++++++++---
.../encode/MailboxStatusResponseEncoderTest.java | 7 ++-
.../pages/architecture/implemented-standards.adoc | 2 +-
src/site/xdoc/protocols/imap4.xml | 1 +
16 files changed, 231 insertions(+), 17 deletions(-)
diff --git a/mpt/impl/imap-mailbox/core/src/main/java/org/apache/james/mpt/imapmailbox/suite/QuotaTest.java b/mpt/impl/imap-mailbox/core/src/main/java/org/apache/james/mpt/imapmailbox/suite/QuotaTest.java
index 5a6a14f509..bbc04deabc 100644
--- a/mpt/impl/imap-mailbox/core/src/main/java/org/apache/james/mpt/imapmailbox/suite/QuotaTest.java
+++ b/mpt/impl/imap-mailbox/core/src/main/java/org/apache/james/mpt/imapmailbox/suite/QuotaTest.java
@@ -34,7 +34,7 @@ import org.junit.jupiter.api.Test;
public abstract class QuotaTest implements ImapTestConstants {
- private static final QuotaCountLimit MAX_MESSAGE_QUOTA = QuotaCountLimit.count(4096);
+ private static final QuotaCountLimit MAX_MESSAGE_QUOTA = QuotaCountLimit.count(7);
private static final QuotaSizeLimit MAX_STORAGE_QUOTA = QuotaSizeLimit.size(5 * 1024L * 1024L * 1024L);
protected abstract ImapHostSystem createImapHostSystem();
diff --git a/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/Quota.test b/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/Quota.test
index a5037a02f7..5965061c0d 100644
--- a/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/Quota.test
+++ b/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/Quota.test
@@ -63,6 +63,19 @@ S: \* 2 EXISTS
S: \* 2 RECENT
S: A004 OK (\[.+\] )?APPEND completed.
+C: A006 GETQUOTAROOT #private.imapuser.test
+S: \* QUOTAROOT "#private\.imapuser\.test" #private&imapuser
+S: \* QUOTA #private&imapuser \(MESSAGE 6 7\)
+S: \* QUOTA #private&imapuser \(STORAGE 1 5242880\)
+S: A006 OK GETQUOTAROOT completed.
+
+C: A007 GETQUOTA #private&imapuser
+# seven because of pre inserted messages for imapuser
+S: \* QUOTA #private&imapuser \(MESSAGE 6 7\)
+# 1 = 1KB : 254 * 4 + 3 * 310 = 1946 so shrinked to 1KB
+S: \* QUOTA #private&imapuser \(STORAGE 1 5242880\)
+S: A007 OK GETQUOTA completed.
+
C: A005 APPEND #private.imapuser.test (\Seen \Draft) "09-Apr-2008 15:17:51 +0200" {310+}
C: Date: Mon, 7 Feb 1994 21:52:25 -0800 (PST)
C: From: Fred Foobar <fo...@Blurdybloop.COM>
@@ -84,16 +97,29 @@ AWAIT
C: A006 GETQUOTAROOT #private.imapuser.test
S: \* QUOTAROOT "#private\.imapuser\.test" #private&imapuser
-S: \* QUOTA #private&imapuser \(MESSAGE 7 4096\)
+S: \* QUOTA #private&imapuser \(MESSAGE 7 7\)
S: \* QUOTA #private&imapuser \(STORAGE 1 5242880\)
S: A006 OK GETQUOTAROOT completed.
C: A007 GETQUOTA #private&imapuser
# seven because of pre inserted messages for imapuser
-S: \* QUOTA #private&imapuser \(MESSAGE 7 4096\)
+S: \* QUOTA #private&imapuser \(MESSAGE 7 7\)
# 1 = 1KB : 254 * 4 + 3 * 310 = 1946 so shrinked to 1KB
S: \* QUOTA #private&imapuser \(STORAGE 1 5242880\)
S: A007 OK GETQUOTA completed.
C: A007 SETQUOTA #private&imapuser (MESSAGE 4096) (STORAGE 5242880)
-S: A007 NO SETQUOTA You need the Full admin rights right to perform command SETQUOTA on mailbox Can not perform SETQUOTA commands.
\ No newline at end of file
+S: A007 NO SETQUOTA You need the Full admin rights right to perform command SETQUOTA on mailbox Can not perform SETQUOTA commands.
+
+C: A005 APPEND #private.imapuser.test (\Seen \Draft) "09-Apr-2008 15:17:51 +0200" {310+}
+C: Date: Mon, 7 Feb 1994 21:52:25 -0800 (PST)
+C: From: Fred Foobar <fo...@Blurdybloop.COM>
+C: Subject: afternoon meeting 2
+C: To: mooch@owatagu.siam.edu
+C: Message-Id: <B2...@Blurdybloop.COM>
+C: MIME-Version: 1.0
+C: Content-Type: TEXT/PLAIN; CHARSET=US-ASCII
+C:
+C: Hello Joe, could we change that to 4:00pm tomorrow?
+C:
+S: A005 NO \[OVERQUOTA\] APPEND failed. Over quota.
\ No newline at end of file
diff --git a/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/Status.test b/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/Status.test
index 1a0b8ccf69..0e060cba36 100644
--- a/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/Status.test
+++ b/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/Status.test
@@ -62,6 +62,65 @@ C: a008 STATUS statustest (UNSEEN SIZE MESSAGES )
S: \* STATUS \"statustest\" \(MESSAGES 1 SIZE 254 UNSEEN 1\)
S: a008 OK STATUS completed.
+C: A009 APPEND statustest {254+}
+C: Date: Mon, 7 Feb 1994 21:52:25 -0800 (PST)
+C: From: Fred Foobar <fo...@Blurdybloop.COM>
+C: Subject: Test 01
+C: To: mooch@owatagu.siam.edu
+C: Message-Id: <B2...@Blurdybloop.COM>
+C: MIME-Version: 1.0
+C: Content-Type: TEXT/PLAIN; CHARSET=US-ASCII
+C:
+C: Test 01
+C:
+S: A009 OK (\[.+\] )?APPEND completed.
+
+C: A010 APPEND statustest {254+}
+C: Date: Mon, 7 Feb 1994 21:52:25 -0800 (PST)
+C: From: Fred Foobar <fo...@Blurdybloop.COM>
+C: Subject: Test 01
+C: To: mooch@owatagu.siam.edu
+C: Message-Id: <B2...@Blurdybloop.COM>
+C: MIME-Version: 1.0
+C: Content-Type: TEXT/PLAIN; CHARSET=US-ASCII
+C:
+C: Test 01
+C:
+S: A010 OK (\[.+\] )?APPEND completed.
+
+C: A011 APPEND statustest {254+}
+C: Date: Mon, 7 Feb 1994 21:52:25 -0800 (PST)
+C: From: Fred Foobar <fo...@Blurdybloop.COM>
+C: Subject: Test 01
+C: To: mooch@owatagu.siam.edu
+C: Message-Id: <B2...@Blurdybloop.COM>
+C: MIME-Version: 1.0
+C: Content-Type: TEXT/PLAIN; CHARSET=US-ASCII
+C:
+C: Test 01
+C:
+S: A011 OK (\[.+\] )?APPEND completed.
+
+C: 10 SELECT statustest
+S: \* FLAGS \(\\Answered \\Deleted \\Draft \\Flagged \\Seen\)
+S: \* 4 EXISTS
+S: \* 4 RECENT
+S: \* OK \[UIDVALIDITY \d+\].*
+S: \* OK \[UNSEEN 1\] .*
+S: \* OK \[PERMANENTFLAGS \(\\Answered \\Deleted \\Draft \\Flagged \\\Seen( \\\*)?\)\].*
+S: \* OK \[HIGHESTMODSEQ \d+\].*
+S: \* OK \[UIDNEXT 5\].*
+S: 10 OK \[READ-WRITE\] SELECT completed.
+
+C: A012 STORE 1:2 FLAGS (\Deleted)
+S: \* 1 FETCH \(FLAGS \(\\Deleted \\Recent\)\)
+S: \* 2 FETCH \(FLAGS \(\\Deleted \\Recent\)\)
+S: A012 OK STORE completed.
+
+C: A013 STATUS statustest (UNSEEN SIZE MESSAGES DELETED DELETED-STORAGE)
+S: \* STATUS \"statustest\" \(MESSAGES 4 SIZE 1016 DELETED 2 DELETED-STORAGE 508 UNSEEN 4\)
+S: A013 OK STATUS completed.
+
# Cleanup
C: a1 DELETE statustest
S: a1 OK DELETE completed.
diff --git a/protocols/imap/src/main/java/org/apache/james/imap/api/ImapConstants.java b/protocols/imap/src/main/java/org/apache/james/imap/api/ImapConstants.java
index 96a372c96c..d38f1d1244 100644
--- a/protocols/imap/src/main/java/org/apache/james/imap/api/ImapConstants.java
+++ b/protocols/imap/src/main/java/org/apache/james/imap/api/ImapConstants.java
@@ -92,6 +92,8 @@ public interface ImapConstants {
Capability SUPPORTS_ACL = Capability.of("ACL");
Capability SUPPORTS_QUOTA = Capability.of("QUOTA");
+ Capability SUPPORTS_QUOTA_RES_STORAGE = Capability.of("QUOTA=RES-STORAGE");
+ Capability SUPPORTS_QUOTA_RES_MESSAGE = Capability.of("QUOTA=RES-MESSAGE");
Capability SUPPORTS_MOVE = Capability.of("MOVE");
@@ -152,6 +154,10 @@ public interface ImapConstants {
String STATUS_SIZE = "SIZE";
+ String STATUS_DELETED = "DELETED";
+
+ String STATUS_DELETED_STORAGE = "DELETED-STORAGE";
+
String STATUS_HIGHESTMODSEQ = "HIGHESTMODSEQ";
ImapCommand CAPABILITY_COMMAND = ImapCommand.anyStateCommand("CAPABILITY");
diff --git a/protocols/imap/src/main/java/org/apache/james/imap/api/display/HumanReadableText.java b/protocols/imap/src/main/java/org/apache/james/imap/api/display/HumanReadableText.java
index 7690f96de6..d7a9ed6c34 100644
--- a/protocols/imap/src/main/java/org/apache/james/imap/api/display/HumanReadableText.java
+++ b/protocols/imap/src/main/java/org/apache/james/imap/api/display/HumanReadableText.java
@@ -84,6 +84,8 @@ public class HumanReadableText {
public static final HumanReadableText FAILURE_NO_SUCH_MAILBOX = new HumanReadableText("org.apache.james.imap.FAILURE_NO_SUCH_MAILBOX", "failed. No such mailbox.");
+ public static final HumanReadableText FAILURE_OVERQUOTA = new HumanReadableText("org.apache.james.imap.OVERQUOTA", "failed. Over quota.");
+
public static final HumanReadableText FAILURE_NO_QUOTA_RESOURCE = new HumanReadableText("org.apache.james.imap.FAILURE_NO_SUCH_QUOTA_RESOURCE", "failed. No such quota resource.");
public static final HumanReadableText START_TRANSACTION_FAILED = new HumanReadableText("org.apache.james.imap.START_TRANSACTION_FAILED", "failed. Cannot start transaction.");
diff --git a/protocols/imap/src/main/java/org/apache/james/imap/api/message/StatusDataItems.java b/protocols/imap/src/main/java/org/apache/james/imap/api/message/StatusDataItems.java
index 5f1d441062..ba3d7a1b0d 100644
--- a/protocols/imap/src/main/java/org/apache/james/imap/api/message/StatusDataItems.java
+++ b/protocols/imap/src/main/java/org/apache/james/imap/api/message/StatusDataItems.java
@@ -30,10 +30,15 @@ public class StatusDataItems {
MESSAGES,
RECENT,
UID_NEXT,
- SIZE,
UID_VALIDITY,
UNSEEN,
- HIGHEST_MODSEQ
+ HIGHEST_MODSEQ,
+ // See https://www.iana.org/go/rfc8438
+ SIZE,
+ // See https://www.rfc-editor.org/rfc/rfc9208.html
+ DELETED,
+ // See https://www.rfc-editor.org/rfc/rfc9208.html
+ DELETED_STORAGE
}
private final EnumSet<StatusItem> statusItems;
@@ -70,6 +75,14 @@ public class StatusDataItems {
return statusItems.contains(StatusItem.SIZE);
}
+ public boolean isDeleted() {
+ return statusItems.contains(StatusItem.DELETED);
+ }
+
+ public boolean isDeletedStorage() {
+ return statusItems.contains(StatusItem.DELETED_STORAGE);
+ }
+
@Override
public String toString() {
return MoreObjects.toStringHelper(SIMPLE_NAME)
diff --git a/protocols/imap/src/main/java/org/apache/james/imap/api/message/response/StatusResponse.java b/protocols/imap/src/main/java/org/apache/james/imap/api/message/response/StatusResponse.java
index 44cf9b051d..ec4becc973 100644
--- a/protocols/imap/src/main/java/org/apache/james/imap/api/message/response/StatusResponse.java
+++ b/protocols/imap/src/main/java/org/apache/james/imap/api/message/response/StatusResponse.java
@@ -153,6 +153,7 @@ public interface StatusResponse extends ImapResponseMessage {
/** RFC2060 <code>TRYCREATE</code> response code */
private static final ResponseCode TRYCREATE = new ResponseCode("TRYCREATE");
+ private static final ResponseCode OVERQUOTA = new ResponseCode("OVERQUOTA");
/** RFC5162 <code>CLOSED</code> response code */
private static final ResponseCode CLOSED = new ResponseCode("CLOSED");
@@ -289,6 +290,10 @@ public interface StatusResponse extends ImapResponseMessage {
return TRYCREATE;
}
+ public static ResponseCode overQuota() {
+ return OVERQUOTA;
+ }
+
/**
* Creates a RFC2060 <code>UIDVALIDITY</code> response code.
*
diff --git a/protocols/imap/src/main/java/org/apache/james/imap/decode/parser/StatusCommandParser.java b/protocols/imap/src/main/java/org/apache/james/imap/decode/parser/StatusCommandParser.java
index 356ea4c165..5d47339516 100644
--- a/protocols/imap/src/main/java/org/apache/james/imap/decode/parser/StatusCommandParser.java
+++ b/protocols/imap/src/main/java/org/apache/james/imap/decode/parser/StatusCommandParser.java
@@ -75,6 +75,9 @@ public class StatusCommandParser extends AbstractImapCommandParser {
if (c == 'm' || c == 'M') {
return readMessages(request);
}
+ if (c == 'd' || c == 'D') {
+ return readDeleted(request);
+ }
if (c == 'r' || c == 'R') {
return readRecent(request);
}
@@ -182,6 +185,29 @@ public class StatusCommandParser extends AbstractImapCommandParser {
return StatusDataItems.StatusItem.MESSAGES;
}
+ private StatusDataItems.StatusItem readDeleted(ImapRequestLineReader request) throws DecodingException {
+ assertChar(request, 'd', 'D');
+ assertChar(request, 'e', 'E');
+ assertChar(request, 'l', 'L');
+ assertChar(request, 'e', 'E');
+ assertChar(request, 't', 'T');
+ assertChar(request, 'e', 'E');
+ assertChar(request, 'd', 'D');
+ char c = request.nextWordChar();
+ if (c == '-') {
+ assertChar(request, '-', '-');
+ assertChar(request, 's', 'S');
+ assertChar(request, 't', 'T');
+ assertChar(request, 'o', 'O');
+ assertChar(request, 'r', 'R');
+ assertChar(request, 'a', 'A');
+ assertChar(request, 'g', 'G');
+ assertChar(request, 'e', 'E');
+ return StatusDataItems.StatusItem.DELETED_STORAGE;
+ }
+ return StatusDataItems.StatusItem.DELETED;
+ }
+
private void assertChar(ImapRequestLineReader reader, char low, char up) throws DecodingException {
char c = reader.consume();
if (c != low && c != up) {
diff --git a/protocols/imap/src/main/java/org/apache/james/imap/encode/MailboxStatusResponseEncoder.java b/protocols/imap/src/main/java/org/apache/james/imap/encode/MailboxStatusResponseEncoder.java
index c213ab7732..62925f69f8 100644
--- a/protocols/imap/src/main/java/org/apache/james/imap/encode/MailboxStatusResponseEncoder.java
+++ b/protocols/imap/src/main/java/org/apache/james/imap/encode/MailboxStatusResponseEncoder.java
@@ -41,6 +41,8 @@ public class MailboxStatusResponseEncoder implements ImapConstants, ImapResponse
Long messages = response.getMessages();
Long recent = response.getRecent();
Long size = response.getSize();
+ Long deleted = response.getDeleted();
+ Long deletedStorage = response.getDeletedStorage();
MessageUid uidNext = response.getUidNext();
ModSeq highestModSeq = response.getHighestModSeq();
UidValidity uidValidity = response.getUidValidity();
@@ -62,6 +64,16 @@ public class MailboxStatusResponseEncoder implements ImapConstants, ImapResponse
composer.message(size);
}
+ if (deleted != null) {
+ composer.message(STATUS_DELETED);
+ composer.message(deleted);
+ }
+
+ if (deletedStorage != null) {
+ composer.message(STATUS_DELETED_STORAGE);
+ composer.message(deletedStorage);
+ }
+
if (recent != null) {
composer.message(STATUS_RECENT);
composer.message(recent);
diff --git a/protocols/imap/src/main/java/org/apache/james/imap/message/response/MailboxStatusResponse.java b/protocols/imap/src/main/java/org/apache/james/imap/message/response/MailboxStatusResponse.java
index 28458e6b8b..4e00153cbe 100644
--- a/protocols/imap/src/main/java/org/apache/james/imap/message/response/MailboxStatusResponse.java
+++ b/protocols/imap/src/main/java/org/apache/james/imap/message/response/MailboxStatusResponse.java
@@ -29,6 +29,8 @@ import org.apache.james.mailbox.model.UidValidity;
*/
public class MailboxStatusResponse implements ImapResponseMessage {
private final Long size;
+ private final Long deleted;
+ private final Long deletedStorage;
private final Long messages;
private final Long recent;
private final MessageUid uidNext;
@@ -37,8 +39,10 @@ public class MailboxStatusResponse implements ImapResponseMessage {
private final String mailbox;
private final ModSeq highestModSeq;
- public MailboxStatusResponse(Long size, Long messages, Long recent, MessageUid uidNext, ModSeq highestModSeq, UidValidity uidValidity, Long unseen, String mailbox) {
+ public MailboxStatusResponse(Long size, Long deleted, Long deletedStorage, Long messages, Long recent, MessageUid uidNext, ModSeq highestModSeq, UidValidity uidValidity, Long unseen, String mailbox) {
this.size = size;
+ this.deleted = deleted;
+ this.deletedStorage = deletedStorage;
this.messages = messages;
this.recent = recent;
this.uidNext = uidNext;
@@ -52,6 +56,14 @@ public class MailboxStatusResponse implements ImapResponseMessage {
return size;
}
+ public Long getDeleted() {
+ return deleted;
+ }
+
+ public Long getDeletedStorage() {
+ return deletedStorage;
+ }
+
/**
* Gets the <code>MESSAGES</code> count for the mailbox.
*
diff --git a/protocols/imap/src/main/java/org/apache/james/imap/processor/AppendProcessor.java b/protocols/imap/src/main/java/org/apache/james/imap/processor/AppendProcessor.java
index cba9380f08..a3295336c6 100644
--- a/protocols/imap/src/main/java/org/apache/james/imap/processor/AppendProcessor.java
+++ b/protocols/imap/src/main/java/org/apache/james/imap/processor/AppendProcessor.java
@@ -39,6 +39,7 @@ import org.apache.james.mailbox.MailboxSession;
import org.apache.james.mailbox.MessageManager;
import org.apache.james.mailbox.exception.MailboxException;
import org.apache.james.mailbox.exception.MailboxNotFoundException;
+import org.apache.james.mailbox.exception.OverQuotaException;
import org.apache.james.mailbox.model.ComposedMessageId;
import org.apache.james.mailbox.model.Content;
import org.apache.james.mailbox.model.MailboxPath;
@@ -79,6 +80,13 @@ public class AppendProcessor extends AbstractMailboxProcessor<AppendRequest> {
no(request, responder, HumanReadableText.FAILURE_NO_SUCH_MAILBOX, StatusResponse.ResponseCode.tryCreate());
return Mono.empty();
})
+ .doOnEach(logOnError(OverQuotaException.class, e -> LOGGER.info("Append failed for mailbox {} because overquota", mailboxPath)))
+ .onErrorResume(OverQuotaException.class, e -> {
+ // Indicates that the mailbox does not exist
+ // So TRY CREATE
+ no(request, responder, HumanReadableText.FAILURE_OVERQUOTA, StatusResponse.ResponseCode.overQuota());
+ return Mono.empty();
+ })
.doOnEach(logOnError(MailboxException.class, e -> LOGGER.error("Append failed for mailbox {}", mailboxPath, e)))
.onErrorResume(MailboxException.class, e -> {
// Some other issue
diff --git a/protocols/imap/src/main/java/org/apache/james/imap/processor/GetQuotaRootProcessor.java b/protocols/imap/src/main/java/org/apache/james/imap/processor/GetQuotaRootProcessor.java
index b92af1f24f..d1f761061f 100644
--- a/protocols/imap/src/main/java/org/apache/james/imap/processor/GetQuotaRootProcessor.java
+++ b/protocols/imap/src/main/java/org/apache/james/imap/processor/GetQuotaRootProcessor.java
@@ -54,8 +54,8 @@ import reactor.core.publisher.Mono;
* GETQUOTAROOT Processor
*/
public class GetQuotaRootProcessor extends AbstractMailboxProcessor<GetQuotaRootRequest> implements CapabilityImplementingProcessor {
+ private static final List<Capability> CAPABILITIES = ImmutableList.of(ImapConstants.SUPPORTS_QUOTA, ImapConstants.SUPPORTS_QUOTA_RES_MESSAGE, ImapConstants.SUPPORTS_QUOTA_RES_STORAGE);
- private static final List<Capability> CAPABILITIES = ImmutableList.of(ImapConstants.SUPPORTS_QUOTA);
private final QuotaRootResolver quotaRootResolver;
private final QuotaManager quotaManager;
diff --git a/protocols/imap/src/main/java/org/apache/james/imap/processor/StatusProcessor.java b/protocols/imap/src/main/java/org/apache/james/imap/processor/StatusProcessor.java
index a2d8ae0072..a7151e1539 100644
--- a/protocols/imap/src/main/java/org/apache/james/imap/processor/StatusProcessor.java
+++ b/protocols/imap/src/main/java/org/apache/james/imap/processor/StatusProcessor.java
@@ -24,7 +24,8 @@ import static org.apache.james.mailbox.MessageManager.MailboxMetaData.RecentMode
import java.util.List;
import java.util.Optional;
-import java.util.stream.Collectors;
+
+import javax.mail.Flags;
import org.apache.james.imap.api.display.HumanReadableText;
import org.apache.james.imap.api.message.Capability;
@@ -131,15 +132,18 @@ public class StatusProcessor extends AbstractMailboxProcessor<StatusRequest> imp
MessageManager.MailboxMetaData metaData,
MailboxSession session) {
StatusDataItems statusDataItems = request.getStatusDataItems();
- return size(statusDataItems, mailbox, session)
- .map(maybeSize -> {
+ return iterateMailbox(statusDataItems, mailbox, session)
+ .map(maybeIterationResult -> {
Long messages = messages(statusDataItems, metaData);
Long recent = recent(statusDataItems, metaData);
MessageUid uidNext = uidNext(statusDataItems, metaData);
UidValidity uidValidity = uidValidity(statusDataItems, metaData);
Long unseen = unseen(statusDataItems, metaData);
ModSeq highestModSeq = highestModSeq(statusDataItems, metaData);
- return new MailboxStatusResponse(maybeSize.orElse(null),
+ return new MailboxStatusResponse(
+ maybeIterationResult.flatMap(result -> result.getSize(statusDataItems)).orElse(null),
+ maybeIterationResult.flatMap(result -> result.getDeleted(statusDataItems)).orElse(null),
+ maybeIterationResult.flatMap(result -> result.getDeletedStorage(statusDataItems)).orElse(null),
messages, recent, uidNext, highestModSeq, uidValidity, unseen, request.getMailboxName());
});
}
@@ -200,16 +204,53 @@ public class StatusProcessor extends AbstractMailboxProcessor<StatusRequest> imp
}
}
- private Mono<Optional<Long>> size(StatusDataItems statusDataItems, MessageManager messageManager, MailboxSession session) {
+ private Mono<Optional<MailboxIterationResult>> iterateMailbox(StatusDataItems statusDataItems, MessageManager messageManager, MailboxSession session) {
if (statusDataItems.isSize()) {
return Flux.from(messageManager.getMessagesReactive(MessageRange.all(), FetchGroup.MINIMAL, session))
- .collect(Collectors.summingLong(MessageResult::getSize))
+ .reduce(new MailboxIterationResult(), MailboxIterationResult::accumulate)
.map(Optional::of);
} else {
return Mono.just(Optional.empty());
}
}
+ public static class MailboxIterationResult {
+ private long size = 0;
+ private long deleted = 0;
+ private long deletedStorage = 0;
+
+ public MailboxIterationResult accumulate(MessageResult messageResult) {
+ if (messageResult.getFlags().contains(Flags.Flag.DELETED)) {
+ deleted++;
+ deletedStorage += messageResult.getSize();
+ }
+ size += messageResult.getSize();
+ return this;
+ }
+
+ public Optional<Long> getSize(StatusDataItems items) {
+ if (items.isSize()) {
+ return Optional.of(size);
+ }
+ return Optional.empty();
+ }
+
+ public Optional<Long> getDeleted(StatusDataItems items) {
+ if (items.isDeleted()) {
+ return Optional.of(deleted);
+ }
+ return Optional.empty();
+ }
+
+ public Optional<Long> getDeletedStorage(StatusDataItems items) {
+ if (items.isDeletedStorage()) {
+ return Optional.of(deletedStorage);
+ }
+ return Optional.empty();
+ }
+ }
+
+
@Override
protected MDCBuilder mdc(StatusRequest request) {
return MDCBuilder.create()
diff --git a/protocols/imap/src/test/java/org/apache/james/imap/encode/MailboxStatusResponseEncoderTest.java b/protocols/imap/src/test/java/org/apache/james/imap/encode/MailboxStatusResponseEncoderTest.java
index 2e404db171..eed9f47974 100644
--- a/protocols/imap/src/test/java/org/apache/james/imap/encode/MailboxStatusResponseEncoderTest.java
+++ b/protocols/imap/src/test/java/org/apache/james/imap/encode/MailboxStatusResponseEncoderTest.java
@@ -53,10 +53,13 @@ class MailboxStatusResponseEncoderTest {
final MessageUid uidNext = MessageUid.of(5);
final UidValidity uidValidity = UidValidity.of(7L);
final Long unseen = 11L;
+ final Long size = 42L;
+ final Long deleted = 23L;
+ final Long deletedStorage = 13L;
final String mailbox = "A mailbox named desire";
- encoder.encode(new MailboxStatusResponse(null, messages, recent, uidNext,
+ encoder.encode(new MailboxStatusResponse(null, null, deletedStorage, messages, recent, uidNext,
null, uidValidity, unseen, mailbox), composer);
- assertThat(writer.getString()).isEqualTo("* STATUS \"A mailbox named desire\" (MESSAGES 2 RECENT 3 UIDNEXT 5 UIDVALIDITY 7 UNSEEN 11)\r\n");
+ assertThat(writer.getString()).isEqualTo("* STATUS \"A mailbox named desire\" (MESSAGES 2 DELETED-STORAGE 13 RECENT 3 UIDNEXT 5 UIDVALIDITY 7 UNSEEN 11)\r\n");
}
}
diff --git a/server/apps/distributed-app/docs/modules/ROOT/pages/architecture/implemented-standards.adoc b/server/apps/distributed-app/docs/modules/ROOT/pages/architecture/implemented-standards.adoc
index 5437a24f09..4510321c51 100644
--- a/server/apps/distributed-app/docs/modules/ROOT/pages/architecture/implemented-standards.adoc
+++ b/server/apps/distributed-app/docs/modules/ROOT/pages/architecture/implemented-standards.adoc
@@ -50,7 +50,7 @@ The following IMAP specifications are implemented:
- link:https://datatracker.ietf.org/doc/html/rfc3501.html[RFC-3501] INTERNET MESSAGE ACCESS PROTOCOL - VERSION 4rev1
- link:https://datatracker.ietf.org/doc/html/rfc2177.html[RFC-2177] IMAP IDLE (mailbox scoped push notifications)
- - link:https://datatracker.ietf.org/doc/html/rfc2087.html[RFC-2087] IMAP Quota
+ - link:https://www.rfc-editor.org/rfc/rfc9208.html[RFC-9208] IMAP QUOTA Extension
- link:https://datatracker.ietf.org/doc/html/rfc2342.html[RFC-2342] IMAP namespace
- link:https://datatracker.ietf.org/doc/html/rfc2088.html[RFC-2088] IMAP non synchronized literals
- link:https://datatracker.ietf.org/doc/html/rfc4315.html[RFC-4315] IMAP UIDPLUS
diff --git a/src/site/xdoc/protocols/imap4.xml b/src/site/xdoc/protocols/imap4.xml
index f6bee705f6..34c0332474 100644
--- a/src/site/xdoc/protocols/imap4.xml
+++ b/src/site/xdoc/protocols/imap4.xml
@@ -59,6 +59,7 @@
<li>MOVE (RFC 6851 https://tools.ietf.org/html/rfc6851 on master). This is enabled only if you use a MailboxManager exposing the Move capability</li>
<li>METADATA Extension (RFC 5464 http://www.ietf.org/rfc/rfc5464.txt on master). This is enabled only if you use a MailboxManager exposing the Annotation capability</li>
<li>IMAP Extension for STATUS=SIZE (https://www.rfc-editor.org/rfc/rfc8438.html)</li>
+ <li>IMAP QUOTA (https://www.rfc-editor.org/rfc/rfc9208.html)</li>
</ul>
<p>We follow RFC2683 recommendations for our implementations:</p>
<ul>
---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@james.apache.org
For additional commands, e-mail: notifications-help@james.apache.org