Fall back to HELO command if the server doesn't know EHLO (SMTP)

Fixes issue 2704
This commit is contained in:
cketti 2012-01-05 03:53:23 +01:00
parent 3c5a63adeb
commit d848b434f5
1 changed files with 84 additions and 13 deletions

View File

@ -288,20 +288,11 @@ public class SmtpTransport extends Transport {
}
}
List<String> results = executeSimpleCommand("EHLO " + localHost);
List<String> results = sendHello(localHost);
m8bitEncodingAllowed = results.contains("8BITMIME");
/*
* TODO may need to add code to fall back to HELO I switched it from
* using HELO on non STARTTLS connections because of AOL's mail
* server. It won't let you use AUTH without EHLO.
* We should really be paying more attention to the capabilities
* and only attempting auth if it's available, and warning the user
* if not.
*/
if (mConnectionSecurity == CONNECTION_SECURITY_TLS_OPTIONAL
|| mConnectionSecurity == CONNECTION_SECURITY_TLS_REQUIRED) {
if (results.contains("STARTTLS")) {
@ -322,7 +313,7 @@ public class SmtpTransport extends Transport {
* Now resend the EHLO. Required by RFC2487 Sec. 5.2, and more specifically,
* Exim.
*/
results = executeSimpleCommand("EHLO " + localHost);
results = sendHello(localHost);
} else if (mConnectionSecurity == CONNECTION_SECURITY_TLS_REQUIRED) {
throw new MessagingException("TLS not supported but required");
}
@ -408,6 +399,46 @@ public class SmtpTransport extends Transport {
}
}
/**
* Send the client "identity" using the EHLO or HELO command.
*
* <p>
* We first try the EHLO command. If the server sends a negative response, it probably doesn't
* support the EHLO command. So we try the older HELO command that all servers need to support.
* And if that fails, too, we pretend everything is fine and continue unimpressed.
* </p>
*
* @param host
* The EHLO/HELO parameter as defined by the RFC.
*
* @return The list of capabilities as returned by the EHLO command or an empty list.
*
* @throws IOException
* In case of a network error.
* @throws MessagingException
* In case of a malformed response.
*/
private List<String> sendHello(String host) throws IOException, MessagingException {
try {
//TODO: We currently assume the extension keywords returned by the server are always
// uppercased. But the RFC allows mixed-case keywords!
return executeSimpleCommand("EHLO " + host);
} catch (NegativeSmtpReplyException e) {
if (K9.DEBUG) {
Log.v(K9.LOG_TAG, "Server doesn't support the EHLO command. Trying HELO...");
}
try {
executeSimpleCommand("HELO " + host);
} catch (NegativeSmtpReplyException e2) {
Log.w(K9.LOG_TAG, "Server doesn't support the HELO command. Continuing anyway.");
}
}
return new ArrayList<String>(0);
}
@Override
public void sendMessage(Message message) throws MessagingException {
ArrayList<Address> addresses = new ArrayList<Address>();
@ -570,12 +601,28 @@ public class SmtpTransport extends Transport {
}
private void checkLine(String line) throws MessagingException {
if (line.length() < 1) {
int length = line.length();
if (length < 1) {
throw new MessagingException("SMTP response is 0 length");
}
char c = line.charAt(0);
if ((c == '4') || (c == '5')) {
throw new MessagingException(line);
int replyCode = -1;
String message = line;
if (length >= 3) {
try {
replyCode = Integer.parseInt(line.substring(0, 3));
} catch (NumberFormatException e) { /* ignore */ }
if (length > 4) {
message = line.substring(4);
} else {
message = "";
}
}
throw new NegativeSmtpReplyException(replyCode, message);
}
}
@ -680,4 +727,28 @@ public class SmtpTransport extends Transport {
throw new AuthenticationFailedException("Unable to negotiate MD5 CRAM");
}
}
/**
* Exception that is thrown when the server sends a negative reply (reply codes 4xx or 5xx).
*/
static class NegativeSmtpReplyException extends MessagingException {
private static final long serialVersionUID = 8696043577357897135L;
private final int mReplyCode;
private final String mReplyText;
public NegativeSmtpReplyException(int replyCode, String replyText) {
super("Negative SMTP reply: " + replyCode + " " + replyText);
mReplyCode = replyCode;
mReplyText = replyText;
}
public int getReplyCode() {
return mReplyCode;
}
public String getReplyText() {
return mReplyText;
}
}
}