diff --git a/dev/Common/Cache.js b/dev/Common/Cache.js index 5d69af953..9a56ccbdf 100644 --- a/dev/Common/Cache.js +++ b/dev/Common/Cache.js @@ -62,7 +62,7 @@ export const * @returns {?FolderModel} */ getFolderFromCacheList = folderFullName => - FOLDERS_CACHE[folderFullName] ? FOLDERS_CACHE[folderFullName] : null, + FOLDERS_CACHE[folderFullName] || null, /** * @param {string} folderFullName diff --git a/dev/Common/Folders.js b/dev/Common/Folders.js index 0d3d15720..ba1bf0e13 100644 --- a/dev/Common/Folders.js +++ b/dev/Common/Folders.js @@ -9,11 +9,16 @@ import { SettingsUserStore } from 'Stores/User/Settings'; import { FolderUserStore } from 'Stores/User/Folder'; import { MessagelistUserStore } from 'Stores/User/Messagelist'; import { getNotification } from 'Common/Translator'; -import { Settings, } from 'Common/Globals'; +import { Settings } from 'Common/Globals'; import { serverRequest } from 'Common/Links'; import Remote from 'Remote/User/Fetch'; +import { b64EncodeJSONSafe } from 'Common/Utils'; +import { SettingsGet } from 'Common/Globals'; +import { SUB_QUERY_PREFIX } from 'Common/Links'; +import { AppUserStore } from 'Stores/User/App'; + export const sortFolders = folders => { @@ -28,34 +33,41 @@ sortFolders = folders => { }, /** - * @param {?Function} fCallback - * @param {string} folder - * @param {Array=} list = [] + * @param {object} params */ -fetchFolderInformation = (fCallback, folder, list = []) => { - let fetch = !arrayLength(list); - const uids = [], - folderFromCache = getFolderFromCacheList(folder); +messageList = params => { + const +// folder = getFolderFromCacheList(params.folder.fullName), + folder = getFolderFromCacheList(params.folder), + folderHash = folder?.hash || ''; - 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; + params = Object.assign({ + offset: 0, + limit: SettingsUserStore.messagesPerPage(), + search: '', + uidNext: folder?.uidNext || 0, // Used to check for new messages + sort: FolderUserStore.sortMode() + }, params); + if (AppUserStore.threadsAllowed() && SettingsUserStore.useThreads()) { + params.useThreads = 1; + } else { + params.threadUid = 0; } - 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(); + let sGetAdd = ''; + if (folderHash) { + params.hash = folderHash + '-' + SettingsGet('AccountHash'); + sGetAdd = 'MessageList/' + SUB_QUERY_PREFIX + '/' + b64EncodeJSONSafe(params); + params = {}; } + + Remote.abort('MessageList'); + Remote.request('MessageList', + null, + params, + 60000, // 60 seconds before aborting + sGetAdd + ); }, /** @@ -127,8 +139,22 @@ refreshFoldersInterval = 300000, */ folderInformation = (folder, list) => { if (folder?.trim()) { - fetchFolderInformation( - (iError, data) => { + 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', (iError, data) => { if (!iError && data.Result) { const result = data.Result, folderFromCache = getFolderFromCacheList(result.Folder); @@ -159,15 +185,20 @@ folderInformation = (folder, list) => { if (folderFromCache.fullName === FolderUserStore.currentFolderFullName()) { MessagelistUserStore.reload(); } else if (getFolderInboxName() === folderFromCache.fullName) { - Remote.messageList(null, {Folder: getFolderInboxName()}, true); +// messageList({folder: getFolderFromCacheList(getFolderInboxName())}); + messageList({folder: getFolderInboxName()}); } } } } - }, - folder, - list - ); + }, { + Folder: folder, + FlagsUids: uids, + UidNext: folderFromCache?.uidNext || 0 // Used to check for new messages + }); + } else if (SettingsUserStore.useThreads()) { + MessagelistUserStore.reloadFlagsAndCachedMessage(); + } } }, diff --git a/dev/Remote/AbstractFetch.js b/dev/Remote/AbstractFetch.js index 8978331d2..546fc26f9 100644 --- a/dev/Remote/AbstractFetch.js +++ b/dev/Remote/AbstractFetch.js @@ -130,11 +130,6 @@ export class AbstractFetchRemote sGetAdd ? null : (params || {}), undefined === iTimeout ? 30000 : pInt(iTimeout), data => { - let cached = false; - if (data?.Time) { - cached = pInt(data.Time) > Date.now() - start; - } - let iError = 0; if (sAction && oRequests[sAction]) { abort(sAction, 0, 1); @@ -157,9 +152,12 @@ export class AbstractFetchRemote fCallback && fCallback( iError, data, - cached, - sAction, - params + /** + * Responses like "304 Not Modified" are returned as "200 OK" + * 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 = ''; - if (data?.Time) { - isCached = pInt(data.Time) > microtime() - start; + if (data?.epoch) { + isCached = data.epoch > microtime() - start; } // backward capability switch (true) { diff --git a/dev/Remote/User/Fetch.js b/dev/Remote/User/Fetch.js index aca3ddf26..80346ca78 100644 --- a/dev/Remote/User/Fetch.js +++ b/dev/Remote/User/Fetch.js @@ -9,57 +9,11 @@ import { SUB_QUERY_PREFIX } from 'Common/Links'; import { AppUserStore } from 'Stores/User/App'; import { SettingsUserStore } from 'Stores/User/Settings'; -import { FolderUserStore } from 'Stores/User/Folder'; import { AbstractFetchRemote } from 'Remote/AbstractFetch'; class RemoteUserFetch extends AbstractFetchRemote { - /** - * @param {Function} fCallback - * @param {object} params - * @param {boolean=} bSilent = false - */ - messageList(fCallback, params, bSilent = false) { - const - sFolderFullName = pString(params.Folder), - folder = getFolderFromCacheList(sFolderFullName), - folderHash = folder?.hash || ''; - - params = Object.assign({ - offset: 0, - limit: SettingsUserStore.messagesPerPage(), - search: '', - uidNext: folder?.uidNext || 0, // Used to check for new messages - sort: FolderUserStore.sortMode(), - Hash: folderHash + SettingsGet('AccountHash') - }, params); - params.Folder = sFolderFullName; - if (AppUserStore.threadsAllowed() && SettingsUserStore.useThreads()) { - params.useThreads = 1; - } else { - params.threadUid = 0; - } - - let sGetAdd = ''; - - if (folderHash && (!params.search || !params.search.includes('is:'))) { - sGetAdd = 'MessageList/' + - SUB_QUERY_PREFIX + - '/' + - b64EncodeJSONSafe(params); - params = {}; - } - - bSilent || this.abort('MessageList'); - this.request('MessageList', - fCallback, - params, - 60000, // 60 seconds before aborting - sGetAdd - ); - } - /** * @param {?Function} fCallback * @param {string} sFolderFullName diff --git a/dev/Stores/User/Messagelist.js b/dev/Stores/User/Messagelist.js index 64f0f94a1..ca5862429 100644 --- a/dev/Stores/User/Messagelist.js +++ b/dev/Stores/User/Messagelist.js @@ -293,7 +293,8 @@ MessagelistUserStore.reload = (bDropPagePosition = false, bDropCurrentFolderCach MessagelistUserStore.loading(false); }, { - Folder: FolderUserStore.currentFolderFullName(), +// folder: FolderUserStore.currentFolder() ? self.currentFolder().fullName : ''), + folder: FolderUserStore.currentFolderFullName(), offset: iOffset, limit: SettingsUserStore.messagesPerPage(), search: MessagelistUserStore.listSearch(), diff --git a/dev/View/Popup/Domain.js b/dev/View/Popup/Domain.js index a3a0f6287..fcec744dc 100644 --- a/dev/View/Popup/Domain.js +++ b/dev/View/Popup/Domain.js @@ -210,7 +210,15 @@ export class DomainPopupView extends AbstractViewPopup { createOrAddCommand() { this.saving(true); 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), { 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() { this.testing(false); this.testingDone(false); diff --git a/dev/sieve.js b/dev/sieve.js index 5c21ab374..33a8dec9e 100644 --- a/dev/sieve.js +++ b/dev/sieve.js @@ -55,13 +55,11 @@ window.Sieve = { deleteScript: script => { serverError(false); Remote.request('FiltersScriptDelete', - (iError, data) => { - if (iError) { - setError(data?.ErrorMessageAdditional || getNotification(iError)); - } else { - scripts.remove(script); - } - }, + (iError, data) => + iError + ? setError(data?.ErrorMessageAdditional || getNotification(iError)) + : scripts.remove(script) + , {name:script.name()} ); }, @@ -69,13 +67,11 @@ window.Sieve = { setActiveScript(name) { serverError(false); Remote.request('FiltersScriptActivate', - (iError, data) => { - if (iError) { - setError(data?.ErrorMessageAdditional || iError) - } else { - scripts.forEach(script => script.active(script.name() === name)); - } - }, + (iError, data) => + iError + ? setError(data?.ErrorMessageAdditional || iError) + : scripts.forEach(script => script.active(script.name() === name)) + , {name:name} ); } diff --git a/snappymail/v/0.0.0/app/libraries/MailSo/Mail/MailClient.php b/snappymail/v/0.0.0/app/libraries/MailSo/Mail/MailClient.php index dcca293d0..f3ada3268 100644 --- a/snappymail/v/0.0.0/app/libraries/MailSo/Mail/MailClient.php +++ b/snappymail/v/0.0.0/app/libraries/MailSo/Mail/MailClient.php @@ -582,11 +582,11 @@ class MailClient $sSerializedHash = ''; $sSerializedLog = ''; if ($bUseCacheAfterSearch) { - $sSerializedHash = 'GetUids/'. - ($bUseSort ? 'S' . $sSort : 'N').'/'. - $this->oImapClient->Hash().'/'. - $sFolderName.'/'.$sSearchCriterias; - $sSerializedLog = '"'.$sFolderName.'" / '.$sSearchCriterias.''; + $sSerializedHash = 'Get' + . ($bReturnUid ? 'UIDS/' : 'IDS/') + . ($bUseSort ? 'S' . $sSort : 'N') + . "/{$this->oImapClient->Hash()}/{$sFolderName}/{$sSearchCriterias}"; + $sSerializedLog = "\"{$sFolderName}\" / {$sSort} / {$sSearchCriterias}"; $sSerialized = $oCacher->Get($sSerializedHash); if (!empty($sSerialized)) { @@ -596,11 +596,9 @@ class MailClient \is_array($aSerialized['Uids']) ) { if ($this->oLogger) { - $this->oLogger->Write('Get Serialized UIDS from cache ('.$sSerializedLog.') [count:'.\count($aSerialized['Uids']).']'); - } - if (\is_array($aSerialized['Uids'])) { - return $aSerialized['Uids']; + $this->oLogger->Write('Get Serialized '.($bReturnUid?'UIDS':'IDS').' from cache ('.$sSerializedLog.') [count:'.\count($aSerialized['Uids']).']'); } + return $aSerialized['Uids']; } } } @@ -631,7 +629,7 @@ class MailClient ))); 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).']'); } } diff --git a/snappymail/v/0.0.0/app/libraries/MailSo/Mail/MessageListParams.php b/snappymail/v/0.0.0/app/libraries/MailSo/Mail/MessageListParams.php index 5a6ce68a3..d3b622ff4 100644 --- a/snappymail/v/0.0.0/app/libraries/MailSo/Mail/MessageListParams.php +++ b/snappymail/v/0.0.0/app/libraries/MailSo/Mail/MessageListParams.php @@ -51,4 +51,18 @@ class MessageListParams // 0 > $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 + ])); + } } diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions.php index 5f6a82b5e..f30f26c19 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions.php @@ -408,6 +408,12 @@ class Actions return $this->oMailClient; } + public function ImapClient(): \MailSo\Imap\ImapClient + { +// $this->initMailClientConnection(); + return $this->MailClient()->ImapClient(); + } + // Stores data in AdditionalAccount else MainAccount public function LocalStorageProvider(): Providers\Storage { @@ -1066,9 +1072,9 @@ class Actions 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( $this->etag($sKey), 1382478804, @@ -1080,11 +1086,11 @@ class Actions 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)); -// $this->cacheByKey($sKey, $bForce); +// \MailSo\Base\Http::checkLastModified(1382478804); } } diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Messages.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Messages.php index 3b97f5430..2641479c9 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Messages.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Messages.php @@ -24,12 +24,12 @@ trait Messages $oParams = new \MailSo\Mail\MessageListParams; $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)) { - $this->verifyCacheByKey($sRawKey); - -// $oParams->sHash = (string) $aValues['Hash']; - $oParams->sFolderName = (string) $aValues['Folder']; + // GET + $sHash = (string) $aValues['hash']; + $oParams->sFolderName = (string) $aValues['folder']; $oParams->iLimit = $aValues['limit']; $oParams->iOffset = $aValues['offset']; $oParams->sSearch = (string) $aValues['search']; @@ -42,7 +42,8 @@ trait Messages $oParams->iThreadUid = $aValues['threadUid']; } } else { - $oParams->sFolderName = $this->GetActionParam('Folder', ''); + // POST + $oParams->sFolderName = $this->GetActionParam('folder', ''); $oParams->iOffset = $this->GetActionParam('offset', 0); $oParams->iLimit = $this->GetActionParam('limit', 10); $oParams->sSearch = $this->GetActionParam('search', ''); @@ -60,6 +61,16 @@ trait Messages $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 { if (!$this->Config()->Get('imap', 'use_thread', true)) { @@ -75,17 +86,15 @@ trait Messages } $oMessageList = $this->MailClient()->MessageList($oParams); + if ($sHash) { + $this->cacheByKey($sHash); + } + return $this->DefaultResponse($oMessageList); } catch (\Throwable $oException) { throw new ClientException(Notifications::CantGetMessageList, $oException); } - - if ($oMessageList) { - $this->cacheByKey($sRawKey); - } - - return $this->DefaultResponse($oMessageList); } public function DoSaveMessage() : array diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/ServiceActions.php b/snappymail/v/0.0.0/app/libraries/RainLoop/ServiceActions.php index 3898c0e69..4b162948c 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/ServiceActions.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/ServiceActions.php @@ -160,14 +160,17 @@ class ServiceActions $aResponse['Action'] = $sAction ?: 'Unknown'; - if (\is_array($aResponse)) { - $aResponse['Time'] = (int) ((\microtime(true) - $_SERVER['REQUEST_TIME_FLOAT']) * 1000); - } - if (!\headers_sent()) { \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); $sObResult = \ob_get_clean();