mirror of
https://github.com/the-djmaze/snappymail.git
synced 2026-08-29 20:19:22 +03:00
Added domain autconfig detection of @benbucksch
https://datatracker.ietf.org/doc/draft-bucksch-autoconfig/
This commit is contained in:
parent
d4cdfb8fa5
commit
5e0d52b9e7
5 changed files with 158 additions and 0 deletions
|
|
@ -306,6 +306,28 @@ export class DomainPopupView extends AbstractViewPopup {
|
|||
this.testingSmtpError(false);
|
||||
}
|
||||
|
||||
autoconfig() {
|
||||
let domain = this.name();
|
||||
Remote.request('AdminDomainAutoconfig', (iError, oData) => {
|
||||
console.dir({iError, oData});
|
||||
if (!iError) {
|
||||
let server = oData.Result.config.incomingServer[0];
|
||||
this.imapHost(server.hostname);
|
||||
this.imapPort(server.port);
|
||||
this.imapType('STARTTLS' === server.socketType ? 2 : ('SSL' === server.socketType ? 1 : 0));
|
||||
this.imapShortLogin('%EMAILADDRESS%' !== server.username);
|
||||
|
||||
server = oData.Result.config.outgoingServer[0];
|
||||
this.smtpHost(server.hostname);
|
||||
this.smtpPort(server.port);
|
||||
this.smtpType('STARTTLS' === server.socketType ? 2 : ('SSL' === server.socketType ? 1 : 0));
|
||||
this.smtpShortLogin('%EMAILADDRESS%' !== server.username);
|
||||
this.smtpUseAuth(!!server.authentication);
|
||||
this.smtpUsePhpMail(false);
|
||||
}
|
||||
}, {domain});
|
||||
}
|
||||
|
||||
onShow(oDomain) {
|
||||
this.saving(false);
|
||||
this.clearTesting();
|
||||
|
|
|
|||
|
|
@ -74,6 +74,17 @@ trait AdminDomains
|
|||
));
|
||||
}
|
||||
|
||||
public function DoAdminDomainAutoconfig() : array
|
||||
{
|
||||
$this->IsAdminLoggined();
|
||||
$sDomain = $this->GetActionParam('domain');
|
||||
$sEmail = "test@{$sDomain}";
|
||||
return $this->DefaultResponse(array(
|
||||
'email' => $sEmail,
|
||||
'config' => \RainLoop\Providers\Domain\Autoconfig::discover($sEmail)
|
||||
));
|
||||
}
|
||||
|
||||
public function DoAdminDomainTest() : array
|
||||
{
|
||||
$this->IsAdminLoggined();
|
||||
|
|
|
|||
|
|
@ -0,0 +1,108 @@
|
|||
<?php
|
||||
/**
|
||||
* https://datatracker.ietf.org/doc/draft-bucksch-autoconfig/
|
||||
*/
|
||||
|
||||
namespace RainLoop\Providers\Domain;
|
||||
|
||||
abstract class Autoconfig
|
||||
{
|
||||
public static function discover(string $emailaddress)
|
||||
{
|
||||
$domain = \explode('@', $emailaddress, 2)[1];
|
||||
// First try
|
||||
$autoconfig = static::resolve($domain, $emailaddress);
|
||||
// Else try MX
|
||||
if (!$autoconfig) {
|
||||
$regex = '/([^\.]+\.(?:'
|
||||
. \str_replace('.', '\\.', \implode('|', static::publicsuffixes()))
|
||||
. '|[^\.]+'
|
||||
. '))$/';
|
||||
$hostnames = [];
|
||||
foreach (\SnappyMail\DNS::MX($domain) as $hostname) {
|
||||
$mxbasedomain = [];
|
||||
// Extract only the second-level domain from the MX hostname
|
||||
if (\preg_match($regex, $hostname, $mxbasedomain)) {
|
||||
$mxfulldomain = $mxbasedomain = $mxbasedomain[1];
|
||||
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) {
|
||||
$autoconfig = static::resolve($mxfulldomain, $emailaddress);
|
||||
if (!$autoconfig && $mxfulldomain != $mxbasedomain) {
|
||||
$autoconfig = static::resolve($mxbasedomain, $emailaddress);
|
||||
}
|
||||
if ($autoconfig) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $autoconfig;
|
||||
}
|
||||
|
||||
private static function resolve(string $domain, string $emailaddress)
|
||||
{
|
||||
$emailaddress = \urlencode($emailaddress);
|
||||
foreach ([
|
||||
// 4.1. Mail provider
|
||||
"https://autoconfig.{$domain}/.well-known/mail-v1.xml?emailaddress={$emailaddress}",
|
||||
"https://{$domain}/.well-known/autoconfig/mail/config-v1.1.xml",
|
||||
"http://autoconfig.{$domain}/mail/config-v1.1.xml",
|
||||
// 4.2. Central database
|
||||
"https://autoconfig.thunderbird.net/v1.1/{$domain}"
|
||||
] as $url) {
|
||||
$data = \file_get_contents($url);
|
||||
if ($data && \str_contains($data, '<clientConfig')) {
|
||||
$data = \json_decode(
|
||||
\json_encode(
|
||||
\simplexml_load_string($data, 'SimpleXMLElement', \LIBXML_NOCDATA)
|
||||
), true);
|
||||
if (!empty($data['emailProvider'])) {
|
||||
$data = $data['emailProvider'];
|
||||
unset($data['documentation']);
|
||||
$data['incomingServer'] = \array_filter(
|
||||
isset($data['incomingServer'][0]) ? $data['incomingServer'] : [$data['incomingServer']],
|
||||
fn($data) => 'imap' === $data['@attributes']['type']
|
||||
);
|
||||
$data['outgoingServer'] = \array_filter(
|
||||
isset($data['outgoingServer'][0]) ? $data['outgoingServer'] : [$data['outgoingServer']],
|
||||
fn($data) => 'smtp' === $data['@attributes']['type']
|
||||
);
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static function publicsuffixes() : array
|
||||
{
|
||||
$oCache = \RainLoop\Api::Actions()->Cacher();
|
||||
$list = $oCache->Get('public_suffix_list') ?: null;
|
||||
if ($list) {
|
||||
$list = \json_decode($list);
|
||||
if ($list[1] < \time()) {
|
||||
$list = null;
|
||||
} else {
|
||||
$list = $list[0];
|
||||
}
|
||||
}
|
||||
if (null === $list) {
|
||||
$list = [];
|
||||
$data = \file_get_contents('https://publicsuffix.org/list/public_suffix_list.dat');
|
||||
if ($data) {
|
||||
$list = \array_filter(
|
||||
\explode("\n", $data),
|
||||
fn($text) => \strlen($text) && '/' !== $text[0]
|
||||
);
|
||||
}
|
||||
// Don't lookup for 24 hours
|
||||
$oCache->Set('public_suffix_list', \json_encode([$list, time() + 86400]));
|
||||
}
|
||||
return $list ?: [];
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -48,4 +48,20 @@ abstract class DNS
|
|||
}
|
||||
return $BIMI;
|
||||
}
|
||||
|
||||
public static function MX(string $domain) : array
|
||||
{
|
||||
$mxhosts = array();
|
||||
$values = \dns_get_record($domain, \DNS_MX);
|
||||
if ($values) {
|
||||
foreach ($values as $record) {
|
||||
$mxhosts[$record['pri']] = $record['target'];
|
||||
}
|
||||
}
|
||||
if (!$mxhosts) {
|
||||
\getmxrr($hostname, $mxhosts);
|
||||
}
|
||||
\ksort($mxhosts);
|
||||
return \array_values($mxhosts);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@
|
|||
<input name="Name" type="text" class="span4" autofocus="" autocomplete="off" autocorrect="off" autocapitalize="off"
|
||||
data-bind="textInput: name">
|
||||
<div class="alert-error" data-bind="visible: savingError, text: savingError"></div>
|
||||
<button type="button" data-bind="click: autoconfig">autoconfig</button>
|
||||
</div>
|
||||
<div class="span5 domain-desc" data-bind="visible: domainDesc, html: domainDesc"></div>
|
||||
</div>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue