-
Notifications
You must be signed in to change notification settings - Fork 56
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
implement auto-discover function, close #66
- Loading branch information
Robert Virkus
committed
Jun 10, 2020
1 parent
977443f
commit 4209dc3
Showing
9 changed files
with
1,099 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -24,12 +24,32 @@ int smtpServerPort = 465; | |
bool isSmtpServerSecure = true; | ||
void main() async { | ||
await discoverExample(); | ||
await imapExample(); | ||
await smtpExample(); | ||
await popExample(); | ||
exit(0); | ||
} | ||
Future<void> discoverExample() async { | ||
var email = '[email protected]'; | ||
var config = await Discover.discover(email, isLogEnabled: false); | ||
if (config == null) { | ||
print('Unable to discover settings for $email'); | ||
} else { | ||
print('Settings for $email:'); | ||
for (var provider in config.emailProviders) { | ||
print('provider: ${provider.displayName}'); | ||
print('provider-domains: ${provider.domains}'); | ||
print('documentation-url: ${provider.documentationUrl}'); | ||
print('Incoming:'); | ||
print(provider.preferredIncomingServer); | ||
print('Outgoing:'); | ||
print(provider.preferredOutgoingServer); | ||
} | ||
} | ||
} | ||
Future<void> imapExample() async { | ||
var client = ImapClient(isLogEnabled: false); | ||
await client.connectToServer(imapServerHost, imapServerPort, | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -14,12 +14,38 @@ int smtpServerPort = 465; | |
bool isSmtpServerSecure = true; | ||
|
||
void main() async { | ||
await discoverExample(); | ||
await imapExample(); | ||
await smtpExample(); | ||
await popExample(); | ||
exit(0); | ||
} | ||
|
||
Future<void> discoverExample() async { | ||
var email = '[email protected]'; | ||
var config = await Discover.discover(email, isLogEnabled: false); | ||
if (config == null) { | ||
print('Unable to discover settings for $email'); | ||
} else { | ||
print('Settings for $email:'); | ||
for (var provider in config.emailProviders) { | ||
print('provider: ${provider.displayName}'); | ||
print('provider-domains: ${provider.domains}'); | ||
print('documentation-url: ${provider.documentationUrl}'); | ||
print('Incoming:'); | ||
// for (var server in provider.incomingServers) { | ||
// print(server); | ||
// } | ||
print(provider.preferredIncomingServer); | ||
print('Outgoing:'); | ||
// for (var server in provider.outgoingServers) { | ||
// print(server); | ||
// } | ||
print(provider.preferredOutgoingServer); | ||
} | ||
} | ||
} | ||
|
||
Future<void> imapExample() async { | ||
var client = ImapClient(isLogEnabled: false); | ||
await client.connectToServer(imapServerHost, imapServerPort, | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,139 @@ | ||
class ClientConfig { | ||
String version; | ||
List<ConfigEmailProvider> emailProviders; | ||
|
||
bool get isNotValid => | ||
emailProviders == null || | ||
emailProviders.isEmpty || | ||
emailProviders.first.preferredIncomingServer == null || | ||
emailProviders.first.preferredOutgoingServer == null; | ||
bool get isValid => !isNotValid; | ||
|
||
ClientConfig({this.version}); | ||
|
||
void addEmailProvider(ConfigEmailProvider provider) { | ||
emailProviders ??= <ConfigEmailProvider>[]; | ||
emailProviders.add(provider); | ||
} | ||
|
||
ServerConfig get preferredIncomingServer => emailProviders?.isEmpty ?? true | ||
? null | ||
: emailProviders.first.preferredIncomingServer; | ||
ServerConfig get preferredIncomingImapServer => | ||
emailProviders?.isEmpty ?? true | ||
? null | ||
: emailProviders.first.preferredIncomingImapServer; | ||
ServerConfig get preferredIncomingPopServer => emailProviders?.isEmpty ?? true | ||
? null | ||
: emailProviders.first.preferredIncomingPopServer; | ||
ServerConfig get preferredOutgoingServer => emailProviders?.isEmpty ?? true | ||
? null | ||
: emailProviders.first.preferredOutgoingServer; | ||
ServerConfig get preferredOutgoingSmtpServer => | ||
emailProviders?.isEmpty ?? true | ||
? null | ||
: emailProviders.first.preferredOutgoingSmtpServer; | ||
String get displayName => | ||
emailProviders?.isEmpty ?? true ? null : emailProviders.first.displayName; | ||
} | ||
|
||
class ConfigEmailProvider { | ||
String id; | ||
List<String> domains; | ||
String displayName; | ||
String displayShortName; | ||
List<ServerConfig> incomingServers; | ||
List<ServerConfig> outgoingServers; | ||
String documentationUrl; | ||
ServerConfig preferredIncomingServer; | ||
ServerConfig preferredIncomingImapServer; | ||
ServerConfig preferredIncomingPopServer; | ||
ServerConfig preferredOutgoingServer; | ||
ServerConfig preferredOutgoingSmtpServer; | ||
|
||
ConfigEmailProvider( | ||
{this.id, | ||
this.domains, | ||
this.displayName, | ||
this.displayShortName, | ||
this.incomingServers, | ||
this.outgoingServers}); | ||
|
||
void addDomain(String name) { | ||
domains ??= <String>[]; | ||
domains.add(name); | ||
} | ||
|
||
void addIncomingServer(ServerConfig server) { | ||
incomingServers ??= <ServerConfig>[]; | ||
incomingServers.add(server); | ||
preferredIncomingServer ??= server; | ||
if (server.type == ServerType.imap && preferredIncomingImapServer == null) { | ||
preferredIncomingImapServer = server; | ||
} | ||
if (server.type == ServerType.pop && preferredIncomingPopServer == null) { | ||
preferredIncomingPopServer = server; | ||
} | ||
} | ||
|
||
void addOutgoingServer(ServerConfig server) { | ||
outgoingServers ??= <ServerConfig>[]; | ||
outgoingServers.add(server); | ||
preferredOutgoingServer ??= server; | ||
if (server.type == ServerType.smtp && preferredOutgoingSmtpServer == null) { | ||
preferredOutgoingSmtpServer = server; | ||
} | ||
} | ||
} | ||
|
||
enum ServerType { imap, pop, smtp, unknown } | ||
|
||
enum SocketType { plain, ssl, starttls, unknown } | ||
|
||
enum Authentication { | ||
oauth2, | ||
passwordCleartext, | ||
plain, | ||
passwordEncrypted, | ||
secure, | ||
ntlm, | ||
gsapi, | ||
clientIpAddress, | ||
tlsClientCert, | ||
smtpAfterPop, | ||
none, | ||
unknown | ||
} | ||
|
||
enum UsernameType { emailAddress, emailLocalPart, realname, unknown } | ||
|
||
class ServerConfig { | ||
String typeName; | ||
ServerType type; | ||
String hostname; | ||
int port; | ||
SocketType socketType; | ||
String get socketTypeName => | ||
socketType.toString().substring('socketType.'.length); | ||
Authentication authentication; | ||
Authentication authenticationAlternative; | ||
String get authenticationName => | ||
authentication.toString().substring('authentication.'.length); | ||
String username; | ||
UsernameType usernameType; | ||
|
||
bool get isSecureSocket => (socketType == SocketType.ssl); | ||
|
||
ServerConfig( | ||
{this.type, | ||
this.hostname, | ||
this.port, | ||
this.socketType, | ||
this.authentication, | ||
this.username}); | ||
|
||
@override | ||
String toString() { | ||
return '$typeName:\n host: $hostname\n port: $port\n socket: $socketTypeName\n authentication: $authenticationName\n username: $username'; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
import 'package:enough_mail/src/util/discover_helper.dart'; | ||
|
||
import 'client_config.dart'; | ||
|
||
class Discover { | ||
static Future<ClientConfig> discover(String emailAddress, | ||
{bool forceSslConnection = false, bool isLogEnabled = false}) async { | ||
var config = await _discover(emailAddress, isLogEnabled); | ||
if (forceSslConnection && config != null) { | ||
if (config.preferredIncomingImapServer != null && | ||
!config.preferredIncomingImapServer.isSecureSocket) { | ||
config.preferredIncomingImapServer.port = 993; | ||
config.preferredIncomingImapServer.socketType = SocketType.ssl; | ||
} | ||
if (config.preferredIncomingPopServer != null && | ||
!config.preferredIncomingPopServer.isSecureSocket) { | ||
config.preferredIncomingPopServer.port = 995; | ||
config.preferredIncomingPopServer.socketType = SocketType.ssl; | ||
} | ||
if (config.preferredOutgoingSmtpServer != null && | ||
!config.preferredOutgoingSmtpServer.isSecureSocket) { | ||
config.preferredOutgoingSmtpServer.port = 465; | ||
config.preferredOutgoingSmtpServer.socketType = SocketType.ssl; | ||
} | ||
} | ||
return config; | ||
} | ||
|
||
static Future<ClientConfig> _discover( | ||
String emailAddress, bool isLogEnabled) async { | ||
// [1] autodiscover from sub-domain, compare: https://developer.mozilla.org/en-US/docs/Mozilla/Thunderbird/Autoconfiguration | ||
var emailDomain = DiscoverHelper.getDomainFromEmail(emailAddress); | ||
var config = await DiscoverHelper.discoverFromAutoConfigSubdomain( | ||
emailAddress, emailDomain, isLogEnabled); | ||
if (config != null) { | ||
return _updateDisplayNames(config, emailDomain); | ||
} | ||
var mxDomain = await DiscoverHelper.discoverMxDomain(emailDomain); | ||
_log('mxDomain for [$emailDomain] is [$mxDomain]', isLogEnabled); | ||
if (mxDomain != null && mxDomain != emailDomain) { | ||
config = await DiscoverHelper.discoverFromAutoConfigSubdomain( | ||
emailAddress, mxDomain, isLogEnabled); | ||
if (config != null) { | ||
return _updateDisplayNames(config, emailDomain); | ||
} | ||
} | ||
// TODO allow more autodiscover options: | ||
// [2] https://docs.microsoft.com/en-us/previous-versions/office/office-2010/cc511507(v=office.14) | ||
// [3] https://docs.microsoft.com/en-us/exchange/client-developer/exchange-web-services/autodiscover-for-exchange | ||
// [4] https://docs.microsoft.com/en-us/exchange/architecture/client-access/autodiscover | ||
// [6] by trying typical options like imap.$domain, mail.$domain, etc | ||
|
||
//print('querying ISP DB for $mxDomain'); | ||
|
||
// [5] autodiscover from Mozilla ISP DB: https://developer.mozilla.org/en-US/docs/Mozilla/Thunderbird/Autoconfiguration | ||
config = await DiscoverHelper.discoverFromIspDb(mxDomain, isLogEnabled); | ||
//print('got config $config for $mxDomain.'); | ||
return _updateDisplayNames(config, emailDomain); | ||
} | ||
|
||
static ClientConfig _updateDisplayNames( | ||
ClientConfig config, String mailDomain) { | ||
if (config?.emailProviders?.isNotEmpty ?? false) { | ||
for (var provider in config.emailProviders) { | ||
if (provider.displayName != null) { | ||
provider.displayName = | ||
provider.displayName.replaceFirst('%EMAILDOMAIN%', mailDomain); | ||
} | ||
if (provider.displayShortName != null) { | ||
provider.displayShortName = provider.displayShortName | ||
.replaceFirst('%EMAILDOMAIN%', mailDomain); | ||
} | ||
} | ||
} | ||
return config; | ||
} | ||
|
||
static void _log(String text, bool isLogEnabled) { | ||
if (isLogEnabled) { | ||
print(text); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.