diff --git a/app/src/main/java/eu/faircode/email/EmailProvider.java b/app/src/main/java/eu/faircode/email/EmailProvider.java index 3406ebf4be..642814e7fd 100644 --- a/app/src/main/java/eu/faircode/email/EmailProvider.java +++ b/app/src/main/java/eu/faircode/email/EmailProvider.java @@ -320,7 +320,6 @@ public class EmailProvider implements Parcelable { } } } - } catch (Throwable ex) { Log.w(ex); } @@ -338,21 +337,35 @@ public class EmailProvider implements Parcelable { EntityLog.log(context, "Replacing auto config by profile=" + provider.name); return Arrays.asList(provider); } + } - // https://help.dreamhost.com/hc/en-us/articles/214918038-Email-client-configuration-overview - if (autoconfig.imap.host != null && - autoconfig.imap.host.endsWith(".dreamhost.com")) - autoconfig.imap.host = "imap.dreamhost.com"; - - if (autoconfig.smtp.host != null && - autoconfig.smtp.host.endsWith(".dreamhost.com")) - autoconfig.smtp.host = "smtp.dreamhost.com"; - - // https://docs.aws.amazon.com/workmail/latest/userguide/using_IMAP_client.html - if (autoconfig.imap.host != null && - autoconfig.imap.host.endsWith(".awsapps.com")) - autoconfig.partial = false; + Collections.sort(providers, new Comparator() { + @Override + public int compare(EmailProvider p1, EmailProvider p2) { + return -Integer.compare(p1.getScore(), p2.getScore()); } + }); + + for (EmailProvider autoconfig : result) { + EntityLog.log(context, "Provider" + + " score=" + autoconfig.getScore() + + " imap=" + autoconfig.imap + + " smtp=" + autoconfig.smtp); + + // https://help.dreamhost.com/hc/en-us/articles/214918038-Email-client-configuration-overview + if (autoconfig.imap.host != null && + autoconfig.imap.host.endsWith(".dreamhost.com")) + autoconfig.imap.host = "imap.dreamhost.com"; + + if (autoconfig.smtp.host != null && + autoconfig.smtp.host.endsWith(".dreamhost.com")) + autoconfig.smtp.host = "smtp.dreamhost.com"; + + // https://docs.aws.amazon.com/workmail/latest/userguide/using_IMAP_client.html + if (autoconfig.imap.host != null && + autoconfig.imap.host.endsWith(".awsapps.com")) + autoconfig.partial = false; + } return result; } @@ -611,6 +624,7 @@ public class EmailProvider implements Parcelable { provider.imap.host = records[0].name; provider.imap.port = records[0].port; provider.imap.starttls = false; + provider.imap.checkCertificate(context); EntityLog.log(context, "_imaps._tcp." + domain + "=" + provider.imap); } catch (UnknownHostException ignored) { // Identifies an IMAP server that MAY ... require the MUA to use the "STARTTLS" command @@ -620,6 +634,7 @@ public class EmailProvider implements Parcelable { provider.imap.host = records[0].name; provider.imap.port = records[0].port; provider.imap.starttls = (provider.imap.port == 143); + provider.imap.checkCertificate(context); EntityLog.log(context, "_imap._tcp." + domain + "=" + provider.imap); } } @@ -633,6 +648,7 @@ public class EmailProvider implements Parcelable { provider.smtp.host = records[0].name; provider.smtp.port = records[0].port; provider.smtp.starttls = (provider.smtp.port == 587); + provider.smtp.checkCertificate(context); EntityLog.log(context, "_submission._tcp." + domain + "=" + provider.smtp); } catch (UnknownHostException ignored) { // https://tools.ietf.org/html/rfc8314 @@ -642,6 +658,7 @@ public class EmailProvider implements Parcelable { provider.smtp.host = records[0].name; provider.smtp.port = records[0].port; provider.smtp.starttls = false; + provider.smtp.checkCertificate(context); EntityLog.log(context, "_submissions._tcp." + domain + "=" + provider.smtp); } @@ -808,6 +825,12 @@ public class EmailProvider implements Parcelable { } }; + private int getScore() { + if (imap == null || smtp == null) + return -1; + return imap.score + smtp.score; + } + @Override public boolean equals(Object obj) { if (obj instanceof EmailProvider) { @@ -834,6 +857,7 @@ public class EmailProvider implements Parcelable { public int port; public boolean starttls; + private int score = 0; private Future isReachable; private Server() { @@ -843,9 +867,20 @@ public class EmailProvider implements Parcelable { this.host = (prefix == null ? "" : prefix + ".") + domain; this.port = port; this.starttls = starttls; + this.isReachable = getReachable(context); + } + private void checkCertificate(Context context) { + try { + getReachable(context).get(); + } catch (Throwable ex) { + Log.e(ex); + } + } + + private Future getReachable(Context context) { Log.i("Scanning " + this); - this.isReachable = executor.submit(new Callable() { + return executor.submit(new Callable() { // Returns: // false: closed // true: listening @@ -881,10 +916,28 @@ public class EmailProvider implements Parcelable { List names = EntityCertificate.getDnsNames((X509Certificate) cert); EntityLog.log(context, "Certificate " + address + " " + TextUtils.join(",", names)); + if (EntityCertificate.matches(host, names)) { + score += 2; EntityLog.log(context, "Trusted " + address); return true; } + + for (String name : names) + try { + String similar = name; + if (similar.startsWith("*.")) + similar = similar.substring(2); + InetAddress isimilar = InetAddress.getByName(similar); + if (iaddr.equals(isimilar)) { + score += 1; + EntityLog.log(context, "Similar " + similar + " host=" + host); + host = similar; + return true; + } + } catch (Throwable ex) { + Log.w(ex); + } } EntityLog.log(context, "Untrusted " + address); diff --git a/app/src/main/java/eu/faircode/email/EntityCertificate.java b/app/src/main/java/eu/faircode/email/EntityCertificate.java index 820a8985ce..a684b230f7 100644 --- a/app/src/main/java/eu/faircode/email/EntityCertificate.java +++ b/app/src/main/java/eu/faircode/email/EntityCertificate.java @@ -41,7 +41,6 @@ import org.json.JSONException; import org.json.JSONObject; import java.io.ByteArrayInputStream; -import java.net.InetAddress; import java.security.NoSuchAlgorithmException; import java.security.cert.CertificateEncodingException; import java.security.cert.CertificateException; @@ -217,31 +216,6 @@ public class EntityCertificate { return result; } - static String getSimilarDnsName(X509Certificate certificate, @NonNull String host) { - if (certificate == null) - return null; - - try { - InetAddress haddr = InetAddress.getByName(host); - List names = getDnsNames(certificate); - for (String _name : names) - try { - String name = (_name.startsWith("*.") ? _name.substring(2) : _name); - InetAddress naddr = InetAddress.getByName(name); - Log.i("host=" + host + " name=" + _name + "" + - " haddr=" + haddr + " naddr=" + naddr); - if (haddr.equals(naddr)) - return name; - } catch (Throwable ex) { - Log.w(ex); - } - } catch (Throwable ex) { - Log.w(ex); - } - - return null; - } - static boolean matches(String server, List names) { for (String name : names) if (matches(server, name)) { diff --git a/app/src/main/java/eu/faircode/email/FragmentQuickSetup.java b/app/src/main/java/eu/faircode/email/FragmentQuickSetup.java index 994efc6611..f334bcc0c5 100644 --- a/app/src/main/java/eu/faircode/email/FragmentQuickSetup.java +++ b/app/src/main/java/eu/faircode/email/FragmentQuickSetup.java @@ -289,8 +289,8 @@ public class FragmentQuickSetup extends FragmentBase { List providers = EmailProvider.fromEmail(context, email, EmailProvider.Discover.ALL); for (EmailProvider provider : providers) try { - EntityLog.log(context, "imap=" + provider.imap); - EntityLog.log(context, "smtp=" + provider.smtp); + EntityLog.log(context, "Checking" + + " imap=" + provider.imap + " smtp=" + provider.smtp); String user = (provider.user == EmailProvider.UserType.EMAIL ? email : username); Log.i("User type=" + provider.user + " name=" + user); @@ -313,16 +313,7 @@ public class FragmentQuickSetup extends FragmentBase { null, null); } catch (EmailService.UntrustedException ex) { imap_certificate = ex.getCertificate(); - String similar = EntityCertificate.getSimilarDnsName(imap_certificate, provider.imap.host); - if (similar == null) - imap_fingerprint = EntityCertificate.getKeyFingerprint(imap_certificate); - else - provider.imap.host = similar; - iservice.connect( - provider.imap.host, provider.imap.port, - AUTH_TYPE_PASSWORD, null, - user, password, - null, imap_fingerprint); + imap_fingerprint = EntityCertificate.getKeyFingerprint(imap_certificate); } catch (Throwable ex) { Log.w(ex); // Why not AuthenticationFailedException? @@ -338,6 +329,9 @@ public class FragmentQuickSetup extends FragmentBase { AUTH_TYPE_PASSWORD, null, user, password, null, null); + } catch (EmailService.UntrustedException ex1) { + imap_certificate = ex1.getCertificate(); + imap_fingerprint = EntityCertificate.getKeyFingerprint(imap_certificate); } catch (Throwable ex1) { Log.w(ex1); if (!(ex instanceof AuthenticationFailedException) && @@ -381,16 +375,7 @@ public class FragmentQuickSetup extends FragmentBase { null, null); } catch (EmailService.UntrustedException ex) { smtp_certificate = ex.getCertificate(); - String similar = EntityCertificate.getSimilarDnsName(smtp_certificate, provider.smtp.host); - if (similar == null) - smtp_fingerprint = EntityCertificate.getKeyFingerprint(smtp_certificate); - else - provider.smtp.host = similar; - iservice.connect( - provider.smtp.host, provider.smtp.port, - AUTH_TYPE_PASSWORD, null, - user, password, - null, smtp_fingerprint); + smtp_fingerprint = EntityCertificate.getKeyFingerprint(smtp_certificate); } max_size = iservice.getMaxSize();