Auto discovery scores

This commit is contained in:
M66B 2021-08-17 09:52:54 +02:00
parent e866bfaa25
commit 366bf4e463
1 changed files with 73 additions and 64 deletions

View File

@ -183,10 +183,12 @@ public class EmailProvider implements Parcelable {
else if ("email".equals(user)) else if ("email".equals(user))
provider.user = UserType.EMAIL; provider.user = UserType.EMAIL;
} else if ("imap".equals(name)) { } else if ("imap".equals(name)) {
provider.imap.score = 100;
provider.imap.host = xml.getAttributeValue(null, "host"); provider.imap.host = xml.getAttributeValue(null, "host");
provider.imap.port = xml.getAttributeIntValue(null, "port", 0); provider.imap.port = xml.getAttributeIntValue(null, "port", 0);
provider.imap.starttls = xml.getAttributeBooleanValue(null, "starttls", false); provider.imap.starttls = xml.getAttributeBooleanValue(null, "starttls", false);
} else if ("smtp".equals(name)) { } else if ("smtp".equals(name)) {
provider.smtp.score = 100;
provider.smtp.host = xml.getAttributeValue(null, "host"); provider.smtp.host = xml.getAttributeValue(null, "host");
provider.smtp.port = xml.getAttributeIntValue(null, "port", 0); provider.smtp.port = xml.getAttributeIntValue(null, "port", 0);
provider.smtp.starttls = xml.getAttributeBooleanValue(null, "starttls", false); provider.smtp.starttls = xml.getAttributeBooleanValue(null, "starttls", false);
@ -265,15 +267,12 @@ public class EmailProvider implements Parcelable {
for (String d : provider.domain) for (String d : provider.domain)
if (domain.toLowerCase(Locale.ROOT).matches(d)) { if (domain.toLowerCase(Locale.ROOT).matches(d)) {
EntityLog.log(context, "Provider from domain=" + domain + " (" + d + ")"); EntityLog.log(context, "Provider from domain=" + domain + " (" + d + ")");
return Arrays.asList(provider); if (!BuildConfig.DEBUG)
return Arrays.asList(provider);
} }
List<EmailProvider> result = new ArrayList<>(); List<EmailProvider> candidates =
for (EmailProvider provider : _fromDomain(context, domain.toLowerCase(Locale.ROOT), email, discover)) new ArrayList<>(_fromDomain(context, domain.toLowerCase(Locale.ROOT), email, discover));
if (result.contains(provider))
Log.i("Duplicate " + provider);
else
result.add(provider);
try { try {
DnsHelper.DnsRecord[] records = DnsHelper.lookup(context, domain, "mx"); DnsHelper.DnsRecord[] records = DnsHelper.lookup(context, domain, "mx");
@ -285,10 +284,7 @@ public class EmailProvider implements Parcelable {
for (String mx : provider.mx) for (String mx : provider.mx)
if (record.name.toLowerCase(Locale.ROOT).matches(mx)) { if (record.name.toLowerCase(Locale.ROOT).matches(mx)) {
EntityLog.log(context, "Provider from mx=" + record.name + " domain=" + domain); EntityLog.log(context, "Provider from mx=" + record.name + " domain=" + domain);
if (result.contains(provider)) candidates.add(provider);
Log.i("Duplicate " + provider);
else
result.add(provider);
break; break;
} }
@ -296,82 +292,81 @@ public class EmailProvider implements Parcelable {
String pdomain = UriHelper.getParentDomain(context, provider.imap.host); String pdomain = UriHelper.getParentDomain(context, provider.imap.host);
if (mxparent.equalsIgnoreCase(pdomain)) { if (mxparent.equalsIgnoreCase(pdomain)) {
EntityLog.log(context, "Provider from mx=" + record.name + " host=" + provider.imap.host); EntityLog.log(context, "Provider from mx=" + record.name + " host=" + provider.imap.host);
if (result.contains(provider)) candidates.add(provider);
Log.i("Duplicate " + provider);
else
result.add(provider);
break; break;
} }
} }
for (DnsHelper.DnsRecord record : records) { for (DnsHelper.DnsRecord record : records) {
String target = record.name; String target = record.name;
while (result.size() == 0 && target != null && target.indexOf('.') > 0) { while (candidates.size() == 0 && target != null && target.indexOf('.') > 0) {
try { candidates.addAll(_fromDomain(context, target.toLowerCase(Locale.ROOT), email, discover));
for (EmailProvider provider : _fromDomain(context, target.toLowerCase(Locale.ROOT), email, discover)) int dot = target.indexOf('.');
if (result.contains(provider)) target = target.substring(dot + 1);
Log.i("Duplicate " + provider);
else
result.add(provider);
} catch (Throwable ex) {
Log.w(ex);
int dot = target.indexOf('.');
target = target.substring(dot + 1);
}
} }
} }
} catch (Throwable ex) { } catch (Throwable ex) {
Log.w(ex); Log.w(ex);
} }
if (result.size() == 0) if (candidates.size() == 0)
throw new UnknownHostException(context.getString(R.string.title_setup_no_settings, domain)); throw new UnknownHostException(context.getString(R.string.title_setup_no_settings, domain));
for (EmailProvider autoconfig : result) for (EmailProvider candidate : candidates) {
for (EmailProvider provider : providers) { // Always prefer built-in profiles
// Always prefer built-in profiles // - ISPDB is not always correct
// - ISPDB is not always correct // - documentation links
// - documentation links for (EmailProvider provider : providers)
if (provider.imap.host.equals(autoconfig.imap.host) || if (provider.imap.host.equals(candidate.imap.host) ||
provider.smtp.host.equals(autoconfig.smtp.host)) { provider.smtp.host.equals(candidate.smtp.host)) {
EntityLog.log(context, "Replacing auto config by profile=" + provider.name); EntityLog.log(context, "Replacing auto config by profile=" + provider.name);
return Arrays.asList(provider); if (!BuildConfig.DEBUG)
return Arrays.asList(provider);
} }
}
Collections.sort(providers, new Comparator<EmailProvider>() { // https://help.dreamhost.com/hc/en-us/articles/214918038-Email-client-configuration-overview
if (candidate.imap.host != null &&
candidate.imap.host.endsWith(".dreamhost.com"))
candidate.imap.host = "imap.dreamhost.com";
if (candidate.smtp.host != null &&
candidate.smtp.host.endsWith(".dreamhost.com"))
candidate.smtp.host = "smtp.dreamhost.com";
// https://docs.aws.amazon.com/workmail/latest/userguide/using_IMAP_client.html
if (candidate.imap.host != null &&
candidate.imap.host.endsWith(".awsapps.com"))
candidate.partial = false;
}
// Sort by score
Collections.sort(candidates, new Comparator<EmailProvider>() {
@Override @Override
public int compare(EmailProvider p1, EmailProvider p2) { public int compare(EmailProvider p1, EmailProvider p2) {
return -Integer.compare(p1.getScore(), p2.getScore()); return -Integer.compare(p1.getScore(), p2.getScore());
} }
}); });
for (EmailProvider autoconfig : result) { // Log candidates
EntityLog.log(context, "Provider" + for (EmailProvider candidate : candidates)
" score=" + autoconfig.getScore() + EntityLog.log(context, "Candidate" +
" imap=" + autoconfig.imap + " score=" + candidate.getScore() +
" smtp=" + autoconfig.smtp); " imap=" + candidate.imap +
" smtp=" + candidate.smtp);
// https://help.dreamhost.com/hc/en-us/articles/214918038-Email-client-configuration-overview // Remove duplicates
if (autoconfig.imap.host != null && List<EmailProvider> result = new ArrayList<>();
autoconfig.imap.host.endsWith(".dreamhost.com")) for (EmailProvider candidate : candidates)
autoconfig.imap.host = "imap.dreamhost.com"; if (!result.contains(candidate))
result.add(candidate);
if (autoconfig.smtp.host != null && Log.i("Candidates=" + result.size());
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; return result;
} }
@NonNull @NonNull
private static List<EmailProvider> _fromDomain(Context context, String domain, String email, Discover discover) throws IOException { private static List<EmailProvider> _fromDomain(Context context, String domain, String email, Discover discover) {
List<EmailProvider> result = new ArrayList<>(); List<EmailProvider> result = new ArrayList<>();
try { try {
@ -494,10 +489,13 @@ public class EmailProvider implements Parcelable {
if (eventType == XmlPullParser.TEXT) { if (eventType == XmlPullParser.TEXT) {
String host = xml.getText(); String host = xml.getText();
Log.i("Host=" + host); Log.i("Host=" + host);
if (imap) if (imap) {
provider.imap.score = 20;
provider.imap.host = host; provider.imap.host = host;
else if (smtp) } else if (smtp) {
provider.smtp.score = 20;
provider.smtp.host = host; provider.smtp.host = host;
}
} }
continue; continue;
@ -621,20 +619,20 @@ public class EmailProvider implements Parcelable {
if (records.length == 0) if (records.length == 0)
throw new UnknownHostException(domain); throw new UnknownHostException(domain);
// ... service is not supported at all at a particular domain by setting the target of an SRV RR to "." // ... service is not supported at all at a particular domain by setting the target of an SRV RR to "."
provider.imap.score = 50;
provider.imap.host = records[0].name; provider.imap.host = records[0].name;
provider.imap.port = records[0].port; provider.imap.port = records[0].port;
provider.imap.starttls = false; provider.imap.starttls = false;
provider.imap.checkCertificate(context);
EntityLog.log(context, "_imaps._tcp." + domain + "=" + provider.imap); EntityLog.log(context, "_imaps._tcp." + domain + "=" + provider.imap);
} catch (UnknownHostException ignored) { } catch (UnknownHostException ignored) {
// Identifies an IMAP server that MAY ... require the MUA to use the "STARTTLS" command // Identifies an IMAP server that MAY ... require the MUA to use the "STARTTLS" command
DnsHelper.DnsRecord[] records = DnsHelper.lookup(context, "_imap._tcp." + domain, "srv"); DnsHelper.DnsRecord[] records = DnsHelper.lookup(context, "_imap._tcp." + domain, "srv");
if (records.length == 0) if (records.length == 0)
throw new UnknownHostException(domain); throw new UnknownHostException(domain);
provider.imap.score = 50;
provider.imap.host = records[0].name; provider.imap.host = records[0].name;
provider.imap.port = records[0].port; provider.imap.port = records[0].port;
provider.imap.starttls = (provider.imap.port == 143); provider.imap.starttls = (provider.imap.port == 143);
provider.imap.checkCertificate(context);
EntityLog.log(context, "_imap._tcp." + domain + "=" + provider.imap); EntityLog.log(context, "_imap._tcp." + domain + "=" + provider.imap);
} }
} }
@ -645,20 +643,20 @@ public class EmailProvider implements Parcelable {
DnsHelper.DnsRecord[] records = DnsHelper.lookup(context, "_submission._tcp." + domain, "srv"); DnsHelper.DnsRecord[] records = DnsHelper.lookup(context, "_submission._tcp." + domain, "srv");
if (records.length == 0) if (records.length == 0)
throw new UnknownHostException(domain); throw new UnknownHostException(domain);
provider.smtp.score = 50;
provider.smtp.host = records[0].name; provider.smtp.host = records[0].name;
provider.smtp.port = records[0].port; provider.smtp.port = records[0].port;
provider.smtp.starttls = (provider.smtp.port == 587); provider.smtp.starttls = (provider.smtp.port == 587);
provider.smtp.checkCertificate(context);
EntityLog.log(context, "_submission._tcp." + domain + "=" + provider.smtp); EntityLog.log(context, "_submission._tcp." + domain + "=" + provider.smtp);
} catch (UnknownHostException ignored) { } catch (UnknownHostException ignored) {
// https://tools.ietf.org/html/rfc8314 // https://tools.ietf.org/html/rfc8314
DnsHelper.DnsRecord[] records = DnsHelper.lookup(context, "_submissions._tcp." + domain, "srv"); DnsHelper.DnsRecord[] records = DnsHelper.lookup(context, "_submissions._tcp." + domain, "srv");
if (records.length == 0) if (records.length == 0)
throw new UnknownHostException(domain); throw new UnknownHostException(domain);
provider.smtp.score = 50;
provider.smtp.host = records[0].name; provider.smtp.host = records[0].name;
provider.smtp.port = records[0].port; provider.smtp.port = records[0].port;
provider.smtp.starttls = false; provider.smtp.starttls = false;
provider.smtp.checkCertificate(context);
EntityLog.log(context, "_submissions._tcp." + domain + "=" + provider.smtp); EntityLog.log(context, "_submissions._tcp." + domain + "=" + provider.smtp);
} }
@ -770,6 +768,7 @@ public class EmailProvider implements Parcelable {
imap.host = in.readString(); imap.host = in.readString();
imap.port = in.readInt(); imap.port = in.readInt();
imap.starttls = (in.readInt() != 0); imap.starttls = (in.readInt() != 0);
imap.score = in.readInt();
} }
if (in.readInt() == 0) if (in.readInt() == 0)
@ -779,6 +778,7 @@ public class EmailProvider implements Parcelable {
smtp.host = in.readString(); smtp.host = in.readString();
smtp.port = in.readInt(); smtp.port = in.readInt();
smtp.starttls = (in.readInt() != 0); smtp.starttls = (in.readInt() != 0);
smtp.score = in.readInt();
} }
appPassword = (in.readInt() != 0); appPassword = (in.readInt() != 0);
@ -794,6 +794,7 @@ public class EmailProvider implements Parcelable {
dest.writeString(imap.host); dest.writeString(imap.host);
dest.writeInt(imap.port); dest.writeInt(imap.port);
dest.writeInt(imap.starttls ? 1 : 0); dest.writeInt(imap.starttls ? 1 : 0);
dest.writeInt(imap.score);
} }
dest.writeInt(smtp == null ? 0 : 1); dest.writeInt(smtp == null ? 0 : 1);
@ -801,6 +802,7 @@ public class EmailProvider implements Parcelable {
dest.writeString(smtp.host); dest.writeString(smtp.host);
dest.writeInt(smtp.port); dest.writeInt(smtp.port);
dest.writeInt(smtp.starttls ? 1 : 0); dest.writeInt(smtp.starttls ? 1 : 0);
dest.writeInt(smtp.score);
} }
dest.writeInt(appPassword ? 1 : 0); dest.writeInt(appPassword ? 1 : 0);
@ -857,6 +859,12 @@ public class EmailProvider implements Parcelable {
public int port; public int port;
public boolean starttls; public boolean starttls;
// Scores:
// 10 from scan; +2 trusted; +1 trusted alt
// 20 from autoconfig
// 50 from DNS
// 100 from profile
private int score = 0; private int score = 0;
private Future<Boolean> isReachable; private Future<Boolean> isReachable;
@ -864,6 +872,7 @@ public class EmailProvider implements Parcelable {
} }
private Server(Context context, String domain, String prefix, int port, boolean starttls) { private Server(Context context, String domain, String prefix, int port, boolean starttls) {
this.score = 10;
this.host = (prefix == null ? "" : prefix + ".") + domain; this.host = (prefix == null ? "" : prefix + ".") + domain;
this.port = port; this.port = port;
this.starttls = starttls; this.starttls = starttls;
@ -1069,7 +1078,7 @@ public class EmailProvider implements Parcelable {
@NonNull @NonNull
@Override @Override
public String toString() { public String toString() {
return host + ":" + port + (starttls ? " starttls" : " ssl/tls"); return host + ":" + port + (starttls ? " starttls" : " ssl/tls") + " " + score;
} }
} }