(geronimo-mail) 02/05: GERONIMO-6902 - Folder.expunge() always returns an empty array - EXPUNGE responses extracted under the wrong keyword
[email protected] Sat, 18 Jul 2026 19:12:28 +0000
| Newsgroups | gmane.comp.java.geronimo.cvs |
|---|---|
| Message-ID | <[email protected]> |
This is an automated email from the ASF dual-hosted git repository. rzo1 pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/geronimo-mail.git commit b78b1e61c21e9d6dbc22aafdefe6211e1fe1015c Author: Richard Zowalla <[email protected]> AuthorDate: Sat Jul 18 21:10:28 2026 +0200 GERONIMO-6902 - Folder.expunge() always returns an empty array - EXPUNGE responses extracted under the wrong keyword --- .../mail/store/imap/connection/IMAPConnection.java | 8 +- .../mail/store/imap/IMAPExpungeRegressionTest.java | 107 +++++++++++++++++++++ geronimo-mail_2.1_tck/src/tck/geronimo.jtx | 1 - 3 files changed, 113 insertions(+), 3 deletions(-) diff --git a/geronimo-mail_2.1_impl/geronimo-mail_2.1_provider/src/main/java/org/apache/geronimo/mail/store/imap/connection/IMAPConnection.java b/geronimo-mail_2.1_impl/geronimo-mail_2.1_provider/src/main/java/org/apache/geronimo/mail/store/imap/connection/IMAPConnection.java index 9351115..1d1192f 100644 --- a/geronimo-mail_2.1_impl/geronimo-mail_2.1_provider/src/main/java/org/apache/geronimo/mail/store/imap/connection/IMAPConnection.java +++ b/geronimo-mail_2.1_impl/geronimo-mail_2.1_provider/src/main/java/org/apache/geronimo/mail/store/imap/connection/IMAPConnection.java @@ -1359,8 +1359,12 @@ public class IMAPConnection extends MailConnection { public synchronized List expungeMailbox() throws MessagingException { // send the message, and make sure we got an OK response sendCommand("EXPUNGE"); - // extract all of the expunged responses and return. - return extractResponses("EXPUNGED"); + // extract all of the expunged responses and return. The parser queues + // the untagged "* nn EXPUNGE" responses under the "EXPUNGE" keyword, so + // that is the keyword we must extract here. Extracting them prevents the + // pending-response handler from processing the same expunge events a + // second time. + return extractResponses("EXPUNGE"); } public int[] searchMailbox(SearchTerm term) throws MessagingException { diff --git a/geronimo-mail_2.1_impl/geronimo-mail_2.1_provider/src/test/java/org/apache/geronimo/mail/store/imap/IMAPExpungeRegressionTest.java b/geronimo-mail_2.1_impl/geronimo-mail_2.1_provider/src/test/java/org/apache/geronimo/mail/store/imap/IMAPExpungeRegressionTest.java new file mode 100644 index 0000000..84024ba --- /dev/null +++ b/geronimo-mail_2.1_impl/geronimo-mail_2.1_provider/src/test/java/org/apache/geronimo/mail/store/imap/IMAPExpungeRegressionTest.java @@ -0,0 +1,107 @@ +/** + * 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.geronimo.mail.store.imap; + +import java.io.ByteArrayOutputStream; +import java.io.InputStream; +import java.util.Properties; + +import jakarta.mail.Flags; +import jakarta.mail.Folder; +import jakarta.mail.Message; +import jakarta.mail.Session; +import jakarta.mail.Store; + +import org.apache.geronimo.mail.testserver.AbstractProtocolTest; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * Regression test for the expunge-keyword-typo defect surfaced by the + * Jakarta Mail TCK (exception#msgRemoveExp_Test): + * IMAPConnection.expungeMailbox() extracted the untagged expunge responses + * with the keyword "EXPUNGED", but the parser queues them under "EXPUNGE". + * As a result Folder.expunge() always returned an empty array and the + * unextracted responses were double-processed by the pending-response + * handler. + */ +public class IMAPExpungeRegressionTest extends AbstractProtocolTest { + + protected Store connect() throws Exception { + final Properties props = new Properties(); + props.setProperty("mail.imap.port", String.valueOf(imapConf.getListenerPort())); + props.setProperty("mail.debug", "true"); + final Session session = Session.getInstance(props); + final Store store = session.getStore("imap"); + store.connect("127.0.0.1", "serveruser", "serverpass"); + return store; + } + + protected byte[] readMessageResource(final String name) throws Exception { + final ByteArrayOutputStream bout = new ByteArrayOutputStream(); + try (InputStream in = IMAPExpungeRegressionTest.class.getResourceAsStream(name)) { + final byte[] buf = new byte[4096]; + int n; + while ((n = in.read(buf)) != -1) { + bout.write(buf, 0, n); + } + } + return bout.toByteArray(); + } + + @Test + public void testExpungeReturnsExpungedMessages() throws Exception { + start(); + server.createUserMailbox("test1"); + final byte[] message = readMessageResource("/messages/simple.msg"); + server.appendToUserMailbox("test1", message); + server.appendToUserMailbox("test1", message); + server.appendToUserMailbox("test1", message); + + final Store store = connect(); + try { + final Folder folder = store.getDefaultFolder().getFolder("test1"); + folder.open(Folder.READ_WRITE); + try { + assertEquals(3, folder.getMessageCount(), "test setup must provide three messages"); + + // flag two of the three messages for deletion + folder.getMessage(1).setFlag(Flags.Flag.DELETED, true); + folder.getMessage(3).setFlag(Flags.Flag.DELETED, true); + + final Message[] expunged = folder.expunge(); + assertNotNull(expunged, "expunge() must never return null"); + assertEquals(2, expunged.length, + "expunge() must return exactly the messages removed from the folder"); + for (final Message m : expunged) { + assertTrue(m.isExpunged(), "returned messages must be marked expunged"); + } + + // the message cache must have been updated exactly once per expunge + assertEquals(1, folder.getMessageCount(), + "one message must remain after expunging two of three"); + } finally { + folder.close(false); + } + } finally { + store.close(); + } + } +} diff --git a/geronimo-mail_2.1_tck/src/tck/geronimo.jtx b/geronimo-mail_2.1_tck/src/tck/geronimo.jtx index bc723e6..945b564 100644 --- a/geronimo-mail_2.1_tck/src/tck/geronimo.jtx +++ b/geronimo-mail_2.1_tck/src/tck/geronimo.jtx @@ -33,7 +33,6 @@ SignatureTest.html # IMAPFolder.renameTo fix removed a ~90-test cascade). Distinct defects: # folder create/delete (topdog), permanent flags, list/listSubscribed, # several MIME encoding/decoding and unicode behaviours. -javasoft/sqe/tests/jakarta/mail/exception/testlist.html#msgRemoveExp_Test javasoft/sqe/tests/jakarta/mail/Folder/testlist.html#getPermanentFlags_Test javasoft/sqe/tests/jakarta/mail/internet/InternetAddress/testlist.html#unicode_Test javasoft/sqe/tests/jakarta/mail/internet/MimeBodyPart/testlist.html#attachFile_saveFile_Test