shashank created CAMEL-25808:
--------------------------------

             Summary: camel-mail - after the IMAP folder connection dropped 
during a poll, the consumer marks (or deletes) another mail than the one it 
routed
                 Key: CAMEL-25808
                 URL: https://issues.apache.org/jira/browse/CAMEL-25808
             Project: Camel
          Issue Type: Bug
          Components: camel-mail
            Reporter: shashank
            Assignee: shashank


{{MailConsumer.processCommit}} (main 7aae27315946, lines 520-589) marks a mail 
once its exchange completed: it opens the folder again if it is closed, then 
sets SEEN or DELETED (and copies / moves the mail for {{copyTo}} / {{moveTo}}) 
on the mail object of the poll:

{code:java}
// ensure folder is open
if (!folder.isOpen()) {
    folder.open(Folder.READ_WRITE);
}
...
mail.setFlag(Flags.Flag.SEEN, true);   // or DELETED, after 
folder.copyMessages(new Message[] { mail }, dest)
{code}

The folder is closed at that point when its IMAP connection dropped while the 
mail was routed (an idle timeout of the server during a long exchange, a 
network failure): angus-mail cleans the folder up when it notices that, and 
{{isOpen()}} returns false. The mail object belongs to the earlier open. An 
angus-mail {{IMAPMessage}} sends its commands with {{getSequenceNumber()}}, 
which looks its message number up in the message cache of the *current* open of 
its folder ({{folder.messageCache.seqnumOf(msgnum)}}); the new open builds that 
cache from the mailbox as it is now. When mails were expunged between the two 
opens (another mail client, a second Camel consumer on the same mailbox with a 
shared {{idempotentRepository}} closing its folder, or a server that expunges a 
deleted mail at once), the number designates another mail:

* the mail that was routed is not marked: it stays unread and is routed again 
(a duplicate);
* another mail is marked instead. With the default {{unseen=true}} a mail 
marked SEEN is never polled again, so if it was not routed yet (for example a 
mail after the {{maxMessagesPerPoll}} limit) it is never consumed. With 
{{delete=true}} or {{moveTo}} it is deleted (moved) without being routed. 
Nothing is logged.

*Reproduction* ({{MailConsumerFolderReopenedTest}}, GreenMail and the real 
angus-mail client, as the other tests of the module): three mails, 
{{maxMessagesPerPoll=2}}, the consumer connects through a small TCP proxy. 
While the second mail is routed, another client removes the first one (already 
consumed) and the proxy drops the consumer's connections. On main the commit of 
the second mail marks the third mail SEEN: the mock receives "Message 0", 
"Message 1", "Message 1" and never "Message 2" ({{Message with body Message 2 
was expected but not found in [Message 1]}}, 2 of 2 runs).

*Fix*: {{MailConsumer}} fetches the UIDs of the mails of a poll when it 
retrieves them (one FETCH), and counts the opens of the folder. When the folder 
was opened again after the mails of the batch were retrieved, the mail to mark 
is looked up by its UID (same UIDVALIDITY); when it is no longer there, nothing 
is marked (WARN). The batch is not routed further once the folder was opened 
again (the other mail objects are stale too, and with {{mapMailMessage=false}} 
their content would be read from other mails); those mails are given back as 
for a batch cut short by a stop (CAMEL-25710) and consumed by the next poll. 
POP3 is not changed: {{processCommit}} already finds a POP3 mail again by its 
key (see CAMEL-24434 for that key).

*Tests*: the new test fails on main and passes with the fix. camel-mail unit 
tests with the fix: 246 run, 0 failures, 0 errors, 4 skipped.

*How it was found*: deterministic simulation testing with a fake IMAP world 
(camel-test-dst, CAMEL-25738): the real {{MailConsumer}}, a fake IMAP server 
and client that follows RFC 3501 and the angus-mail message cache behaviour 
above, seeded connection drops, a second consumer, other clients that expunge 
and a server that expunges at once. Invariant: no mail is marked SEEN or 
DELETED, or copied, before its exchange completed. 384 of 5000 seeds failed on 
main, first failing seed 4 (a failing seed replays with the same trace hash); 0 
of 5000 with the fix. The world re-finds CAMEL-25710 when that fix is reverted. 
Then the JUnit test above with the real client.

*Affected*: main, camel-4.23.x, camel-4.22.x, camel-4.18.x (the same 
{{processCommit}}; read, not run). Not run against a real IMAP server other 
than GreenMail.

_Filed with Claude Code on behalf of allthingssecurity._




--
This message was sent by Atlassian Jira
(v8.20.10#820010)

Reply via email to