mirror of
https://github.com/the-djmaze/snappymail.git
synced 2026-07-11 00:14:50 +03:00
Let Autoconfig also use Microsoft's autodiscover
This commit is contained in:
parent
d6be75e39d
commit
19946fc551
2 changed files with 132 additions and 33 deletions
|
|
@ -7,49 +7,51 @@ namespace RainLoop\Providers\Domain;
|
|||
|
||||
abstract class Autoconfig
|
||||
{
|
||||
public static function discover(string $emailaddress)
|
||||
public static function discover(string $emailaddress) : ?array
|
||||
{
|
||||
$domain = \explode('@', $emailaddress, 2)[1];
|
||||
$domain = \explode('@', $emailaddress);
|
||||
$domain = \array_pop($domain);
|
||||
// First try
|
||||
$autoconfig = static::resolve($domain, $emailaddress);
|
||||
if ($autoconfig) {
|
||||
return $autoconfig;
|
||||
}
|
||||
// Else try MX
|
||||
if (!$autoconfig) {
|
||||
// regular expression is too large
|
||||
$suffixes = static::publicsuffixes();
|
||||
$hostnames = [];
|
||||
foreach (\SnappyMail\DNS::MX($domain) as $hostname) {
|
||||
// Extract only the second-level domain from the MX hostname
|
||||
$mxbasedomain = \explode('.', $hostname);
|
||||
$i = -2;
|
||||
while (\in_array(\implode('.', \array_slice($mxbasedomain, $i)), $suffixes)) {
|
||||
--$i;
|
||||
}
|
||||
$mxbasedomain = \implode('.', \array_slice($mxbasedomain, $i));
|
||||
if ($mxbasedomain) {
|
||||
$mxfulldomain = $mxbasedomain;
|
||||
if (\substr_count($hostname, '.') > \substr_count($mxbasedomain, '.')) {
|
||||
// Remove the first component from the MX hostname
|
||||
$mxfulldomain = \explode('.', $hostname, 2)[1];
|
||||
}
|
||||
$hostnames[$mxfulldomain] = $mxbasedomain;
|
||||
}
|
||||
$suffixes = static::publicsuffixes();
|
||||
$hostnames = [];
|
||||
foreach (\SnappyMail\DNS::MX($domain) as $hostname) {
|
||||
// Extract only the second-level domain from the MX hostname
|
||||
$mxbasedomain = \explode('.', $hostname);
|
||||
$i = -2;
|
||||
while (\in_array(\implode('.', \array_slice($mxbasedomain, $i)), $suffixes)) {
|
||||
--$i;
|
||||
}
|
||||
foreach ($hostnames as $mxfulldomain => $mxbasedomain) {
|
||||
if ($domain != $mxfulldomain) {
|
||||
$autoconfig = static::resolve($mxfulldomain, $emailaddress);
|
||||
if (!$autoconfig && $mxfulldomain != $mxbasedomain && $domain != $mxbasedomain) {
|
||||
$autoconfig = static::resolve($mxbasedomain, $emailaddress);
|
||||
}
|
||||
if ($autoconfig) {
|
||||
break;
|
||||
}
|
||||
$mxbasedomain = \implode('.', \array_slice($mxbasedomain, $i));
|
||||
if ($mxbasedomain) {
|
||||
$mxfulldomain = $mxbasedomain;
|
||||
if (\substr_count($hostname, '.') > \substr_count($mxbasedomain, '.')) {
|
||||
// Remove the first component from the MX hostname
|
||||
$mxfulldomain = \explode('.', $hostname, 2)[1];
|
||||
}
|
||||
$hostnames[$mxfulldomain] = $mxbasedomain;
|
||||
}
|
||||
}
|
||||
foreach ($hostnames as $mxfulldomain => $mxbasedomain) {
|
||||
if ($domain != $mxfulldomain) {
|
||||
$autoconfig = static::resolve($mxfulldomain, $emailaddress);
|
||||
if (!$autoconfig && $mxfulldomain != $mxbasedomain && $domain != $mxbasedomain) {
|
||||
$autoconfig = static::resolve($mxbasedomain, $emailaddress);
|
||||
}
|
||||
if ($autoconfig) {
|
||||
return $autoconfig;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $autoconfig;
|
||||
// Else try Microsoft autodiscover
|
||||
return static::autodiscover($domain);
|
||||
}
|
||||
|
||||
private static function resolve(string $domain, string $emailaddress)
|
||||
private static function resolve(string $domain, string $emailaddress) : ?array
|
||||
{
|
||||
$emailaddress = \urlencode($emailaddress);
|
||||
foreach ([
|
||||
|
|
@ -77,10 +79,12 @@ abstract class Autoconfig
|
|||
isset($data['outgoingServer'][0]) ? $data['outgoingServer'] : [$data['outgoingServer']],
|
||||
fn($data) => 'smtp' === $data['@attributes']['type']
|
||||
);
|
||||
$data['canonical'] = $url;
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static function publicsuffixes() : array
|
||||
|
|
@ -110,4 +114,86 @@ abstract class Autoconfig
|
|||
return $list ?: [];
|
||||
}
|
||||
|
||||
/**
|
||||
* This is Microsoft
|
||||
*/
|
||||
private static function autodiscover(string $domain) : ?array
|
||||
{
|
||||
foreach ([
|
||||
"https://{$domain}",
|
||||
"https://autodiscover.{$domain}",
|
||||
"http://autodiscover.{$domain}"
|
||||
] as $host) {
|
||||
$result = static::autodiscover_resolve($host, $domain);
|
||||
if ($result) {
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
foreach (\SnappyMail\DNS::SRV("_autodiscover._tcp.{$domain}") as $record) {
|
||||
if (443 == $record['port']) {
|
||||
$result = static::autodiscover_resolve("https://{$record['target']}", $domain);
|
||||
} else if (80 == $record['port']) {
|
||||
$result = static::autodiscover_resolve("http://{$record['target']}", $domain);
|
||||
} else {
|
||||
$result = static::autodiscover_resolve("https://{$record['target']}:{$record['port']}", $domain);
|
||||
}
|
||||
if ($result) {
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static function autodiscover_resolve(string $host, string $domain) : ?array
|
||||
{
|
||||
$email = "autodiscover@{$domain}";
|
||||
$context = \stream_context_create(['http' => [
|
||||
'method' => 'POST',
|
||||
'header' => 'Content-Type: application/xml',
|
||||
'content' => '<?xml version="1.0" encoding="utf-8" ?>
|
||||
<Autodiscover xmlns="http://schemas.microsoft.com/exchange/autodiscover/outlook/requestschema/2006">
|
||||
<Request>
|
||||
<AcceptableResponseSchema>http://schemas.microsoft.com/exchange/autodiscover/outlook/responseschema/2006a</AcceptableResponseSchema>
|
||||
<EMailAddress>'.$email.'</EMailAddress>
|
||||
</Request>
|
||||
</Autodiscover>'
|
||||
]]);
|
||||
$url = "{$host}/autodiscover/autodiscover.xml";
|
||||
$xml = \file_get_contents($url, false, $context);
|
||||
if ($xml) {
|
||||
$data = \json_decode(
|
||||
\json_encode(
|
||||
\simplexml_load_string($xml, 'SimpleXMLElement', \LIBXML_NOCDATA)
|
||||
), true);
|
||||
if (!empty($data['Response']['Account']['Protocol']) && 'email' === $data['Response']['Account']['AccountType']) {
|
||||
$result = [
|
||||
'incomingServer' => [],
|
||||
'outgoingServer' => []
|
||||
];
|
||||
foreach ($data['Response']['Account']['Protocol'] as $entry) {
|
||||
if ('IMAP' === $entry['Type']) {
|
||||
$result['incomingServer'][] = [
|
||||
'hostname' => $entry['Server'],
|
||||
'port' => $entry['Port'],
|
||||
'socketType' => ('on' === $entry['SSL']) ? (993 == $entry['Port'] ? 'SSL' : 'STARTTLS') : '',
|
||||
'authentication' => 'password-cleartext',
|
||||
'username' => ($entry['LoginName'] === $email) ? '%EMAILADDRESS%' : ''
|
||||
];
|
||||
} else if ('SMTP' === $entry['Type']) {
|
||||
$result['outgoingServer'][] = [
|
||||
'hostname' => $entry['Server'],
|
||||
'port' => $entry['Port'],
|
||||
'socketType' => (587 == $entry['Port']) ? 'STARTTLS' : ('on' === $entry['SSL'] ? 'SSL' : ''),
|
||||
'authentication' => 'password-cleartext',
|
||||
'username' => ($entry['LoginName'] === $email) ? '%EMAILADDRESS%' : ''
|
||||
];
|
||||
}
|
||||
}
|
||||
$result['canonical'] = $url;
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -64,4 +64,17 @@ abstract class DNS
|
|||
\ksort($mxhosts);
|
||||
return \array_values($mxhosts);
|
||||
}
|
||||
|
||||
public static function SRV(string $domain) : array
|
||||
{
|
||||
$result = array();
|
||||
$values = \dns_get_record($domain, \DNS_SRV);
|
||||
if ($values) {
|
||||
foreach ($values as $record) {
|
||||
$result[$record['pri']] = $record;
|
||||
}
|
||||
}
|
||||
\ksort($result);
|
||||
return \array_values($result);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue