clean up SmtpTransport
This commit is contained in:
parent
c4f68b873a
commit
9cb7712142
1 changed files with 101 additions and 122 deletions
|
@ -58,26 +58,30 @@ public class SmtpTransport extends Transport {
|
||||||
private static final int SMTP_CONTINUE_REQUEST = 334;
|
private static final int SMTP_CONTINUE_REQUEST = 334;
|
||||||
private static final int SMTP_AUTHENTICATION_FAILURE_ERROR_CODE = 535;
|
private static final int SMTP_AUTHENTICATION_FAILURE_ERROR_CODE = 535;
|
||||||
|
|
||||||
private TrustedSocketFactory mTrustedSocketFactory;
|
|
||||||
private OAuth2TokenProvider oauthTokenProvider;
|
|
||||||
|
|
||||||
private String mHost;
|
private final TrustedSocketFactory trustedSocketFactory;
|
||||||
private int mPort;
|
private final OAuth2TokenProvider oauthTokenProvider;
|
||||||
private String mUsername;
|
|
||||||
private String mPassword;
|
private final String host;
|
||||||
private String mClientCertificateAlias;
|
private final int port;
|
||||||
private AuthType mAuthType;
|
private final String username;
|
||||||
private ConnectionSecurity mConnectionSecurity;
|
private final String password;
|
||||||
private Socket mSocket;
|
private final String clientCertificateAlias;
|
||||||
private PeekableInputStream mIn;
|
private final AuthType authType;
|
||||||
private OutputStream mOut;
|
private final ConnectionSecurity connectionSecurity;
|
||||||
private boolean m8bitEncodingAllowed;
|
|
||||||
private boolean mEnhancedStatusCodesProvided;
|
|
||||||
private int mLargestAcceptableMessage;
|
private Socket socket;
|
||||||
|
private PeekableInputStream inputStream;
|
||||||
|
private OutputStream outputStream;
|
||||||
|
private boolean is8bitEncodingAllowed;
|
||||||
|
private boolean isEnhancedStatusCodesProvided;
|
||||||
|
private int largestAcceptableMessage;
|
||||||
private boolean retryXoauthWithNewToken;
|
private boolean retryXoauthWithNewToken;
|
||||||
|
|
||||||
|
|
||||||
public SmtpTransport(StoreConfig storeConfig, TrustedSocketFactory trustedSocketFactory,
|
public SmtpTransport(StoreConfig storeConfig, TrustedSocketFactory trustedSocketFactory,
|
||||||
OAuth2TokenProvider oauth2TokenProvider) throws MessagingException {
|
OAuth2TokenProvider oauthTokenProvider) throws MessagingException {
|
||||||
ServerSettings settings;
|
ServerSettings settings;
|
||||||
try {
|
try {
|
||||||
settings = TransportUris.decodeTransportUri(storeConfig.getTransportUri());
|
settings = TransportUris.decodeTransportUri(storeConfig.getTransportUri());
|
||||||
|
@ -85,38 +89,39 @@ public class SmtpTransport extends Transport {
|
||||||
throw new MessagingException("Error while decoding transport URI", e);
|
throw new MessagingException("Error while decoding transport URI", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (settings.type == Type.SMTP) {
|
if (settings.type != Type.SMTP) {
|
||||||
throw new IllegalArgumentException("Expected SMTP StoreConfig!");
|
throw new IllegalArgumentException("Expected SMTP StoreConfig!");
|
||||||
}
|
}
|
||||||
|
|
||||||
mHost = settings.host;
|
host = settings.host;
|
||||||
mPort = settings.port;
|
port = settings.port;
|
||||||
|
|
||||||
mConnectionSecurity = settings.connectionSecurity;
|
connectionSecurity = settings.connectionSecurity;
|
||||||
|
|
||||||
mAuthType = settings.authenticationType;
|
authType = settings.authenticationType;
|
||||||
mUsername = settings.username;
|
username = settings.username;
|
||||||
mPassword = settings.password;
|
password = settings.password;
|
||||||
mClientCertificateAlias = settings.clientCertificateAlias;
|
clientCertificateAlias = settings.clientCertificateAlias;
|
||||||
mTrustedSocketFactory = trustedSocketFactory;
|
|
||||||
oauthTokenProvider = oauth2TokenProvider;
|
this.trustedSocketFactory = trustedSocketFactory;
|
||||||
|
this.oauthTokenProvider = oauthTokenProvider;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void open() throws MessagingException {
|
public void open() throws MessagingException {
|
||||||
try {
|
try {
|
||||||
boolean secureConnection = false;
|
boolean secureConnection = false;
|
||||||
InetAddress[] addresses = InetAddress.getAllByName(mHost);
|
InetAddress[] addresses = InetAddress.getAllByName(host);
|
||||||
for (int i = 0; i < addresses.length; i++) {
|
for (int i = 0; i < addresses.length; i++) {
|
||||||
try {
|
try {
|
||||||
SocketAddress socketAddress = new InetSocketAddress(addresses[i], mPort);
|
SocketAddress socketAddress = new InetSocketAddress(addresses[i], port);
|
||||||
if (mConnectionSecurity == ConnectionSecurity.SSL_TLS_REQUIRED) {
|
if (connectionSecurity == ConnectionSecurity.SSL_TLS_REQUIRED) {
|
||||||
mSocket = mTrustedSocketFactory.createSocket(null, mHost, mPort, mClientCertificateAlias);
|
socket = trustedSocketFactory.createSocket(null, host, port, clientCertificateAlias);
|
||||||
mSocket.connect(socketAddress, SOCKET_CONNECT_TIMEOUT);
|
socket.connect(socketAddress, SOCKET_CONNECT_TIMEOUT);
|
||||||
secureConnection = true;
|
secureConnection = true;
|
||||||
} else {
|
} else {
|
||||||
mSocket = new Socket();
|
socket = new Socket();
|
||||||
mSocket.connect(socketAddress, SOCKET_CONNECT_TIMEOUT);
|
socket.connect(socketAddress, SOCKET_CONNECT_TIMEOUT);
|
||||||
}
|
}
|
||||||
} catch (SocketException e) {
|
} catch (SocketException e) {
|
||||||
if (i < (addresses.length - 1)) {
|
if (i < (addresses.length - 1)) {
|
||||||
|
@ -129,15 +134,15 @@ public class SmtpTransport extends Transport {
|
||||||
}
|
}
|
||||||
|
|
||||||
// RFC 1047
|
// RFC 1047
|
||||||
mSocket.setSoTimeout(SOCKET_READ_TIMEOUT);
|
socket.setSoTimeout(SOCKET_READ_TIMEOUT);
|
||||||
|
|
||||||
mIn = new PeekableInputStream(new BufferedInputStream(mSocket.getInputStream(), 1024));
|
inputStream = new PeekableInputStream(new BufferedInputStream(socket.getInputStream(), 1024));
|
||||||
mOut = new BufferedOutputStream(mSocket.getOutputStream(), 1024);
|
outputStream = new BufferedOutputStream(socket.getOutputStream(), 1024);
|
||||||
|
|
||||||
// Eat the banner
|
// Eat the banner
|
||||||
executeCommand(null);
|
executeCommand(null);
|
||||||
|
|
||||||
InetAddress localAddress = mSocket.getLocalAddress();
|
InetAddress localAddress = socket.getLocalAddress();
|
||||||
String localHost = getCanonicalHostName(localAddress);
|
String localHost = getCanonicalHostName(localAddress);
|
||||||
String ipAddr = localAddress.getHostAddress();
|
String ipAddr = localAddress.getHostAddress();
|
||||||
|
|
||||||
|
@ -158,22 +163,22 @@ public class SmtpTransport extends Transport {
|
||||||
|
|
||||||
Map<String, String> extensions = sendHello(localHost);
|
Map<String, String> extensions = sendHello(localHost);
|
||||||
|
|
||||||
m8bitEncodingAllowed = extensions.containsKey("8BITMIME");
|
is8bitEncodingAllowed = extensions.containsKey("8BITMIME");
|
||||||
mEnhancedStatusCodesProvided = extensions.containsKey("ENHANCEDSTATUSCODES");
|
isEnhancedStatusCodesProvided = extensions.containsKey("ENHANCEDSTATUSCODES");
|
||||||
|
|
||||||
if (mConnectionSecurity == ConnectionSecurity.STARTTLS_REQUIRED) {
|
if (connectionSecurity == ConnectionSecurity.STARTTLS_REQUIRED) {
|
||||||
if (extensions.containsKey("STARTTLS")) {
|
if (extensions.containsKey("STARTTLS")) {
|
||||||
executeCommand("STARTTLS");
|
executeCommand("STARTTLS");
|
||||||
|
|
||||||
mSocket = mTrustedSocketFactory.createSocket(
|
socket = trustedSocketFactory.createSocket(
|
||||||
mSocket,
|
socket,
|
||||||
mHost,
|
host,
|
||||||
mPort,
|
port,
|
||||||
mClientCertificateAlias);
|
clientCertificateAlias);
|
||||||
|
|
||||||
mIn = new PeekableInputStream(new BufferedInputStream(mSocket.getInputStream(),
|
inputStream = new PeekableInputStream(new BufferedInputStream(socket.getInputStream(),
|
||||||
1024));
|
1024));
|
||||||
mOut = new BufferedOutputStream(mSocket.getOutputStream(), 1024);
|
outputStream = new BufferedOutputStream(socket.getOutputStream(), 1024);
|
||||||
/*
|
/*
|
||||||
* Now resend the EHLO. Required by RFC2487 Sec. 5.2, and more specifically,
|
* Now resend the EHLO. Required by RFC2487 Sec. 5.2, and more specifically,
|
||||||
* Exim.
|
* Exim.
|
||||||
|
@ -208,12 +213,12 @@ public class SmtpTransport extends Transport {
|
||||||
}
|
}
|
||||||
parseOptionalSizeValue(extensions);
|
parseOptionalSizeValue(extensions);
|
||||||
|
|
||||||
if (!TextUtils.isEmpty(mUsername)
|
if (!TextUtils.isEmpty(username)
|
||||||
&& (!TextUtils.isEmpty(mPassword) ||
|
&& (!TextUtils.isEmpty(password) ||
|
||||||
AuthType.EXTERNAL == mAuthType ||
|
AuthType.EXTERNAL == authType ||
|
||||||
AuthType.XOAUTH2 == mAuthType)) {
|
AuthType.XOAUTH2 == authType)) {
|
||||||
|
|
||||||
switch (mAuthType) {
|
switch (authType) {
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* LOGIN is an obsolete option which is unavailable to users,
|
* LOGIN is an obsolete option which is unavailable to users,
|
||||||
|
@ -224,9 +229,9 @@ public class SmtpTransport extends Transport {
|
||||||
case PLAIN:
|
case PLAIN:
|
||||||
// try saslAuthPlain first, because it supports UTF-8 explicitly
|
// try saslAuthPlain first, because it supports UTF-8 explicitly
|
||||||
if (authPlainSupported) {
|
if (authPlainSupported) {
|
||||||
saslAuthPlain(mUsername, mPassword);
|
saslAuthPlain();
|
||||||
} else if (authLoginSupported) {
|
} else if (authLoginSupported) {
|
||||||
saslAuthLogin(mUsername, mPassword);
|
saslAuthLogin();
|
||||||
} else {
|
} else {
|
||||||
throw new MessagingException(
|
throw new MessagingException(
|
||||||
"Authentication methods SASL PLAIN and LOGIN are unavailable.");
|
"Authentication methods SASL PLAIN and LOGIN are unavailable.");
|
||||||
|
@ -235,21 +240,21 @@ public class SmtpTransport extends Transport {
|
||||||
|
|
||||||
case CRAM_MD5:
|
case CRAM_MD5:
|
||||||
if (authCramMD5Supported) {
|
if (authCramMD5Supported) {
|
||||||
saslAuthCramMD5(mUsername, mPassword);
|
saslAuthCramMD5();
|
||||||
} else {
|
} else {
|
||||||
throw new MessagingException("Authentication method CRAM-MD5 is unavailable.");
|
throw new MessagingException("Authentication method CRAM-MD5 is unavailable.");
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case XOAUTH2:
|
case XOAUTH2:
|
||||||
if (authXoauth2Supported && oauthTokenProvider != null) {
|
if (authXoauth2Supported && oauthTokenProvider != null) {
|
||||||
saslXoauth2(mUsername);
|
saslXoauth2();
|
||||||
} else {
|
} else {
|
||||||
throw new MessagingException("Authentication method XOAUTH2 is unavailable.");
|
throw new MessagingException("Authentication method XOAUTH2 is unavailable.");
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case EXTERNAL:
|
case EXTERNAL:
|
||||||
if (authExternalSupported) {
|
if (authExternalSupported) {
|
||||||
saslAuthExternal(mUsername);
|
saslAuthExternal();
|
||||||
} else {
|
} else {
|
||||||
/*
|
/*
|
||||||
* Some SMTP servers are known to provide no error
|
* Some SMTP servers are known to provide no error
|
||||||
|
@ -274,17 +279,17 @@ public class SmtpTransport extends Transport {
|
||||||
if (secureConnection) {
|
if (secureConnection) {
|
||||||
// try saslAuthPlain first, because it supports UTF-8 explicitly
|
// try saslAuthPlain first, because it supports UTF-8 explicitly
|
||||||
if (authPlainSupported) {
|
if (authPlainSupported) {
|
||||||
saslAuthPlain(mUsername, mPassword);
|
saslAuthPlain();
|
||||||
} else if (authLoginSupported) {
|
} else if (authLoginSupported) {
|
||||||
saslAuthLogin(mUsername, mPassword);
|
saslAuthLogin();
|
||||||
} else if (authCramMD5Supported) {
|
} else if (authCramMD5Supported) {
|
||||||
saslAuthCramMD5(mUsername, mPassword);
|
saslAuthCramMD5();
|
||||||
} else {
|
} else {
|
||||||
throw new MessagingException("No supported authentication methods available.");
|
throw new MessagingException("No supported authentication methods available.");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (authCramMD5Supported) {
|
if (authCramMD5Supported) {
|
||||||
saslAuthCramMD5(mUsername, mPassword);
|
saslAuthCramMD5();
|
||||||
} else {
|
} else {
|
||||||
/*
|
/*
|
||||||
* We refuse to insecurely transmit the password
|
* We refuse to insecurely transmit the password
|
||||||
|
@ -322,9 +327,9 @@ public class SmtpTransport extends Transport {
|
||||||
private void parseOptionalSizeValue(Map<String, String> extensions) {
|
private void parseOptionalSizeValue(Map<String, String> extensions) {
|
||||||
if (extensions.containsKey("SIZE")) {
|
if (extensions.containsKey("SIZE")) {
|
||||||
String optionalsizeValue = extensions.get("SIZE");
|
String optionalsizeValue = extensions.get("SIZE");
|
||||||
if (optionalsizeValue != null && optionalsizeValue != "") {
|
if (optionalsizeValue != null && !"".equals(optionalsizeValue)) {
|
||||||
try {
|
try {
|
||||||
mLargestAcceptableMessage = Integer.parseInt(optionalsizeValue);
|
largestAcceptableMessage = Integer.parseInt(optionalsizeValue);
|
||||||
} catch (NumberFormatException e) {
|
} catch (NumberFormatException e) {
|
||||||
if (K9MailLib.isDebug() && DEBUG_PROTOCOL_SMTP) {
|
if (K9MailLib.isDebug() && DEBUG_PROTOCOL_SMTP) {
|
||||||
Timber.d(e, "Tried to parse %s and get an int", optionalsizeValue);
|
Timber.d(e, "Tried to parse %s and get an int", optionalsizeValue);
|
||||||
|
@ -355,7 +360,7 @@ public class SmtpTransport extends Transport {
|
||||||
* In case of a malformed response.
|
* In case of a malformed response.
|
||||||
*/
|
*/
|
||||||
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<>();
|
||||||
try {
|
try {
|
||||||
List<String> results = executeCommand("EHLO %s", host).results;
|
List<String> results = executeCommand("EHLO %s", host).results;
|
||||||
// Remove the EHLO greeting response
|
// Remove the EHLO greeting response
|
||||||
|
@ -380,7 +385,7 @@ public class SmtpTransport extends Transport {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void sendMessage(Message message) throws MessagingException {
|
public void sendMessage(Message message) throws MessagingException {
|
||||||
List<Address> addresses = new ArrayList<Address>();
|
List<Address> addresses = new ArrayList<>();
|
||||||
{
|
{
|
||||||
addresses.addAll(Arrays.asList(message.getRecipients(RecipientType.TO)));
|
addresses.addAll(Arrays.asList(message.getRecipients(RecipientType.TO)));
|
||||||
addresses.addAll(Arrays.asList(message.getRecipients(RecipientType.CC)));
|
addresses.addAll(Arrays.asList(message.getRecipients(RecipientType.CC)));
|
||||||
|
@ -388,14 +393,13 @@ public class SmtpTransport extends Transport {
|
||||||
}
|
}
|
||||||
message.setRecipients(RecipientType.BCC, null);
|
message.setRecipients(RecipientType.BCC, null);
|
||||||
|
|
||||||
Map<String, List<String>> charsetAddressesMap =
|
Map<String, List<String>> charsetAddressesMap = new HashMap<>();
|
||||||
new HashMap<String, List<String>>();
|
|
||||||
for (Address address : addresses) {
|
for (Address address : addresses) {
|
||||||
String addressString = address.getAddress();
|
String addressString = address.getAddress();
|
||||||
String charset = CharsetSupport.getCharsetFromAddress(addressString);
|
String charset = CharsetSupport.getCharsetFromAddress(addressString);
|
||||||
List<String> addressesOfCharset = charsetAddressesMap.get(charset);
|
List<String> addressesOfCharset = charsetAddressesMap.get(charset);
|
||||||
if (addressesOfCharset == null) {
|
if (addressesOfCharset == null) {
|
||||||
addressesOfCharset = new ArrayList<String>();
|
addressesOfCharset = new ArrayList<>();
|
||||||
charsetAddressesMap.put(charset, addressesOfCharset);
|
charsetAddressesMap.put(charset, addressesOfCharset);
|
||||||
}
|
}
|
||||||
addressesOfCharset.add(addressString);
|
addressesOfCharset.add(addressString);
|
||||||
|
@ -415,13 +419,13 @@ public class SmtpTransport extends Transport {
|
||||||
close();
|
close();
|
||||||
open();
|
open();
|
||||||
|
|
||||||
if (!m8bitEncodingAllowed) {
|
if (!is8bitEncodingAllowed) {
|
||||||
Timber.d("Server does not support 8bit transfer encoding");
|
Timber.d("Server does not support 8bit transfer encoding");
|
||||||
}
|
}
|
||||||
// If the message has attachments and our server has told us about a limit on
|
// If the message has attachments and our server has told us about a limit on
|
||||||
// the size of messages, count the message's size before sending it
|
// the size of messages, count the message's size before sending it
|
||||||
if (mLargestAcceptableMessage > 0 && message.hasAttachments()) {
|
if (largestAcceptableMessage > 0 && message.hasAttachments()) {
|
||||||
if (message.calculateSize() > mLargestAcceptableMessage) {
|
if (message.calculateSize() > largestAcceptableMessage) {
|
||||||
throw new MessagingException("Message too large for server", true);
|
throw new MessagingException("Message too large for server", true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -430,7 +434,7 @@ public class SmtpTransport extends Transport {
|
||||||
Address[] from = message.getFrom();
|
Address[] from = message.getFrom();
|
||||||
try {
|
try {
|
||||||
String fromAddress = from[0].getAddress();
|
String fromAddress = from[0].getAddress();
|
||||||
if (m8bitEncodingAllowed) {
|
if (is8bitEncodingAllowed) {
|
||||||
executeCommand("MAIL FROM:<%s> BODY=8BITMIME", fromAddress);
|
executeCommand("MAIL FROM:<%s> BODY=8BITMIME", fromAddress);
|
||||||
} else {
|
} else {
|
||||||
executeCommand("MAIL FROM:<%s>", fromAddress);
|
executeCommand("MAIL FROM:<%s>", fromAddress);
|
||||||
|
@ -443,7 +447,7 @@ public class SmtpTransport extends Transport {
|
||||||
executeCommand("DATA");
|
executeCommand("DATA");
|
||||||
|
|
||||||
EOLConvertingOutputStream msgOut = new EOLConvertingOutputStream(
|
EOLConvertingOutputStream msgOut = new EOLConvertingOutputStream(
|
||||||
new LineWrapOutputStream(new SmtpDataStuffing(mOut), 1000));
|
new LineWrapOutputStream(new SmtpDataStuffing(outputStream), 1000));
|
||||||
|
|
||||||
message.writeTo(msgOut);
|
message.writeTo(msgOut);
|
||||||
msgOut.endWithCrLfAndFlush();
|
msgOut.endWithCrLfAndFlush();
|
||||||
|
@ -468,26 +472,25 @@ public class SmtpTransport extends Transport {
|
||||||
try {
|
try {
|
||||||
executeCommand("QUIT");
|
executeCommand("QUIT");
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
// don't care
|
||||||
}
|
}
|
||||||
IOUtils.closeQuietly(mIn);
|
IOUtils.closeQuietly(inputStream);
|
||||||
IOUtils.closeQuietly(mOut);
|
IOUtils.closeQuietly(outputStream);
|
||||||
IOUtils.closeQuietly(mSocket);
|
IOUtils.closeQuietly(socket);
|
||||||
mIn = null;
|
inputStream = null;
|
||||||
mOut = null;
|
outputStream = null;
|
||||||
mSocket = null;
|
socket = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private String readLine() throws IOException {
|
private String readLine() throws IOException {
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
int d;
|
int d;
|
||||||
while ((d = mIn.read()) != -1) {
|
while ((d = inputStream.read()) != -1) {
|
||||||
if (((char)d) == '\r') {
|
char c = (char) d;
|
||||||
continue;
|
if (c == '\n') {
|
||||||
} else if (((char)d) == '\n') {
|
|
||||||
break;
|
break;
|
||||||
} else {
|
} else if (c != '\r') {
|
||||||
sb.append((char)d);
|
sb.append(c);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
String ret = sb.toString();
|
String ret = sb.toString();
|
||||||
|
@ -516,8 +519,8 @@ public class SmtpTransport extends Transport {
|
||||||
* SMTP servers misbehave if CR and LF arrive in separate pakets.
|
* SMTP servers misbehave if CR and LF arrive in separate pakets.
|
||||||
* See issue 799.
|
* See issue 799.
|
||||||
*/
|
*/
|
||||||
mOut.write(data);
|
outputStream.write(data);
|
||||||
mOut.flush();
|
outputStream.flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class CommandResponse {
|
private static class CommandResponse {
|
||||||
|
@ -525,7 +528,7 @@ public class SmtpTransport extends Transport {
|
||||||
private final int replyCode;
|
private final int replyCode;
|
||||||
private final List<String> results;
|
private final List<String> results;
|
||||||
|
|
||||||
public CommandResponse(int replyCode, List<String> results) {
|
CommandResponse(int replyCode, List<String> results) {
|
||||||
this.replyCode = replyCode;
|
this.replyCode = replyCode;
|
||||||
this.results = results;
|
this.results = results;
|
||||||
}
|
}
|
||||||
|
@ -565,7 +568,7 @@ public class SmtpTransport extends Transport {
|
||||||
char replyCodeCategory = line.charAt(0);
|
char replyCodeCategory = line.charAt(0);
|
||||||
boolean isReplyCodeErrorCategory = (replyCodeCategory == '4') || (replyCodeCategory == '5');
|
boolean isReplyCodeErrorCategory = (replyCodeCategory == '4') || (replyCodeCategory == '5');
|
||||||
if (isReplyCodeErrorCategory) {
|
if (isReplyCodeErrorCategory) {
|
||||||
if (mEnhancedStatusCodesProvided) {
|
if (isEnhancedStatusCodesProvided) {
|
||||||
throw buildEnhancedNegativeSmtpReplyException(replyCode, results);
|
throw buildEnhancedNegativeSmtpReplyException(replyCode, results);
|
||||||
} else {
|
} else {
|
||||||
String replyText = TextUtils.join(" ", results);
|
String replyText = TextUtils.join(" ", results);
|
||||||
|
@ -620,48 +623,26 @@ public class SmtpTransport extends Transport {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// C: AUTH LOGIN
|
private void saslAuthLogin() throws MessagingException, IOException {
|
||||||
// S: 334 VXNlcm5hbWU6
|
|
||||||
// C: d2VsZG9u
|
|
||||||
// S: 334 UGFzc3dvcmQ6
|
|
||||||
// C: dzNsZDBu
|
|
||||||
// S: 235 2.0.0 OK Authenticated
|
|
||||||
//
|
|
||||||
// Lines 2-5 of the conversation contain base64-encoded information. The same conversation, with base64 strings decoded, reads:
|
|
||||||
//
|
|
||||||
//
|
|
||||||
// C: AUTH LOGIN
|
|
||||||
// S: 334 Username:
|
|
||||||
// C: weldon
|
|
||||||
// S: 334 Password:
|
|
||||||
// C: w3ld0n
|
|
||||||
// S: 235 2.0.0 OK Authenticated
|
|
||||||
|
|
||||||
private void saslAuthLogin(String username, String password) throws MessagingException,
|
|
||||||
AuthenticationFailedException, IOException {
|
|
||||||
try {
|
try {
|
||||||
executeCommand("AUTH LOGIN");
|
executeCommand("AUTH LOGIN");
|
||||||
executeSensitiveCommand(Base64.encode(username));
|
executeSensitiveCommand(Base64.encode(username));
|
||||||
executeSensitiveCommand(Base64.encode(password));
|
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
|
throw new AuthenticationFailedException("AUTH LOGIN failed (" + exception.getMessage() + ")");
|
||||||
throw new AuthenticationFailedException("AUTH LOGIN failed ("
|
|
||||||
+ exception.getMessage() + ")");
|
|
||||||
} else {
|
} else {
|
||||||
throw exception;
|
throw exception;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void saslAuthPlain(String username, String password) throws MessagingException,
|
private void saslAuthPlain() throws MessagingException, IOException {
|
||||||
AuthenticationFailedException, IOException {
|
|
||||||
String data = Base64.encode("\000" + username + "\000" + password);
|
String data = Base64.encode("\000" + username + "\000" + password);
|
||||||
try {
|
try {
|
||||||
executeSensitiveCommand("AUTH PLAIN %s", data);
|
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
|
|
||||||
throw new AuthenticationFailedException("AUTH PLAIN failed ("
|
throw new AuthenticationFailedException("AUTH PLAIN failed ("
|
||||||
+ exception.getMessage() + ")");
|
+ exception.getMessage() + ")");
|
||||||
} else {
|
} else {
|
||||||
|
@ -670,8 +651,7 @@ public class SmtpTransport extends Transport {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void saslAuthCramMD5(String username, String password) throws MessagingException,
|
private void saslAuthCramMD5() throws MessagingException, IOException {
|
||||||
AuthenticationFailedException, IOException {
|
|
||||||
|
|
||||||
List<String> respList = executeCommand("AUTH CRAM-MD5").results;
|
List<String> respList = executeCommand("AUTH CRAM-MD5").results;
|
||||||
if (respList.size() != 1) {
|
if (respList.size() != 1) {
|
||||||
|
@ -679,13 +659,12 @@ public class SmtpTransport extends Transport {
|
||||||
}
|
}
|
||||||
|
|
||||||
String b64Nonce = respList.get(0);
|
String b64Nonce = respList.get(0);
|
||||||
String b64CRAMString = Authentication.computeCramMd5(mUsername, mPassword, b64Nonce);
|
String b64CRAMString = Authentication.computeCramMd5(username, password, b64Nonce);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
executeSensitiveCommand(b64CRAMString);
|
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
|
|
||||||
throw new AuthenticationFailedException(exception.getMessage(), exception);
|
throw new AuthenticationFailedException(exception.getMessage(), exception);
|
||||||
} else {
|
} else {
|
||||||
throw exception;
|
throw exception;
|
||||||
|
@ -693,7 +672,7 @@ public class SmtpTransport extends Transport {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void saslXoauth2(String username) throws MessagingException, IOException {
|
private void saslXoauth2() throws MessagingException, IOException {
|
||||||
retryXoauthWithNewToken = true;
|
retryXoauthWithNewToken = true;
|
||||||
try {
|
try {
|
||||||
attemptXoauth2(username);
|
attemptXoauth2(username);
|
||||||
|
@ -749,14 +728,14 @@ public class SmtpTransport extends Transport {
|
||||||
|
|
||||||
if (response.replyCode == SMTP_CONTINUE_REQUEST) {
|
if (response.replyCode == SMTP_CONTINUE_REQUEST) {
|
||||||
String replyText = TextUtils.join("", response.results);
|
String replyText = TextUtils.join("", response.results);
|
||||||
retryXoauthWithNewToken = XOAuth2ChallengeParser.shouldRetry(replyText, mHost);
|
retryXoauthWithNewToken = XOAuth2ChallengeParser.shouldRetry(replyText, host);
|
||||||
|
|
||||||
//Per Google spec, respond to challenge with empty response
|
//Per Google spec, respond to challenge with empty response
|
||||||
executeCommand("");
|
executeCommand("");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void saslAuthExternal(String username) throws MessagingException, IOException {
|
private void saslAuthExternal() throws MessagingException, IOException {
|
||||||
executeCommand("AUTH EXTERNAL %s", Base64.encode(username));
|
executeCommand("AUTH EXTERNAL %s", Base64.encode(username));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue