You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@james.apache.org by rc...@apache.org on 2022/12/05 02:56:13 UTC
[james-project] branch master updated: JAMES-3754 Implement RFC-2971 IMAP4 ID extension
This is an automated email from the ASF dual-hosted git repository.
rcordier 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 10154970ed JAMES-3754 Implement RFC-2971 IMAP4 ID extension
10154970ed is described below
commit 10154970edf2ca3f70c8dc735672df2fb4efcfdd
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Tue Nov 29 17:44:19 2022 +0700
JAMES-3754 Implement RFC-2971 IMAP4 ID extension
Allows collecting information on the client fleet.
---
.../mpt/imapmailbox/suite/AuthenticatedState.java | 5 ++
.../org/apache/james/imap/scripts/Id.test | 27 +++++++
.../org/apache/james/imap/api/ImapConstants.java | 1 +
.../james/imap/decode/parser/IDCommandParser.java | 83 ++++++++++++++++++++++
.../imap/decode/parser/ImapParserFactory.java | 1 +
.../james/imap/encode/IdResponseEncoder.java | 40 +++++++++++
.../encode/main/DefaultImapEncoderFactory.java | 2 +
.../james/imap/message/request/IDRequest.java | 39 ++++++++++
.../james/imap/message/response/IdResponse.java | 27 +++++++
.../james/imap/processor/DefaultProcessor.java | 1 +
.../apache/james/imap/processor/IdProcessor.java | 70 ++++++++++++++++++
.../pages/architecture/implemented-standards.adoc | 1 +
src/site/xdoc/protocols/imap4.xml | 1 +
13 files changed, 298 insertions(+)
diff --git a/mpt/impl/imap-mailbox/core/src/main/java/org/apache/james/mpt/imapmailbox/suite/AuthenticatedState.java b/mpt/impl/imap-mailbox/core/src/main/java/org/apache/james/mpt/imapmailbox/suite/AuthenticatedState.java
index 1c1606d174..59ac0ee443 100644
--- a/mpt/impl/imap-mailbox/core/src/main/java/org/apache/james/mpt/imapmailbox/suite/AuthenticatedState.java
+++ b/mpt/impl/imap-mailbox/core/src/main/java/org/apache/james/mpt/imapmailbox/suite/AuthenticatedState.java
@@ -49,6 +49,11 @@ public abstract class AuthenticatedState extends BasicImapCommands {
BasicImapCommands.authenticate(simpleScriptedTestProtocol);
}
+ @Test
+ public void testId() throws Exception {
+ simpleScriptedTestProtocol.run("Id");
+ }
+
@Test
public void testNoopUS() throws Exception {
simpleScriptedTestProtocol.run("Noop");
diff --git a/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/Id.test b/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/Id.test
new file mode 100644
index 0000000000..c52fda89d4
--- /dev/null
+++ b/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/Id.test
@@ -0,0 +1,27 @@
+################################################################
+# Licensed to the Apache Software Foundation (ASF) under one #
+# or more contributor license agreements. See the NOTICE file #
+# distributed with this work for additional information #
+# regarding copyright ownership. The ASF licenses this file #
+# to you under the Apache License, Version 2.0 (the #
+# "License"); you may not use this file except in compliance #
+# with the License. You may obtain a copy of the License at #
+# #
+# http://www.apache.org/licenses/LICENSE-2.0 #
+# #
+# Unless required by applicable law or agreed to in writing, #
+# software distributed under the License is distributed on an #
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY #
+# KIND, either express or implied. See the License for the #
+# specific language governing permissions and limitations #
+# under the License. #
+################################################################
+
+C: a0 ID NIL
+S: \* ID \(\)
+S: a0 OK ID completed.
+
+
+C: a1 ID ("vendor", "whatever", "version", "3.7.0", "random", "random value")
+S: \* ID \(\)
+S: a1 OK ID 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 c1029b683e..0e4f1a19e6 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
@@ -173,6 +173,7 @@ public interface ImapConstants {
ImapCommand COMPRESS_COMMAND = ImapCommand.anyStateCommand("COMPRESS");
ImapCommand LOGOUT_COMMAND = ImapCommand.anyStateCommand("LOGOUT");
ImapCommand NOOP_COMMAND = ImapCommand.anyStateCommand("NOOP");
+ ImapCommand ID_COMMAND = ImapCommand.anyStateCommand("ID");
ImapCommand AUTHENTICATE_COMMAND = ImapCommand.nonAuthenticatedStateCommand("AUTHENTICATE");
ImapCommand LOGIN_COMMAND = ImapCommand.nonAuthenticatedStateCommand("LOGIN");
diff --git a/protocols/imap/src/main/java/org/apache/james/imap/decode/parser/IDCommandParser.java b/protocols/imap/src/main/java/org/apache/james/imap/decode/parser/IDCommandParser.java
new file mode 100644
index 0000000000..cf306a43ee
--- /dev/null
+++ b/protocols/imap/src/main/java/org/apache/james/imap/decode/parser/IDCommandParser.java
@@ -0,0 +1,83 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one *
+ * or more contributor license agreements. See the NOTICE file *
+ * distributed with this work for additional information *
+ * regarding copyright ownership. The ASF licenses this file *
+ * to you under the Apache License, Version 2.0 (the *
+ * "License"); you may not use this file except in compliance *
+ * with the License. You may obtain a copy of the License at *
+ * *
+ * http://www.apache.org/licenses/LICENSE-2.0 *
+ * *
+ * Unless required by applicable law or agreed to in writing, *
+ * software distributed under the License is distributed on an *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY *
+ * KIND, either express or implied. See the License for the *
+ * specific language governing permissions and limitations *
+ * under the License. *
+ ****************************************************************/
+package org.apache.james.imap.decode.parser;
+
+import java.util.Optional;
+
+import org.apache.james.imap.api.ImapConstants;
+import org.apache.james.imap.api.ImapMessage;
+import org.apache.james.imap.api.Tag;
+import org.apache.james.imap.api.message.response.StatusResponseFactory;
+import org.apache.james.imap.api.process.ImapSession;
+import org.apache.james.imap.decode.DecodingException;
+import org.apache.james.imap.decode.ImapRequestLineReader;
+import org.apache.james.imap.decode.base.AbstractImapCommandParser;
+import org.apache.james.imap.message.request.IDRequest;
+
+import com.google.common.collect.ImmutableMap;
+
+/**
+ * Parses ID commands
+ *
+ * CF https://www.rfc-editor.org/rfc/rfc2971.html
+ */
+public class IDCommandParser extends AbstractImapCommandParser {
+ public IDCommandParser(StatusResponseFactory statusResponseFactory) {
+ super(ImapConstants.ID_COMMAND, statusResponseFactory);
+ }
+
+ @Override
+ protected ImapMessage decode(ImapRequestLineReader request, Tag tag, ImapSession session) throws DecodingException {
+ char c = request.nextWordChar();
+ if (c != '(') {
+ String s = request.consumeWord(ImapRequestLineReader.NoopCharValidator.INSTANCE);
+ if (s.equalsIgnoreCase("NIL")) {
+ request.eol();
+ return new IDRequest(tag, Optional.empty());
+ }
+ }
+ request.consumeChar('(');
+
+ ImmutableMap.Builder<String, String> parameters = ImmutableMap.builder();
+ boolean first = true;
+
+ while (c != ')') {
+ if (first) {
+ first = false;
+ } else {
+ request.nextWordChar();
+ request.consumeChar(',');
+ }
+ request.nextWordChar();
+ String field = request.consumeQuoted();
+ request.nextWordChar();
+ request.consumeChar(',');
+ request.nextWordChar();
+ String value = request.consumeQuoted();
+
+ parameters.put(field, value);
+
+ c = request.nextWordChar();
+ }
+
+ request.consumeChar(')');
+ request.eol();
+ return new IDRequest(tag, Optional.of(parameters.build()));
+ }
+}
diff --git a/protocols/imap/src/main/java/org/apache/james/imap/decode/parser/ImapParserFactory.java b/protocols/imap/src/main/java/org/apache/james/imap/decode/parser/ImapParserFactory.java
index 81515ff876..2676ae4cf1 100644
--- a/protocols/imap/src/main/java/org/apache/james/imap/decode/parser/ImapParserFactory.java
+++ b/protocols/imap/src/main/java/org/apache/james/imap/decode/parser/ImapParserFactory.java
@@ -90,6 +90,7 @@ public class ImapParserFactory implements ImapCommandParserFactory {
new StoreCommandParser(statusResponseFactory),
new UidCommandParser(this, statusResponseFactory),
new IdleCommandParser(statusResponseFactory),
+ new IDCommandParser(statusResponseFactory),
new StartTLSCommandParser(statusResponseFactory),
// RFC3691
diff --git a/protocols/imap/src/main/java/org/apache/james/imap/encode/IdResponseEncoder.java b/protocols/imap/src/main/java/org/apache/james/imap/encode/IdResponseEncoder.java
new file mode 100644
index 0000000000..7c11c56c5a
--- /dev/null
+++ b/protocols/imap/src/main/java/org/apache/james/imap/encode/IdResponseEncoder.java
@@ -0,0 +1,40 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one *
+ * or more contributor license agreements. See the NOTICE file *
+ * distributed with this work for additional information *
+ * regarding copyright ownership. The ASF licenses this file *
+ * to you under the Apache License, Version 2.0 (the *
+ * "License"); you may not use this file except in compliance *
+ * with the License. You may obtain a copy of the License at *
+ * *
+ * http://www.apache.org/licenses/LICENSE-2.0 *
+ * *
+ * Unless required by applicable law or agreed to in writing, *
+ * software distributed under the License is distributed on an *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY *
+ * KIND, either express or implied. See the License for the *
+ * specific language governing permissions and limitations *
+ * under the License. *
+ ****************************************************************/
+
+package org.apache.james.imap.encode;
+
+import java.io.IOException;
+
+import org.apache.james.imap.message.response.IdResponse;
+
+public class IdResponseEncoder implements ImapResponseEncoder<IdResponse> {
+ @Override
+ public Class<IdResponse> acceptableMessages() {
+ return IdResponse.class;
+ }
+
+ @Override
+ public void encode(IdResponse existsResponse, ImapResponseComposer composer) throws IOException {
+ composer.untagged()
+ .message("ID")
+ .openParen()
+ .closeParen()
+ .end();
+ }
+}
diff --git a/protocols/imap/src/main/java/org/apache/james/imap/encode/main/DefaultImapEncoderFactory.java b/protocols/imap/src/main/java/org/apache/james/imap/encode/main/DefaultImapEncoderFactory.java
index a135f457f1..9208107a5a 100644
--- a/protocols/imap/src/main/java/org/apache/james/imap/encode/main/DefaultImapEncoderFactory.java
+++ b/protocols/imap/src/main/java/org/apache/james/imap/encode/main/DefaultImapEncoderFactory.java
@@ -36,6 +36,7 @@ import org.apache.james.imap.encode.ExistsResponseEncoder;
import org.apache.james.imap.encode.ExpungeResponseEncoder;
import org.apache.james.imap.encode.FetchResponseEncoder;
import org.apache.james.imap.encode.FlagsResponseEncoder;
+import org.apache.james.imap.encode.IdResponseEncoder;
import org.apache.james.imap.encode.ImapEncoder;
import org.apache.james.imap.encode.ImapEncoderFactory;
import org.apache.james.imap.encode.ImapResponseComposer;
@@ -109,6 +110,7 @@ public class DefaultImapEncoderFactory implements ImapEncoderFactory {
new FetchResponseEncoder(neverAddBodyStructureExtensions),
new ExpungeResponseEncoder(),
new ExistsResponseEncoder(),
+ new IdResponseEncoder(),
new MailboxStatusResponseEncoder(),
new SearchResponseEncoder(),
new LSubResponseEncoder(),
diff --git a/protocols/imap/src/main/java/org/apache/james/imap/message/request/IDRequest.java b/protocols/imap/src/main/java/org/apache/james/imap/message/request/IDRequest.java
new file mode 100644
index 0000000000..40411135fd
--- /dev/null
+++ b/protocols/imap/src/main/java/org/apache/james/imap/message/request/IDRequest.java
@@ -0,0 +1,39 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one *
+ * or more contributor license agreements. See the NOTICE file *
+ * distributed with this work for additional information *
+ * regarding copyright ownership. The ASF licenses this file *
+ * to you under the Apache License, Version 2.0 (the *
+ * "License"); you may not use this file except in compliance *
+ * with the License. You may obtain a copy of the License at *
+ * *
+ * http://www.apache.org/licenses/LICENSE-2.0 *
+ * *
+ * Unless required by applicable law or agreed to in writing, *
+ * software distributed under the License is distributed on an *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY *
+ * KIND, either express or implied. See the License for the *
+ * specific language governing permissions and limitations *
+ * under the License. *
+ ****************************************************************/
+package org.apache.james.imap.message.request;
+
+import java.util.Map;
+import java.util.Optional;
+
+import org.apache.james.imap.api.ImapConstants;
+import org.apache.james.imap.api.Tag;
+
+public class IDRequest extends AbstractImapRequest {
+
+ private final Optional<Map<String, String>> parameters;
+
+ public IDRequest(Tag tag, Optional<Map<String, String>> parameters) {
+ super(tag, ImapConstants.ID_COMMAND);
+ this.parameters = parameters;
+ }
+
+ public Optional<Map<String, String>> getParameters() {
+ return parameters;
+ }
+}
diff --git a/protocols/imap/src/main/java/org/apache/james/imap/message/response/IdResponse.java b/protocols/imap/src/main/java/org/apache/james/imap/message/response/IdResponse.java
new file mode 100644
index 0000000000..463d18bf83
--- /dev/null
+++ b/protocols/imap/src/main/java/org/apache/james/imap/message/response/IdResponse.java
@@ -0,0 +1,27 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one *
+ * or more contributor license agreements. See the NOTICE file *
+ * distributed with this work for additional information *
+ * regarding copyright ownership. The ASF licenses this file *
+ * to you under the Apache License, Version 2.0 (the *
+ * "License"); you may not use this file except in compliance *
+ * with the License. You may obtain a copy of the License at *
+ * *
+ * http://www.apache.org/licenses/LICENSE-2.0 *
+ * *
+ * Unless required by applicable law or agreed to in writing, *
+ * software distributed under the License is distributed on an *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY *
+ * KIND, either express or implied. See the License for the *
+ * specific language governing permissions and limitations *
+ * under the License. *
+ ****************************************************************/
+
+package org.apache.james.imap.message.response;
+
+import org.apache.james.imap.api.message.response.ImapResponseMessage;
+
+public class IdResponse implements ImapResponseMessage {
+ public IdResponse() {
+ }
+}
diff --git a/protocols/imap/src/main/java/org/apache/james/imap/processor/DefaultProcessor.java b/protocols/imap/src/main/java/org/apache/james/imap/processor/DefaultProcessor.java
index 7812c1fe6a..356d161990 100644
--- a/protocols/imap/src/main/java/org/apache/james/imap/processor/DefaultProcessor.java
+++ b/protocols/imap/src/main/java/org/apache/james/imap/processor/DefaultProcessor.java
@@ -61,6 +61,7 @@ public class DefaultProcessor implements ImapProcessor {
builder.add(new SystemMessageProcessor(mailboxManager));
builder.add(new LogoutProcessor(mailboxManager, statusResponseFactory, metricFactory));
builder.add(capabilityProcessor);
+ builder.add(new IdProcessor(mailboxManager, statusResponseFactory, metricFactory));
builder.add(new CheckProcessor(mailboxManager, statusResponseFactory, metricFactory));
builder.add(new LoginProcessor(mailboxManager, statusResponseFactory, metricFactory));
builder.add(new RenameProcessor(mailboxManager, statusResponseFactory, metricFactory));
diff --git a/protocols/imap/src/main/java/org/apache/james/imap/processor/IdProcessor.java b/protocols/imap/src/main/java/org/apache/james/imap/processor/IdProcessor.java
new file mode 100644
index 0000000000..9a09c2540d
--- /dev/null
+++ b/protocols/imap/src/main/java/org/apache/james/imap/processor/IdProcessor.java
@@ -0,0 +1,70 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one *
+ * or more contributor license agreements. See the NOTICE file *
+ * distributed with this work for additional information *
+ * regarding copyright ownership. The ASF licenses this file *
+ * to you under the Apache License, Version 2.0 (the *
+ * "License"); you may not use this file except in compliance *
+ * with the License. You may obtain a copy of the License at *
+ * *
+ * http://www.apache.org/licenses/LICENSE-2.0 *
+ * *
+ * Unless required by applicable law or agreed to in writing, *
+ * software distributed under the License is distributed on an *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY *
+ * KIND, either express or implied. See the License for the *
+ * specific language governing permissions and limitations *
+ * under the License. *
+ ****************************************************************/
+
+package org.apache.james.imap.processor;
+
+import java.util.List;
+
+import org.apache.james.imap.api.message.Capability;
+import org.apache.james.imap.api.message.response.StatusResponseFactory;
+import org.apache.james.imap.api.process.ImapSession;
+import org.apache.james.imap.message.request.IDRequest;
+import org.apache.james.imap.message.response.IdResponse;
+import org.apache.james.mailbox.MailboxManager;
+import org.apache.james.metrics.api.MetricFactory;
+import org.apache.james.util.MDCBuilder;
+import org.apache.james.util.MDCStructuredLogger;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.collect.ImmutableList;
+
+import reactor.core.publisher.Mono;
+
+public class IdProcessor extends AbstractMailboxProcessor<IDRequest> implements CapabilityImplementingProcessor {
+ private static final Logger LOGGER = LoggerFactory.getLogger(IdProcessor.class);
+ private static final ImmutableList<Capability> CAPABILITIES = ImmutableList.of(Capability.of("ID"));
+
+ public IdProcessor(MailboxManager mailboxManager, StatusResponseFactory factory, MetricFactory metricFactory) {
+ super(IDRequest.class, mailboxManager, factory, metricFactory);
+ }
+
+ @Override
+ protected Mono<Void> processRequestReactive(IDRequest request, ImapSession session, Responder responder) {
+ MDCStructuredLogger.forLogger(LOGGER)
+ .field("parameters", request.getParameters().map(Object::toString).orElse("NIL"))
+ .log(logger -> logger.info("Received id information"));
+
+ responder.respond(new IdResponse());
+
+ return unsolicitedResponses(session, responder, false)
+ .then(Mono.fromRunnable(() -> okComplete(request, responder)));
+ }
+
+ @Override
+ protected MDCBuilder mdc(IDRequest message) {
+ return MDCBuilder.create()
+ .addToContext(MDCBuilder.ACTION, "ID");
+ }
+
+ @Override
+ public List<Capability> getImplementedCapabilities(ImapSession session) {
+ return CAPABILITIES;
+ }
+}
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 d3b54aa1f7..f2992260a9 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
@@ -67,6 +67,7 @@ The following IMAP specifications are implemented:
- link:https://www.rfc-editor.org/rfc/rfc7889.html[RFC-7889] IMAP Extension for APPENDLIMIT
- link:https://www.rfc-editor.org/rfc/rfc8474.html[RFC-8474] IMAP Extension for Object Identifiers
- link:https://datatracker.ietf.org/doc/html/rfc8438.html[RFC-8438] IMAP Extension for STATUS=SIZE
+ - link:https://datatracker.ietf.org/doc/html/rfc2971.html[RFC-2971] IMAP ID Extension
Partially implemented specifications:
diff --git a/src/site/xdoc/protocols/imap4.xml b/src/site/xdoc/protocols/imap4.xml
index 14e852096e..c2f747b455 100644
--- a/src/site/xdoc/protocols/imap4.xml
+++ b/src/site/xdoc/protocols/imap4.xml
@@ -62,6 +62,7 @@
<li>IMAP Extension for OBJECTID (https://www.rfc-editor.org/rfc/rfc8474.html)</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>
+ <li>IMAP ID (https://www.rfc-editor.org/rfc/rfc2971.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