Refactoring

This commit is contained in:
M66B 2020-04-15 20:08:17 +02:00
parent c239a1245d
commit f1e9b23a96
8 changed files with 209 additions and 159 deletions

View File

@ -1,10 +1,28 @@
package eu.faircode.email; package eu.faircode.email;
/*
This file is part of FairEmail.
FairEmail is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
FairEmail is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with FairEmail. If not, see <http://www.gnu.org/licenses/>.
Copyright 2018-2020 by Marcel Bokhorst (M66B)
*/
import android.accounts.AccountsException; import android.accounts.AccountsException;
import android.content.Context; import android.content.Context;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.net.ConnectivityManager; import android.net.ConnectivityManager;
import android.net.LinkProperties;
import android.net.Network; import android.net.Network;
import android.net.NetworkCapabilities; import android.net.NetworkCapabilities;
import android.net.NetworkInfo; import android.net.NetworkInfo;
@ -17,27 +35,13 @@ import androidx.preference.PreferenceManager;
import com.sun.mail.iap.ConnectionException; import com.sun.mail.iap.ConnectionException;
import org.xbill.DNS.Lookup;
import org.xbill.DNS.MXRecord;
import org.xbill.DNS.Record;
import org.xbill.DNS.SimpleResolver;
import org.xbill.DNS.Type;
import java.io.IOException; import java.io.IOException;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
import javax.mail.Address;
import javax.mail.internet.InternetAddress;
public class ConnectionHelper { public class ConnectionHelper {
// https://dns.watch/
private static final String DEFAULT_DNS = "84.200.69.80";
// Roam like at home // Roam like at home
// https://en.wikipedia.org/wiki/European_Union_roaming_regulations // https://en.wikipedia.org/wiki/European_Union_roaming_regulations
private static final List<String> RLAH_COUNTRY_CODES = Collections.unmodifiableList(Arrays.asList( private static final List<String> RLAH_COUNTRY_CODES = Collections.unmodifiableList(Arrays.asList(
@ -332,106 +336,4 @@ public class ConnectionHelper {
return Settings.System.getInt(context.getContentResolver(), return Settings.System.getInt(context.getContentResolver(),
Settings.Global.AIRPLANE_MODE_ON, 0) != 0; Settings.Global.AIRPLANE_MODE_ON, 0) != 0;
} }
static String getDnsServer(Context context) {
ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
if (cm == null)
return DEFAULT_DNS;
LinkProperties props = null;
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M)
for (Network network : cm.getAllNetworks()) {
NetworkInfo ni = cm.getNetworkInfo(network);
if (ni != null && ni.isConnected()) {
props = cm.getLinkProperties(network);
Log.i("Old props=" + props);
break;
}
}
else {
Network active = cm.getActiveNetwork();
if (active == null)
return DEFAULT_DNS;
props = cm.getLinkProperties(active);
Log.i("New props=" + props);
}
if (props == null)
return DEFAULT_DNS;
List<InetAddress> dns = props.getDnsServers();
if (dns.size() == 0)
return DEFAULT_DNS;
else
return dns.get(0).getHostAddress();
}
static boolean lookupMx(Address[] addresses, Context context) throws UnknownHostException {
boolean ok = true;
if (addresses != null)
for (Address address : addresses)
try {
String email = ((InternetAddress) address).getAddress();
if (email == null)
continue;
int d = email.lastIndexOf("@");
if (d < 0)
continue;
String domain = email.substring(d + 1);
Lookup lookup = new Lookup(domain, Type.MX);
SimpleResolver resolver = new SimpleResolver(ConnectionHelper.getDnsServer(context));
lookup.setResolver(resolver);
Log.i("Lookup MX=" + domain + " @" + resolver.getAddress());
lookup.run();
if (lookup.getResult() == Lookup.HOST_NOT_FOUND ||
lookup.getResult() == Lookup.TYPE_NOT_FOUND) {
Log.i("Lookup MX=" + domain + " result=" + lookup.getErrorString());
throw new UnknownHostException(context.getString(R.string.title_no_server, domain));
} else if (lookup.getResult() != Lookup.SUCCESSFUL)
ok = false;
} catch (UnknownHostException ex) {
throw ex;
} catch (Throwable ex) {
Log.e(ex);
ok = false;
}
return ok;
}
static InetAddress lookupMx(String domain, Context context) {
try {
Lookup lookup = new Lookup(domain, Type.MX);
SimpleResolver resolver = new SimpleResolver(getDnsServer(context));
lookup.setResolver(resolver);
Log.i("Lookup MX=" + domain + " @" + resolver.getAddress());
lookup.run();
if (lookup.getResult() == Lookup.SUCCESSFUL) {
Record[] answers = lookup.getAnswers();
if (answers != null && answers.length > 0 && answers[0] instanceof MXRecord) {
MXRecord mx = (MXRecord) answers[0];
return InetAddress.getByName(mx.getTarget().toString(true));
}
}
} catch (Throwable ex) {
Log.w(ex);
}
return null;
}
static String getParentDomain(String host) {
if (host != null) {
String[] h = host.split("\\.");
if (h.length >= 2)
return h[h.length - 2] + "." + h[h.length - 1];
}
return host;
}
} }

View File

@ -2284,9 +2284,9 @@ class Core {
boolean check_mx = prefs.getBoolean("check_mx", false); boolean check_mx = prefs.getBoolean("check_mx", false);
if (check_mx) if (check_mx)
try { try {
if (ConnectionHelper.lookupMx( if (DNSHelper.lookupMx(
message.reply == null || message.reply.length == 0 context, message.reply == null || message.reply.length == 0
? message.from : message.reply, context)) ? message.from : message.reply))
message.mx = true; message.mx = true;
} catch (UnknownHostException ex) { } catch (UnknownHostException ex) {
message.mx = false; message.mx = false;
@ -2347,12 +2347,12 @@ class Core {
String r = ((InternetAddress) reply).getAddress(); String r = ((InternetAddress) reply).getAddress();
int rat = (r == null ? -1 : r.indexOf('@')); int rat = (r == null ? -1 : r.indexOf('@'));
if (rat > 0) { if (rat > 0) {
String rdomain = ConnectionHelper.getParentDomain(r.substring(rat + 1)); String rdomain = DNSHelper.getParentDomain(r.substring(rat + 1));
for (Address from : message.from) { for (Address from : message.from) {
String f = ((InternetAddress) from).getAddress(); String f = ((InternetAddress) from).getAddress();
int fat = (f == null ? -1 : f.indexOf('@')); int fat = (f == null ? -1 : f.indexOf('@'));
if (fat > 0) { if (fat > 0) {
String fdomain = ConnectionHelper.getParentDomain(f.substring(fat + 1)); String fdomain = DNSHelper.getParentDomain(f.substring(fat + 1));
if (!rdomain.equalsIgnoreCase(fdomain)) { if (!rdomain.equalsIgnoreCase(fdomain)) {
if (message.warning == null) if (message.warning == null)
message.warning = context.getString(R.string.title_reply_domain, fdomain, rdomain); message.warning = context.getString(R.string.title_reply_domain, fdomain, rdomain);

View File

@ -0,0 +1,175 @@
package eu.faircode.email;
/*
This file is part of FairEmail.
FairEmail is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
FairEmail is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with FairEmail. If not, see <http://www.gnu.org/licenses/>.
Copyright 2018-2020 by Marcel Bokhorst (M66B)
*/
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.LinkProperties;
import android.net.Network;
import android.net.NetworkInfo;
import android.os.Build;
import androidx.annotation.NonNull;
import org.xbill.DNS.Lookup;
import org.xbill.DNS.MXRecord;
import org.xbill.DNS.Record;
import org.xbill.DNS.SimpleResolver;
import org.xbill.DNS.TextParseException;
import org.xbill.DNS.Type;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.List;
import javax.mail.Address;
import javax.mail.internet.InternetAddress;
public class DNSHelper {
// https://dns.watch/
private static final String DEFAULT_DNS = "84.200.69.80";
static boolean lookupMx(Context context, Address[] addresses) throws UnknownHostException {
boolean ok = true;
if (addresses != null)
for (Address address : addresses)
try {
String email = ((InternetAddress) address).getAddress();
if (email == null)
continue;
int d = email.lastIndexOf("@");
if (d < 0)
continue;
String domain = email.substring(d + 1);
Lookup lookup = new Lookup(domain, Type.MX);
SimpleResolver resolver = new SimpleResolver(getDnsServer(context));
lookup.setResolver(resolver);
Log.i("Lookup MX=" + domain + " @" + resolver.getAddress());
lookup.run();
if (lookup.getResult() == Lookup.HOST_NOT_FOUND ||
lookup.getResult() == Lookup.TYPE_NOT_FOUND) {
Log.i("Lookup MX=" + domain + " result=" + lookup.getErrorString());
throw new UnknownHostException(context.getString(R.string.title_no_server, domain));
} else if (lookup.getResult() != Lookup.SUCCESSFUL)
ok = false;
} catch (UnknownHostException ex) {
throw ex;
} catch (Throwable ex) {
Log.e(ex);
ok = false;
}
return ok;
}
static InetAddress lookupMx(Context context, String domain) {
try {
Lookup lookup = new Lookup(domain, Type.MX);
SimpleResolver resolver = new SimpleResolver(getDnsServer(context));
lookup.setResolver(resolver);
Log.i("Lookup MX=" + domain + " @" + resolver.getAddress());
lookup.run();
if (lookup.getResult() == Lookup.SUCCESSFUL) {
Record[] answers = lookup.getAnswers();
if (answers != null && answers.length > 0 && answers[0] instanceof MXRecord) {
MXRecord mx = (MXRecord) answers[0];
return InetAddress.getByName(mx.getTarget().toString(true));
}
}
} catch (Throwable ex) {
Log.w(ex);
}
return null;
}
@NonNull
static Record[] lookup(Context context, String name, int type) throws TextParseException, UnknownHostException {
Lookup lookup = new Lookup(name, type);
SimpleResolver resolver = new SimpleResolver(getDnsServer(context));
lookup.setResolver(resolver);
Log.i("Lookup name=" + name + " @" + resolver.getAddress() + " type=" + type);
Record[] records = lookup.run();
if (lookup.getResult() != Lookup.SUCCESSFUL)
if (lookup.getResult() == Lookup.HOST_NOT_FOUND ||
lookup.getResult() == Lookup.TYPE_NOT_FOUND)
throw new UnknownHostException(name);
else
throw new UnknownHostException(lookup.getErrorString());
if (records.length == 0)
throw new UnknownHostException(name);
for (Record record : records)
Log.i("Found record=" + record);
return records;
}
static String getParentDomain(String host) {
if (host != null) {
String[] h = host.split("\\.");
if (h.length >= 2)
return h[h.length - 2] + "." + h[h.length - 1];
}
return host;
}
private static String getDnsServer(Context context) {
ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
if (cm == null)
return DEFAULT_DNS;
LinkProperties props = null;
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M)
for (Network network : cm.getAllNetworks()) {
NetworkInfo ni = cm.getNetworkInfo(network);
if (ni != null && ni.isConnected()) {
props = cm.getLinkProperties(network);
Log.i("Old props=" + props);
break;
}
}
else {
Network active = cm.getActiveNetwork();
if (active == null)
return DEFAULT_DNS;
props = cm.getLinkProperties(active);
Log.i("New props=" + props);
}
if (props == null)
return DEFAULT_DNS;
List<InetAddress> dns = props.getDnsServers();
if (dns.size() == 0)
return DEFAULT_DNS;
else
return dns.get(0).getHostAddress();
}
}

View File

@ -25,11 +25,9 @@ import android.text.TextUtils;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import org.xbill.DNS.Lookup;
import org.xbill.DNS.MXRecord; import org.xbill.DNS.MXRecord;
import org.xbill.DNS.Record; import org.xbill.DNS.Record;
import org.xbill.DNS.SRVRecord; import org.xbill.DNS.SRVRecord;
import org.xbill.DNS.SimpleResolver;
import org.xbill.DNS.TextParseException; import org.xbill.DNS.TextParseException;
import org.xbill.DNS.Type; import org.xbill.DNS.Type;
import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParser;
@ -228,7 +226,7 @@ public class EmailProvider {
try { try {
// Retry at MX server addresses // Retry at MX server addresses
Record[] records = lookupDNS(context, domain, Type.MX); Record[] records = DNSHelper.lookup(context, domain, Type.MX);
for (Record record : records) { for (Record record : records) {
String target = ((MXRecord) record).getTarget().toString(true); String target = ((MXRecord) record).getTarget().toString(true);
while (autoconfig == null && target != null && target.indexOf('.') > 0) { while (autoconfig == null && target != null && target.indexOf('.') > 0) {
@ -507,7 +505,7 @@ public class EmailProvider {
if (discover == Discover.ALL || discover == Discover.IMAP) { if (discover == Discover.ALL || discover == Discover.IMAP) {
try { try {
// Identifies an IMAP server where TLS is initiated directly upon connection to the IMAP server. // Identifies an IMAP server where TLS is initiated directly upon connection to the IMAP server.
Record[] records = lookupDNS(context, "_imaps._tcp." + domain, Type.SRV); Record[] records = DNSHelper.lookup(context, "_imaps._tcp." + domain, Type.SRV);
// ... 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 "."
SRVRecord srv = (SRVRecord) records[0]; SRVRecord srv = (SRVRecord) records[0];
provider.imap.host = srv.getTarget().toString(true); provider.imap.host = srv.getTarget().toString(true);
@ -515,7 +513,7 @@ public class EmailProvider {
provider.imap.starttls = false; provider.imap.starttls = false;
} catch (UnknownHostException ex) { } catch (UnknownHostException ex) {
// 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
Record[] records = lookupDNS(context, "_imap._tcp." + domain, Type.SRV); Record[] records = DNSHelper.lookup(context, "_imap._tcp." + domain, Type.SRV);
SRVRecord srv = (SRVRecord) records[0]; SRVRecord srv = (SRVRecord) records[0];
provider.imap.host = srv.getTarget().toString(true); provider.imap.host = srv.getTarget().toString(true);
provider.imap.port = srv.getPort(); provider.imap.port = srv.getPort();
@ -525,7 +523,7 @@ public class EmailProvider {
if (discover == Discover.ALL || discover == Discover.SMTP) { if (discover == Discover.ALL || discover == Discover.SMTP) {
// Note that this covers connections both with and without Transport Layer Security (TLS) // Note that this covers connections both with and without Transport Layer Security (TLS)
Record[] records = lookupDNS(context, "_submission._tcp." + domain, Type.SRV); Record[] records = DNSHelper.lookup(context, "_submission._tcp." + domain, Type.SRV);
SRVRecord srv = (SRVRecord) records[0]; SRVRecord srv = (SRVRecord) records[0];
provider.smtp.host = srv.getTarget().toString(true); provider.smtp.host = srv.getTarget().toString(true);
provider.smtp.port = srv.getPort(); provider.smtp.port = srv.getPort();
@ -618,31 +616,6 @@ public class EmailProvider {
provider.documentation.append("<a href=\"").append(href).append("\">").append(title).append("</a>"); provider.documentation.append("<a href=\"").append(href).append("\">").append(title).append("</a>");
} }
@NonNull
private static Record[] lookupDNS(Context context, String name, int type) throws TextParseException, UnknownHostException {
Lookup lookup = new Lookup(name, type);
SimpleResolver resolver = new SimpleResolver(ConnectionHelper.getDnsServer(context));
lookup.setResolver(resolver);
Log.i("Lookup name=" + name + " @" + resolver.getAddress() + " type=" + type);
Record[] records = lookup.run();
if (lookup.getResult() != Lookup.SUCCESSFUL)
if (lookup.getResult() == Lookup.HOST_NOT_FOUND ||
lookup.getResult() == Lookup.TYPE_NOT_FOUND)
throw new UnknownHostException(name);
else
throw new UnknownHostException(lookup.getErrorString());
if (records.length == 0)
throw new UnknownHostException(name);
for (Record record : records)
Log.i("Found record=" + record);
return records;
}
@NonNull @NonNull
@Override @Override
public String toString() { public String toString() {

View File

@ -1077,7 +1077,7 @@ public class FragmentAccount extends FragmentBase {
db.beginTransaction(); db.beginTransaction();
if (account != null && !account.password.equals(password)) { if (account != null && !account.password.equals(password)) {
String domain = ConnectionHelper.getParentDomain(account.host); String domain = DNSHelper.getParentDomain(account.host);
String match = (Objects.equals(account.host, domain) ? account.host : "%." + domain); String match = (Objects.equals(account.host, domain) ? account.host : "%." + domain);
int count = db.identity().setIdentityPassword(account.id, account.user, password, match); int count = db.identity().setIdentityPassword(account.id, account.user, password, match);
Log.i("Updated passwords=" + count + " match=" + match); Log.i("Updated passwords=" + count + " match=" + match);

View File

@ -3818,7 +3818,7 @@ public class FragmentCompose extends FragmentBase {
for (InternetAddress address : ato) for (InternetAddress address : ato)
address.validate(); address.validate();
if (lookup_mx) if (lookup_mx)
ConnectionHelper.lookupMx(ato, context); DNSHelper.lookupMx(context, ato);
} }
} catch (AddressException ex) { } catch (AddressException ex) {
throw new AddressException(context.getString(R.string.title_address_parse_error, throw new AddressException(context.getString(R.string.title_address_parse_error,
@ -3832,7 +3832,7 @@ public class FragmentCompose extends FragmentBase {
for (InternetAddress address : acc) for (InternetAddress address : acc)
address.validate(); address.validate();
if (lookup_mx) if (lookup_mx)
ConnectionHelper.lookupMx(acc, context); DNSHelper.lookupMx(context, acc);
} }
} catch (AddressException ex) { } catch (AddressException ex) {
throw new AddressException(context.getString(R.string.title_address_parse_error, throw new AddressException(context.getString(R.string.title_address_parse_error,
@ -3846,7 +3846,7 @@ public class FragmentCompose extends FragmentBase {
for (InternetAddress address : abcc) for (InternetAddress address : abcc)
address.validate(); address.validate();
if (lookup_mx) if (lookup_mx)
ConnectionHelper.lookupMx(abcc, context); DNSHelper.lookupMx(context, abcc);
} }
} catch (AddressException ex) { } catch (AddressException ex) {
throw new AddressException(context.getString(R.string.title_address_parse_error, throw new AddressException(context.getString(R.string.title_address_parse_error,

View File

@ -351,7 +351,7 @@ public class FragmentPop extends FragmentBase {
db.beginTransaction(); db.beginTransaction();
if (account != null && !account.password.equals(password)) { if (account != null && !account.password.equals(password)) {
String domain = ConnectionHelper.getParentDomain(account.host); String domain = DNSHelper.getParentDomain(account.host);
String match = (Objects.equals(account.host, domain) ? account.host : "%." + domain); String match = (Objects.equals(account.host, domain) ? account.host : "%." + domain);
int count = db.identity().setIdentityPassword(account.id, account.user, password, match); int count = db.identity().setIdentityPassword(account.id, account.user, password, match);
Log.i("Updated passwords=" + count + " match=" + match); Log.i("Updated passwords=" + count + " match=" + match);

View File

@ -47,7 +47,7 @@ public class IPInfo {
if (to == null || !to.contains("@")) if (to == null || !to.contains("@"))
throw new UnknownHostException(); throw new UnknownHostException();
String domain = to.substring(to.indexOf('@') + 1); String domain = to.substring(to.indexOf('@') + 1);
InetAddress address = ConnectionHelper.lookupMx(domain, context); InetAddress address = DNSHelper.lookupMx(context, domain);
if (address == null) if (address == null)
throw new UnknownHostException(); throw new UnknownHostException();
return new Pair<>(domain, getOrganization(address)); return new Pair<>(domain, getOrganization(address));