Repository: thunderbird/thunderbird-android (language: kotlin)

Issue:
Can't connect to POP3 server (IONOS, GMX, maybe others)
### Checklist

- [X] I have used the search function to see if someone else has already submitted the same bug report.
- [X] I will describe the problem with as much detail as possible.

### App version

5.806

### Where did you get the app from?

Google Play

### Android version

8.0

### Device model

Shift 5me

### Steps to reproduce

Open K-9 mail inbox; start refreshing the view manually.

### Expected behavior

K-9 is supposed to load new mails. Or at least give some error message saying that something's wrong.

### Actual behavior

Nothing. K-9 shows the reload-icon for a second and nothing else happens. Checking the inbox settings reveals an actual error message which I try to translate from German: "Unable to finish setup. Connection to server not possible. (Unable to exectute pop3 command)"

But my server settings are fine. I didn't change anything and other mail clients work ok using the same settings. I also checked if sending a message works: It does.

Restarting the app doesn't help; restarting the phone doesn't help. Currently K-9 is useless for me.

### Logs

_No response_


Discussion hints:
Related:
- https://forum.k9mail.app/t/no-pop3-email-from-ionos-unable-to-execute-pop3-command/4017
- https://forum.k9mail.app/t/fetching-mails-from-gmx-stopped-working-certificate-error-configuration-fails-starttls-not-available/4021

When establishing a connection K-9 Mail sends an `AUTH` command without arguments, trying to figure out which authentication mechanisms are supported. 

https://github.com/k9mail/k-9/blob/de15580e400b8101a10d824fbc6e8a111373880d/mail/protocols/pop3/src/main/java/com/fsck/k9/mail/store/pop3/Pop3Connection.java#L178-L211

The server software used by (at least) GMX and IONOS doesn't support this command and closes the connection after receiving the command due to "too many errors".

We'll probably release a beta version fixing this issue later today.

Pull request: Stop using AUTH without arguments (POP3)
At least one server software closes the connection right away after receiving the AUTH command without arguments. Since using the AUTH command without arguments never made it into a standard, I decided to completely remove this functionality.

Fixes #5879

Test command: ./gradlew :mail:protocols:pop3:testDebugUnitTest --tests "com.fsck.k9.mail.store.pop3.Pop3StoreTest" --tests "com.fsck.k9.mail.store.pop3.Pop3ConnectionTest"

Fix patch:
diff --git a/mail/protocols/pop3/src/main/java/com/fsck/k9/mail/store/pop3/Pop3Connection.java b/mail/protocols/pop3/src/main/java/com/fsck/k9/mail/store/pop3/Pop3Connection.java
index 666d4006533..2a0d004e263 100644
--- a/mail/protocols/pop3/src/main/java/com/fsck/k9/mail/store/pop3/Pop3Connection.java
+++ b/mail/protocols/pop3/src/main/java/com/fsck/k9/mail/store/pop3/Pop3Connection.java
@@ -175,40 +175,6 @@ boolean isOpen() {
 
     private Pop3Capabilities getCapabilities() throws IOException {
         Pop3Capabilities capabilities = new Pop3Capabilities();
-        try {
-            /*
-             * Try sending an AUTH command with no arguments.
-             *
-             * The server may respond with a list of supported SASL
-             * authentication mechanisms.
-             *
-             * Ref.: http://tools.ietf.org/html/draft-myers-sasl-pop3-05
-             *
-             * While this never became a standard, there are servers that
-             * support it, and Thunderbird includes this check.
-             */
-            executeSimpleCommand(AUTH_COMMAND);
-            String response;
-            while ((response = readLine()) != null) {
-                if (response.equals(".")) {
-                    break;
-                }
-                response = response.toUpperCase(Locale.US);
-                switch (response) {
-                    case AUTH_PLAIN_CAPABILITY:
-                        capabilities.authPlain = true;
-                        break;
-                    case AUTH_CRAM_MD5_CAPABILITY:
-                        capabilities.cramMD5 = true;
-                        break;
-                    case AUTH_EXTERNAL_CAPABILITY:
-                        capabilities.external = true;
-                        break;
-                }
-            }
-        } catch (MessagingException ignored) {
-            // Assume AUTH command with no arguments is not supported.
-        }
         try {
             executeSimpleCommand(CAPA_COMMAND);
             String response;
@@ -231,6 +197,9 @@ private Pop3Capabilities getCapabilities() throws IOException {
                     if (saslAuthMechanisms.contains(AUTH_CRAM_MD5_CAPABILITY)) {
                         capabilities.cramMD5 = true;
                     }
+                    if (saslAuthMechanisms.contains(AUTH_EXTERNAL_CAPABILITY)) {
+                        capabilities.external = true;
+                    }
                 }
             }
 


Test patch:
diff --git a/mail/protocols/pop3/src/test/java/com/fsck/k9/mail/store/pop3/Pop3ConnectionTest.java b/mail/protocols/pop3/src/test/java/com/fsck/k9/mail/store/pop3/Pop3ConnectionTest.java
index 5bcab0b0256..9f8d0b6e18d 100644
--- a/mail/protocols/pop3/src/test/java/com/fsck/k9/mail/store/pop3/Pop3ConnectionTest.java
+++ b/mail/protocols/pop3/src/test/java/com/fsck/k9/mail/store/pop3/Pop3ConnectionTest.java
@@ -42,29 +42,19 @@ public class Pop3ConnectionTest {
     private static String username = "user";
     private static String password = "password";
     private static final String INITIAL_RESPONSE = "+OK POP3 server greeting\r\n";
-    private static final String AUTH = "AUTH\r\n";
-    private static final String AUTH_HANDLE_RESPONSE =
-            "+OK Listing of supported mechanisms follows\r\n" +
-            "PLAIN\r\n" +
-            "CRAM-MD5\r\n" +
-            "EXTERNAL\r\n" +
-            ".\r\n";
     private static final String CAPA =
             "CAPA\r\n";
     private static final String CAPA_RESPONSE =
             "+OK Listing of supported mechanisms follows\r\n" +
-            "PLAIN\r\n" +
-            "CRAM-MD5\r\n" +
-            "EXTERNAL\r\n" +
+            "SASL PLAIN CRAM-MD5 EXTERNAL\r\n" +
             ".\r\n";
     private static final String AUTH_PLAIN_WITH_LOGIN = "AUTH PLAIN\r\n" +
             new String(Base64.encodeBase64(("\000"+username+"\000"+password).getBytes())) + "\r\n";
     private static final String AUTH_PLAIN_AUTHENTICATED_RESPONSE = "+OK\r\n" + "+OK\r\n";
 
-    private static final String SUCCESSFUL_PLAIN_AUTH = AUTH + CAPA + AUTH_PLAIN_WITH_LOGIN;
+    private static final String SUCCESSFUL_PLAIN_AUTH = CAPA + AUTH_PLAIN_WITH_LOGIN;
     private static final String SUCCESSFUL_PLAIN_AUTH_RESPONSE =
             INITIAL_RESPONSE +
-            AUTH_HANDLE_RESPONSE +
             CAPA_RESPONSE +
             AUTH_PLAIN_AUTHENTICATED_RESPONSE;
 /**
@@ -323,12 +313,9 @@ public void open_withStartTLS_authenticatesOverSecureSocket() throws Exception {
     private MockPop3Server setupUnavailableStartTLSConnection() throws IOException {new MockPop3Server();
         MockPop3Server server = new MockPop3Server();
         server.output("+OK POP3 server greeting");
-        server.expect("AUTH");
-        server.output("+OK Listing of supported mechanisms follows");
-        server.output("PLAIN");
-        server.output(".");
         server.expect("CAPA");
         server.output("+OK Listing of supported mechanisms follows");
+        server.output("SASL PLAIN");
         server.output(".");
         server.start();
         settings.setHost(server.getHost());
@@ -339,13 +326,10 @@ public void open_withStartTLS_authenticatesOverSecureSocket() throws Exception {
 
     private void setupServerWithStartTLSAvailable(MockPop3Server server) {
         server.output("+OK POP3 server greeting");
-        server.expect("AUTH");
-        server.output("+OK Listing of supported mechanisms follows");
-        server.output("PLAIN");
-        server.output(".");
         server.expect("CAPA");
         server.output("+OK Listing of supported mechanisms follows");
         server.output("STLS");
+        server.output("SASL PLAIN");
         server.output(".");
     }
 
@@ -357,17 +341,9 @@ public void open_withAuthTypePlainAndPlainAuthCapability_performsPlainAuth() thr
 
         MockPop3Server server = new MockPop3Server();
         server.output("+OK POP3 server greeting");
-        server.expect("AUTH");
-        server.output("+OK Listing of supported mechanisms follows");
-        server.output("PLAIN");
-        server.output("CRAM-MD5");
-        server.output("EXTERNAL");
-        server.output(".");
         server.expect("CAPA");
         server.output("+OK Listing of supported mechanisms follows");
-        server.output("PLAIN");
-        server.output("CRAM-MD5");
-        server.output("EXTERNAL");
+        server.output("SASL PLAIN CRAM-MD5 EXTERNAL");
         server.output(".");
         server.expect("AUTH PLAIN");
         server.output("+OK");
@@ -385,17 +361,9 @@ public void open_withAuthTypePlainAndPlainAuthCapabilityAndInvalidPasswordRespon
 
         MockPop3Server server = new MockPop3Server();
         server.output("+OK POP3 server greeting");
-        server.expect("AUTH");
-        server.output("+OK Listing of supported mechanisms follows");
-        server.output("PLAIN");
-        server.output("CRAM-MD5");
-        server.output("EXTERNAL");
-        server.output(".");
         server.expect("CAPA");
         server.output("+OK Listing of supported mechanisms follows");
-        server.output("PLAIN");
-        server.output("CRAM-MD5");
-        server.output("EXTERNAL");
+        server.output("SASL PLAIN CRAM-MD5 EXTERNAL");
         server.output(".");
         server.expect("AUTH PLAIN");
         server.output("+OK");
@@ -416,15 +384,9 @@ public void open_withAuthTypePlainAndNoPlainAuthCapability_performsLogin() throw
 
         MockPop3Server server = new MockPop3Server();
         server.output("+OK POP3 server greeting");
-        server.expect("AUTH");
-        server.output("+OK Listing of supported mechanisms follows");
-        server.output("CRAM-MD5");
-        server.output("EXTERNAL");
-        server.output(".");
         server.expect("CAPA");
         server.output("+OK Listing of supported mechanisms follows");
-        server.output("CRAM-MD5");
-        server.output("EXTERNAL");
+        server.output("SASL CRAM-MD5 EXTERNAL");
         server.output(".");
         server.expect("USER user");
         server.output("+OK");
@@ -445,15 +407,9 @@ public void open_withAuthTypePlainAndNoPlainAuthCapabilityAndLoginFailure_throws
 
         MockPop3Server server = new MockPop3Server();
         server.output("+OK POP3 server greeting");
-        server.expect("AUTH");
-        server.output("+OK Listing of supported mechanisms follows");
-        server.output("CRAM-MD5");
-        server.output("EXTERNAL");
-        server.output(".");
         server.expect("CAPA");
         server.output("+OK Listing of supported mechanisms follows");
-        server.output("CRAM-MD5");
-        server.output("EXTERNAL");
+        server.output("SASL CRAM-MD5 EXTERNAL");
         server.output(".");
         server.expect("USER user");
         server.output("+OK");
@@ -471,17 +427,9 @@ public void open_withAuthTypeCramMd5AndCapability_performsCramMd5Auth() throws I
 
         MockPop3Server server = new MockPop3Server();
         server.output("+OK POP3 server greeting");
-        server.expect("AUTH");
-        server.output("+OK Listing of supported mechanisms follows");
-        server.output("PLAIN");
-        server.output("CRAM-MD5");
-        server.output("EXTERNAL");
-        server.output(".");
         server.expect("CAPA");
         server.output("+OK Listing of supported mechanisms follows");
-        server.output("PLAIN");
-        server.output("CRAM-MD5");
-        server.output("EXTERNAL");
+        server.output("SASL PLAIN CRAM-MD5 EXTERNAL");
         server.output(".");
         server.expect("AUTH CRAM-MD5");
         server.output("+ abcd");
@@ -500,17 +448,9 @@ public void open_withAuthTypeCramMd5AndCapabilityAndCramFailure_throwsException(
 
         MockPop3Server server = new MockPop3Server();
         server.output("+OK POP3 server greeting");
-        server.expect("AUTH");
-        server.output("+OK Listing of supported mechanisms follows");
-        server.output("PLAIN");
-        server.output("CRAM-MD5");
-        server.output("EXTERNAL");
-        server.output(".");
         server.expect("CAPA");
         server.output("+OK Listing of supported mechanisms follows");
-        server.output("PLAIN");
-        server.output("CRAM-MD5");
-        server.output("EXTERNAL");
+        server.output("SASL PLAIN CRAM-MD5 EXTERNAL");
         server.output(".");
         server.expect("AUTH CRAM-MD5");
         server.output("+ abcd");
@@ -531,15 +471,9 @@ public void open_withAuthTypeCramMd5AndNoCapability_performsApopAuth() throws IO
 
         MockPop3Server server = new MockPop3Server();
         server.output("+OK abc<a>abcd");
-        server.expect("AUTH");
-        server.output("+OK Listing of supported mechanisms follows");
-        server.output("PLAIN");
-        server.output("EXTERNAL");
-        server.output(".");
         server.expect("CAPA");
         server.output("+OK Listing of supported mechanisms follows");
-        server.output("PLAIN");
-        server.output("EXTERNAL");
+        server.output("SASL PLAIN EXTERNAL");
         server.output(".");
         server.expect("APOP user c8e8c560e385faaa6367d4145572b8ea");
         server.output("+OK");
@@ -556,15 +490,9 @@ public void open_withAuthTypeCramMd5AndNoCapabilityAndApopFailure_throwsExceptio
 
         MockPop3Server server = new MockPop3Server();
         server.output("+OK abc<a>abcd");
-        server.expect("AUTH");
-        server.output("+OK Listing of supported mechanisms follows");
-        server.output("PLAIN");
-        server.output("EXTERNAL");
-        server.output(".");
         server.expect("CAPA");
         server.output("+OK Listing of supported mechanisms follows");
-        server.output("PLAIN");
-        server.output("EXTERNAL");
+        server.output("SASL PLAIN EXTERNAL");
         server.output(".");
         server.expect("APOP user c8e8c560e385faaa6367d4145572b8ea");
         server.output("-ERR");
@@ -583,17 +511,9 @@ public void open_withAuthTypeExternalAndCapability_performsExternalAuth() throws
 
         MockPop3Server server = new MockPop3Server();
         server.output("+OK POP3 server greeting");
-        server.expect("AUTH");
-        server.output("+OK Listing of supported mechanisms follows");
-        server.output("PLAIN");
-        server.output("CRAM-MD5");
-        server.output("EXTERNAL");
-        server.output(".");
         server.expect("CAPA");
         server.output("+OK Listing of supported mechanisms follows");
-        server.output("PLAIN");
-        server.output("CRAM-MD5");
-        server.output("EXTERNAL");
+        server.output("SASL CRAM-MD5 EXTERNAL");
         server.output(".");
         server.expect("AUTH EXTERNAL dXNlcg==");
         server.output("+OK");
@@ -609,16 +529,9 @@ public void open_withAuthTypeExternalAndNoCapability_throwsCVE() throws IOExcept
 
         MockPop3Server server = new MockPop3Server();
         server.output("+OK POP3 server greeting");
-        server.expect("AUTH");
-        server.output("+OK Listing of supported mechanisms follows");
-        server.output("PLAIN");
-        server.output("CRAM-MD5");
-        server.output(".");
         server.expect("CAPA");
         server.output("+OK Listing of supported mechanisms follows");
-        server.output("PLAIN");
-        server.output("CRAM-MD5");
-        server.output("EXTERNAL");
+        server.output("SASL PLAIN CRAM-MD5");
         server.output(".");
 
         try {
@@ -638,17 +551,9 @@ public void open_withAuthTypeExternalAndCapability_withRejection_throwsCVE() thr
 
         MockPop3Server server = new MockPop3Server();
         server.output("+OK POP3 server greeting");
-        server.expect("AUTH");
-        server.output("+OK Listing of supported mechanisms follows");
-        server.output("PLAIN");
-        server.output("CRAM-MD5");
-        server.output("EXTERNAL");
-        server.output(".");
         server.expect("CAPA");
         server.output("+OK Listing of supported mechanisms follows");
-        server.output("PLAIN");
-        server.output("CRAM-MD5");
-        server.output("EXTERNAL");
+        server.output("SASL PLAIN CRAM-MD5 EXTERNAL");
         server.output(".");
         server.expect("AUTH EXTERNAL dXNlcg==");
         server.output("-ERR Invalid certificate");
diff --git a/mail/protocols/pop3/src/test/java/com/fsck/k9/mail/store/pop3/Pop3StoreTest.java b/mail/protocols/pop3/src/test/java/com/fsck/k9/mail/store/pop3/Pop3StoreTest.java
index 6363592e47e..d7021637ca8 100644
--- a/mail/protocols/pop3/src/test/java/com/fsck/k9/mail/store/pop3/Pop3StoreTest.java
+++ b/mail/protocols/pop3/src/test/java/com/fsck/k9/mail/store/pop3/Pop3StoreTest.java
@@ -28,17 +28,9 @@
 
 public class Pop3StoreTest {
     private static final String INITIAL_RESPONSE = "+OK POP3 server greeting\r\n";
-    private static final String AUTH = "AUTH\r\n";
-    private static final String AUTH_HANDLE_RESPONSE = "+OK Listing of supported mechanisms follows\r\n" +
-            "PLAIN\r\n" +
-            "CRAM-MD5\r\n" +
-            "EXTERNAL\r\n" +
-            ".\r\n";
     private static final String CAPA = "CAPA\r\n";
     private static final String CAPA_RESPONSE = "+OK Listing of supported mechanisms follows\r\n" +
-            "PLAIN\r\n" +
-            "CRAM-MD5\r\n" +
-            "EXTERNAL\r\n" +
+            "SASL PLAIN CRAM-MD5 EXTERNAL\r\n" +
             ".\r\n";
     private static final String AUTH_PLAIN_WITH_LOGIN = "AUTH PLAIN\r\n" +
             new String(Base64.encodeBase64(("\000user\000password").getBytes())) + "\r\n";
@@ -94,7 +86,6 @@ public void checkSetting_whenConnectionThrowsException_shouldThrowMessagingExcep
     public void checkSetting_whenUidlUnsupported_shouldThrowMessagingException()
             throws Exception {
         String response = INITIAL_RESPONSE +
-                AUTH_HANDLE_RESPONSE +
                 CAPA_RESPONSE +
                 AUTH_PLAIN_AUTHENTICATED_RESPONSE +
                 STAT_RESPONSE +
@@ -109,7 +100,6 @@ public void checkSetting_whenUidlUnsupported_shouldThrowMessagingException()
     public void checkSetting_whenUidlSupported_shouldReturn()
             throws Exception {
         String response = INITIAL_RESPONSE +
-                AUTH_HANDLE_RESPONSE +
                 CAPA_RESPONSE +
                 AUTH_PLAIN_AUTHENTICATED_RESPONSE +
                 STAT_RESPONSE +
@@ -125,7 +115,6 @@ public void checkSetting_whenUidlSupported_shouldReturn()
     @Test
     public void open_withAuthResponseUsingAuthPlain_shouldRetrieveMessageCountOnAuthenticatedSocket() throws Exception {
         String response = INITIAL_RESPONSE +
-                AUTH_HANDLE_RESPONSE +
                 CAPA_RESPONSE +
                 AUTH_PLAIN_AUTHENTICATED_RESPONSE +
                 STAT_RESPONSE;
@@ -137,13 +126,12 @@ public void open_withAuthResponseUsingAuthPlain_shouldRetrieveMessageCountOnAuth
         folder.open();
 
         assertEquals(20, folder.getMessageCount());
-        assertEquals(AUTH + CAPA + AUTH_PLAIN_WITH_LOGIN + STAT, byteArrayOutputStream.toString("UTF-8"));
+        assertEquals(CAPA + AUTH_PLAIN_WITH_LOGIN + STAT, byteArrayOutputStream.toString("UTF-8"));
     }
 
     @Test(expected = AuthenticationFailedException.class)
     public void open_withFailedAuth_shouldThrow() throws Exception {
         String response = INITIAL_RESPONSE +
-                AUTH_HANDLE_RESPONSE +
                 CAPA_RESPONSE +
                 AUTH_PLAIN_FAILED_RESPONSE;
         when(mockSocket.getInputStream()).thenReturn(new ByteArrayInputStream(response.getBytes("UTF-8")));