This commit is contained in:
cm-schl 2023-01-11 16:48:14 +01:00
commit f10f93fd8f
18 changed files with 137 additions and 128 deletions

View file

@ -62,7 +62,7 @@ export const
* @returns {?FolderModel} * @returns {?FolderModel}
*/ */
getFolderFromCacheList = folderFullName => getFolderFromCacheList = folderFullName =>
FOLDERS_CACHE[folderFullName] ? FOLDERS_CACHE[folderFullName] : null, FOLDERS_CACHE[folderFullName] || null,
/** /**
* @param {string} folderFullName * @param {string} folderFullName

View file

@ -9,7 +9,7 @@ import { SettingsUserStore } from 'Stores/User/Settings';
import { FolderUserStore } from 'Stores/User/Folder'; import { FolderUserStore } from 'Stores/User/Folder';
import { MessagelistUserStore } from 'Stores/User/Messagelist'; import { MessagelistUserStore } from 'Stores/User/Messagelist';
import { getNotification } from 'Common/Translator'; import { getNotification } from 'Common/Translator';
import { Settings, } from 'Common/Globals'; import { Settings } from 'Common/Globals';
import { serverRequest } from 'Common/Links'; import { serverRequest } from 'Common/Links';
import Remote from 'Remote/User/Fetch'; import Remote from 'Remote/User/Fetch';
@ -27,37 +27,6 @@ sortFolders = folders => {
} }
}, },
/**
* @param {?Function} fCallback
* @param {string} folder
* @param {Array=} list = []
*/
fetchFolderInformation = (fCallback, folder, list = []) => {
let fetch = !arrayLength(list);
const uids = [],
folderFromCache = getFolderFromCacheList(folder);
if (!fetch) {
list.forEach(messageListItem => {
MessageFlagsCache.getFor(folder, messageListItem.uid) || uids.push(messageListItem.uid);
messageListItem.threads.forEach(uid => {
MessageFlagsCache.getFor(folder, uid) || uids.push(uid);
});
});
fetch = uids.length;
}
if (fetch) {
Remote.request('FolderInformation', fCallback, {
Folder: folder,
FlagsUids: uids,
UidNext: folderFromCache?.uidNext || 0 // Used to check for new messages
});
} else if (SettingsUserStore.useThreads()) {
MessagelistUserStore.reloadFlagsAndCachedMessage();
}
},
/** /**
* @param {Array=} aDisabled * @param {Array=} aDisabled
* @param {Array=} aHeaderLines * @param {Array=} aHeaderLines
@ -127,8 +96,22 @@ refreshFoldersInterval = 300000,
*/ */
folderInformation = (folder, list) => { folderInformation = (folder, list) => {
if (folder?.trim()) { if (folder?.trim()) {
fetchFolderInformation( let fetch = !arrayLength(list);
(iError, data) => { const uids = [],
folderFromCache = getFolderFromCacheList(folder);
if (!fetch) {
list.forEach(messageListItem => {
MessageFlagsCache.getFor(folder, messageListItem.uid) || uids.push(messageListItem.uid);
messageListItem.threads.forEach(uid => {
MessageFlagsCache.getFor(folder, uid) || uids.push(uid);
});
});
fetch = uids.length;
}
if (fetch) {
Remote.request('FolderInformation', (iError, data) => {
if (!iError && data.Result) { if (!iError && data.Result) {
const result = data.Result, const result = data.Result,
folderFromCache = getFolderFromCacheList(result.Folder); folderFromCache = getFolderFromCacheList(result.Folder);
@ -159,15 +142,20 @@ folderInformation = (folder, list) => {
if (folderFromCache.fullName === FolderUserStore.currentFolderFullName()) { if (folderFromCache.fullName === FolderUserStore.currentFolderFullName()) {
MessagelistUserStore.reload(); MessagelistUserStore.reload();
} else if (getFolderInboxName() === folderFromCache.fullName) { } else if (getFolderInboxName() === folderFromCache.fullName) {
Remote.messageList(null, {Folder: getFolderInboxName()}, true); // Remote.messageList(null, {folder: getFolderFromCacheList(getFolderInboxName())}, true);
Remote.messageList(null, {folder: getFolderInboxName()}, true);
} }
} }
} }
} }
}, }, {
folder, Folder: folder,
list FlagsUids: uids,
); UidNext: folderFromCache?.uidNext || 0 // Used to check for new messages
});
} else if (SettingsUserStore.useThreads()) {
MessagelistUserStore.reloadFlagsAndCachedMessage();
}
} }
}, },

View file

@ -130,11 +130,6 @@ export class AbstractFetchRemote
sGetAdd ? null : (params || {}), sGetAdd ? null : (params || {}),
undefined === iTimeout ? 30000 : pInt(iTimeout), undefined === iTimeout ? 30000 : pInt(iTimeout),
data => { data => {
let cached = false;
if (data?.Time) {
cached = pInt(data.Time) > Date.now() - start;
}
let iError = 0; let iError = 0;
if (sAction && oRequests[sAction]) { if (sAction && oRequests[sAction]) {
abort(sAction, 0, 1); abort(sAction, 0, 1);
@ -157,9 +152,12 @@ export class AbstractFetchRemote
fCallback && fCallback( fCallback && fCallback(
iError, iError,
data, data,
cached, /**
sAction, * Responses like "304 Not Modified" are returned as "200 OK"
params * This is an attempt to detect if the request comes from cache.
* But when client has wrong date/time, it will fail.
*/
data?.epoch && data.epoch < Math.floor(start / 1000) - 60
); );
} }
) )
@ -203,8 +201,8 @@ export class AbstractFetchRemote
} }
/* /*
let isCached = false, type = ''; let isCached = false, type = '';
if (data?.Time) { if (data?.epoch) {
isCached = pInt(data.Time) > microtime() - start; isCached = data.epoch > microtime() - start;
} }
// backward capability // backward capability
switch (true) { switch (true) {

View file

@ -22,8 +22,8 @@ class RemoteUserFetch extends AbstractFetchRemote {
*/ */
messageList(fCallback, params, bSilent = false) { messageList(fCallback, params, bSilent = false) {
const const
sFolderFullName = pString(params.Folder), // folder = getFolderFromCacheList(params.folder.fullName),
folder = getFolderFromCacheList(sFolderFullName), folder = getFolderFromCacheList(params.folder),
folderHash = folder?.hash || ''; folderHash = folder?.hash || '';
params = Object.assign({ params = Object.assign({
@ -31,10 +31,8 @@ class RemoteUserFetch extends AbstractFetchRemote {
limit: SettingsUserStore.messagesPerPage(), limit: SettingsUserStore.messagesPerPage(),
search: '', search: '',
uidNext: folder?.uidNext || 0, // Used to check for new messages uidNext: folder?.uidNext || 0, // Used to check for new messages
sort: FolderUserStore.sortMode(), sort: FolderUserStore.sortMode()
Hash: folderHash + SettingsGet('AccountHash')
}, params); }, params);
params.Folder = sFolderFullName;
if (AppUserStore.threadsAllowed() && SettingsUserStore.useThreads()) { if (AppUserStore.threadsAllowed() && SettingsUserStore.useThreads()) {
params.useThreads = 1; params.useThreads = 1;
} else { } else {
@ -42,12 +40,9 @@ class RemoteUserFetch extends AbstractFetchRemote {
} }
let sGetAdd = ''; let sGetAdd = '';
if (folderHash) {
if (folderHash && (!params.search || !params.search.includes('is:'))) { params.hash = folderHash + '-' + SettingsGet('AccountHash');
sGetAdd = 'MessageList/' + sGetAdd = 'MessageList/' + SUB_QUERY_PREFIX + '/' + b64EncodeJSONSafe(params);
SUB_QUERY_PREFIX +
'/' +
b64EncodeJSONSafe(params);
params = {}; params = {};
} }

View file

@ -293,7 +293,8 @@ MessagelistUserStore.reload = (bDropPagePosition = false, bDropCurrentFolderCach
MessagelistUserStore.loading(false); MessagelistUserStore.loading(false);
}, },
{ {
Folder: FolderUserStore.currentFolderFullName(), // folder: FolderUserStore.currentFolder() ? self.currentFolder().fullName : ''),
folder: FolderUserStore.currentFolderFullName(),
offset: iOffset, offset: iOffset,
limit: SettingsUserStore.messagesPerPage(), limit: SettingsUserStore.messagesPerPage(),
search: MessagelistUserStore.listSearch(), search: MessagelistUserStore.listSearch(),

View file

@ -76,7 +76,7 @@ dialog:not(.animate) {
*/ */
.rl-mobile dialog { .rl-mobile dialog {
margin: 0 auto; margin: 0 auto;
max-height: calc(100vh - 86px); /* max-height: calc(100vh - 86px);*/
width: 100%; width: 100%;
} }

View file

@ -210,7 +210,15 @@ export class DomainPopupView extends AbstractViewPopup {
createOrAddCommand() { createOrAddCommand() {
this.saving(true); this.saving(true);
Remote.request('AdminDomainSave', Remote.request('AdminDomainSave',
this.onDomainCreateOrSaveResponse.bind(this), iError => {
this.saving(false);
if (iError) {
this.savingError(getNotification(iError));
} else {
DomainAdminStore.fetch();
this.close();
}
},
Object.assign(domainToParams(this), { Object.assign(domainToParams(this), {
Create: this.edit() ? 0 : 1 Create: this.edit() ? 0 : 1
}) })
@ -263,16 +271,6 @@ export class DomainPopupView extends AbstractViewPopup {
}); });
} }
onDomainCreateOrSaveResponse(iError) {
this.saving(false);
if (iError) {
this.savingError(getNotification(iError));
} else {
DomainAdminStore.fetch();
this.close();
}
}
clearTesting() { clearTesting() {
this.testing(false); this.testing(false);
this.testingDone(false); this.testingDone(false);

View file

@ -55,13 +55,11 @@ window.Sieve = {
deleteScript: script => { deleteScript: script => {
serverError(false); serverError(false);
Remote.request('FiltersScriptDelete', Remote.request('FiltersScriptDelete',
(iError, data) => { (iError, data) =>
if (iError) { iError
setError(data?.ErrorMessageAdditional || getNotification(iError)); ? setError(data?.ErrorMessageAdditional || getNotification(iError))
} else { : scripts.remove(script)
scripts.remove(script); ,
}
},
{name:script.name()} {name:script.name()}
); );
}, },
@ -69,13 +67,11 @@ window.Sieve = {
setActiveScript(name) { setActiveScript(name) {
serverError(false); serverError(false);
Remote.request('FiltersScriptActivate', Remote.request('FiltersScriptActivate',
(iError, data) => { (iError, data) =>
if (iError) { iError
setError(data?.ErrorMessageAdditional || iError) ? setError(data?.ErrorMessageAdditional || iError)
} else { : scripts.forEach(script => script.active(script.name() === name))
scripts.forEach(script => script.active(script.name() === name)); ,
}
},
{name:name} {name:name}
); );
} }

View file

@ -6,8 +6,8 @@ class RecaptchaPlugin extends \RainLoop\Plugins\AbstractPlugin
NAME = 'reCaptcha', NAME = 'reCaptcha',
AUTHOR = 'SnappyMail', AUTHOR = 'SnappyMail',
URL = 'https://snappymail.eu/', URL = 'https://snappymail.eu/',
VERSION = '2.13', VERSION = '2.14',
RELEASE = '2022-12-08', RELEASE = '2023-01-11',
REQUIRED = '2.23', REQUIRED = '2.23',
CATEGORY = 'General', CATEGORY = 'General',
LICENSE = 'MIT', LICENSE = 'MIT',
@ -141,10 +141,12 @@ class RecaptchaPlugin extends \RainLoop\Plugins\AbstractPlugin
public function ContentSecurityPolicy(\SnappyMail\HTTP\CSP $CSP) public function ContentSecurityPolicy(\SnappyMail\HTTP\CSP $CSP)
{ {
$CSP->script[] = 'https://www.google.com/recaptcha/'; // $CSP->script[] = 'https://www.google.com/recaptcha/';
$CSP->script[] = 'https://www.gstatic.com/recaptcha/'; $CSP->script[] = 'https://www.gstatic.com/recaptcha/';
$CSP->frame[] = 'https://www.google.com/recaptcha/'; $CSP->script[] = 'https://www.recaptcha.net/recaptcha/';
$CSP->frame[] = 'https://recaptcha.google.com/recaptcha/'; // $CSP->frame[] = 'https://www.google.com/recaptcha/';
// $CSP->frame[] = 'https://recaptcha.google.com/recaptcha/';
$CSP->frame[] = 'https://www.recaptcha.net/recaptcha/';
} }
} }

View file

@ -582,11 +582,11 @@ class MailClient
$sSerializedHash = ''; $sSerializedHash = '';
$sSerializedLog = ''; $sSerializedLog = '';
if ($bUseCacheAfterSearch) { if ($bUseCacheAfterSearch) {
$sSerializedHash = 'GetUids/'. $sSerializedHash = 'Get'
($bUseSort ? 'S' . $sSort : 'N').'/'. . ($bReturnUid ? 'UIDS/' : 'IDS/')
$this->oImapClient->Hash().'/'. . ($bUseSort ? 'S' . $sSort : 'N')
$sFolderName.'/'.$sSearchCriterias; . "/{$this->oImapClient->Hash()}/{$sFolderName}/{$sSearchCriterias}";
$sSerializedLog = '"'.$sFolderName.'" / '.$sSearchCriterias.''; $sSerializedLog = "\"{$sFolderName}\" / {$sSort} / {$sSearchCriterias}";
$sSerialized = $oCacher->Get($sSerializedHash); $sSerialized = $oCacher->Get($sSerializedHash);
if (!empty($sSerialized)) { if (!empty($sSerialized)) {
@ -596,14 +596,12 @@ class MailClient
\is_array($aSerialized['Uids']) \is_array($aSerialized['Uids'])
) { ) {
if ($this->oLogger) { if ($this->oLogger) {
$this->oLogger->Write('Get Serialized UIDS from cache ('.$sSerializedLog.') [count:'.\count($aSerialized['Uids']).']'); $this->oLogger->Write('Get Serialized '.($bReturnUid?'UIDS':'IDS').' from cache ('.$sSerializedLog.') [count:'.\count($aSerialized['Uids']).']');
} }
if (\is_array($aSerialized['Uids'])) {
return $aSerialized['Uids']; return $aSerialized['Uids'];
} }
} }
} }
}
$aResultUids = []; $aResultUids = [];
if ($bUseSort) { if ($bUseSort) {
@ -631,7 +629,7 @@ class MailClient
))); )));
if ($this->oLogger) { if ($this->oLogger) {
$this->oLogger->Write('Save Serialized UIDS to cache ('.$sSerializedLog.') [count:'.\count($aResultUids).']'); $this->oLogger->Write('Save Serialized '.($bReturnUid?'UIDS':'IDS').' to cache ('.$sSerializedLog.') [count:'.\count($aResultUids).']');
} }
} }

View file

@ -51,4 +51,18 @@ class MessageListParams
// 0 > $oParams->iLimit // 0 > $oParams->iLimit
// 999 < $oParams->iLimit // 999 < $oParams->iLimit
} }
public function hash() : string
{
return \md5(\implode('-', [
$this->sFolderName,
$this->iOffset,
$this->iLimit,
$this->bHideDeleted ? '1' : '0',
$this->sSearch,
$this->bUseSort ? $this->sSort : '',
$this->bUseThreads ? $this->iThreadUid : '',
$this->iPrevUidNext
]));
}
} }

View file

@ -157,7 +157,7 @@ class Header
} }
// https://www.rfc-editor.org/rfc/rfc2822#section-2.1.1 // https://www.rfc-editor.org/rfc/rfc2822#section-2.1.1
return \wordwrap($this->NameWithDelimitrom() . $sValue, 78, "\r\n "); return \wordwrap($this->NameWithDelimitrom() . $sResult, 78, "\r\n ");
} }
public function IsSubject() : bool public function IsSubject() : bool

View file

@ -408,6 +408,12 @@ class Actions
return $this->oMailClient; return $this->oMailClient;
} }
public function ImapClient(): \MailSo\Imap\ImapClient
{
// $this->initMailClientConnection();
return $this->MailClient()->ImapClient();
}
// Stores data in AdditionalAccount else MainAccount // Stores data in AdditionalAccount else MainAccount
public function LocalStorageProvider(): Providers\Storage public function LocalStorageProvider(): Providers\Storage
{ {
@ -590,7 +596,7 @@ class Actions
{ {
$sLine = $this->oConfig->Get('logs', 'auth_logging_format', ''); $sLine = $this->oConfig->Get('logs', 'auth_logging_format', '');
if (!empty($sLine)) { if (!empty($sLine)) {
$this->LoggerAuth()->Write($this->compileLogParams($sLine, $oAccount, false, $aAdditionalParams)); $this->LoggerAuth()->Write($this->compileLogParams($sLine, $oAccount, false, $aAdditionalParams), \LOG_WARNING);
} }
if (($this->oConfig->Get('logs', 'auth_logging', false) || $this->oConfig->Get('logs', 'auth_syslog', false)) if (($this->oConfig->Get('logs', 'auth_logging', false) || $this->oConfig->Get('logs', 'auth_syslog', false))
&& \openlog('snappymail', 0, \LOG_AUTHPRIV)) { && \openlog('snappymail', 0, \LOG_AUTHPRIV)) {
@ -1066,9 +1072,9 @@ class Actions
return \md5($sKey . $this->oConfig->Get('cache', 'index', '')); return \md5($sKey . $this->oConfig->Get('cache', 'index', ''));
} }
public function cacheByKey(string $sKey, bool $bForce = false): bool public function cacheByKey(string $sKey): bool
{ {
if ($sKey && ($bForce || ($this->oConfig->Get('cache', 'enable', true) && $this->oConfig->Get('cache', 'http', true)))) { if ($sKey && $this->oConfig->Get('cache', 'enable', true) && $this->oConfig->Get('cache', 'http', true)) {
\MailSo\Base\Http::ServerUseCache( \MailSo\Base\Http::ServerUseCache(
$this->etag($sKey), $this->etag($sKey),
1382478804, 1382478804,
@ -1080,11 +1086,11 @@ class Actions
return false; return false;
} }
public function verifyCacheByKey(string $sKey, bool $bForce = false): void public function verifyCacheByKey(string $sKey): void
{ {
if ($sKey && ($bForce || ($this->oConfig->Get('cache', 'enable', true) && $this->oConfig->Get('cache', 'http', true)))) { if ($sKey && $this->oConfig->Get('cache', 'enable', true) && $this->oConfig->Get('cache', 'http', true)) {
\MailSo\Base\Http::checkETag($this->etag($sKey)); \MailSo\Base\Http::checkETag($this->etag($sKey));
// $this->cacheByKey($sKey, $bForce); // \MailSo\Base\Http::checkLastModified(1382478804);
} }
} }

View file

@ -4,6 +4,7 @@ namespace RainLoop\Actions;
use RainLoop\Enumerations\PluginPropertyType; use RainLoop\Enumerations\PluginPropertyType;
use RainLoop\Exceptions\ClientException; use RainLoop\Exceptions\ClientException;
use RainLoop\Notifications;
trait AdminExtensions trait AdminExtensions
{ {

View file

@ -24,12 +24,12 @@ trait Messages
$oParams = new \MailSo\Mail\MessageListParams; $oParams = new \MailSo\Mail\MessageListParams;
$sRawKey = $this->GetActionParam('RawKey', ''); $sRawKey = $this->GetActionParam('RawKey', '');
$aValues = \json_decode(\MailSo\Base\Utils::UrlSafeBase64Decode($sRawKey), true); $aValues = $sRawKey ? \json_decode(\MailSo\Base\Utils::UrlSafeBase64Decode($sRawKey), true) : null;
$sHash = '';
if ($aValues && 6 < \count($aValues)) { if ($aValues && 6 < \count($aValues)) {
$this->verifyCacheByKey($sRawKey); // GET
$sHash = (string) $aValues['hash'];
// $oParams->sHash = (string) $aValues['Hash']; $oParams->sFolderName = (string) $aValues['folder'];
$oParams->sFolderName = (string) $aValues['Folder'];
$oParams->iLimit = $aValues['limit']; $oParams->iLimit = $aValues['limit'];
$oParams->iOffset = $aValues['offset']; $oParams->iOffset = $aValues['offset'];
$oParams->sSearch = (string) $aValues['search']; $oParams->sSearch = (string) $aValues['search'];
@ -42,7 +42,8 @@ trait Messages
$oParams->iThreadUid = $aValues['threadUid']; $oParams->iThreadUid = $aValues['threadUid'];
} }
} else { } else {
$oParams->sFolderName = $this->GetActionParam('Folder', ''); // POST
$oParams->sFolderName = $this->GetActionParam('folder', '');
$oParams->iOffset = $this->GetActionParam('offset', 0); $oParams->iOffset = $this->GetActionParam('offset', 0);
$oParams->iLimit = $this->GetActionParam('limit', 10); $oParams->iLimit = $this->GetActionParam('limit', 10);
$oParams->sSearch = $this->GetActionParam('search', ''); $oParams->sSearch = $this->GetActionParam('search', '');
@ -60,6 +61,16 @@ trait Messages
$oAccount = $this->initMailClientConnection(); $oAccount = $this->initMailClientConnection();
if ($sHash) {
$oInfo = $this->ImapClient()->FolderStatusAndSelect($oParams->sFolderName);
$aRequestHash = \explode('-', $sHash);
$sFolderHash = $oInfo->getHash($this->ImapClient()->Hash());
$sHash = $oParams->hash() . '-' . $sFolderHash;
if ($aRequestHash[0] == $sFolderHash) {
$this->verifyCacheByKey($sHash);
}
}
try try
{ {
if (!$this->Config()->Get('imap', 'use_thread', true)) { if (!$this->Config()->Get('imap', 'use_thread', true)) {
@ -75,17 +86,15 @@ trait Messages
} }
$oMessageList = $this->MailClient()->MessageList($oParams); $oMessageList = $this->MailClient()->MessageList($oParams);
if ($sHash) {
$this->cacheByKey($sHash);
}
return $this->DefaultResponse($oMessageList);
} }
catch (\Throwable $oException) catch (\Throwable $oException)
{ {
throw new ClientException(Notifications::CantGetMessageList, $oException); throw new ClientException(Notifications::CantGetMessageList, $oException);
} }
if ($oMessageList) {
$this->cacheByKey($sRawKey);
}
return $this->DefaultResponse($oMessageList);
} }
public function DoSaveMessage() : array public function DoSaveMessage() : array

View file

@ -2,7 +2,7 @@
namespace RainLoop\Plugins; namespace RainLoop\Plugins;
use \RainLoop\Enumerations\PluginPropertyType; use RainLoop\Enumerations\PluginPropertyType;
class Property implements \JsonSerializable class Property implements \JsonSerializable
{ {

View file

@ -2,7 +2,7 @@
namespace RainLoop\Providers\AddressBook\Classes; namespace RainLoop\Providers\AddressBook\Classes;
use \Sabre\VObject\Component\VCard; use Sabre\VObject\Component\VCard;
class Contact implements \JsonSerializable class Contact implements \JsonSerializable
{ {

View file

@ -160,14 +160,17 @@ class ServiceActions
$aResponse['Action'] = $sAction ?: 'Unknown'; $aResponse['Action'] = $sAction ?: 'Unknown';
if (\is_array($aResponse)) {
$aResponse['Time'] = (int) ((\microtime(true) - $_SERVER['REQUEST_TIME_FLOAT']) * 1000);
}
if (!\headers_sent()) { if (!\headers_sent()) {
\header('Content-Type: application/json; charset=utf-8'); \header('Content-Type: application/json; charset=utf-8');
} }
if (\is_array($aResponse)) {
$aResponse['epoch'] = \time();
// if ($this->Config()->Get('debug', 'enable', false)) {
// $aResponse['rtime'] = \round(\microtime(true) - $_SERVER['REQUEST_TIME_FLOAT'], 3);
// }
}
$sResult = Utils::jsonEncode($aResponse); $sResult = Utils::jsonEncode($aResponse);
$sObResult = \ob_get_clean(); $sObResult = \ob_get_clean();