From 6f006eca4444bf819481e6942ee2098c922a2ad2 Mon Sep 17 00:00:00 2001 From: David Hobley Date: Wed, 23 Sep 2026 08:36:21 +1000 Subject: [PATCH] fix(imap): parse STATUS responses with an unquoted mailbox name StatusParser required the mailbox name to be double-quoted (`STATUS "INBOX" (...)`). RFC 3501 allows the name to be sent as an atom, and Dovecot does so (`STATUS INBOX (...)`), so the regex never matched and MESSAGES/UNSEEN/UIDNEXT were silently left at 0 for every mailbox. The regex now accepts either a quoted string or an atom. --- lib/src/private/imap/status_parser.dart | 5 ++++- test/src/imap/status_parser_test.dart | 22 ++++++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/lib/src/private/imap/status_parser.dart b/lib/src/private/imap/status_parser.dart index 2199404d..27d2fb11 100644 --- a/lib/src/private/imap/status_parser.dart +++ b/lib/src/private/imap/status_parser.dart @@ -6,7 +6,10 @@ import 'response_parser.dart'; /// Parses status responses class StatusParser extends ResponseParser { /// Creates a new parser - StatusParser(this.box) : _regex = RegExp(r'(STATUS "[^"]+?" )(.*)'); + // Matches both a quoted ("INBOX") and an unquoted atom (INBOX) mailbox + // name: servers such as Dovecot return the atom form in STATUS responses. + StatusParser(this.box) + : _regex = RegExp(r'(STATUS (?:"[^"]+?"|[^ (]+) )(.*)'); /// The current mailbox Mailbox box; diff --git a/test/src/imap/status_parser_test.dart b/test/src/imap/status_parser_test.dart index 6244118e..885524fc 100644 --- a/test/src/imap/status_parser_test.dart +++ b/test/src/imap/status_parser_test.dart @@ -3,6 +3,7 @@ import 'package:enough_mail/src/private/imap/all_parsers.dart'; import 'package:enough_mail/src/private/imap/imap_response.dart'; import 'package:enough_mail/src/private/imap/imap_response_line.dart'; import 'package:test/test.dart'; + // cSpell:disable void main() { @@ -58,4 +59,25 @@ void main() { expect(processed, true); expect(box.messagesExists, 2); }); + + test('Status with unquoted atom mailbox name', () { + // Dovecot answers `STATUS INBOX (...)` rather than `STATUS "INBOX" (...)`. + // The parser used to require the quotes, so the response never matched + // and every count stayed at its default of 0. + const responseText = 'STATUS INBOX (MESSAGES 231 UNSEEN 5 UIDNEXT 4392)'; + final details = ImapResponse()..add(ImapResponseLine(responseText)); + final box = Mailbox( + encodedName: 'INBOX', + encodedPath: 'INBOX', + flags: [MailboxFlag.inbox], + pathSeparator: '/', + ); + final parser = StatusParser(box); + final response = Response()..status = ResponseStatus.ok; + final processed = parser.parseUntagged(details, response); + expect(processed, true); + expect(box.messagesExists, 231); + expect(box.messagesUnseen, 5); + expect(box.uidNext, 4392); + }); }