mirror of
https://github.com/the-djmaze/snappymail.git
synced 2026-08-30 20:49:20 +03:00
Merge branch 'the-djmaze:master' into master
This commit is contained in:
commit
dc07313869
29 changed files with 683 additions and 528 deletions
|
|
@ -1,5 +1,4 @@
|
|||
(rl => {
|
||||
// if (rl.settings.get('Nextcloud'))
|
||||
const
|
||||
queue = [],
|
||||
avatars = new Map,
|
||||
|
|
@ -45,6 +44,9 @@
|
|||
fn = url=>{element.src = url};
|
||||
if (url) {
|
||||
fn(url);
|
||||
} else if (msg.avatar) {
|
||||
let bimi = 'pass' == msg.from[0].dkimStatus ? 1 : 0;
|
||||
fn(`?Avatar/${bimi}/${msg.avatar}`);
|
||||
} else {
|
||||
queue.push([msg, fn]);
|
||||
runQueue();
|
||||
|
|
@ -62,7 +64,7 @@
|
|||
|
||||
if (messageItemHeader) {
|
||||
messageItemHeader.prepend(Element.fromHTML(
|
||||
`<img class="fromPic" data-bind="visible: viewUserPicVisible, attr: {'src': viewUserPic() }">`
|
||||
`<img class="fromPic" data-bind="visible: viewUserPicVisible, attr: {'src': viewUserPic() }" loading="lazy">`
|
||||
));
|
||||
}
|
||||
|
||||
|
|
@ -80,6 +82,9 @@
|
|||
};
|
||||
if (url) {
|
||||
fn(url);
|
||||
} else if (msg.avatar) {
|
||||
let bimi = 'pass' == msg.from[0].dkimStatus ? 1 : 0;
|
||||
fn(`?Avatar/${bimi}/${msg.avatar}`);
|
||||
} else {
|
||||
// let from = msg.from[0], bimi = 'pass' == from.dkimStatus ? 1 : 0;
|
||||
// view.viewUserPic(`?Avatar/${bimi}/${encodeURIComponent(from.email)}`);
|
||||
|
|
@ -93,7 +98,7 @@
|
|||
|
||||
if ('MailMessageList' === e.detail.viewModelTemplateID) {
|
||||
document.getElementById('MailMessageList').content.querySelector('.messageCheckbox')
|
||||
.append(Element.fromHTML(`<img class="fromPic" data-bind="fromPic:$data">`));
|
||||
.append(Element.fromHTML(`<img class="fromPic" data-bind="fromPic:$data" loading="lazy">`));
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -3,12 +3,12 @@
|
|||
class AvatarsPlugin extends \RainLoop\Plugins\AbstractPlugin
|
||||
{
|
||||
const
|
||||
NAME = 'Avatar',
|
||||
NAME = 'Avatars',
|
||||
AUTHOR = 'SnappyMail',
|
||||
URL = 'https://snappymail.eu/',
|
||||
VERSION = '1.0',
|
||||
RELEASE = '2022-11-23',
|
||||
REQUIRED = '2.22.0',
|
||||
VERSION = '1.1',
|
||||
RELEASE = '2022-11-27',
|
||||
REQUIRED = '2.22.4',
|
||||
CATEGORY = 'Contacts',
|
||||
LICENSE = 'MIT',
|
||||
DESCRIPTION = 'Show photo of sender in message and messages list (supports BIMI and Gravatar, Contacts is still TODO)';
|
||||
|
|
@ -19,6 +19,30 @@ class AvatarsPlugin extends \RainLoop\Plugins\AbstractPlugin
|
|||
$this->addJs('avatars.js');
|
||||
$this->addJsonHook('Avatar', 'DoAvatar');
|
||||
$this->addPartHook('Avatar', 'ServiceAvatar');
|
||||
// https://github.com/the-djmaze/snappymail/issues/714
|
||||
$this->Config()->Get('plugin', 'delay', true)
|
||||
|| $this->addHook('filter.json-response', 'FilterJsonResponse');
|
||||
}
|
||||
|
||||
public function FilterJsonResponse(string $sAction, array &$aResponseItem)
|
||||
{
|
||||
if ('MessageList' === $sAction && !empty($aResponseItem['Result']['@Collection'])) {
|
||||
foreach ($aResponseItem['Result']['@Collection'] as $id => $message) {
|
||||
$aResponseItem['Result']['@Collection'][$id]['Avatar'] = static::encryptFrom($message['From'][0]);
|
||||
}
|
||||
} else if ('Message' === $sAction && !empty($aResponseItem['Result']['From'])) {
|
||||
$aResponseItem['Result']['Avatar'] = static::encryptFrom($aResponseItem['Result']['From'][0]);
|
||||
}
|
||||
}
|
||||
|
||||
private static function encryptFrom($mFrom)
|
||||
{
|
||||
if ($mFrom instanceof \MailSo\Mime\Email) {
|
||||
$mFrom = $mFrom->jsonSerialize();
|
||||
}
|
||||
return \is_array($mFrom)
|
||||
? \SnappyMail\Crypt::EncryptUrlSafe($mFrom['Email'])
|
||||
: null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -28,7 +52,7 @@ class AvatarsPlugin extends \RainLoop\Plugins\AbstractPlugin
|
|||
{
|
||||
$bBimi = !empty($this->jsonParam('bimi'));
|
||||
$sEmail = $this->jsonParam('email');
|
||||
$aResult = $this->getAvatar(\urldecode($sEmail), !empty($sEmail));
|
||||
$aResult = $this->getAvatar($sEmail, !empty($bBimi));
|
||||
if ($aResult) {
|
||||
$aResult = [
|
||||
'type' => $aResult[0],
|
||||
|
|
@ -39,15 +63,14 @@ class AvatarsPlugin extends \RainLoop\Plugins\AbstractPlugin
|
|||
}
|
||||
|
||||
/**
|
||||
* GET /?Avatar/${bimi}/${from.email}
|
||||
* Not fond of this idea because email address is exposed
|
||||
* Maybe use btoa(from.email) or Crypto.subtle.encrypt({name:'AES-GCM',iv:''}, token, from.email)
|
||||
* GET /?Avatar/${bimi}/Encrypted(${from.email})
|
||||
* Nextcloud Mail uses insecure unencrypted 'index.php/apps/mail/api/avatars/url/local%40example.com'
|
||||
*/
|
||||
// public function ServiceAvatar(...$aParts)
|
||||
public function ServiceAvatar(string $sServiceName, string $sBimi, string $sEmail)
|
||||
{
|
||||
$aResult = $this->getAvatar(\urldecode($sEmail), !empty($sEmail));
|
||||
if ($aResult) {
|
||||
$sEmail = \SnappyMail\Crypt::DecryptUrlSafe($sEmail);
|
||||
if ($sEmail && ($aResult = $this->getAvatar($sEmail, !empty($sBimi)))) {
|
||||
\header('Content-Type: '.$aResult[0]);
|
||||
echo $aResult[1];
|
||||
return true;
|
||||
|
|
@ -58,6 +81,9 @@ class AvatarsPlugin extends \RainLoop\Plugins\AbstractPlugin
|
|||
protected function configMapping() : array
|
||||
{
|
||||
return array(
|
||||
\RainLoop\Plugins\Property::NewInstance('delay')->SetLabel('Delay loading')
|
||||
->SetType(\RainLoop\Enumerations\PluginPropertyType::BOOL)
|
||||
->SetDefaultValue(true),
|
||||
\RainLoop\Plugins\Property::NewInstance('bimi')->SetLabel('Use BIMI (https://bimigroup.org/)')
|
||||
->SetType(\RainLoop\Enumerations\PluginPropertyType::BOOL)
|
||||
->SetDefaultValue(false),
|
||||
|
|
@ -73,28 +99,30 @@ class AvatarsPlugin extends \RainLoop\Plugins\AbstractPlugin
|
|||
return null;
|
||||
}
|
||||
|
||||
$oActions = \RainLoop\Api::Actions();
|
||||
$oActions->verifyCacheByKey($sEmail);
|
||||
$sAsciiEmail = \mb_strtolower(\MailSo\Base\Utils::IdnToAscii($sEmail, true));
|
||||
$sEmailId = \sha1($sAsciiEmail);
|
||||
|
||||
\MailSo\Base\Http::setETag($sEmailId);
|
||||
\header('Cache-Control: private');
|
||||
// \header('Expires: '.\gmdate('D, j M Y H:i:s', \time() + 86400).' UTC');
|
||||
|
||||
$aResult = null;
|
||||
|
||||
$sAsciiEmail = \MailSo\Base\Utils::IdnToAscii($sEmail, true);
|
||||
$sEmailId = \sha1(\strtolower($sAsciiEmail));
|
||||
|
||||
$sFile = \APP_PRIVATE_DATA . 'avatars/' . $sEmailId;
|
||||
$aFiles = \glob("{$sFile}.*");
|
||||
if ($aFiles) {
|
||||
\MailSo\Base\Http::setLastModified(\filemtime($aFiles[0]));
|
||||
$aResult = [
|
||||
\mime_content_type($aFiles[0]),
|
||||
\file_get_contents($aFiles[0])
|
||||
];
|
||||
$oActions->cacheByKey($sEmail);
|
||||
return $aResult;
|
||||
}
|
||||
|
||||
// TODO: lookup contacts vCard and return PHOTO value
|
||||
/*
|
||||
if (!$aResult) {
|
||||
$oActions = \RainLoop\Api::Actions();
|
||||
$oAccount = $oActions->getAccountFromToken();
|
||||
if ($oAccount) {
|
||||
$oAddressBookProvider = $oActions->AddressBookProvider($oAccount);
|
||||
|
|
@ -147,6 +175,7 @@ class AvatarsPlugin extends \RainLoop\Plugins\AbstractPlugin
|
|||
$sFile . \SnappyMail\File\MimeType::toExtension($aResult[0]),
|
||||
$aResult[1]
|
||||
);
|
||||
\MailSo\Base\Http::setLastModified(\time());
|
||||
}
|
||||
|
||||
if (!$aResult) {
|
||||
|
|
@ -157,18 +186,18 @@ class AvatarsPlugin extends \RainLoop\Plugins\AbstractPlugin
|
|||
'empty-contact' // DATA_IMAGE_USER_DOT_PIC
|
||||
];
|
||||
foreach ($aServices as $service) {
|
||||
if (\file_exists(__DIR__ . "/images/{$service}.png")) {
|
||||
$file = __DIR__ . "/images/{$service}.png";
|
||||
if (\file_exists($file)) {
|
||||
\MailSo\Base\Http::setLastModified(\filemtime($file));
|
||||
$aResult = [
|
||||
'image/png',
|
||||
\file_get_contents(__DIR__ . "/images/{$service}.png")
|
||||
\file_get_contents($file)
|
||||
];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$oActions->cacheByKey($sEmail);
|
||||
|
||||
return $aResult;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ class LoginRemotePlugin extends \RainLoop\Plugins\AbstractPlugin
|
|||
NAME = 'Login Remote',
|
||||
AUTHOR = 'SnappyMail',
|
||||
URL = 'https://snappymail.eu/',
|
||||
VERSION = '1.1',
|
||||
VERSION = '1.2',
|
||||
RELEASE = '2022-11-24',
|
||||
REQUIRED = '2.21.0',
|
||||
CATEGORY = 'Login',
|
||||
|
|
|
|||
|
|
@ -4,8 +4,8 @@ class NextcloudPlugin extends \RainLoop\Plugins\AbstractPlugin
|
|||
{
|
||||
const
|
||||
NAME = 'Nextcloud',
|
||||
VERSION = '2.11',
|
||||
RELEASE = '2022-11-14',
|
||||
VERSION = '2.12',
|
||||
RELEASE = '2022-11-24',
|
||||
CATEGORY = 'Integrations',
|
||||
DESCRIPTION = 'Integrate with Nextcloud v20+',
|
||||
REQUIRED = '2.19.0';
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue