Repository navigation
Conversation
Client::getConnection() runs checkConnection(), which verifies the session with a NOOP. idle() called it on every loop iteration, i.e. while the idle session was in IDLE. rfc2177 only allows DONE there - Dovecot answers "BAD Expected DONE" and ends the IDLE, checkConnection() then silently opens a new session without SELECT/IDLE, so no EXISTS is ever reported. Fetch the protocol connection once before entering IDLE and use it directly inside the loop. The liveness check after an empty read now uses the stream metadata (eof) instead of connected(), which also sends a NOOP. Adds a regression test with a small fake IMAP server that reacts to any command other than DONE during IDLE the way Dovecot does. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Folder::idle()calls$idle_client->getConnection()on every loop iteration.Client::getConnection()runscheckConnection()→isConnected()→ImapProtocol::connected(), which sends aNOOP— while the idle session is in IDLE.RFC 2177 only allows
DONEduring IDLE. Dovecot answersBAD Expected DONEand terminates the IDLE command.checkConnection()catches the failure and silently callsconnect(), which opens a new session withoutSELECTand withoutIDLE. From then on noEXISTSis ever reported; new messages are only seen by whatever polls next. Theconnected()check in thecatchblock has the same effect.Fix
nextLine(),meta()).eof) whether the connection is still alive, instead ofconnected()(which sends a NOOP).The main client (
$this->client) is not in IDLE, so itsisConnected()check in theEXISTSbranch is left unchanged.Test
tests/IdleTest.phpstarts a small fake IMAP server (tests/support/idle_server.php) that reacts to any command other thanDONEduring IDLE like Dovecot does, and records every command. The test asserts that no command exceptDONEis sent while idling and that the idle session is not silently re-established. It fails on currentmaster(TAG6 NOOPreceived during IDLE) and passes with this change.Related: #624 (busy loop / throw after silent periods in the same method).