Separated IMAP/SMTP server discovery

This commit is contained in:
M66B 2019-08-17 11:26:47 +02:00
parent 24bf725ea0
commit f8a9f3326d
4 changed files with 103 additions and 74 deletions

View File

@ -68,6 +68,8 @@ public class EmailProvider {
public UserType user = UserType.EMAIL; public UserType user = UserType.EMAIL;
public StringBuilder documentation; // html public StringBuilder documentation; // html
enum Discover {ALL, IMAP, SMTP}
enum UserType {LOCAL, EMAIL} enum UserType {LOCAL, EMAIL}
private static final int DNS_TIMEOUT = 5 * 1000; // milliseconds private static final int DNS_TIMEOUT = 5 * 1000; // milliseconds
@ -143,8 +145,8 @@ public class EmailProvider {
return result; return result;
} }
static EmailProvider fromDomain(Context context, String domain) throws IOException { static EmailProvider fromDomain(Context context, String domain, Discover discover) throws IOException {
EmailProvider autoconfig = _fromDomain(context, domain); EmailProvider autoconfig = _fromDomain(context, domain, discover);
// Always prefer built-in profiles // Always prefer built-in profiles
// - ISPDB is not always correct // - ISPDB is not always correct
@ -160,11 +162,11 @@ public class EmailProvider {
return autoconfig; return autoconfig;
} }
private static EmailProvider _fromDomain(Context context, String domain) throws IOException { private static EmailProvider _fromDomain(Context context, String domain, Discover discover) throws IOException {
try { try {
// Assume the provider knows best // Assume the provider knows best
Log.i("Provider from DNS domain=" + domain); Log.i("Provider from DNS domain=" + domain);
return fromDNS(context, domain); return fromDNS(context, domain, discover);
} catch (Throwable ex) { } catch (Throwable ex) {
Log.w(ex); Log.w(ex);
try { try {
@ -176,7 +178,7 @@ public class EmailProvider {
try { try {
// Scan ports // Scan ports
Log.i("Provider from template domain=" + domain); Log.i("Provider from template domain=" + domain);
return fromTemplate(context, domain); return fromTemplate(context, domain, discover);
} catch (Throwable ex2) { } catch (Throwable ex2) {
Log.w(ex2); Log.w(ex2);
throw new UnknownHostException(context.getString(R.string.title_setup_no_settings, domain)); throw new UnknownHostException(context.getString(R.string.title_setup_no_settings, domain));
@ -365,38 +367,43 @@ public class EmailProvider {
return provider; return provider;
} }
private static EmailProvider fromDNS(Context context, String domain) throws TextParseException, UnknownHostException { private static EmailProvider fromDNS(Context context, String domain, Discover discover) throws TextParseException, UnknownHostException {
// https://tools.ietf.org/html/rfc6186 // https://tools.ietf.org/html/rfc6186
SRVRecord imap; EmailProvider provider = new EmailProvider(domain);
boolean starttls;
try { if (discover == Discover.ALL || discover == Discover.IMAP) {
// Identifies an IMAP server where TLS is initiated directly upon connection to the IMAP server. SRVRecord imap;
imap = lookup(context, "_imaps._tcp." + domain); boolean starttls;
// ... service is not supported at all at a particular domain by setting the target of an SRV RR to "." try {
if (TextUtils.isEmpty(imap.getTarget().toString(true))) // Identifies an IMAP server where TLS is initiated directly upon connection to the IMAP server.
throw new UnknownHostException(imap.toString()); imap = lookup(context, "_imaps._tcp." + domain);
starttls = false; // ... service is not supported at all at a particular domain by setting the target of an SRV RR to "."
} catch (UnknownHostException ex) { if (TextUtils.isEmpty(imap.getTarget().toString(true)))
// Identifies an IMAP server that MAY ... require the MUA to use the "STARTTLS" command throw new UnknownHostException(imap.toString());
imap = lookup(context, "_imap._tcp." + domain); starttls = false;
if (TextUtils.isEmpty(imap.getTarget().toString(true))) } catch (UnknownHostException ex) {
throw new UnknownHostException(imap.toString()); // Identifies an IMAP server that MAY ... require the MUA to use the "STARTTLS" command
starttls = (imap.getPort() == 143); imap = lookup(context, "_imap._tcp." + domain);
if (TextUtils.isEmpty(imap.getTarget().toString(true)))
throw new UnknownHostException(imap.toString());
starttls = (imap.getPort() == 143);
}
provider.imap_host = imap.getTarget().toString(true);
provider.imap_port = imap.getPort();
provider.imap_starttls = starttls;
} }
// Note that this covers connections both with and without Transport Layer Security (TLS) if (discover == Discover.ALL || discover == Discover.SMTP) {
SRVRecord smtp = lookup(context, "_submission._tcp." + domain); // Note that this covers connections both with and without Transport Layer Security (TLS)
if (TextUtils.isEmpty(smtp.getTarget().toString(true))) SRVRecord smtp = lookup(context, "_submission._tcp." + domain);
throw new UnknownHostException(smtp.toString()); if (TextUtils.isEmpty(smtp.getTarget().toString(true)))
throw new UnknownHostException(smtp.toString());
EmailProvider provider = new EmailProvider(domain); provider.smtp_host = smtp.getTarget().toString(true);
provider.imap_host = imap.getTarget().toString(true); provider.smtp_port = smtp.getPort();
provider.imap_port = imap.getPort(); provider.smtp_starttls = (provider.smtp_port == 587);
provider.imap_starttls = starttls; }
provider.smtp_host = smtp.getTarget().toString(true);
provider.smtp_port = smtp.getPort();
provider.smtp_starttls = (provider.smtp_port == 587);
return provider; return provider;
} }
@ -444,52 +451,74 @@ public class EmailProvider {
} }
} }
private static EmailProvider fromTemplate(Context context, String domain) private static EmailProvider fromTemplate(Context context, String domain, Discover discover)
throws ExecutionException, InterruptedException, UnknownHostException { throws ExecutionException, InterruptedException, UnknownHostException {
List<Server> imaps = new ArrayList<>(); Server imap = null;
List<Server> smtps = new ArrayList<>(); Server smtp = null;
// SSL if (discover == Discover.ALL || discover == Discover.IMAP) {
imaps.add(new Server(domain, null, 993)); List<Server> imaps = new ArrayList<>();
imaps.add(new Server(domain, "imap", 993)); // SSL
imaps.add(new Server(domain, "mail", 993)); imaps.add(new Server(domain, null, 993));
imaps.add(new Server(domain, "mx", 993)); imaps.add(new Server(domain, "imap", 993));
// STARTTLS imaps.add(new Server(domain, "mail", 993));
imaps.add(new Server(domain, null, 143)); imaps.add(new Server(domain, "mx", 993));
imaps.add(new Server(domain, "imap", 143)); // STARTTLS
imaps.add(new Server(domain, "mail", 143)); imaps.add(new Server(domain, null, 143));
imaps.add(new Server(domain, "mx", 143)); imaps.add(new Server(domain, "imap", 143));
imaps.add(new Server(domain, "mail", 143));
imaps.add(new Server(domain, "mx", 143));
// SSL for (Server server : imaps)
smtps.add(new Server(domain, null, 465)); if (server.reachable.get()) {
smtps.add(new Server(domain, "smtp", 465)); imap = server;
smtps.add(new Server(domain, "mail", 465)); break;
smtps.add(new Server(domain, "mx", 465)); }
// STARTTLS
smtps.add(new Server(domain, null, 587));
smtps.add(new Server(domain, "smtp", 587));
smtps.add(new Server(domain, "mail", 587));
smtps.add(new Server(domain, "mx", 587));
for (Server imap : imaps) if (imap == null)
if (imap.reachable.get()) throw new UnknownHostException(domain + " template");
for (Server smtp : smtps) }
if (smtp.reachable.get()) {
EmailProvider provider = new EmailProvider();
provider.name = domain;
provider.imap_host = imap.host; if (discover == Discover.ALL || discover == Discover.SMTP) {
provider.imap_port = imap.port; List<Server> smtps = new ArrayList<>();
provider.imap_starttls = (imap.port == 143); // SSL
smtps.add(new Server(domain, null, 465));
smtps.add(new Server(domain, "smtp", 465));
smtps.add(new Server(domain, "mail", 465));
smtps.add(new Server(domain, "mx", 465));
// STARTTLS
smtps.add(new Server(domain, null, 587));
smtps.add(new Server(domain, "smtp", 587));
smtps.add(new Server(domain, "mail", 587));
smtps.add(new Server(domain, "mx", 587));
provider.smtp_host = smtp.host; for (Server server : smtps)
provider.smtp_port = smtp.port; if (server.reachable.get()) {
provider.smtp_starttls = (smtp.port == 587); smtp = server;
break;
}
return provider; if (smtp == null)
} throw new UnknownHostException(domain + " template");
}
throw new UnknownHostException(domain + " template");
EmailProvider provider = new EmailProvider();
provider.name = domain;
if (imap != null) {
provider.imap_host = imap.host;
provider.imap_port = imap.port;
provider.imap_starttls = (imap.port == 143);
}
if (smtp != null) {
provider.smtp_host = smtp.host;
provider.smtp_port = smtp.port;
provider.smtp_starttls = (smtp.port == 587);
}
return provider;
} }
private static void addDocumentation(EmailProvider provider, String href, String title) { private static void addDocumentation(EmailProvider provider, String href, String title) {

View File

@ -443,7 +443,7 @@ public class FragmentAccount extends FragmentBase {
@Override @Override
protected EmailProvider onExecute(Context context, Bundle args) throws Throwable { protected EmailProvider onExecute(Context context, Bundle args) throws Throwable {
String domain = args.getString("domain"); String domain = args.getString("domain");
return EmailProvider.fromDomain(context, domain); return EmailProvider.fromDomain(context, domain, EmailProvider.Discover.IMAP);
} }
@Override @Override

View File

@ -472,7 +472,7 @@ public class FragmentIdentity extends FragmentBase {
@Override @Override
protected EmailProvider onExecute(Context context, Bundle args) throws Throwable { protected EmailProvider onExecute(Context context, Bundle args) throws Throwable {
String domain = args.getString("domain"); String domain = args.getString("domain");
return EmailProvider.fromDomain(context, domain); return EmailProvider.fromDomain(context, domain, EmailProvider.Discover.SMTP);
} }
@Override @Override

View File

@ -235,7 +235,7 @@ public class FragmentQuickSetup extends FragmentBase {
throw new IllegalArgumentException(context.getString(R.string.title_email_invalid)); throw new IllegalArgumentException(context.getString(R.string.title_email_invalid));
String[] dparts = email.split("@"); String[] dparts = email.split("@");
EmailProvider provider = EmailProvider.fromDomain(context, dparts[1]); EmailProvider provider = EmailProvider.fromDomain(context, dparts[1], EmailProvider.Discover.ALL);
if (provider.link != null) if (provider.link != null)
args.putString("link", provider.link); args.putString("link", provider.link);