Files
sharedinbox/packages/enough_mail/lib/src/discover/discover.dart
T
Thomas GüttlerandClaude Sonnet 4.6 71952ed36b Fix: vendor enough_mail as regular files instead of gitlink
The directory was tracked as a mode-160000 gitlink (bare submodule
reference) without a .gitmodules entry, causing 'has no commit checked
out' errors on commit. Re-added as ordinary tracked files so the
vendored copy is fully part of this repo.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-16 08:52:01 +02:00

198 lines
6.7 KiB
Dart

import '../mail/mail_account.dart';
import '../private/util/discover_helper.dart';
import 'client_config.dart';
/// Helps discovering email connection settings based on an email address.
///
/// Use [discover] to initiate the discovery process.
class Discover {
Discover._();
/// Tries to discover mail settings for the specified [emailAddress].
///
/// Optionally set [forceSslConnection] to `true` when not encrypted
/// connections should not be allowed.
///
/// Set [isLogEnabled] to `true` to output debugging information during
/// the discovery process.
///
/// You can use the discovered client settings directly or by converting
/// them to a [MailAccount] first with calling
/// [MailAccount.fromDiscoveredSettings].
static Future<ClientConfig?> discover(
String emailAddress, {
bool forceSslConnection = false,
bool isLogEnabled = false,
}) async {
final config = await _discover(emailAddress, isLogEnabled);
if (forceSslConnection && config != null) {
final preferredIncomingImapServer = config.preferredIncomingImapServer;
if (preferredIncomingImapServer != null &&
!preferredIncomingImapServer.isSecureSocket) {
config.preferredIncomingImapServer =
preferredIncomingImapServer.copyWith(
port: 993,
socketType: SocketType.ssl,
);
}
final preferredIncomingPopServer = config.preferredIncomingPopServer;
if (preferredIncomingPopServer != null &&
!preferredIncomingPopServer.isSecureSocket) {
config.preferredIncomingPopServer = preferredIncomingPopServer.copyWith(
port: 995,
socketType: SocketType.ssl,
);
}
final preferredOutgoingSmtpServer = config.preferredOutgoingSmtpServer;
if (preferredOutgoingSmtpServer != null &&
!preferredOutgoingSmtpServer.isSecureSocket) {
config.preferredOutgoingSmtpServer =
preferredOutgoingSmtpServer.copyWith(
port: 465,
socketType: SocketType.ssl,
);
}
}
return config;
}
/// Tries to complete the specified [partialAccount] information.
///
/// This is useful when mail configuration settings cannot be discovered
/// automatically and the user
/// only provides some information such as the host domains of the incoming
/// and outgoing servers.
/// Warning: this method assumes that the host domain has been specified by
/// the user and contains a corresponding assert statement.
static Future<MailAccount?> complete(
MailAccount partialAccount, {
bool isLogEnabled = false,
}) async {
final incoming = partialAccount.incoming.serverConfig;
assert(
partialAccount.email.isNotEmpty, 'MailAccount requires email address');
assert(incoming.hostname.isNotEmpty,
'MailAccount required incoming server host to be specified');
final outgoing = partialAccount.outgoing.serverConfig;
assert(outgoing.hostname.isNotEmpty,
'MailAccount required outgoing server host to be specified');
final infos = <DiscoverConnectionInfo>[];
if (incoming.port == 0 ||
incoming.socketType == SocketType.unknown ||
incoming.type == ServerType.unknown) {
DiscoverHelper.addIncomingVariations(incoming.hostname, infos);
}
if (outgoing.port == 0 ||
outgoing.socketType == SocketType.unknown ||
outgoing.type == ServerType.unknown) {
DiscoverHelper.addOutgoingVariations(outgoing.hostname, infos);
}
if (infos.isNotEmpty) {
final baseDomain =
DiscoverHelper.getDomainFromEmail(partialAccount.email);
final clientConfig = await DiscoverHelper.discoverFromConnections(
baseDomain,
infos,
isLogEnabled: isLogEnabled,
);
if (clientConfig == null) {
_log(
'Unable to discover remaining settings from $partialAccount',
isLogEnabled,
);
return null;
}
return partialAccount.copyWith(
incoming: partialAccount.incoming.copyWith(
serverConfig: clientConfig.preferredIncomingServer,
),
outgoing: partialAccount.outgoing.copyWith(
serverConfig: clientConfig.preferredOutgoingServer,
),
);
}
return null;
}
static Future<ClientConfig?> _discover(
String emailAddress,
bool isLogEnabled,
) async {
// [1] auto-discover from sub-domain,
// compare: https://developer.mozilla.org/en-US/docs/Mozilla/Thunderbird/Autoconfiguration
final emailDomain = DiscoverHelper.getDomainFromEmail(emailAddress);
var config = await DiscoverHelper.discoverFromAutoConfigSubdomain(
emailAddress,
domain: emailDomain,
isLogEnabled: isLogEnabled,
);
if (config == null) {
final mxDomain = await DiscoverHelper.discoverMxDomain(emailDomain);
_log('mxDomain for [$emailDomain] is [$mxDomain]', isLogEnabled);
if (mxDomain != null && mxDomain != emailDomain) {
config = await DiscoverHelper.discoverFromAutoConfigSubdomain(
emailAddress,
domain: mxDomain,
isLogEnabled: isLogEnabled,
);
}
//print('querying ISP DB for $mxDomain');
// [5] auto-discover from Mozilla ISP DB:
// https://developer.mozilla.org/en-US/docs/Mozilla/Thunderbird/Autoconfiguration
final hasMxDomain = mxDomain != null && mxDomain != emailDomain;
config ??= await DiscoverHelper.discoverFromIspDb(
emailDomain,
isLogEnabled: isLogEnabled,
);
if (hasMxDomain) {
config ??= await DiscoverHelper.discoverFromIspDb(
mxDomain,
isLogEnabled: isLogEnabled,
);
}
// try to guess incoming and outgoing server names based on the domain
final domains = hasMxDomain ? [emailDomain, mxDomain] : [emailDomain];
config ??= await DiscoverHelper.discoverFromCommonDomains(
domains,
isLogEnabled: isLogEnabled,
);
}
//print('got config $config for $mxDomain.');
return _updateDisplayNames(config, emailDomain);
}
static ClientConfig? _updateDisplayNames(
ClientConfig? config,
String mailDomain,
) {
final emailProviders = config?.emailProviders;
if (emailProviders != null && emailProviders.isNotEmpty) {
for (final provider in 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);
}
}
}