(partially) Fix IMAP LOGIN

Previously, the user name and password were being transmitted as IMAP (RFC
3501) quoted strings.

The problem is that quoted strings are only permitted to carry 7-bit
(ASCII) data, whereas user names and passwords entered in K-9 Mail may not
be ASCII, so K-9 was violating the RFC by sending them as quoted strings.

The solution is to transmit the credentials as IMAP literal strings, which
are permitted for user names and passwords, and which permit the
transmission of 8-bit data.

This is only a partial attempt for fixing the LOGIN command for users with
non-ASCII credentials.  The problem is that IMAP permits 8-bit data for
user names and passwords (if transmitted as literals), but the RFC says
nothing about the character encoding for 8-bit data.  This commit encodes
them as UTF-8.

The RFC author's comments on the subject:
http://mailman2.u.washington.edu/pipermail/imap-protocol/2008-February/000822.html

Ideally, users should avoid the LOGIN command and use the SASL PLAIN
mechanism (within TLS) which explicitly permits UTF-8. (K-9 Mail always
chooses PLAIN over LOGIN, when PLAIN is available.)
This commit is contained in:
Joe Steele 2014-02-24 11:58:41 -05:00
parent 6f49ebd975
commit 3877f58515
1 changed files with 26 additions and 4 deletions

View File

@ -132,6 +132,7 @@ public class ImapStore extends Store {
private static final String CAPABILITY_AUTH_CRAM_MD5 = "AUTH=CRAM-MD5";
private static final String CAPABILITY_AUTH_PLAIN = "AUTH=PLAIN";
private static final String CAPABILITY_LOGINDISABLED = "LOGINDISABLED";
private static final String CAPABILITY_LITERAL_PLUS = "LITERAL+";
private static final String COMMAND_IDLE = "IDLE";
private static final String CAPABILITY_NAMESPACE = "NAMESPACE";
private static final String COMMAND_NAMESPACE = "NAMESPACE";
@ -2687,10 +2688,31 @@ public class ImapStore extends Store {
}
protected void login() throws IOException, MessagingException {
receiveCapabilities(executeSimpleCommand(String.format(
"LOGIN %s %s",
ImapStore.encodeString(mSettings.getUsername()),
ImapStore.encodeString(mSettings.getPassword())), true));
boolean hasLiteralPlus = hasCapability(CAPABILITY_LITERAL_PLUS);
String tag;
byte[] username = mSettings.getUsername().getBytes();
byte[] password = mSettings.getPassword().getBytes();
tag = sendCommand(String.format("LOGIN {%d%s}",
username.length, hasLiteralPlus ? "+" : ""), true);
if (!hasLiteralPlus) {
readContinuationResponse(tag);
}
mOut.write(username);
mOut.write(String.format(" {%d%s}\r\n", password.length,
hasLiteralPlus ? "+" : "").getBytes());
if (!hasLiteralPlus) {
mOut.flush();
readContinuationResponse(tag);
}
mOut.write(password);
mOut.write('\r');
mOut.write('\n');
mOut.flush();
try {
receiveCapabilities(readStatusResponse(tag, "LOGIN", null));
} catch (MessagingException e) {
throw new AuthenticationFailedException(e.getMessage());
}
}
protected void authCramMD5() throws MessagingException, IOException {