From 480500a95494df8323e81f2ddc702336bf7631fa Mon Sep 17 00:00:00 2001 From: Robert Virkus Date: Tue, 8 Jun 2021 11:31:34 +0200 Subject: [PATCH] fix login on servers without capabilities reponse resolves #159 --- lib/imap/imap_client.dart | 5 +++++ lib/src/imap/capability_parser.dart | 15 ++++++++----- test/imap/imap_client_test.dart | 34 +++++++++++++++++++++++++++++ 3 files changed, 49 insertions(+), 5 deletions(-) diff --git a/lib/imap/imap_client.dart b/lib/imap/imap_client.dart index def1081c..793b24e7 100644 --- a/lib/imap/imap_client.dart +++ b/lib/imap/imap_client.dart @@ -217,6 +217,11 @@ class ImapClient extends ClientBase { void onConnectionEstablished( ConnectionInfo connectionInfo, String serverGreeting) { _serverInfo = ImapServerInfo(connectionInfo); + final startIndex = serverGreeting.indexOf('[CAPABILITY '); + if (startIndex != -1) { + CapabilityParser.parseCapabilities( + serverGreeting, startIndex + '[CAPABILITY '.length, _serverInfo); + } if (_queue.isNotEmpty) { // this can happen when a connection was re-established, e.g. when trying to complete an IDLE connection for (final task in _queue) { diff --git a/lib/src/imap/capability_parser.dart b/lib/src/imap/capability_parser.dart index 66d1cfed..d043b94b 100644 --- a/lib/src/imap/capability_parser.dart +++ b/lib/src/imap/capability_parser.dart @@ -6,6 +6,7 @@ import 'imap_response.dart'; class CapabilityParser extends ResponseParser> { final ImapServerInfo info; + List? _capabilities; CapabilityParser(this.info); @override @@ -13,9 +14,10 @@ class CapabilityParser extends ResponseParser> { ImapResponse details, Response> response) { if (response.isOkStatus) { if (details.parseText.startsWith('OK [CAPABILITY ')) { - parseCapabilities(details.first.line!, 'OK [CAPABILITY '.length); + parseCapabilities(details.first.line!, 'OK [CAPABILITY '.length, info); + _capabilities = info.capabilities; } - return info.capabilities; + return _capabilities ?? []; } return null; } @@ -25,16 +27,19 @@ class CapabilityParser extends ResponseParser> { ImapResponse details, Response>? response) { var line = details.parseText; if (line.startsWith('OK [CAPABILITY ')) { - parseCapabilities(line, 'OK [CAPABILITY '.length); + parseCapabilities(line, 'OK [CAPABILITY '.length, info); + _capabilities = info.capabilities; return true; } else if (line.startsWith('CAPABILITY ')) { - parseCapabilities(line, 'CAPABILITY '.length); + parseCapabilities(line, 'CAPABILITY '.length, info); + _capabilities = info.capabilities; return true; } return super.parseUntagged(details, response); } - void parseCapabilities(String details, int startIndex) { + static void parseCapabilities( + String details, int startIndex, ImapServerInfo info) { var closeIndex = details.lastIndexOf(']'); var capText; if (closeIndex == -1) { diff --git a/test/imap/imap_client_test.dart b/test/imap/imap_client_test.dart index 36e209c5..74027953 100644 --- a/test/imap/imap_client_test.dart +++ b/test/imap/imap_client_test.dart @@ -54,6 +54,40 @@ void main() { expect(capResponse[2].name, 'ENABLE'); }); + test('ImapClient login without capability', () async { + // setup own initial response for test: + client = ImapClient(bus: EventBus(sync: true), isLogEnabled: false); + + client.eventBus + .on() + .listen((e) => expungedMessages.add(e.messageSequenceId)); + client.eventBus + .on() + .listen((e) => vanishedMessages = e.vanishedMessages); + client.eventBus.on().listen((e) => fetchEvents.add(e)); + + final connection = MockConnection(); + client.connect(connection.socketClient, + connectionInformation: ConnectionInfo('imap.qq.com', 993, true)); + mockServer = MockImapServer.connect(connection.socketServer); + connection.socketServer.write( + '* OK [CAPABILITY IMAP4 IMAP4rev1 ID AUTH=PLAIN AUTH=LOGIN AUTH=XOAUTH2 NAMESPACE] QQMail XMIMAP4Server ready\r\n'); + // allow processing of server greeting: + await Future.delayed(const Duration(milliseconds: 15)); + + mockServer.response = ' OK LOGIN completed'; + final capResponse = await client.login('testuser', 'testpassword'); + expect(capResponse, isNotNull, + reason: 'login response does not contain a result'); + expect(capResponse.isEmpty, true, + reason: 'login response should not contain a single capability'); + expect(capResponse.length, 0); + expect(client.serverInfo.capabilities, isNotNull); + expect(client.serverInfo.capabilities!.length, 7); + expect(client.serverInfo.capabilities![2].name, 'ID'); + expect(client.serverInfo.capabilities![6].name, 'NAMESPACE'); + }); + test('ImapClient authenticateWithOAuth2', () async { mockServer.response = ' OK AUTH completed'; final authResponse =