Nicer API for sending SMTP commands

This commit is contained in:
cketti 2017-03-14 04:13:50 +01:00
parent 2e38bba70a
commit 34588c1d9d

View file

@ -285,7 +285,7 @@ public class SmtpTransport extends Transport {
mOut = new BufferedOutputStream(mSocket.getOutputStream(), 1024); mOut = new BufferedOutputStream(mSocket.getOutputStream(), 1024);
// Eat the banner // Eat the banner
executeSimpleCommand(null, false); executeCommand(null);
InetAddress localAddress = mSocket.getLocalAddress(); InetAddress localAddress = mSocket.getLocalAddress();
String localHost = getCanonicalHostName(localAddress); String localHost = getCanonicalHostName(localAddress);
@ -313,7 +313,7 @@ public class SmtpTransport extends Transport {
if (mConnectionSecurity == ConnectionSecurity.STARTTLS_REQUIRED) { if (mConnectionSecurity == ConnectionSecurity.STARTTLS_REQUIRED) {
if (extensions.containsKey("STARTTLS")) { if (extensions.containsKey("STARTTLS")) {
executeSimpleCommand("STARTTLS", false); executeCommand("STARTTLS");
mSocket = mTrustedSocketFactory.createSocket( mSocket = mTrustedSocketFactory.createSocket(
mSocket, mSocket,
@ -507,7 +507,7 @@ public class SmtpTransport extends Transport {
private Map<String, String> sendHello(String host) throws IOException, MessagingException { private Map<String, String> sendHello(String host) throws IOException, MessagingException {
Map<String, String> extensions = new HashMap<String, String>(); Map<String, String> extensions = new HashMap<String, String>();
try { try {
List<String> results = executeSimpleCommand("EHLO " + host, false).results; List<String> results = executeCommand("EHLO %s", host).results;
// Remove the EHLO greeting response // Remove the EHLO greeting response
results.remove(0); results.remove(0);
for (String result : results) { for (String result : results) {
@ -520,7 +520,7 @@ public class SmtpTransport extends Transport {
} }
try { try {
executeSimpleCommand("HELO " + host, false); executeCommand("HELO %s", host);
} catch (NegativeSmtpReplyException e2) { } catch (NegativeSmtpReplyException e2) {
Log.w(LOG_TAG, "Server doesn't support the HELO command. Continuing anyway."); Log.w(LOG_TAG, "Server doesn't support the HELO command. Continuing anyway.");
} }
@ -579,12 +579,18 @@ public class SmtpTransport extends Transport {
boolean entireMessageSent = false; boolean entireMessageSent = false;
Address[] from = message.getFrom(); Address[] from = message.getFrom();
try { try {
executeSimpleCommand("MAIL FROM:" + "<" + from[0].getAddress() + ">" String fromAddress = from[0].getAddress();
+ (m8bitEncodingAllowed ? " BODY=8BITMIME" : ""), false); if (m8bitEncodingAllowed) {
for (String address : addresses) { executeCommand("MAIL FROM:<%s> BODY=8BITMIME", fromAddress);
executeSimpleCommand("RCPT TO:" + "<" + address + ">", false); } else {
executeCommand("MAIL FROM:<%s>", fromAddress);
} }
executeSimpleCommand("DATA", false);
for (String address : addresses) {
executeCommand("RCPT TO:<%s>", address);
}
executeCommand("DATA");
EOLConvertingOutputStream msgOut = new EOLConvertingOutputStream( EOLConvertingOutputStream msgOut = new EOLConvertingOutputStream(
new LineWrapOutputStream(new SmtpDataStuffing(mOut), 1000)); new LineWrapOutputStream(new SmtpDataStuffing(mOut), 1000));
@ -593,7 +599,7 @@ public class SmtpTransport extends Transport {
msgOut.endWithCrLfAndFlush(); msgOut.endWithCrLfAndFlush();
entireMessageSent = true; // After the "\r\n." is attempted, we may have sent the message entireMessageSent = true; // After the "\r\n." is attempted, we may have sent the message
executeSimpleCommand(".", false); executeCommand(".");
} catch (NegativeSmtpReplyException e) { } catch (NegativeSmtpReplyException e) {
throw e; throw e;
} catch (Exception e) { } catch (Exception e) {
@ -610,7 +616,7 @@ public class SmtpTransport extends Transport {
@Override @Override
public void close() { public void close() {
try { try {
executeSimpleCommand("QUIT", false); executeCommand("QUIT");
} catch (Exception e) { } catch (Exception e) {
} }
@ -675,10 +681,20 @@ public class SmtpTransport extends Transport {
} }
} }
private CommandResponse executeSimpleCommand(String command, boolean sensitive) private CommandResponse executeSensitiveCommand(String format, Object... args)
throws IOException, MessagingException {
return executeCommand(true, format, args);
}
private CommandResponse executeCommand(String format, Object... args) throws IOException, MessagingException {
return executeCommand(false, format, args);
}
private CommandResponse executeCommand(boolean sensitive, String format, Object... args)
throws IOException, MessagingException { throws IOException, MessagingException {
List<String> results = new ArrayList<>(); List<String> results = new ArrayList<>();
if (command != null) { if (format != null) {
String command = String.format(Locale.ROOT, format, args);
writeLine(command, sensitive); writeLine(command, sensitive);
} }
@ -771,9 +787,9 @@ public class SmtpTransport extends Transport {
private void saslAuthLogin(String username, String password) throws MessagingException, private void saslAuthLogin(String username, String password) throws MessagingException,
AuthenticationFailedException, IOException { AuthenticationFailedException, IOException {
try { try {
executeSimpleCommand("AUTH LOGIN", false); executeCommand("AUTH LOGIN");
executeSimpleCommand(Base64.encode(username), true); executeSensitiveCommand(Base64.encode(username));
executeSimpleCommand(Base64.encode(password), true); executeSensitiveCommand(Base64.encode(password));
} catch (NegativeSmtpReplyException exception) { } catch (NegativeSmtpReplyException exception) {
if (exception.getReplyCode() == SMTP_AUTHENTICATION_FAILURE_ERROR_CODE) { if (exception.getReplyCode() == SMTP_AUTHENTICATION_FAILURE_ERROR_CODE) {
// Authentication credentials invalid // Authentication credentials invalid
@ -789,7 +805,7 @@ public class SmtpTransport extends Transport {
AuthenticationFailedException, IOException { AuthenticationFailedException, IOException {
String data = Base64.encode("\000" + username + "\000" + password); String data = Base64.encode("\000" + username + "\000" + password);
try { try {
executeSimpleCommand("AUTH PLAIN " + data, true); executeSensitiveCommand("AUTH PLAIN %s", data);
} catch (NegativeSmtpReplyException exception) { } catch (NegativeSmtpReplyException exception) {
if (exception.getReplyCode() == SMTP_AUTHENTICATION_FAILURE_ERROR_CODE) { if (exception.getReplyCode() == SMTP_AUTHENTICATION_FAILURE_ERROR_CODE) {
// Authentication credentials invalid // Authentication credentials invalid
@ -804,7 +820,7 @@ public class SmtpTransport extends Transport {
private void saslAuthCramMD5(String username, String password) throws MessagingException, private void saslAuthCramMD5(String username, String password) throws MessagingException,
AuthenticationFailedException, IOException { AuthenticationFailedException, IOException {
List<String> respList = executeSimpleCommand("AUTH CRAM-MD5", false).results; List<String> respList = executeCommand("AUTH CRAM-MD5").results;
if (respList.size() != 1) { if (respList.size() != 1) {
throw new MessagingException("Unable to negotiate CRAM-MD5"); throw new MessagingException("Unable to negotiate CRAM-MD5");
} }
@ -813,7 +829,7 @@ public class SmtpTransport extends Transport {
String b64CRAMString = Authentication.computeCramMd5(mUsername, mPassword, b64Nonce); String b64CRAMString = Authentication.computeCramMd5(mUsername, mPassword, b64Nonce);
try { try {
executeSimpleCommand(b64CRAMString, true); executeSensitiveCommand(b64CRAMString);
} catch (NegativeSmtpReplyException exception) { } catch (NegativeSmtpReplyException exception) {
if (exception.getReplyCode() == SMTP_AUTHENTICATION_FAILURE_ERROR_CODE) { if (exception.getReplyCode() == SMTP_AUTHENTICATION_FAILURE_ERROR_CODE) {
// Authentication credentials invalid // Authentication credentials invalid
@ -877,20 +893,18 @@ public class SmtpTransport extends Transport {
private void attemptXoauth2(String username) throws MessagingException, IOException { private void attemptXoauth2(String username) throws MessagingException, IOException {
String token = oauthTokenProvider.getToken(username, OAuth2TokenProvider.OAUTH2_TIMEOUT); String token = oauthTokenProvider.getToken(username, OAuth2TokenProvider.OAUTH2_TIMEOUT);
String authString = Authentication.computeXoauth(username, token); String authString = Authentication.computeXoauth(username, token);
CommandResponse response = executeSimpleCommand("AUTH XOAUTH2 " + authString, true); CommandResponse response = executeSensitiveCommand("AUTH XOAUTH2 %s", authString);
if (response.replyCode == SMTP_CONTINUE_REQUEST) { if (response.replyCode == SMTP_CONTINUE_REQUEST) {
retryXoauthWithNewToken = XOAuth2ChallengeParser.shouldRetry(join(response.results, ""), mHost); retryXoauthWithNewToken = XOAuth2ChallengeParser.shouldRetry(join(response.results, ""), mHost);
//Per Google spec, respond to challenge with empty response //Per Google spec, respond to challenge with empty response
executeSimpleCommand("", false); executeCommand("");
} }
} }
private void saslAuthExternal(String username) throws MessagingException, IOException { private void saslAuthExternal(String username) throws MessagingException, IOException {
executeSimpleCommand( executeCommand("AUTH EXTERNAL %s", Base64.encode(username));
String.format("AUTH EXTERNAL %s",
Base64.encode(username)), false);
} }
@VisibleForTesting @VisibleForTesting