From a05bbbbf8d94547ac765315a3ffae91b2a360208 Mon Sep 17 00:00:00 2001 From: Philip Whitehouse Date: Thu, 23 Mar 2017 23:38:11 +0000 Subject: [PATCH] Request post auth capability when not provided --- .../k9/mail/store/imap/ImapConnection.java | 90 ++++----- .../mail/store/imap/ImapConnectionTest.java | 178 +++++++++++++----- 2 files changed, 183 insertions(+), 85 deletions(-) diff --git a/k9mail-library/src/main/java/com/fsck/k9/mail/store/imap/ImapConnection.java b/k9mail-library/src/main/java/com/fsck/k9/mail/store/imap/ImapConnection.java index b95e27a59..0079506c7 100644 --- a/k9mail-library/src/main/java/com/fsck/k9/mail/store/imap/ImapConnection.java +++ b/k9mail-library/src/main/java/com/fsck/k9/mail/store/imap/ImapConnection.java @@ -124,9 +124,11 @@ class ImapConnection { upgradeToTlsIfNecessary(); - authenticate(); + List responses = authenticate(); authSuccess = true; + extractOrRequestCapabilities(responses); + enableCompressionIfRequested(); retrievePathPrefixIfNecessary(); @@ -242,26 +244,39 @@ class ImapConnection { private void readInitialResponse() throws IOException { ImapResponse initialResponse = responseParser.readResponse(); - if (K9MailLib.isDebug() && DEBUG_PROTOCOL_IMAP) { Timber.v("%s <<< %s", getLogId(), initialResponse); } - extractCapabilities(Collections.singletonList(initialResponse)); } private List extractCapabilities(List responses) { CapabilityResponse capabilityResponse = CapabilityResponse.parse(responses); - if (capabilityResponse != null) { Set receivedCapabilities = capabilityResponse.getCapabilities(); - if (K9MailLib.isDebug()) { Timber.d("Saving %s capabilities for %s", receivedCapabilities, getLogId()); } - capabilities = receivedCapabilities; } + return responses; + } + + private List extractOrRequestCapabilities(List responses) + throws IOException, MessagingException { + CapabilityResponse capabilityResponse = CapabilityResponse.parse(responses); + if (capabilityResponse != null) { + Set receivedCapabilities = capabilityResponse.getCapabilities(); + if (K9MailLib.isDebug()) { + Log.d(LOG_TAG, "Saving " + receivedCapabilities + " capabilities for " + getLogId()); + } + capabilities = receivedCapabilities; + } else { + if (K9MailLib.isDebug()) { + Log.i(LOG_TAG, "Did not get capabilities in post-auth banner, requesting CAPABILITY for " + getLogId()); + } + requestCapabilities(); + } return responses; } @@ -270,11 +285,9 @@ class ImapConnection { if (!capabilities.isEmpty()) { return; } - if (K9MailLib.isDebug()) { Timber.i("Did not get capabilities in banner, requesting CAPABILITY for %s", getLogId()); } - requestCapabilities(); } @@ -325,45 +338,40 @@ class ImapConnection { requestCapabilities(); } - @SuppressWarnings("EnumSwitchStatementWhichMissesCases") - private void authenticate() throws MessagingException, IOException { + private List authenticate() throws MessagingException, IOException { switch (settings.getAuthType()) { case XOAUTH2: if (oauthTokenProvider == null) { throw new MessagingException("No OAuthToken Provider available."); } else if (hasCapability(Capabilities.AUTH_XOAUTH2) && hasCapability(Capabilities.SASL_IR)) { - authXoauth2withSASLIR(); + return authXoauth2withSASLIR(); } else { throw new MessagingException("Server doesn't support SASL XOAUTH2."); } - break; case CRAM_MD5: { if (hasCapability(Capabilities.AUTH_CRAM_MD5)) { - authCramMD5(); + return authCramMD5(); } else { throw new MessagingException("Server doesn't support encrypted passwords using CRAM-MD5."); } - break; } case PLAIN: { if (hasCapability(Capabilities.AUTH_PLAIN)) { - saslAuthPlainWithLoginFallback(); + return saslAuthPlainWithLoginFallback(); } else if (!hasCapability(Capabilities.LOGINDISABLED)) { - login(); + return login(); } else { throw new MessagingException("Server doesn't support unencrypted passwords using AUTH=PLAIN " + "and LOGIN is disabled."); } - break; } case EXTERNAL: { if (hasCapability(Capabilities.AUTH_EXTERNAL)) { - saslAuthExternal(); + return saslAuthExternal(); } else { // Provide notification to user of a problem authenticating using client certificates throw new CertificateValidationException(CertificateValidationException.Reason.MissingCapability); } - break; } default: { throw new MessagingException("Unhandled authentication method found in the server settings (bug)."); @@ -371,27 +379,27 @@ class ImapConnection { } } - private void authXoauth2withSASLIR() throws IOException, MessagingException { + private List authXoauth2withSASLIR() throws IOException, MessagingException { retryXoauth2WithNewToken = true; try { - attemptXOAuth2(); + return attemptXOAuth2(); } catch (NegativeImapResponseException e) { oauthTokenProvider.invalidateToken(settings.getUsername()); if (!retryXoauth2WithNewToken) { - handlePermanentXoauth2Failure(e); + return handlePermanentXoauth2Failure(e); } else { - handleTemporaryXoauth2Failure(e); + return handleTemporaryXoauth2Failure(e); } } } - private void handlePermanentXoauth2Failure(NegativeImapResponseException e) throws AuthenticationFailedException { + private List handlePermanentXoauth2Failure(NegativeImapResponseException e) throws AuthenticationFailedException { Timber.v(e, "Permanent failure during XOAUTH2"); throw new AuthenticationFailedException(e.getMessage(), e); } - private void handleTemporaryXoauth2Failure(NegativeImapResponseException e) throws IOException, MessagingException { + private List handleTemporaryXoauth2Failure(NegativeImapResponseException e) throws IOException, MessagingException { //We got a response indicating a retry might suceed after token refresh //We could avoid this if we had a reasonable chance of knowing //if a token was invalid before use (e.g. due to expiry). But we don't @@ -399,30 +407,28 @@ class ImapConnection { Timber.v(e, "Temporary failure - retrying with new token"); try { - attemptXOAuth2(); + return attemptXOAuth2(); } catch (NegativeImapResponseException e2) { //Okay, we failed on a new token. //Invalidate the token anyway but assume it's permanent. Timber.v(e, "Authentication exception for new token, permanent error assumed"); oauthTokenProvider.invalidateToken(settings.getUsername()); - handlePermanentXoauth2Failure(e2); + return handlePermanentXoauth2Failure(e2); } } - private void attemptXOAuth2() throws MessagingException, IOException { + private List attemptXOAuth2() throws MessagingException, IOException { String token = oauthTokenProvider.getToken(settings.getUsername(), OAuth2TokenProvider.OAUTH2_TIMEOUT); String authString = Authentication.computeXoauth(settings.getUsername(), token); String tag = sendSaslIrCommand(Commands.AUTHENTICATE_XOAUTH2, authString, true); - List responses = responseParser.readStatusResponse(tag, Commands.AUTHENTICATE_XOAUTH2, getLogId(), + return responseParser.readStatusResponse(tag, Commands.AUTHENTICATE_XOAUTH2, getLogId(), new UntaggedHandler() { @Override public void handleAsyncUntaggedResponse(ImapResponse response) throws IOException { handleXOAuthUntaggedResponse(response); } }); - - extractCapabilities(responses); } private void handleXOAuthUntaggedResponse(ImapResponse response) throws IOException { @@ -436,7 +442,7 @@ class ImapConnection { } } - private void authCramMD5() throws MessagingException, IOException { + private List authCramMD5() throws MessagingException, IOException { String command = Commands.AUTHENTICATE_CRAM_MD5; String tag = sendCommand(command, false); @@ -454,25 +460,25 @@ class ImapConnection { outputStream.flush(); try { - extractCapabilities(responseParser.readStatusResponse(tag, command, getLogId(), null)); + return responseParser.readStatusResponse(tag, command, getLogId(), null); } catch (NegativeImapResponseException e) { throw new AuthenticationFailedException(e.getMessage()); } } - private void saslAuthPlainWithLoginFallback() throws IOException, MessagingException { + private List saslAuthPlainWithLoginFallback() throws IOException, MessagingException { try { - saslAuthPlain(); + return saslAuthPlain(); } catch (AuthenticationFailedException e) { if (!isConnected()) { throw e; } - login(); + return login(); } } - private void saslAuthPlain() throws IOException, MessagingException { + private List saslAuthPlain() throws IOException, MessagingException { String command = Commands.AUTHENTICATE_PLAIN; String tag = sendCommand(command, false); @@ -487,7 +493,7 @@ class ImapConnection { outputStream.flush(); try { - extractCapabilities(responseParser.readStatusResponse(tag, command, getLogId(), null)); + return responseParser.readStatusResponse(tag, command, getLogId(), null); } catch (NegativeImapResponseException e) { if (e.wasByeResponseReceived()) { close(); @@ -497,7 +503,7 @@ class ImapConnection { } } - private void login() throws IOException, MessagingException { + private List login() throws IOException, MessagingException { /* * Use quoted strings which permit spaces and quotes. (Using IMAP * string literals would be better, but some servers are broken @@ -512,16 +518,16 @@ class ImapConnection { try { String command = String.format(Commands.LOGIN + " \"%s\" \"%s\"", username, password); - extractCapabilities(executeSimpleCommand(command, true)); + return executeSimpleCommand(command, true); } catch (NegativeImapResponseException e) { throw new AuthenticationFailedException(e.getMessage()); } } - private void saslAuthExternal() throws IOException, MessagingException { + private List saslAuthExternal() throws IOException, MessagingException { try { String command = Commands.AUTHENTICATE_EXTERNAL + " " + Base64.encode(settings.getUsername()); - extractCapabilities(executeSimpleCommand(command, false)); + return executeSimpleCommand(command, false); } catch (NegativeImapResponseException e) { /* * Provide notification to the user of a problem authenticating diff --git a/k9mail-library/src/test/java/com/fsck/k9/mail/store/imap/ImapConnectionTest.java b/k9mail-library/src/test/java/com/fsck/k9/mail/store/imap/ImapConnectionTest.java index 60bf3e77b..1ae4d3f04 100644 --- a/k9mail-library/src/test/java/com/fsck/k9/mail/store/imap/ImapConnectionTest.java +++ b/k9mail-library/src/test/java/com/fsck/k9/mail/store/imap/ImapConnectionTest.java @@ -75,7 +75,29 @@ public class ImapConnectionTest { } @Test - public void open_withCapabilitiesInInitialResponse_shouldNotIssueCapabilitiesCommand() throws Exception { + public void open_withNoCapabilitiesInInitialResponse_shouldIssuePreAuthCapabilitiesCommand() throws Exception { + settings.setAuthType(AuthType.PLAIN); + MockImapServer server = new MockImapServer(); + server.output("* OK example.org server"); + server.expect("1 CAPABILITY"); + server.output("* CAPABILITY IMAP4 IMAP4REV1 AUTH=PLAIN"); + server.output("1 OK CAPABILITY Completed"); + server.expect("2 AUTHENTICATE PLAIN"); + server.output("+"); + server.expect(ByteString.encodeUtf8("\000" + USERNAME + "\000" + PASSWORD).base64()); + server.output("2 OK Success"); + simplePostAuthenticationDialog(server, true, 3); + + ImapConnection imapConnection = startServerAndCreateImapConnection(server); + + imapConnection.open(); + + server.verifyConnectionStillOpen(); + server.verifyInteractionCompleted(); + } + + @Test + public void open_withCapabilitiesInInitialResponse_shouldNotIssuePreAuthCapabilitiesCommand() throws Exception { settings.setAuthType(AuthType.PLAIN); MockImapServer server = new MockImapServer(); server.output("* OK [CAPABILITY IMAP4 IMAP4REV1 AUTH=PLAIN]"); @@ -83,9 +105,7 @@ public class ImapConnectionTest { server.output("+"); server.expect(ByteString.encodeUtf8("\000" + USERNAME + "\000" + PASSWORD).base64()); server.output("1 OK Success"); - server.expect("2 LIST \"\" \"\""); - server.output("* LIST () \"/\" foo/bar"); - server.output("2 OK"); + simplePostAuthenticationDialog(server, true, 2); ImapConnection imapConnection = startServerAndCreateImapConnection(server); imapConnection.open(); @@ -103,7 +123,7 @@ public class ImapConnectionTest { server.output("+"); server.expect(ByteString.encodeUtf8("\000" + USERNAME + "\000" + PASSWORD).base64()); server.output("2 OK Success"); - simplePostAuthenticationDialog(server); + simplePostAuthenticationDialog(server, true); ImapConnection imapConnection = startServerAndCreateImapConnection(server); imapConnection.open(); @@ -119,7 +139,7 @@ public class ImapConnectionTest { preAuthenticationDialog(server); server.expect("2 LOGIN \"" + USERNAME + "\" \"" + PASSWORD + "\""); server.output("2 OK LOGIN completed"); - simplePostAuthenticationDialog(server); + simplePostAuthenticationDialog(server, true); ImapConnection imapConnection = startServerAndCreateImapConnection(server); imapConnection.open(); imapConnection.close(); @@ -163,7 +183,7 @@ public class ImapConnectionTest { server.output("2 NO Login Failure"); server.expect("3 LOGIN \"" + USERNAME + "\" \"" + PASSWORD + "\""); server.output("3 OK LOGIN completed"); - simplePostAuthenticationDialog(server, "4"); + simplePostAuthenticationDialog(server, true, 4); ImapConnection imapConnection = startServerAndCreateImapConnection(server); imapConnection.open(); @@ -230,7 +250,7 @@ public class ImapConnectionTest { preAuthenticationDialog(server); server.expect("2 LOGIN \"" + USERNAME + "\" \"" + PASSWORD + "\""); server.output("2 OK LOGIN completed"); - simplePostAuthenticationDialog(server); + simplePostAuthenticationDialog(server, true); ImapConnection imapConnection = startServerAndCreateImapConnection(server); imapConnection.open(); @@ -248,7 +268,7 @@ public class ImapConnectionTest { server.output("+ " + ByteString.encodeUtf8("<0000.000000000@example.org>").base64()); server.expect("dXNlciA2ZjdiOTcyYjk5YTI4NDk4OTRhN2YyMmE3MGRhZDg0OQ=="); server.output("2 OK Success"); - simplePostAuthenticationDialog(server); + simplePostAuthenticationDialog(server, true); ImapConnection imapConnection = startServerAndCreateImapConnection(server); imapConnection.open(); @@ -306,7 +326,7 @@ public class ImapConnectionTest { preAuthenticationDialog(server, "SASL-IR AUTH=XOAUTH AUTH=XOAUTH2"); server.expect("2 AUTHENTICATE XOAUTH2 " + XOAUTH_STRING); server.output("2 OK Success"); - simplePostAuthenticationDialog(server); + simplePostAuthenticationDialog(server, true); ImapConnection imapConnection = startServerAndCreateImapConnection(server); imapConnection.open(); @@ -351,7 +371,7 @@ public class ImapConnectionTest { server.output("2 NO SASL authentication failed"); server.expect("3 AUTHENTICATE XOAUTH2 " + XOAUTH_STRING_RETRY); server.output("3 OK Success"); - simplePostAuthenticationDialog(server, "4"); + simplePostAuthenticationDialog(server, true, 4); ImapConnection imapConnection = startServerAndCreateImapConnection(server); imapConnection.open(); @@ -377,7 +397,7 @@ public class ImapConnectionTest { server.output("2 NO SASL authentication failed"); server.expect("3 AUTHENTICATE XOAUTH2 " + XOAUTH_STRING_RETRY); server.output("3 OK Success"); - simplePostAuthenticationDialog(server, "4"); + simplePostAuthenticationDialog(server, true, 4); ImapConnection imapConnection = startServerAndCreateImapConnection(server); imapConnection.open(); @@ -403,7 +423,7 @@ public class ImapConnectionTest { server.output("2 NO SASL authentication failed"); server.expect("3 AUTHENTICATE XOAUTH2 " + XOAUTH_STRING_RETRY); server.output("3 OK Success"); - simplePostAuthenticationDialog(server, "4"); + simplePostAuthenticationDialog(server, true, 4); ImapConnection imapConnection = startServerAndCreateImapConnection(server); imapConnection.open(); @@ -431,7 +451,7 @@ public class ImapConnectionTest { server.output("+ 433ba3a3a"); server.expect(""); server.output("3 NO SASL authentication failed"); - simplePostAuthenticationDialog(server); + simplePostAuthenticationDialog(server, true); ImapConnection imapConnection = startServerAndCreateImapConnection(server); try { @@ -453,7 +473,7 @@ public class ImapConnectionTest { preAuthenticationDialog(server, "SASL-IR AUTH=XOAUTH AUTH=XOAUTH2"); server.expect("2 AUTHENTICATE XOAUTH2 " + XOAUTH_STRING); server.output("2 OK [CAPABILITY IMAP4REV1 IDLE XM-GM-EXT-1]"); - simplePostAuthenticationDialog(server); + simplePostAuthenticationDialog(server, false); ImapConnection imapConnection = startServerAndCreateImapConnection(server); imapConnection.open(); server.verifyConnectionStillOpen(); @@ -468,7 +488,7 @@ public class ImapConnectionTest { preAuthenticationDialog(server, "AUTH=EXTERNAL"); server.expect("2 AUTHENTICATE EXTERNAL " + ByteString.encodeUtf8(USERNAME).base64()); server.output("2 OK Success"); - simplePostAuthenticationDialog(server); + simplePostAuthenticationDialog(server, true); ImapConnection imapConnection = startServerAndCreateImapConnection(server); imapConnection.open(); @@ -516,10 +536,73 @@ public class ImapConnectionTest { server.verifyInteractionCompleted(); } + @Test + public void open_withNoPostAuthCapabilityResponse_shouldIssueCapabilityCommand() throws Exception { + settings.setAuthType(AuthType.PLAIN); + MockImapServer server = new MockImapServer(); + preAuthenticationDialog(server, "AUTH=PLAIN"); + server.expect("2 AUTHENTICATE PLAIN"); + server.output("+"); + server.expect(ByteString.encodeUtf8("\000" + USERNAME + "\000" + PASSWORD).base64()); + server.output("2 OK Success"); + server.expect("3 CAPABILITY"); + server.output("* CAPABILITY IDLE"); + server.output("3 OK CAPABILITY Completed"); + simplePostAuthenticationDialog(server, false, 4); + ImapConnection imapConnection = startServerAndCreateImapConnection(server); + + imapConnection.open(); + + server.verifyConnectionStillOpen(); + server.verifyInteractionCompleted(); + assertTrue(imapConnection.isIdleCapable()); + } + + @Test + public void open_withUntaggedPostAuthCapabilityResponse_shouldNotIssueCapabilityCommand() throws Exception { + settings.setAuthType(AuthType.PLAIN); + MockImapServer server = new MockImapServer(); + preAuthenticationDialog(server, "AUTH=PLAIN"); + server.expect("2 AUTHENTICATE PLAIN"); + server.output("+"); + server.expect(ByteString.encodeUtf8("\000" + USERNAME + "\000" + PASSWORD).base64()); + server.output("* CAPABILITY IMAP4rev1 UNSELECT IDLE QUOTA ID XLIST CHILDREN X-GM-EXT-1 UIDPLUS " + + "ENABLE MOVE CONDSTORE ESEARCH UTF8=ACCEPT LIST-EXTENDED LIST-STATUS LITERAL- SPECIAL-USE " + + "APPENDLIMIT=35651584"); + server.output("2 OK"); + simplePostAuthenticationDialog(server, false, 3); + ImapConnection imapConnection = startServerAndCreateImapConnection(server); + + imapConnection.open(); + + server.verifyConnectionStillOpen(); + server.verifyInteractionCompleted(); + assertTrue(imapConnection.isIdleCapable()); + } + + @Test + public void open_withPostAuthCapabilityResponse_shouldNotIssueCapabilityCommand() throws Exception { + settings.setAuthType(AuthType.PLAIN); + MockImapServer server = new MockImapServer(); + preAuthenticationDialog(server, "AUTH=PLAIN"); + server.expect("2 AUTHENTICATE PLAIN"); + server.output("+"); + server.expect(ByteString.encodeUtf8("\000" + USERNAME + "\000" + PASSWORD).base64()); + server.output("2 OK [CAPABILITY IDLE]"); + simplePostAuthenticationDialog(server, false, 3); + ImapConnection imapConnection = startServerAndCreateImapConnection(server); + + imapConnection.open(); + + server.verifyConnectionStillOpen(); + server.verifyInteractionCompleted(); + assertTrue(imapConnection.isIdleCapable()); + } + @Test public void open_withNamespaceCapability_shouldIssueNamespaceCommand() throws Exception { MockImapServer server = new MockImapServer(); - simplePreAuthAndLoginDialog(server, "NAMESPACE"); + simplePreAuthAndLoginDialog(server, "", "NAMESPACE"); server.expect("3 NAMESPACE"); server.output("* NAMESPACE ((\"\" \"/\")) NIL NIL"); server.output("3 OK command completed"); @@ -573,10 +656,10 @@ public class ImapConnectionTest { server.output("2 OK [CAPABILITY IMAP4REV1 NAMESPACE]"); server.startTls(); server.expect("3 CAPABILITY"); - server.output("* CAPABILITY IMAP4 IMAP4REV1 NAMESPACE"); + server.output("* CAPABILITY IMAP4 IMAP4REV1"); server.output("3 OK"); server.expect("4 LOGIN \"" + USERNAME + "\" \"" + PASSWORD + "\""); - server.output("4 OK LOGIN completed"); + server.output("4 OK [CAPABILITY NAMESPACE] LOGIN completed"); server.expect("5 NAMESPACE"); server.output("* NAMESPACE ((\"\" \"/\")) NIL NIL"); server.output("5 OK command completed"); @@ -632,11 +715,11 @@ public class ImapConnectionTest { public void open_withCompressDeflateCapability_shouldEnableCompression() throws Exception { settings.setUseCompression(true); MockImapServer server = new MockImapServer(); - simplePreAuthAndLoginDialog(server, "COMPRESS=DEFLATE"); + simplePreAuthAndLoginDialog(server, "", "COMPRESS=DEFLATE"); server.expect("3 COMPRESS DEFLATE"); server.output("3 OK"); server.enableCompression(); - simplePostAuthenticationDialog(server, "4"); + simplePostAuthenticationDialog(server, false, 4); ImapConnection imapConnection = startServerAndCreateImapConnection(server); imapConnection.open(); @@ -650,10 +733,10 @@ public class ImapConnectionTest { settings.setAuthType(AuthType.PLAIN); settings.setUseCompression(true); MockImapServer server = new MockImapServer(); - simplePreAuthAndLoginDialog(server, "COMPRESS=DEFLATE"); + simplePreAuthAndLoginDialog(server, "", "COMPRESS=DEFLATE"); server.expect("3 COMPRESS DEFLATE"); server.output("3 NO"); - simplePostAuthenticationDialog(server, "4"); + simplePostAuthenticationDialog(server, false, 4); ImapConnection imapConnection = startServerAndCreateImapConnection(server); imapConnection.open(); @@ -667,7 +750,7 @@ public class ImapConnectionTest { settings.setAuthType(AuthType.PLAIN); settings.setUseCompression(true); MockImapServer server = new MockImapServer(); - simplePreAuthAndLoginDialog(server, "COMPRESS=DEFLATE"); + simplePreAuthAndLoginDialog(server, "", "COMPRESS=DEFLATE"); server.expect("3 COMPRESS DEFLATE"); server.closeConnection(); ImapConnection imapConnection = startServerAndCreateImapConnection(server); @@ -687,7 +770,7 @@ public class ImapConnectionTest { settings.setAuthType(AuthType.PLAIN); settings.setUseCompression(true); MockImapServer server = new MockImapServer(); - simplePreAuthAndLoginDialog(server, ""); + simplePreAuthAndLoginDialog(server, "",""); server.expect("3 LIST \"\" \"\""); server.output("* Now what?"); ImapConnection imapConnection = startServerAndCreateImapConnection(server); @@ -707,7 +790,7 @@ public class ImapConnectionTest { settings.setAuthType(AuthType.PLAIN); settings.setUseCompression(true); MockImapServer server = new MockImapServer(); - simplePreAuthAndLoginDialog(server, ""); + simplePreAuthAndLoginDialog(server, "", ""); server.expect("3 LIST \"\" \"\""); server.output("3 NO"); ImapConnection imapConnection = startServerAndCreateImapConnection(server); @@ -790,7 +873,7 @@ public class ImapConnectionTest { @Test public void isIdleCapable_withIdleCapability() throws Exception { MockImapServer server = new MockImapServer(); - ImapConnection imapConnection = simpleOpenWithCapabilities(server, "IDLE"); + ImapConnection imapConnection = simpleOpenWithCapabilities(server, "", "IDLE"); boolean result = imapConnection.isIdleCapable(); @@ -803,7 +886,7 @@ public class ImapConnectionTest { public void sendContinuation() throws Exception { settings.setAuthType(AuthType.PLAIN); MockImapServer server = new MockImapServer(); - simpleOpenDialog(server, "IDLE"); + simpleOpenDialog(server, "", "IDLE"); server.expect("4 IDLE"); server.output("+ idling"); server.expect("DONE"); @@ -833,11 +916,11 @@ public class ImapConnectionTest { } private ImapConnection simpleOpen(MockImapServer server) throws Exception { - return simpleOpenWithCapabilities(server, ""); + return simpleOpenWithCapabilities(server, "", ""); } - private ImapConnection simpleOpenWithCapabilities(MockImapServer server, String capabilities) throws Exception { - simpleOpenDialog(server, capabilities); + private ImapConnection simpleOpenWithCapabilities(MockImapServer server, String preAuthCapabilities, String postAuthCapabilities) throws Exception { + simpleOpenDialog(server, preAuthCapabilities, postAuthCapabilities); ImapConnection imapConnection = startServerAndCreateImapConnection(server); imapConnection.open(); @@ -856,27 +939,36 @@ public class ImapConnectionTest { server.output("1 OK CAPABILITY"); } - private void simplePostAuthenticationDialog(MockImapServer server) { - simplePostAuthenticationDialog(server, "3"); + private void simplePostAuthenticationDialog(MockImapServer server, boolean requestCapabilities) { + simplePostAuthenticationDialog(server, requestCapabilities, 3); } - private void simplePostAuthenticationDialog(MockImapServer server, String tag) { - server.expect(tag + " LIST \"\" \"\""); - server.output("* LIST () \"/\" foo/bar"); - server.output(tag + " OK"); + private void simplePostAuthenticationDialog(MockImapServer server, boolean requestCapabilities, int tag) { + if (requestCapabilities) { + server.expect(tag+" CAPABILITY"); + server.output("* CAPABILITY IMAP4 IMAP4REV1 "); + server.output(tag+" OK CAPABILITY"); + server.expect((tag+1) + " LIST \"\" \"\""); + server.output("* LIST () \"/\" foo/bar"); + server.output((tag+1) + " OK"); + } else { + server.expect(tag + " LIST \"\" \"\""); + server.output("* LIST () \"/\" foo/bar"); + server.output(tag + " OK"); + } } - private void simpleOpenDialog(MockImapServer server, String capabilities) { - simplePreAuthAndLoginDialog(server, capabilities); - simplePostAuthenticationDialog(server); + private void simpleOpenDialog(MockImapServer server, String preAuthCapabilities, String postAuthCapabilities) { + simplePreAuthAndLoginDialog(server, preAuthCapabilities, postAuthCapabilities); + simplePostAuthenticationDialog(server, false); } - private void simplePreAuthAndLoginDialog(MockImapServer server, String capabilities) { + private void simplePreAuthAndLoginDialog(MockImapServer server, String preAuthCapabilities, String postAuthCapabilities) { settings.setAuthType(AuthType.PLAIN); - preAuthenticationDialog(server, capabilities); + preAuthenticationDialog(server, preAuthCapabilities); server.expect("2 LOGIN \"" + USERNAME + "\" \"" + PASSWORD + "\""); - server.output("2 OK LOGIN completed"); + server.output("2 OK [CAPABILITY " + postAuthCapabilities + "] LOGIN completed"); } }