Improved connect fallback

pull/172/head
M66B 5 years ago
parent 961212e45b
commit 08098c2285

@ -12,7 +12,6 @@ import androidx.preference.PreferenceManager;
import com.sun.mail.imap.IMAPFolder; import com.sun.mail.imap.IMAPFolder;
import com.sun.mail.imap.IMAPStore; import com.sun.mail.imap.IMAPStore;
import com.sun.mail.smtp.SMTPTransport; import com.sun.mail.smtp.SMTPTransport;
import com.sun.mail.util.MailConnectException;
import net.openid.appauth.AuthState; import net.openid.appauth.AuthState;
import net.openid.appauth.AuthorizationException; import net.openid.appauth.AuthorizationException;
@ -219,7 +218,10 @@ public class MailService implements AutoCloseable {
} }
} }
public String connect(String host, int port, int auth, String provider, String user, String password, String fingerprint) throws MessagingException { public String connect(
String host, int port,
int auth, String provider, String user, String password,
String fingerprint) throws MessagingException {
SSLSocketFactoryService factory = null; SSLSocketFactoryService factory = null;
try { try {
factory = new SSLSocketFactoryService(host, insecure, fingerprint); factory = new SSLSocketFactoryService(host, insecure, fingerprint);
@ -237,16 +239,12 @@ public class MailService implements AutoCloseable {
if (auth == AUTH_TYPE_GMAIL || auth == AUTH_TYPE_OAUTH) if (auth == AUTH_TYPE_GMAIL || auth == AUTH_TYPE_OAUTH)
properties.put("mail." + protocol + ".auth.mechanisms", "XOAUTH2"); properties.put("mail." + protocol + ".auth.mechanisms", "XOAUTH2");
//if (BuildConfig.DEBUG)
// throw new MailConnectException(
// new SocketConnectException("Debug", new Exception("Test"), host, port, 0));
if (auth == AUTH_TYPE_OAUTH) { if (auth == AUTH_TYPE_OAUTH) {
AuthState authState = OAuthRefresh(context, provider, password); AuthState authState = OAuthRefresh(context, provider, password);
_connect(context, host, port, user, authState.getAccessToken(), factory); connect(host, port, user, authState.getAccessToken(), factory);
return authState.jsonSerializeString(); return authState.jsonSerializeString();
} else { } else {
_connect(context, host, port, user, password, factory); connect(host, port, user, password, factory);
return null; return null;
} }
} catch (AuthenticationFailedException ex) { } catch (AuthenticationFailedException ex) {
@ -264,7 +262,7 @@ public class MailService implements AutoCloseable {
if (token == null) if (token == null)
throw new IllegalArgumentException("No token on refresh"); throw new IllegalArgumentException("No token on refresh");
_connect(context, host, port, user, token, factory); connect(host, port, user, token, factory);
return token; return token;
} }
@ -275,26 +273,50 @@ public class MailService implements AutoCloseable {
} }
else if (auth == AUTH_TYPE_OAUTH) { else if (auth == AUTH_TYPE_OAUTH) {
AuthState authState = OAuthRefresh(context, provider, password); AuthState authState = OAuthRefresh(context, provider, password);
_connect(context, host, port, user, authState.getAccessToken(), factory); connect(host, port, user, authState.getAccessToken(), factory);
return authState.jsonSerializeString(); return authState.jsonSerializeString();
} else } else
throw ex; throw ex;
} catch (MailConnectException ex) { }
try { }
// Some devices resolve IPv6 addresses while not having IPv6 connectivity
InetAddress[] iaddrs = InetAddress.getAllByName(host); private void connect(
Log.i("Fallback count=" + iaddrs.length); String host, int port, String user, String password,
if (iaddrs.length > 1) SSLSocketFactoryService factory) throws MessagingException {
for (InetAddress iaddr : iaddrs) try {
try { //if (BuildConfig.DEBUG)
Log.i("Falling back to " + iaddr.getHostAddress()); // throw new MailConnectException(
_connect(context, iaddr.getHostAddress(), port, user, password, factory); // new SocketConnectException("Debug", new IOException("Test"), host, port, 0));
return null;
} catch (MessagingException ex1) { _connect(host, port, user, password, factory);
Log.w(ex1); } catch (MessagingException ex) {
} boolean ioError = false;
} catch (Throwable ex1) { Throwable ce = ex;
Log.w(ex1); while (ce != null) {
if (factory != null && ce instanceof CertificateException)
throw new UntrustedException(factory.getFingerPrint(), ex);
if (ce instanceof IOException)
ioError = true;
ce = ce.getCause();
}
if (ioError) {
try {
// Some devices resolve IPv6 addresses while not having IPv6 connectivity
InetAddress[] iaddrs = InetAddress.getAllByName(host);
Log.i("Fallback count=" + iaddrs.length);
if (iaddrs.length > 1)
for (InetAddress iaddr : iaddrs)
try {
Log.i("Falling back to " + iaddr.getHostAddress());
_connect(iaddr.getHostAddress(), port, user, password, factory);
return;
} catch (MessagingException ex1) {
Log.w(ex1);
}
} catch (Throwable ex1) {
Log.w(ex1);
}
} }
throw ex; throw ex;
@ -302,92 +324,79 @@ public class MailService implements AutoCloseable {
} }
private void _connect( private void _connect(
Context context,
String host, int port, String user, String password, String host, int port, String user, String password,
SSLSocketFactoryService factory) throws MessagingException { SSLSocketFactoryService factory) throws MessagingException {
try { isession = Session.getInstance(properties, null);
isession = Session.getInstance(properties, null); isession.setDebug(debug);
isession.setDebug(debug); //System.setProperty("mail.socket.debug", Boolean.toString(debug));
//System.setProperty("mail.socket.debug", Boolean.toString(debug));
if ("pop3".equals(protocol) || "pop3s".equals(protocol)) {
isession.setDebug(true);
iservice = isession.getStore(protocol);
iservice.connect(host, port, user, password);
} else if ("imap".equals(protocol) || "imaps".equals(protocol)) { if ("pop3".equals(protocol) || "pop3s".equals(protocol)) {
iservice = isession.getStore(protocol); isession.setDebug(true);
if (listener != null) iservice = isession.getStore(protocol);
((IMAPStore) iservice).addStoreListener(listener); iservice.connect(host, port, user, password);
iservice.connect(host, port, user, password);
// https://www.ietf.org/rfc/rfc2971.txt } else if ("imap".equals(protocol) || "imaps".equals(protocol)) {
IMAPStore istore = (IMAPStore) getStore(); iservice = isession.getStore(protocol);
if (istore.hasCapability("ID")) if (listener != null)
try { ((IMAPStore) iservice).addStoreListener(listener);
Map<String, String> id = new LinkedHashMap<>(); iservice.connect(host, port, user, password);
id.put("name", context.getString(R.string.app_name));
id.put("version", BuildConfig.VERSION_NAME); // https://www.ietf.org/rfc/rfc2971.txt
Map<String, String> sid = istore.id(id); IMAPStore istore = (IMAPStore) getStore();
if (sid != null) { if (istore.hasCapability("ID"))
Map<String, String> crumb = new HashMap<>(); try {
for (String key : sid.keySet()) { Map<String, String> id = new LinkedHashMap<>();
crumb.put(key, sid.get(key)); id.put("name", context.getString(R.string.app_name));
EntityLog.log(context, "Server " + key + "=" + sid.get(key)); id.put("version", BuildConfig.VERSION_NAME);
} Map<String, String> sid = istore.id(id);
Log.breadcrumb("server", crumb); if (sid != null) {
Map<String, String> crumb = new HashMap<>();
for (String key : sid.keySet()) {
crumb.put(key, sid.get(key));
EntityLog.log(context, "Server " + key + "=" + sid.get(key));
} }
} catch (MessagingException ex) { Log.breadcrumb("server", crumb);
Log.w(ex);
}
} else if ("smtp".equals(protocol) || "smtps".equals(protocol)) {
String[] c = BuildConfig.APPLICATION_ID.split("\\.");
Collections.reverse(Arrays.asList(c));
String domain = TextUtils.join(".", c);
String haddr = domain;
if (useip)
try {
// This assumes getByName always returns the same address (type)
InetAddress addr = InetAddress.getByName(host);
if (addr instanceof Inet4Address)
haddr = "[" + Inet4Address.getLocalHost().getHostAddress() + "]";
else
haddr = "[IPv6:" + Inet6Address.getLocalHost().getHostAddress() + "]";
} catch (UnknownHostException ex) {
Log.w(ex);
} }
} catch (MessagingException ex) {
Log.w(ex);
}
Log.i("Using localhost=" + haddr); } else if ("smtp".equals(protocol) || "smtps".equals(protocol)) {
properties.put("mail." + protocol + ".localhost", haddr); String[] c = BuildConfig.APPLICATION_ID.split("\\.");
Collections.reverse(Arrays.asList(c));
String domain = TextUtils.join(".", c);
iservice = isession.getTransport(protocol); String haddr = domain;
if (useip)
try { try {
iservice.connect(host, port, user, password); // This assumes getByName always returns the same address (type)
} catch (MessagingException ex) { InetAddress addr = InetAddress.getByName(host);
if (useip && if (addr instanceof Inet4Address)
ex.getMessage() != null && haddr = "[" + Inet4Address.getLocalHost().getHostAddress() + "]";
ex.getMessage().toLowerCase().contains("syntactically invalid")) { else
Log.w("Using localhost=" + domain, ex); haddr = "[IPv6:" + Inet6Address.getLocalHost().getHostAddress() + "]";
((SMTPTransport) iservice).setLocalHost(domain); } catch (UnknownHostException ex) {
iservice.connect(host, port, user, password); Log.w(ex);
} else
throw ex;
}
} else
throw new NoSuchProviderException(protocol);
} catch (MessagingException ex) {
if (factory != null) {
Throwable ce = ex;
while (ce != null) {
if (ce instanceof CertificateException)
throw new UntrustedException(factory.getFingerPrint(), ex);
ce = ce.getCause();
} }
Log.i("Using localhost=" + haddr);
properties.put("mail." + protocol + ".localhost", haddr);
iservice = isession.getTransport(protocol);
try {
iservice.connect(host, port, user, password);
} catch (MessagingException ex) {
if (useip &&
ex.getMessage() != null &&
ex.getMessage().toLowerCase().contains("syntactically invalid")) {
Log.w("Using localhost=" + domain, ex);
((SMTPTransport) iservice).setLocalHost(domain);
iservice.connect(host, port, user, password);
} else
throw ex;
} }
throw ex; } else
} throw new NoSuchProviderException(protocol);
} }
private static class ErrorHolder { private static class ErrorHolder {

Loading…
Cancel
Save