Changes for #714

This commit is contained in:
the-djmaze 2022-11-27 16:05:44 +01:00
parent a085bbb51d
commit e5ed52b79e
5 changed files with 55 additions and 39 deletions

View file

@ -105,6 +105,7 @@ export class AbstractModel {
// fall through // fall through
case 'undefined': case 'undefined':
default: default:
this[key] = value;
// console.log((typeof this[key])+' '+(model.name)+'.'+key+' not revived'); // console.log((typeof this[key])+' '+(model.name)+'.'+key+' not revived');
} }
} catch (e) { } catch (e) {

View file

@ -1,5 +1,4 @@
(rl => { (rl => {
// if (rl.settings.get('Nextcloud'))
const const
queue = [], queue = [],
avatars = new Map, avatars = new Map,
@ -45,6 +44,9 @@
fn = url=>{element.src = url}; fn = url=>{element.src = url};
if (url) { if (url) {
fn(url); fn(url);
} else if (msg.avatar) {
let bimi = 'pass' == msg.from[0].dkimStatus ? 1 : 0;
fn(`?Avatar/${bimi}/${msg.avatar}`);
} else { } else {
queue.push([msg, fn]); queue.push([msg, fn]);
runQueue(); runQueue();
@ -80,6 +82,9 @@
}; };
if (url) { if (url) {
fn(url); fn(url);
} else if (msg.avatar) {
let bimi = 'pass' == msg.from[0].dkimStatus ? 1 : 0;
fn(`?Avatar/${bimi}/${msg.avatar}`);
} else { } else {
// let from = msg.from[0], bimi = 'pass' == from.dkimStatus ? 1 : 0; // let from = msg.from[0], bimi = 'pass' == from.dkimStatus ? 1 : 0;
// view.viewUserPic(`?Avatar/${bimi}/${encodeURIComponent(from.email)}`); // view.viewUserPic(`?Avatar/${bimi}/${encodeURIComponent(from.email)}`);

View file

@ -3,10 +3,10 @@
class AvatarsPlugin extends \RainLoop\Plugins\AbstractPlugin class AvatarsPlugin extends \RainLoop\Plugins\AbstractPlugin
{ {
const const
NAME = 'Avatar', NAME = 'Avatars',
AUTHOR = 'SnappyMail', AUTHOR = 'SnappyMail',
URL = 'https://snappymail.eu/', URL = 'https://snappymail.eu/',
VERSION = '1.0', VERSION = '1.1',
RELEASE = '2022-11-23', RELEASE = '2022-11-23',
REQUIRED = '2.22.0', REQUIRED = '2.22.0',
CATEGORY = 'Contacts', CATEGORY = 'Contacts',
@ -19,6 +19,29 @@ class AvatarsPlugin extends \RainLoop\Plugins\AbstractPlugin
$this->addJs('avatars.js'); $this->addJs('avatars.js');
$this->addJsonHook('Avatar', 'DoAvatar'); $this->addJsonHook('Avatar', 'DoAvatar');
$this->addPartHook('Avatar', 'ServiceAvatar'); $this->addPartHook('Avatar', 'ServiceAvatar');
// TODO: https://github.com/the-djmaze/snappymail/issues/714
// $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 +51,7 @@ class AvatarsPlugin extends \RainLoop\Plugins\AbstractPlugin
{ {
$bBimi = !empty($this->jsonParam('bimi')); $bBimi = !empty($this->jsonParam('bimi'));
$sEmail = $this->jsonParam('email'); $sEmail = $this->jsonParam('email');
$aResult = $this->getAvatar(\urldecode($sEmail), !empty($sEmail)); $aResult = $this->getAvatar($sEmail, !empty($bBimi));
if ($aResult) { if ($aResult) {
$aResult = [ $aResult = [
'type' => $aResult[0], 'type' => $aResult[0],
@ -39,15 +62,17 @@ class AvatarsPlugin extends \RainLoop\Plugins\AbstractPlugin
} }
/** /**
* GET /?Avatar/${bimi}/${from.email} * GET /?Avatar/${bimi}/Encrypted(${from.email})
* Not fond of this idea because email address is exposed * Nextcloud Mail uses insecure unencrypted 'index.php/apps/mail/api/avatars/url/local%40example.com'
* Maybe use btoa(from.email) or Crypto.subtle.encrypt({name:'AES-GCM',iv:''}, token, from.email)
*/ */
// public function ServiceAvatar(...$aParts) // public function ServiceAvatar(...$aParts)
public function ServiceAvatar(string $sServiceName, string $sBimi, string $sEmail) public function ServiceAvatar(string $sServiceName, string $sBimi, string $sEmail)
{ {
$aResult = $this->getAvatar(\urldecode($sEmail), !empty($sEmail)); $sEmail = \SnappyMail\Crypt::DecryptUrlSafe($sEmail);
if ($aResult) { $oActions = \RainLoop\Api::Actions();
$oActions->verifyCacheByKey($sEmail, true);
if ($sEmail && ($aResult = $this->getAvatar($sEmail, !empty($sBimi)))) {
$oActions->Http()->ServerUseCache($oActions->etag($sEmail), \time(), \time() + 86400);
\header('Content-Type: '.$aResult[0]); \header('Content-Type: '.$aResult[0]);
echo $aResult[1]; echo $aResult[1];
return true; return true;
@ -74,7 +99,7 @@ class AvatarsPlugin extends \RainLoop\Plugins\AbstractPlugin
} }
$oActions = \RainLoop\Api::Actions(); $oActions = \RainLoop\Api::Actions();
$oActions->verifyCacheByKey($sEmail); // $oActions->verifyCacheByKey($sEmail, true);
$aResult = null; $aResult = null;
@ -88,7 +113,7 @@ class AvatarsPlugin extends \RainLoop\Plugins\AbstractPlugin
\mime_content_type($aFiles[0]), \mime_content_type($aFiles[0]),
\file_get_contents($aFiles[0]) \file_get_contents($aFiles[0])
]; ];
$oActions->cacheByKey($sEmail); // $oActions->Http()->ServerUseCache($oActions->etag($sEmail), \time(), \time() + 86400);
return $aResult; return $aResult;
} }
@ -167,7 +192,7 @@ class AvatarsPlugin extends \RainLoop\Plugins\AbstractPlugin
} }
} }
$oActions->cacheByKey($sEmail); // $oActions->Http()->ServerUseCache($oActions->etag($sEmail), \time(), \time() + 86400);
return $aResult; return $aResult;
} }

View file

@ -18,30 +18,20 @@ namespace MailSo\Cache\Drivers;
*/ */
class File implements \MailSo\Cache\DriverInterface class File implements \MailSo\Cache\DriverInterface
{ {
/** private string $sCacheFolder;
* @var string
*/
private $sCacheFolder;
/** private string $sKeyPrefix = '';
* @var string
*/
private $sKeyPrefix;
function __construct(string $sCacheFolder, string $sKeyPrefix = '') function __construct(string $sCacheFolder, string $sKeyPrefix = '')
{ {
$this->sCacheFolder = $sCacheFolder; $this->sCacheFolder = \rtrim(\trim($sCacheFolder), '\\/').'/';
$this->sCacheFolder = rtrim(trim($this->sCacheFolder), '\\/').'/'; if (!empty($sKeyPrefix)) {
$sKeyPrefix = \str_pad(\preg_replace('/[^a-zA-Z0-9_]/', '_',
$this->sKeyPrefix = $sKeyPrefix; \rtrim(\trim($sKeyPrefix), '\\/')), 5, '_');
if (!empty($this->sKeyPrefix))
{
$this->sKeyPrefix = \str_pad(\preg_replace('/[^a-zA-Z0-9_]/', '_',
rtrim(trim($this->sKeyPrefix), '\\/')), 5, '_');
$this->sKeyPrefix = '__/'. $this->sKeyPrefix = '__/'.
\substr($this->sKeyPrefix, 0, 2).'/'.\substr($this->sKeyPrefix, 2, 2).'/'. \substr($sKeyPrefix, 0, 2).'/'.\substr($sKeyPrefix, 2, 2).'/'.
$this->sKeyPrefix.'/'; $sKeyPrefix.'/';
} }
} }

View file

@ -1067,25 +1067,20 @@ class Actions
public function cacheByKey(string $sKey, bool $bForce = false): bool public function cacheByKey(string $sKey, bool $bForce = false): bool
{ {
$bResult = false;
if (!empty($sKey) && ($bForce || ($this->oConfig->Get('cache', 'enable', true) && $this->oConfig->Get('cache', 'http', true)))) { if (!empty($sKey) && ($bForce || ($this->oConfig->Get('cache', 'enable', true) && $this->oConfig->Get('cache', 'http', true)))) {
$iExpires = $this->oConfig->Get('cache', 'http_expires', 3600); $iExpires = $this->oConfig->Get('cache', 'http_expires', 3600);
if (0 < $iExpires) { if (0 < $iExpires) {
$this->Http()->ServerUseCache($this->etag($sKey), 1382478804, \time() + $iExpires); $this->Http()->ServerUseCache($this->etag($sKey), 1382478804, \time() + $iExpires);
$bResult = true; return true;
} }
} }
if (!$bResult) {
$this->Http()->ServerNoCache(); $this->Http()->ServerNoCache();
} return false;
return $bResult;
} }
public function verifyCacheByKey(string $sKey, bool $bForce = false): void public function verifyCacheByKey(string $sKey, bool $bForce = false): void
{ {
if (!empty($sKey) && ($bForce || $this->oConfig->Get('cache', 'enable', true) && $this->oConfig->Get('cache', 'http', true))) { if (!empty($sKey) && ($bForce || ($this->oConfig->Get('cache', 'enable', true) && $this->oConfig->Get('cache', 'http', true)))) {
$sIfNoneMatch = $this->Http()->GetHeader('If-None-Match', ''); $sIfNoneMatch = $this->Http()->GetHeader('If-None-Match', '');
if ($this->etag($sKey) === $sIfNoneMatch) { if ($this->etag($sKey) === $sIfNoneMatch) {
\MailSo\Base\Http::StatusHeader(304); \MailSo\Base\Http::StatusHeader(304);