Skip to content

Commit

Permalink
fix login on servers without capabilities reponse
Browse files Browse the repository at this point in the history
resolves #159
  • Loading branch information
robert-virkus committed Jun 8, 2021
1 parent dc7231b commit 480500a
Show file tree
Hide file tree
Showing 3 changed files with 49 additions and 5 deletions.
5 changes: 5 additions & 0 deletions lib/imap/imap_client.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
15 changes: 10 additions & 5 deletions lib/src/imap/capability_parser.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,18 @@ import 'imap_response.dart';

class CapabilityParser extends ResponseParser<List<Capability>> {
final ImapServerInfo info;
List<Capability>? _capabilities;
CapabilityParser(this.info);

@override
List<Capability>? parse(
ImapResponse details, Response<List<Capability>> 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;
}
Expand All @@ -25,16 +27,19 @@ class CapabilityParser extends ResponseParser<List<Capability>> {
ImapResponse details, Response<List<Capability>>? 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) {
Expand Down
34 changes: 34 additions & 0 deletions test/imap/imap_client_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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<ImapExpungeEvent>()
.listen((e) => expungedMessages.add(e.messageSequenceId));
client.eventBus
.on<ImapVanishedEvent>()
.listen((e) => vanishedMessages = e.vanishedMessages);
client.eventBus.on<ImapFetchEvent>().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 = '<tag> 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 = '<tag> OK AUTH completed';
final authResponse =
Expand Down

0 comments on commit 480500a

Please sign in to comment.