- Shipped
- August 14, 2026 at 2:09 AM UTC
- Author
- Kamo
- Commit
- 9698977
Two round-trip problems on the /messages critical path, both invisible in the code that caused them. listFolders opened each folder read-only to read two integers off it. That is a SELECT and a CLOSE per folder, and it makes the server build each mailbox index along the way, so a twenty-folder mailbox spent forty round trips answering a question about forty numbers. The sidebar reloads this on mount and again on every mailbox change — mail arriving, a message read, a move, a delete — so it is the most-called IMAP operation in the product. getUnreadCount's own javadoc has said listFolders was "far too expensive" since it was written. STATUS returns exactly those counts and touches no message. Servers advertising RFC 5819 LIST-STATUS, Dovecot among them, now answer the whole tree in one command; anything else gets one STATUS per closed folder, where Jakarta Mail's status cache serves unread and total from a single response. Only a folder whose server refuses STATUS is opened now. The list path had an N+1 nothing named. FetchProfile.Item.ENVELOPE expands to ENVELOPE INTERNALDATE RFC822.SIZE — no BODYSTRUCTURE and no headers — but toEnvelope calls getContentType() for the attachment heuristic and reads Message-ID for threading. Jakarta Mail answers an un-prefetched accessor by quietly issuing its own single-message FETCH, so a fifty-message page cost up to a hundred extra round trips. Search was worse: it prefetched nothing at all. Both now share one profile that covers every field toEnvelope reads. The LIST-STATUS reply is parsed here rather than by Jakarta Mail, so it is parsed under test: IMAPResponse accepts a literal protocol line, and the ten cases cover ordering, \Noselect, absent STATUS and unrelated traffic. One of them is a bug this found — ListInfo decodes modified UTF-7 mailbox names and Status does not, so joining them as they arrive would have reported zero unread for exactly those folders with an accent in the name, and nothing else.