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 2026/03/10 03:40:33 UTC
(james-project) branch 3.9.x updated: JAMES-4184 POP3 TOP msg 0 - only read headers (#2965)
This is an automated email from the ASF dual-hosted git repository.
rcordier pushed a commit to branch 3.9.x
in repository https://gitbox.apache.org/repos/asf/james-project.git
The following commit(s) were added to refs/heads/3.9.x by this push:
new 386bd0e843 JAMES-4184 POP3 TOP msg 0 - only read headers (#2965)
386bd0e843 is described below
commit 386bd0e843281db738f5ce70ef8c14c8f687eecb
Author: Benoit TELLIER <bt...@linagora.com>
AuthorDate: Mon Mar 9 04:25:15 2026 +0100
JAMES-4184 POP3 TOP msg 0 - only read headers (#2965)
---
.../james/protocols/pop3/core/TopCmdHandler.java | 9 +++--
.../james/protocols/pop3/mailbox/Mailbox.java | 9 +++++
.../mailbox/DistributedMailboxAdapter.java | 18 ++++++++++
.../james/pop3server/mailbox/MailboxAdapter.java | 24 +++++++++++---
.../apache/james/pop3server/POP3ServerTest.java | 38 ++++++++++++++++++++++
5 files changed, 91 insertions(+), 7 deletions(-)
diff --git a/protocols/pop3/src/main/java/org/apache/james/protocols/pop3/core/TopCmdHandler.java b/protocols/pop3/src/main/java/org/apache/james/protocols/pop3/core/TopCmdHandler.java
index bf15a77b7c..4e08416d1b 100644
--- a/protocols/pop3/src/main/java/org/apache/james/protocols/pop3/core/TopCmdHandler.java
+++ b/protocols/pop3/src/main/java/org/apache/james/protocols/pop3/core/TopCmdHandler.java
@@ -60,8 +60,9 @@ public class TopCmdHandler extends AbstractPOP3CommandHandler implements CapaCap
if (args.getLineCount().isEmpty()) {
return handleSyntaxError();
}
- InputStream content = getMessageContent(session, data);
- InputStream in = new CountingBodyInputStream(new CRLFTerminatedInputStream(new ExtraDotInputStream(content)), args.getLineCount().get());
+ int lineCount = args.getLineCount().get();
+ InputStream content = lineCount == 0 ? getMessageHeaders(session, data) : getMessageContent(session, data);
+ InputStream in = new CountingBodyInputStream(new CRLFTerminatedInputStream(new ExtraDotInputStream(content)), lineCount);
return new POP3StreamResponse(POP3Response.OK_RESPONSE, "Message follows", in);
}
@@ -95,6 +96,10 @@ public class TopCmdHandler extends AbstractPOP3CommandHandler implements CapaCap
return session.getUserMailbox().getMessage(data.getUid());
}
+ protected InputStream getMessageHeaders(POP3Session session, MessageMetaData data) throws IOException {
+ return session.getUserMailbox().getMessageHeaders(data.getUid());
+ }
+
@Override
public Set<String> getImplementedCapabilities(POP3Session session) {
if (session.getHandlerState() == POP3Session.TRANSACTION) {
diff --git a/protocols/pop3/src/main/java/org/apache/james/protocols/pop3/mailbox/Mailbox.java b/protocols/pop3/src/main/java/org/apache/james/protocols/pop3/mailbox/Mailbox.java
index 38b5fbb7cc..6f61cf4963 100644
--- a/protocols/pop3/src/main/java/org/apache/james/protocols/pop3/mailbox/Mailbox.java
+++ b/protocols/pop3/src/main/java/org/apache/james/protocols/pop3/mailbox/Mailbox.java
@@ -33,6 +33,15 @@ public interface Mailbox {
*/
InputStream getMessage(String uid) throws IOException;
+ /**
+ * Return only the headers as {@link InputStream} for the given <code>uid</code>.
+ * Implementations may override this for better performance (e.g. using FetchGroup.HEADERS).
+ * @exception IOException If message can not be found or is inaccessible
+ */
+ default InputStream getMessageHeaders(String uid) throws IOException {
+ return getMessage(uid);
+ }
+
/**
* Return a immutable {@link List} which holds the {@link MessageMetaData}
* for all messages in the {@link Mailbox}
diff --git a/server/protocols/protocols-pop3-distributed/src/main/java/org/apache/james/pop3server/mailbox/DistributedMailboxAdapter.java b/server/protocols/protocols-pop3-distributed/src/main/java/org/apache/james/pop3server/mailbox/DistributedMailboxAdapter.java
index baee0734f5..3a6a36b231 100644
--- a/server/protocols/protocols-pop3-distributed/src/main/java/org/apache/james/pop3server/mailbox/DistributedMailboxAdapter.java
+++ b/server/protocols/protocols-pop3-distributed/src/main/java/org/apache/james/pop3server/mailbox/DistributedMailboxAdapter.java
@@ -113,6 +113,24 @@ public class DistributedMailboxAdapter implements Mailbox {
}
}
+ @Override
+ public InputStream getMessageHeaders(String uid) throws IOException {
+ try {
+ MessageId messageId = messageIdFactory.fromString(uid);
+ Iterator<MessageResult> messages = messageIdManager.getMessage(messageId, FetchGroup.HEADERS, session).iterator();
+ if (messages.hasNext()) {
+ return messages.next().getHeaders().getInputStream();
+ } else {
+ LOGGER.warn("Removing {} from {} POP3 projection for user {} as it is not backed by a MailboxMessage",
+ uid, mailbox.getId().serialize(), session.getUser().asString());
+ Mono.from(metadataStore.remove(mailbox.getId(), messageId)).block();
+ throw new IOException("Message does not exist for uid " + uid);
+ }
+ } catch (MailboxException e) {
+ throw new IOException("Unable to retrieve message headers for uid " + uid, e);
+ }
+ }
+
@Override
public List<MessageMetaData> getMessages() {
return Flux.from(metadataStore.stat(mailbox.getId()))
diff --git a/server/protocols/protocols-pop3/src/main/java/org/apache/james/pop3server/mailbox/MailboxAdapter.java b/server/protocols/protocols-pop3/src/main/java/org/apache/james/pop3server/mailbox/MailboxAdapter.java
index e6de9b79f1..f582ab61b1 100644
--- a/server/protocols/protocols-pop3/src/main/java/org/apache/james/pop3server/mailbox/MailboxAdapter.java
+++ b/server/protocols/protocols-pop3/src/main/java/org/apache/james/pop3server/mailbox/MailboxAdapter.java
@@ -40,9 +40,6 @@ import org.apache.james.protocols.pop3.mailbox.MessageMetaData;
import com.google.common.collect.ImmutableList;
public class MailboxAdapter implements Mailbox {
- private static final FetchGroup FULL_GROUP = FetchGroup.FULL_CONTENT;
- private static final FetchGroup METADATA_GROUP = FetchGroup.MINIMAL;
-
private final MessageManager manager;
private final MailboxSession session;
@@ -58,7 +55,7 @@ public class MailboxAdapter implements Mailbox {
public InputStream getMessage(String uid) throws IOException {
try {
mailboxManager.startProcessingRequest(session);
- Iterator<MessageResult> results = manager.getMessages(MessageUid.of(Long.parseLong(uid)).toRange(), FULL_GROUP, session);
+ Iterator<MessageResult> results = manager.getMessages(MessageUid.of(Long.parseLong(uid)).toRange(), FetchGroup.FULL_CONTENT, session);
if (results.hasNext()) {
return results.next().getFullContent().getInputStream();
} else {
@@ -71,11 +68,28 @@ public class MailboxAdapter implements Mailbox {
}
}
+ @Override
+ public InputStream getMessageHeaders(String uid) throws IOException {
+ try {
+ mailboxManager.startProcessingRequest(session);
+ Iterator<MessageResult> results = manager.getMessages(MessageUid.of(Long.parseLong(uid)).toRange(), FetchGroup.HEADERS, session);
+ if (results.hasNext()) {
+ return results.next().getHeaders().getInputStream();
+ } else {
+ throw new IOException("Message does not exist for uid " + uid);
+ }
+ } catch (MailboxException e) {
+ throw new IOException("Unable to retrieve message headers for uid " + uid, e);
+ } finally {
+ mailboxManager.endProcessingRequest(session);
+ }
+ }
+
@Override
public List<MessageMetaData> getMessages() throws IOException {
try {
mailboxManager.startProcessingRequest(session);
- Iterator<MessageResult> results = manager.getMessages(MessageRange.all(), METADATA_GROUP, session);
+ Iterator<MessageResult> results = manager.getMessages(MessageRange.all(), FetchGroup.MINIMAL, session);
List<MessageMetaData> mList = new ArrayList<>();
while (results.hasNext()) {
MessageResult result = results.next();
diff --git a/server/protocols/protocols-pop3/src/test/java/org/apache/james/pop3server/POP3ServerTest.java b/server/protocols/protocols-pop3/src/test/java/org/apache/james/pop3server/POP3ServerTest.java
index d0e091e41f..00f12a6359 100644
--- a/server/protocols/protocols-pop3/src/test/java/org/apache/james/pop3server/POP3ServerTest.java
+++ b/server/protocols/protocols-pop3/src/test/java/org/apache/james/pop3server/POP3ServerTest.java
@@ -544,6 +544,44 @@ public class POP3ServerTest {
mailboxManager.deleteMailbox(mailboxPath, session);
}
+ @Test
+ void topWithZeroLinesShouldReturnOnlyHeaders() throws Exception {
+ finishSetUp(pop3Configuration);
+
+ pop3Client = new POP3Client();
+ InetSocketAddress bindedAddress = new ProtocolServerUtils(pop3Server).retrieveBindedAddress();
+ pop3Client.connect(bindedAddress.getAddress().getHostAddress(), bindedAddress.getPort());
+
+ Username username = Username.of("topzero");
+ usersRepository.addUser(username, "password");
+
+ MailboxPath mailboxPath = MailboxPath.inbox(username);
+ MailboxSession session = mailboxManager.authenticate(username, "password").withoutDelegation();
+ mailboxManager.createMailbox(mailboxPath, session);
+ setupTestMails(session, mailboxManager.getMailbox(mailboxPath, session));
+
+ pop3Client.login("topzero", "password");
+ POP3MessageInfo[] entries = pop3Client.listMessages();
+
+ Reader reader = pop3Client.retrieveMessageTop(entries[0].number, 0);
+ assertThat(reader).isNotNull();
+
+ StringBuilder responseBody = new StringBuilder();
+ char[] buffer = new char[1024];
+ int n;
+ while ((n = reader.read(buffer)) != -1) {
+ responseBody.append(buffer, 0, n);
+ }
+ reader.close();
+
+ String response = responseBody.toString();
+ assertThat(response).contains("Return-path: return@test.com");
+ assertThat(response).contains("Subject: test");
+ assertThat(response).doesNotContain("Body Text POP3ServerTest.setupTestMails");
+
+ mailboxManager.deleteMailbox(mailboxPath, session);
+ }
+
@Test
void pop3SessionShouldTolerateConcurrentDeletes() throws Exception {
finishSetUp(pop3Configuration);
---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@james.apache.org
For additional commands, e-mail: notifications-help@james.apache.org