Changes for BIMI-Selector #1394

This commit is contained in:
the-djmaze 2024-01-22 00:01:46 +01:00
parent 8292c47f7c
commit 58955f792a
5 changed files with 28 additions and 10 deletions

View file

@ -39,9 +39,15 @@
avatars = new Map, avatars = new Map,
ncAvatars = new Map, ncAvatars = new Map,
templateId = 'MailMessageView', templateId = 'MailMessageView',
getBimiSelector = msg => {
// Get 's' value out of 'v=BIMI1; s=foo;'
let bimiSelector = msg.headers().valueByName('BIMI-Selector');
bimiSelector = bimiSelector ? bimiSelector.match(/;.*s=([^\s;]+)/)[1] : '';
return bimiSelector || '';
},
getAvatarUid = msg => { getAvatarUid = msg => {
let from = msg.from[0], let from = msg.from[0],
bimi = 'pass' == from.dkimStatus ? 1 : 0; bimi = ('pass' == from.dkimStatus ? 1 : 0) + '-' + getBimiSelector(msg);
return `${bimi}/${from.email.toLowerCase()}`; return `${bimi}/${from.email.toLowerCase()}`;
}, },
getAvatar = msg => ncAvatars.get(msg.from[0].email.toLowerCase()) || avatars.get(getAvatarUid(msg)), getAvatar = msg => ncAvatars.get(msg.from[0].email.toLowerCase()) || avatars.get(getAvatarUid(msg)),
@ -97,6 +103,7 @@
runQueue(); runQueue();
}, 'Avatar', { }, 'Avatar', {
bimi: 'pass' == from.dkimStatus ? 1 : 0, bimi: 'pass' == from.dkimStatus ? 1 : 0,
bimiSelector: getBimiSelector(item[0]),
email: from.email email: from.email
}); });
break; break;

View file

@ -84,8 +84,9 @@ class AvatarsPlugin extends \RainLoop\Plugins\AbstractPlugin
public function DoAvatar() : array public function DoAvatar() : array
{ {
$bBimi = !empty($this->jsonParam('bimi')); $bBimi = !empty($this->jsonParam('bimi'));
$sBimiSelector = $this->jsonParam('bimiSelector') ?: '';
$sEmail = $this->jsonParam('email'); $sEmail = $this->jsonParam('email');
$aResult = $this->getAvatar($sEmail, !empty($bBimi)); $aResult = $this->getAvatar($sEmail, $bBimi, $sBimiSelector);
if ($aResult) { if ($aResult) {
$aResult = [ $aResult = [
'type' => $aResult[0], 'type' => $aResult[0],
@ -103,7 +104,9 @@ class AvatarsPlugin extends \RainLoop\Plugins\AbstractPlugin
public function ServiceAvatar(string $sServiceName, string $sBimi, string $sEmail) public function ServiceAvatar(string $sServiceName, string $sBimi, string $sEmail)
{ {
$sEmail = \SnappyMail\Crypt::DecryptUrlSafe($sEmail); $sEmail = \SnappyMail\Crypt::DecryptUrlSafe($sEmail);
if ($sEmail && ($aResult = $this->getAvatar($sEmail, !empty($sBimi)))) { $aBimi = \explode('-', $sBimi, 2);
$sBimiSelector = isset($aBimi[1]) ? $aBimi[1] : 'default';
if ($sEmail && ($aResult = $this->getAvatar($sEmail, !empty($aBimi[0]), $sBimiSelector))) {
\header('Content-Type: '.$aResult[0]); \header('Content-Type: '.$aResult[0]);
echo $aResult[1]; echo $aResult[1];
} else { } else {
@ -190,7 +193,7 @@ class AvatarsPlugin extends \RainLoop\Plugins\AbstractPlugin
return null; return null;
} }
private function getAvatar(string $sEmail, bool $bBimi) : ?array private function getAvatar(string $sEmail, bool $bBimi, string $sBimiSelector = '') : ?array
{ {
if (!\strpos($sEmail, '@')) { if (!\strpos($sEmail, '@')) {
return null; return null;
@ -235,7 +238,7 @@ class AvatarsPlugin extends \RainLoop\Plugins\AbstractPlugin
$aUrls = []; $aUrls = [];
if ($this->Config()->Get('plugin', 'bimi', false)) { if ($this->Config()->Get('plugin', 'bimi', false)) {
$BIMI = $bBimi ? \SnappyMail\DNS::BIMI($sDomain) : null; $BIMI = $bBimi ? \SnappyMail\DNS::BIMI($sDomain, $sBimiSelector) : null;
if ($BIMI) { if ($BIMI) {
$aUrls[] = $BIMI; $aUrls[] = $BIMI;
// $aResult = ['text/uri-list', $BIMI]; // $aResult = ['text/uri-list', $BIMI];

View file

@ -75,6 +75,8 @@ class MailClient
MimeHeader::LIST_UNSUBSCRIBE, MimeHeader::LIST_UNSUBSCRIBE,
// https://autocrypt.org/level1.html#the-autocrypt-header // https://autocrypt.org/level1.html#the-autocrypt-header
MimeHeader::AUTOCRYPT, MimeHeader::AUTOCRYPT,
// https://www.ietf.org/archive/id/draft-brand-indicators-for-message-identification-04.html#bimi-selector
MimeHeader::BIMI_SELECTOR,
// SPAM // SPAM
MimeHeader::X_SPAM_STATUS, MimeHeader::X_SPAM_STATUS,
MimeHeader::X_SPAM_FLAG, MimeHeader::X_SPAM_FLAG,

View file

@ -93,5 +93,8 @@ abstract class Header
// https://autocrypt.org/level1.html#the-autocrypt-header // https://autocrypt.org/level1.html#the-autocrypt-header
AUTOCRYPT = 'Autocrypt', AUTOCRYPT = 'Autocrypt',
// https://www.ietf.org/archive/id/draft-brand-indicators-for-message-identification-04.html#bimi-selector
BIMI_SELECTOR = 'BIMI-Selector',
LIST_UNSUBSCRIBE = 'List-Unsubscribe'; LIST_UNSUBSCRIBE = 'List-Unsubscribe';
} }

View file

@ -6,12 +6,15 @@ abstract class DNS
{ {
/** /**
* $domain = 'bimigroup.org' * $domain = 'bimigroup.org'
* $selector = 'default'
* Then a TXT lookup is done on 'default._bimi.bimigroup.org' * Then a TXT lookup is done on 'default._bimi.bimigroup.org'
*/ */
public static function BIMI(string $domain) : string public static function BIMI(string $domain, string $selector = 'default') : string
{ {
$selector = \trim($selector) ?: 'default';
$oCache = \RainLoop\Api::Actions()->Cacher(); $oCache = \RainLoop\Api::Actions()->Cacher();
$BIMI = $oCache->Get("dns-bimi-{$domain}") ?: null; $sCacheKey = "dns-bimi-{$domain}-{$selector}";
$BIMI = $oCache->Get($sCacheKey) ?: null;
if ($BIMI) { if ($BIMI) {
$BIMI = \json_decode($BIMI); $BIMI = \json_decode($BIMI);
if ($BIMI[1] < \time()) { if ($BIMI[1] < \time()) {
@ -22,12 +25,12 @@ abstract class DNS
} }
if (null === $BIMI) { if (null === $BIMI) {
$BIMI = ''; $BIMI = '';
$values = \dns_get_record("default._bimi.{$domain}", \DNS_TXT); $values = \dns_get_record("{$selector}._bimi.{$domain}", \DNS_TXT);
if ($values) { if ($values) {
foreach ($values as $value) { foreach ($values as $value) {
if (\str_starts_with($value['txt'], 'v=BIMI1')) { if (\str_starts_with($value['txt'], 'v=BIMI1')) {
$BIMI = \preg_replace('/^.+l=([^;]+)(;.*)?$/D', '$1', $value['txt']); $BIMI = \preg_replace('/^.+l=([^;]+)(;.*)?$/D', '$1', $value['txt']);
$oCache->Set("dns-bimi-{$domain}", \json_encode([ $oCache->Set($sCacheKey, \json_encode([
$BIMI, $BIMI,
time() + $value['ttl'] time() + $value['ttl']
])); ]));
@ -37,7 +40,7 @@ abstract class DNS
} }
if (!$BIMI) { if (!$BIMI) {
// Don't lookup for 24 hours // Don't lookup for 24 hours
$oCache->Set("dns-bimi-{$domain}", \json_encode([ $oCache->Set($sCacheKey, \json_encode([
$BIMI, $BIMI,
time() + 86400 time() + 86400
])); ]));