Added Microsoft auto discovery

This commit is contained in:
M66B 2023-06-14 10:07:35 +02:00
parent 3db629e3ef
commit 2d7a53299d
4 changed files with 326 additions and 1 deletions

View File

@ -28,4 +28,8 @@ public class Misc {
public static List<String> getISPDBUrls(Context context, String domain, String email) {
return new ArrayList<>();
}
}
public static List<String> getMSUrls(Context context, String domain, String email) {
return new ArrayList<>();
}
}

View File

@ -49,4 +49,11 @@ public class Misc {
return Collections.unmodifiableList(result);
}
public static List<String> getMSUrls(Context context, String domain, String email) {
return Collections.unmodifiableList(Arrays.asList(
"https://" + domain + "/autodiscover/autodiscover.xml",
"https://autodiscover." + domain + "/autodiscover/autodiscover.xml"
));
}
}

View File

@ -22,6 +22,7 @@ package eu.faircode.email;
import static android.system.OsConstants.ECONNREFUSED;
import android.content.Context;
import android.content.SharedPreferences;
import android.os.Parcel;
import android.os.Parcelable;
import android.system.ErrnoException;
@ -29,6 +30,7 @@ import android.text.TextUtils;
import android.util.Xml;
import androidx.annotation.NonNull;
import androidx.preference.PreferenceManager;
import com.sun.mail.util.LineInputStream;
@ -112,6 +114,7 @@ public class EmailProvider implements Parcelable {
private static final int SCAN_TIMEOUT = 10 * 1000; // milliseconds
private static final int ISPDB_TIMEOUT = 10 * 1000; // milliseconds
private static final int MS_TIMEOUT = 10 * 1000; // milliseconds
private static final List<String> PROPRIETARY = Collections.unmodifiableList(Arrays.asList(
"protonmail.ch",
@ -627,6 +630,14 @@ public class EmailProvider implements Parcelable {
Log.w(ex);
}
try {
// Check Microsoft auto discovery
Log.i("Provider from MS domain=" + domain);
result.add(fromMSAutodiscovery(context, domain, email, intf));
} catch (Throwable ex) {
Log.w(ex);
}
try {
// Scan ports
Log.i("Provider from scan domain=" + domain);
@ -858,6 +869,159 @@ public class EmailProvider implements Parcelable {
}
}
@NonNull
private static EmailProvider fromMSAutodiscovery(Context context, String domain, String email, IDiscovery intf) throws Throwable {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
boolean open_safe = prefs.getBoolean("open_safe", false);
// https://learn.microsoft.com/en-us/Exchange/architecture/client-access/autodiscover
// https://github.com/gronke/email-autodiscover/blob/master/mail/autodiscover.xml
for (String link : Misc.getMSUrls(context, domain, email))
try {
URL url = new URL(link);
return getMSAutodiscovery(context, domain, url, !open_safe, intf);
} catch (Throwable ex) {
Log.i(ex);
}
throw new UnknownHostException(domain);
}
private static EmailProvider getMSAutodiscovery(Context context, String domain, URL url, boolean unsafe, IDiscovery intf) throws IOException, XmlPullParserException {
EmailProvider provider = new EmailProvider(domain);
HttpURLConnection request = null;
try {
Log.i("Fetching " + url);
intf.onStatus("MS " + url);
request = (HttpURLConnection) url.openConnection();
request.setRequestMethod("GET");
request.setReadTimeout(MS_TIMEOUT);
request.setConnectTimeout(MS_TIMEOUT);
request.setDoInput(true);
ConnectionHelper.setUserAgent(context, request);
if (unsafe && request instanceof HttpsURLConnection) {
((HttpsURLConnection) request).setHostnameVerifier(new HostnameVerifier() {
@Override
public boolean verify(String hostname, SSLSession session) {
return true;
}
});
}
request.connect();
int status = request.getResponseCode();
if (status != HttpURLConnection.HTTP_OK)
throw new FileNotFoundException("Error " + status + ": " + request.getResponseMessage());
// https://developer.android.com/reference/org/xmlpull/v1/XmlPullParser
XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
XmlPullParser xml = factory.newPullParser();
xml.setInput(new InputStreamReader(request.getInputStream()));
EntityLog.log(context, "Parsing " + url);
boolean isAccount = false;
boolean isEmail = false;
boolean isSettings = false;
boolean isProtocol = false;
boolean isImap = false;
boolean isSmtp = false;
String host = null;
Integer port = null;
int eventType = xml.getEventType();
while (eventType != XmlPullParser.END_DOCUMENT) {
if (eventType == XmlPullParser.START_TAG) {
String name = xml.getName();
if ("Account".equals(name)) {
// <AccountType>email</AccountType>
isAccount = true;
} else if ("AccountType".equals(name) && isAccount) {
// <AccountType>email</AccountType>
eventType = xml.next();
if (eventType == XmlPullParser.TEXT && "email".equals(xml.getText()))
isEmail = true;
continue;
} else if ("Action".equals(name) && isAccount) {
// <Action>settings</Action>
eventType = xml.next();
if (eventType == XmlPullParser.TEXT && "settings".equals(xml.getText()))
isSettings = true;
continue;
} else if ("Protocol".equals(name) && isAccount) {
// <AccountType>email</AccountType>
isProtocol = true;
} else if (isAccount && isEmail && isSettings && isProtocol) {
if ("Type".equals(name)) {
// <AccountType>email</AccountType>
eventType = xml.next();
if (eventType == XmlPullParser.TEXT) {
String type = xml.getText();
if ("IMAP".equals(type))
isImap = true;
else if ("SMTP".equals(type))
isSmtp = true;
}
continue;
} else if ("Server".equals(name)) {
eventType = xml.next();
if (eventType == XmlPullParser.TEXT)
host = xml.getText();
continue;
} else if ("Port".equals(name)) {
eventType = xml.next();
if (eventType == XmlPullParser.TEXT) {
String p = xml.getText();
if (TextUtils.isDigitsOnly(p))
port = Integer.parseInt(p);
}
continue;
}
}
} else if (eventType == XmlPullParser.END_TAG) {
String name = xml.getName();
if ("Account".equals(name)) {
isAccount = false;
isEmail = false;
isSettings = false;
} else if ("Protocol".equals(name)) {
if (isProtocol && host != null && port != null) {
if (isImap) {
provider.imap.score = 20;
provider.imap.host = host;
provider.imap.port = port;
provider.imap.starttls = (provider.imap.port == 143);
} else if (isSmtp) {
provider.smtp.score = 20;
provider.smtp.host = host;
provider.smtp.port = port;
provider.smtp.starttls = (provider.smtp.port == 587);
}
}
isProtocol = false;
isImap = false;
isSmtp = false;
host = null;
port = null;
}
}
eventType = xml.next();
}
provider.validate();
Log.e("MS=" + url);
return provider;
} finally {
if (request != null)
request.disconnect();
}
}
@NonNull
private static EmailProvider fromDNS(Context context, String domain, Discover discover, IDiscovery intf) throws UnknownHostException {
// https://tools.ietf.org/html/rfc6186

View File

@ -0,0 +1,150 @@
<?xml version="1.0" encoding="utf-8" ?>
<Autodiscover
xmlns="http://schemas.microsoft.com/exchange/autodiscover/responseschema/2006">
<!-- Response: Required
This tag serves as an indication that the retrieved XML is an Autodiscovery Response
-->
<Response
xmlns="http://schemas.microsoft.com/exchange/autodiscover/outlook/responseschema/2006a">
<!-- User: Optional
This tag gives user-specific information. Autodiscover must be UTF-8 encoded.
-->
<User>
<!-- DisplayName: Optional
The server may have a good formal display name. The client can decide to accept it or change it. This will save the user time in the default case.
-->
<DisplayName>%INFO/NAME%</DisplayName>
</User>
<!-- Account: Required
This tag specifies the type of account, such as Email vs Newsgroups, vs SIP server, etc.
-->
<Account>
<!-- AccountType: Required
This value indicates the type of the account.
VALUES:
email: The values under this Account tag indicate configuration settings for an email server.
nntp: The values under this Account tag indicate configuration settings for a NNTP server. (not used by Outlook 2007)
-->
<AccountType>email</AccountType>
<!-- Action: Required
This value indicates if the goal of this account results is to provide the settings or redirect to another web server that can provide results.
VALUES:
redirectUrl: If this value is specified, then the URL tag will specify the http: or https: URL containing the Autodiscover results to be used. In order to prevent the server from being able to send the client into an infinite loop, the client should stop redirecting after 10 redirects.
redirectAddr: If this value is specified, then the XML tag will specify the e-mail address that Outlook should use to execute Autodiscover again. In other words, the server is telling the client that the e-mail address the client should really be using for Autodiscover is not the one that was posted, but the one specified in this tag.
settings: If this value is specified, then the XML will contain the settings needed to configure the account. The settings will primarily be under the PROTOCOL tag.
-->
<Action>settings</Action>
<!-- RedirectUrl: Required if ACTION tag has value of 'redirectUrl'. Otherwise this tag must not exist.
The value will be a https: URL that the client should use to obtain the Autodiscover settings or a http: URL that the client should use for further redirection.
-->
<!--
<RedirectUrl>redirect.URL</RedirectUrl>
-->
<!-- RedirectAddr: Required if ACTION tag has value of 'redirectAddr'. Otherwise this tag must not exist.
The value will be an email address that the client should use to rediscover settings using the Autodiscover protocol.
-->
<!--
<RedirectAddr>email@address</RedirectAddr>
-->
<!-- Image: Optional
This is a JPG picture to brand the ISP configuration experience with. The client can choose whether or not they download this picture to display. (not used by Outlook 2007)
-->
<!--
<Image>http://path.to.image.com/image.jpg</Image>
-->
<!-- ServiceHome: Optional
This is a link to the ISPs Home Page. The client can choose whether or not they expose this link to the user. (not used by Outlook 2007)
-->
<ServiceHome>%INFO/URL%</ServiceHome>
<!-- Protocol: Required if ACTION tag has value of 'settings'. Otherwise, this tag must not exist.
The tag encloses the specifications for a single account type. The list of Protocol tags are in order of preference of the server. The client may over ride the preference.
-->
<Protocol>
<!-- TYPE: Required.
The value here specifies what kind of mail account is being configured.
POP3: The protocol to connect to this server is POP3. Only applicable for AccountType=email.
SMTP: The protocol to connect to this server is SMTP. Only applicable for AccountType=email.
IMAP: The protocol to connect to this server is IMAP. Only applicable for AccountType=email.
DAV: The protocol to connect to this server is DAV. Only applicable for AccountType=email.
WEB: Email is accessed from a web browser using an URL from the SERVER tag. Only applicable for AccountType=email. (not used by Outlook 2007)
NNTP: The protocol to connect to this server is NNTP. Only applicable for AccountType=nntp. (not used by Outlook 2007)
-->
<Type>IMAP</Type>
<!-- ExpirationDate: Optional.
The value here specifies the last date which these settings should be used. After that date, the settings should be rediscovered via Autodiscover again. If no value is specified, the default will be no expiration.
-->
<!--
<ExpirationDate>YYYYMMDD</ExpirationDate>
-->
<!-- TTL: Optional.
The value here specifies the time to live in hours that these settings are valid for. After that time has elapsed (from the time the settings were retrieved), the settings should be rediscovered via Autodiscovery again. A value of 0 indicates that no rediscovery will be required. If no value is specified, the default will be a TTL of 1 hour.
-->
<TTL>%TTL%</TTL>
<!-- Server: Required.
The value here specifies the name of the mail server corresponding to the server type specified above.
For protocols such as POP3, SMTP, IMAP, or NNTP, this value will be either a hostname or an IP address.
For protocols such as DAV or WEB, this will be an URL.
-->
<Server>%SERVER/IMAP/HOST%</Server>
<!--IP Addr or DNS name of server-->
<!-- Port: Optional.
The value specifies the Port number to use. If no value is specified, the default settings will be used depending on the mail server type. This value is not used if the SERVER tag contains an URL.
-->
<Port>%SERVER/IMAP/PORT%</Port>
<!-- LoginName: Optional.
This value specifies the user's login. If no value is specified, the default will be set to the string preceding the '@' in the email address. If the Login name contains a domain, the format should be <Username>@<Domain>. Such as JoeUser@SalesDomain.
-->
<!--
<LoginName>johndoe</LoginName>
-->
<!-- DomainRequired: Optional. Default is off.
If this value is true, then a domain is required during authentication. If the domain is not specified in the LOGINNAME tag, or the LOGINNAME tag was not specified, the user will need to enter the domain before authentication will succeed.
-->
<DomainRequired>%SERVER/IMAP/DOMAIN_REQUIRED%</DomainRequired>
<!-- DomainName: Optional.
This value specifies the user's domain. If no value is specified, the default authentication will be to use the e-mail address as a UPN format <Username>@<Domain>. Such as JoeUser@SalesDomain.
-->
<!--
<DomainName></DomainName>
-->
<!-- SPA: (Secure Password Authentication) Optional.
This value specifies whether or not secure password authentication is needed.
If unspecified, the default is set to on.
-->
<SPA>off</SPA>
<!-- SSL: Optional.
This value specifies whether secure login is needed.
If unspecified, the default is set to on.
-->
<SSL>%SERVER/IMAP/SSL_ON%</SSL>
<!-- AuthRequired: Optional.
This value specifies whether authentication is needed (password).
If unspecified, the default is set to on.
-->
<AuthRequired>on</AuthRequired>
<!-- Optional: Is Authentication required? -->
<!-- UsePOPAuth: Optional.
This value can only be used for SMTP types.
If specified, then the authentication information provided for the POP3 type account will also be used for SMTP.
-->
<UsePOPAuth>on</UsePOPAuth>
<!-- SMTPLast: Optional. Default is off.
If this value is true, then the SMTP server requires that email be downloaded before sending email via the SMTP server. This is often required because the SMTP server verifies that the authentication succeeded when downloading email.
-->
<SMTPLast>off</SMTPLast>
</Protocol>
<Protocol>
<Type>SMTP</Type>
<TTL>%TTL%</TTL>
<Server>%SERVER/SMTP/HOST%</Server>
<Port>%SERVER/SMTP/PORT%</Port>
<DomainRequired>%SERVER/SMTP/DOMAIN_REQUIRED%</DomainRequired>
<SPA>off</SPA>
<SSL>%SERVER/SMTP/SSL_ON%</SSL>
<AuthRequired>on</AuthRequired>
<UsePOPAuth>on</UsePOPAuth>
<SMTPLast>off</SMTPLast>
</Protocol>
</Account>
</Response>
</Autodiscover>