diff --git a/dev/Common/Links.js b/dev/Common/Links.js index 4d9abfd6c..980e14e88 100644 --- a/dev/Common/Links.js +++ b/dev/Common/Links.js @@ -237,13 +237,6 @@ export function staticPrefix(path) { return VERSION_PREFIX + 'static/' + path; } -/** - * @returns {string} - */ -export function emptyContactPic() { - return staticPrefix('css/images/empty-contact.png'); -} - /** * @param {string} fileName * @returns {string} diff --git a/dev/Knoin/AbstractModel.js b/dev/Knoin/AbstractModel.js index 1895703c4..3cb7b0f15 100644 --- a/dev/Knoin/AbstractModel.js +++ b/dev/Knoin/AbstractModel.js @@ -13,12 +13,10 @@ export class AbstractModel { */ constructor() { /* - constructor(props) { if (new.target === Parent) { throw new Error("Can't instantiate abstract class!"); } this.sModelName = new.target.name; - props && Object.entries(props).forEach(([key, value]) => '@' !== key[0] && (this[key] = value)); */ } @@ -59,7 +57,8 @@ export class AbstractModel { // Object/Folder // Object/Message // Object/Template - return this.validJson(json) ? new this(json) : null; + return this.validJson(json) ? new this() : null; +// json && Object.entries(json).forEach(([key, value]) => '@' !== key[0] && (this[key] = value)); } } diff --git a/dev/Model/Contact.js b/dev/Model/Contact.js index 39242db31..5080c60de 100644 --- a/dev/Model/Contact.js +++ b/dev/Model/Contact.js @@ -2,7 +2,6 @@ import ko from 'ko'; import { ContactPropertyType } from 'Common/Enums'; import { pInt, pString } from 'Common/Utils'; -import { emptyContactPic } from 'Common/Links'; import { AbstractModel } from 'Knoin/AbstractModel'; @@ -10,7 +9,7 @@ class ContactModel extends AbstractModel { constructor() { super(); - this.idContact = 0; + this.id = 0; this.display = ''; this.properties = []; this.readOnly = false; @@ -53,11 +52,11 @@ class ContactModel extends AbstractModel { static reviveFromJson(json) { const contact = super.reviveFromJson(json); if (contact) { - contact.idContact = pInt(json.IdContact); - contact.display = pString(json.Display); - contact.readOnly = !!json.ReadOnly; + contact.id = pInt(json.id); + contact.display = pString(json.display); + contact.readOnly = !!json.readOnly; - if (Array.isNotEmpty(json.Properties)) { + if (Array.isNotEmpty(json.properties)) { json.Properties.forEach(property => { if (property && property.Type && null != property.Value && null != property.TypeStr) { contact.properties.push([pInt(property.Type), pString(property.Value), pString(property.TypeStr)]); @@ -68,18 +67,11 @@ class ContactModel extends AbstractModel { return contact; } - /** - * @returns {string} - */ - srcAttr() { - return emptyContactPic(); - } - /** * @returns {string} */ generateUid() { - return pString(this.idContact); + return pString(this.id); } /** diff --git a/dev/View/Popup/Contacts.js b/dev/View/Popup/Contacts.js index e97e74cb2..f8db23177 100644 --- a/dev/View/Popup/Contacts.js +++ b/dev/View/Popup/Contacts.js @@ -155,7 +155,7 @@ class ContactsPopupView extends AbstractViewNext { }); this.contactsCheckedOrSelectedUids = ko.computed(() => - this.contactsCheckedOrSelected().map(contact => contact.idContact) + this.contactsCheckedOrSelected().map(contact => contact.id) ); this.selector = new Selector( @@ -436,7 +436,7 @@ class ContactsPopupView extends AbstractViewNext { if (contacts.length) { contacts.forEach(contact => { - if (currentContact && currentContact.idContact === contact.idContact) { + if (currentContact && currentContact.id === contact.id) { currentContact = null; this.currentContact(null); } @@ -500,7 +500,7 @@ class ContactsPopupView extends AbstractViewNext { this.viewReadOnly(false); if (contact) { - id = contact.idContact; + id = contact.id; if (Array.isNotEmpty(contact.properties)) { contact.properties.forEach(property => { if (property && property[0]) { diff --git a/snappymail/v/0.0.0/app/libraries/MailSo/Mime/Email.php b/snappymail/v/0.0.0/app/libraries/MailSo/Mime/Email.php index dc4a302bd..0df1c2ea1 100644 --- a/snappymail/v/0.0.0/app/libraries/MailSo/Mime/Email.php +++ b/snappymail/v/0.0.0/app/libraries/MailSo/Mime/Email.php @@ -15,7 +15,7 @@ namespace MailSo\Mime; * @category MailSo * @package Mime */ -class Email +class Email implements \JsonSerializable { /** * @var string @@ -253,4 +253,15 @@ class Email return \trim($sReturn); } + + public function jsonSerialize() + { + return array( + '@Object' => 'Object/Email', + 'Name' => \MailSo\Base\Utils::Utf8Clear($this->GetDisplayName()), + 'Email' => \MailSo\Base\Utils::Utf8Clear($this->GetEmail(true)), + 'DkimStatus' => $this->GetDkimStatus(), + 'DkimValue' => $this->GetDkimValue() + ); + } } 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 6834f9339..95a09bf65 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions.php @@ -8,6 +8,8 @@ use RainLoop\Enumerations\UploadClientError; class Actions { use Actions\Admin; + use Actions\User; + use Actions\Response; const AUTH_TFA_SIGN_ME_TOKEN_KEY = 'rltfasmauth'; const AUTH_SIGN_ME_TOKEN_KEY = 'rlsmauth'; @@ -1850,74 +1852,6 @@ NewThemeLink IncludeCss TemplatesLink LangLink IncludeBackground PluginsLink Aut return \MailSo\Base\Utils::Md5Rand(APP_SALT.$sEmail); } - /** - * @throws \MailSo\Base\Exceptions\Exception - */ - public function DoLogin() : array - { - $sEmail = \MailSo\Base\Utils::Trim($this->GetActionParam('Email', '')); - $sPassword = $this->GetActionParam('Password', ''); - $sLanguage = $this->GetActionParam('Language', ''); - $bSignMe = '1' === (string) $this->GetActionParam('SignMe', '0'); - - $sAdditionalCode = $this->GetActionParam('AdditionalCode', ''); - $bAdditionalCodeSignMe = '1' === (string) $this->GetActionParam('AdditionalCodeSignMe', '0'); - - $oAccount = null; - - $this->Logger()->AddSecret($sPassword); - - if ('sleep@sleep.dev' === $sEmail && 0 < \strlen($sPassword) && - \is_numeric($sPassword) && $this->Config()->Get('debug', 'enable', false) && - 0 < (int) $sPassword && 30 > (int) $sPassword - ) - { - \sleep((int) $sPassword); - throw new Exceptions\ClientException(Notifications::AuthError); - } - - try - { - $oAccount = $this->LoginProcess($sEmail, $sPassword, - $bSignMe ? $this->generateSignMeToken($sEmail) : '', - $sAdditionalCode, $bAdditionalCodeSignMe); - } - catch (Exceptions\ClientException $oException) - { - if ($oException && - Notifications::AccountTwoFactorAuthRequired === $oException->getCode()) - { - return $this->DefaultResponse(__FUNCTION__, true, array( - 'TwoFactorAuth' => true - )); - } - else - { - throw $oException; - } - } - - $this->AuthToken($oAccount); - - if ($oAccount && 0 < \strlen($sLanguage)) - { - $oSettings = $this->SettingsProvider()->Load($oAccount); - if ($oSettings) - { - $sLanguage = $this->ValidateLanguage($sLanguage); - $sCurrentLanguage = $oSettings->GetConf('Language', ''); - - if ($sCurrentLanguage !== $sLanguage) - { - $oSettings->SetConf('Language', $sLanguage); - $this->SettingsProvider()->Save($oAccount, $oSettings); - } - } - } - - return $this->TrueResponse(__FUNCTION__); - } - public function GetAccounts(Model\Account $oAccount) : array { if ($this->GetCapa(false, false, Enumerations\Capa::ADDITIONAL_ACCOUNTS, $oAccount)) @@ -2172,7 +2106,7 @@ NewThemeLink IncludeCss TemplatesLink LangLink IncludeBackground PluginsLink Aut continue; } - $aResult[] = $oItem->ToSimpleJSON(false); + $aResult[] = $oItem->ToSimpleJSON(); } return $this->StorageProvider(true)->Put($oAccount, @@ -2187,7 +2121,7 @@ NewThemeLink IncludeCss TemplatesLink LangLink IncludeBackground PluginsLink Aut $aResult = array(); foreach ($aTemplates as $oItem) { - $aResult[] = $oItem->ToSimpleJSON(false); + $aResult[] = $oItem->ToSimpleJSON(); } return $this->StorageProvider(true)->Put($oAccount, @@ -2197,517 +2131,6 @@ NewThemeLink IncludeCss TemplatesLink LangLink IncludeBackground PluginsLink Aut ); } - /** - * @throws \MailSo\Base\Exceptions\Exception - */ - public function DoFilters() : array - { - $oAccount = $this->getAccountFromToken(); - - if (!$this->GetCapa(false, false, Enumerations\Capa::FILTERS, $oAccount)) - { - return $this->FalseResponse(__FUNCTION__); - } - - $aFakeFilters = null; - - $this->Plugins() - ->RunHook('filter.filters-fake', array($oAccount, &$aFakeFilters)) - ; - - if ($aFakeFilters) - { - return $this->DefaultResponse(__FUNCTION__, $aFakeFilters); - } - - return $this->DefaultResponse(__FUNCTION__, - $this->FiltersProvider()->Load($oAccount, $oAccount->DomainSieveAllowRaw())); - } - - /** - * @throws \MailSo\Base\Exceptions\Exception - */ - public function DoFiltersSave() : array - { - $oAccount = $this->getAccountFromToken(); - - if (!$this->GetCapa(false, false, Enumerations\Capa::FILTERS, $oAccount)) - { - return $this->FalseResponse(__FUNCTION__); - } - - $aIncFilters = $this->GetActionParam('Filters', array()); - - $sRaw = $this->GetActionParam('Raw', ''); - $bRawIsActive = '1' === (string) $this->GetActionParam('RawIsActive', '0'); - - $aFilters = array(); - foreach ($aIncFilters as $aFilter) - { - if (is_array($aFilter)) - { - $oFilter = new Providers\Filters\Classes\Filter(); - if ($oFilter->FromJSON($aFilter)) - { - $aFilters[] = $oFilter; - } - } - } - - $this->Plugins() - ->RunHook('filter.filters-save', array($oAccount, &$aFilters, &$sRaw, &$bRawIsActive)) - ; - - return $this->DefaultResponse(__FUNCTION__, $this->FiltersProvider()->Save($oAccount, - $aFilters, $sRaw, $bRawIsActive)); - } - - /** - * @throws \MailSo\Base\Exceptions\Exception - */ - public function DoAccountSetup() : array - { - $oAccount = $this->getAccountFromToken(); - - if (!$this->GetCapa(false, false, Enumerations\Capa::ADDITIONAL_ACCOUNTS, $oAccount)) - { - return $this->FalseResponse(__FUNCTION__); - } - - $sParentEmail = $oAccount->ParentEmailHelper(); - - $aAccounts = $this->GetAccounts($oAccount); - - $sEmail = \trim($this->GetActionParam('Email', '')); - $sPassword = $this->GetActionParam('Password', ''); - $bNew = '1' === (string) $this->GetActionParam('New', '1'); - - $sEmail = \MailSo\Base\Utils::IdnToAscii($sEmail, true); - if ($bNew && ($oAccount->Email() === $sEmail || $sParentEmail === $sEmail || isset($aAccounts[$sEmail]))) - { - throw new Exceptions\ClientException(Notifications::AccountAlreadyExists); - } - else if (!$bNew && !isset($aAccounts[$sEmail])) - { - throw new Exceptions\ClientException(Notifications::AccountDoesNotExist); - } - - $oNewAccount = $this->LoginProcess($sEmail, $sPassword, '', '', false, true); - $oNewAccount->SetParentEmail($sParentEmail); - - $aAccounts[$oNewAccount->Email()] = $oNewAccount->GetAuthToken(); - if (!$oAccount->IsAdditionalAccount()) - { - $aAccounts[$oAccount->Email()] = $oAccount->GetAuthToken(); - } - - $this->SetAccounts($oAccount, $aAccounts); - return $this->TrueResponse(__FUNCTION__); - } - - /** - * @throws \MailSo\Base\Exceptions\Exception - */ - public function DoAccountDelete() : array - { - $oAccount = $this->getAccountFromToken(); - - if (!$this->GetCapa(false, false, Enumerations\Capa::ADDITIONAL_ACCOUNTS, $oAccount)) - { - return $this->FalseResponse(__FUNCTION__); - } - - $sParentEmail = $oAccount->ParentEmailHelper(); - $sEmailToDelete = \trim($this->GetActionParam('EmailToDelete', '')); - $sEmailToDelete = \MailSo\Base\Utils::IdnToAscii($sEmailToDelete, true); - - $aAccounts = $this->GetAccounts($oAccount); - - if (0 < \strlen($sEmailToDelete) && $sEmailToDelete !== $sParentEmail && isset($aAccounts[$sEmailToDelete])) - { - unset($aAccounts[$sEmailToDelete]); - - $oAccountToChange = null; - if ($oAccount->Email() === $sEmailToDelete && !empty($aAccounts[$sParentEmail])) - { - $oAccountToChange = $this->GetAccountFromCustomToken($aAccounts[$sParentEmail], false, false); - if ($oAccountToChange) - { - $this->AuthToken($oAccountToChange); - } - } - - $this->SetAccounts($oAccount, $aAccounts); - return $this->TrueResponse(__FUNCTION__, array('Reload' => !!$oAccountToChange)); - } - - return $this->FalseResponse(__FUNCTION__); - } - - /** - * @throws \MailSo\Base\Exceptions\Exception - */ - public function DoAttachmentsActions() : array - { - if (!$this->GetCapa(false, false, Enumerations\Capa::ATTACHMENTS_ACTIONS)) - { - return $this->FalseResponse(__FUNCTION__); - } - - $oAccount = $this->initMailClientConnection(); - - $sAction = $this->GetActionParam('Do', ''); - $aHashes = $this->GetActionParam('Hashes', null); - - $mResult = false; - $bError = false; - $aData = false; - - if (\is_array($aHashes) && 0 < \count($aHashes)) - { - $aData = array(); - foreach ($aHashes as $sZipHash) - { - $aResult = $this->getMimeFileByHash($oAccount, $sZipHash); - if (!empty($aResult['FileHash'])) - { - $aData[] = $aResult; - } - else - { - $bError = true; - break; - } - } - } - - $oFilesProvider = $this->FilesProvider(); - if (!empty($sAction) && !$bError && \is_array($aData) && 0 < \count($aData) && - $oFilesProvider && $oFilesProvider->IsActive()) - { - $bError = false; - switch (\strtolower($sAction)) - { - case 'zip': - - if (\class_exists('ZipArchive')) - { - $sZipHash = \MailSo\Base\Utils::Md5Rand(); - $sZipFileName = $oFilesProvider->GenerateLocalFullFileName($oAccount, $sZipHash); - - if (!empty($sZipFileName)) - { - $oZip = new \ZipArchive(); - $oZip->open($sZipFileName, \ZIPARCHIVE::CREATE | \ZIPARCHIVE::OVERWRITE); - $oZip->setArchiveComment('SnappyMail/'.APP_VERSION); - - foreach ($aData as $aItem) - { - $sFileName = (string) (isset($aItem['FileName']) ? $aItem['FileName'] : 'file.dat'); - $sFileHash = (string) (isset($aItem['FileHash']) ? $aItem['FileHash'] : ''); - - if (!empty($sFileHash)) - { - $sFullFileNameHash = $oFilesProvider->GetFileName($oAccount, $sFileHash); - if (!$oZip->addFile($sFullFileNameHash, $sFileName)) - { - $bError = true; - } - } - } - - if (!$bError) - { - $bError = !$oZip->close(); - } - else - { - $oZip->close(); - } - } - - foreach ($aData as $aItem) - { - $sFileHash = (string) (isset($aItem['FileHash']) ? $aItem['FileHash'] : ''); - if (!empty($sFileHash)) - { - $oFilesProvider->Clear($oAccount, $sFileHash); - } - } - - if (!$bError) - { - $mResult = array( - 'Files' => array(array( - 'FileName' => 'attachments.zip', - 'Hash' => Utils::EncodeKeyValuesQ(array( - 'V' => APP_VERSION, - 'Account' => $oAccount ? \md5($oAccount->Hash()) : '', - 'FileName' => 'attachments.zip', - 'MimeType' => 'application/zip', - 'FileHash' => $sZipHash - )) - )) - ); - } - } - break; - } - } - else - { - $bError = true; - } - - $this->requestSleep(); - return $this->DefaultResponse(__FUNCTION__, $bError ? false : $mResult); - } - - /** - * @throws \MailSo\Base\Exceptions\Exception - */ - public function DoIdentityUpdate() : array - { - $oAccount = $this->getAccountFromToken(); - - $oIdentity = new Model\Identity(); - if (!$oIdentity->FromJSON($this->GetActionParams(), true)) - { - throw new Exceptions\ClientException(Notifications::InvalidInputArgument); - } - - $aIdentities = $this->GetIdentities($oAccount); - - $bAdded = false; - $aIdentitiesForSave = array(); - foreach ($aIdentities as $oItem) - { - if ($oItem) - { - if ($oItem->Id() === $oIdentity->Id()) - { - $aIdentitiesForSave[] = $oIdentity; - $bAdded = true; - } - else - { - $aIdentitiesForSave[] = $oItem; - } - } - } - - if (!$bAdded) - { - $aIdentitiesForSave[] = $oIdentity; - } - - return $this->DefaultResponse(__FUNCTION__, $this->SetIdentities($oAccount, $aIdentitiesForSave)); - } - - /** - * @throws \MailSo\Base\Exceptions\Exception - */ - public function DoIdentityDelete() : array - { - $oAccount = $this->getAccountFromToken(); - - if (!$this->GetCapa(false, false, Enumerations\Capa::IDENTITIES, $oAccount)) - { - return $this->FalseResponse(__FUNCTION__); - } - - $sId = \trim($this->GetActionParam('IdToDelete', '')); - if (empty($sId)) - { - throw new Exceptions\ClientException(Notifications::UnknownError); - } - - $aNew = array(); - $aIdentities = $this->GetIdentities($oAccount); - - foreach ($aIdentities as $oItem) - { - if ($oItem && $sId !== $oItem->Id()) - { - $aNew[] = $oItem; - } - } - - return $this->DefaultResponse(__FUNCTION__, $this->SetIdentities($oAccount, $aNew)); - } - - /** - * @throws \MailSo\Base\Exceptions\Exception - */ - public function DoTemplateSetup() : array - { - $oAccount = $this->getAccountFromToken(); - - if (!$this->GetCapa(false, false, Enumerations\Capa::TEMPLATES, $oAccount)) - { - return $this->FalseResponse(__FUNCTION__); - } - - $oTemplate = new Model\Template(); - if (!$oTemplate->FromJSON($this->GetActionParams(), true)) - { - throw new Exceptions\ClientException(Notifications::InvalidInputArgument); - } - - if ('' === $oTemplate->Id()) - { - $oTemplate->GenerateID(); - } - - $aTemplatesForSave = array(); - $aTemplates = $this->GetTemplates($oAccount); - - - foreach ($aTemplates as $oItem) - { - if ($oItem && $oItem->Id() !== $oTemplate->Id()) - { - $aTemplatesForSave[] = $oItem; - } - } - - $aTemplatesForSave[] = $oTemplate; - - return $this->DefaultResponse(__FUNCTION__, $this->SetTemplates($oAccount, $aTemplatesForSave)); - } - - /** - * @throws \MailSo\Base\Exceptions\Exception - */ - public function DoTemplateDelete() : array - { - $oAccount = $this->getAccountFromToken(); - - if (!$this->GetCapa(false, false, Enumerations\Capa::TEMPLATES, $oAccount)) - { - return $this->FalseResponse(__FUNCTION__); - } - - $sId = \trim($this->GetActionParam('IdToDelete', '')); - if (empty($sId)) - { - throw new Exceptions\ClientException(Notifications::UnknownError); - } - - $aNew = array(); - $aTemplates = $this->GetTemplates($oAccount); - foreach ($aTemplates as $oItem) - { - if ($oItem && $sId !== $oItem->Id()) - { - $aNew[] = $oItem; - } - } - - return $this->DefaultResponse(__FUNCTION__, $this->SetTemplates($oAccount, $aNew)); - } - - /** - * @throws \MailSo\Base\Exceptions\Exception - */ - public function DoTemplateGetByID() : array - { - $oAccount = $this->getAccountFromToken(); - - if (!$this->GetCapa(false, false, Enumerations\Capa::TEMPLATES, $oAccount)) - { - return $this->FalseResponse(__FUNCTION__); - } - - $sId = \trim($this->GetActionParam('ID', '')); - if (empty($sId)) - { - throw new Exceptions\ClientException(Notifications::UnknownError); - } - - $oTemplate = false; - $aTemplates = $this->GetTemplates($oAccount); - - foreach ($aTemplates as $oItem) - { - if ($oItem && $sId === $oItem->Id()) - { - $oTemplate = $oItem; - break; - } - } - - $oTemplate->SetPopulateAlways(true); - return $this->DefaultResponse(__FUNCTION__, $oTemplate); - } - - /** - * @throws \MailSo\Base\Exceptions\Exception - */ - public function DoAccountsAndIdentitiesSortOrder() : array - { - $oAccount = $this->getAccountFromToken(); - - $aAccounts = $this->GetActionParam('Accounts', null); - $aIdentities = $this->GetActionParam('Identities', null); - - if (!\is_array($aAccounts) && !\is_array($aIdentities)) - { - return $this->FalseResponse(__FUNCTION__); - } - - return $this->DefaultResponse(__FUNCTION__, $this->StorageProvider()->Put($oAccount, - Providers\Storage\Enumerations\StorageType::CONFIG, 'accounts_identities_order', - \json_encode(array( - 'Accounts' => \is_array($aAccounts) ? $aAccounts : array(), - 'Identities' => \is_array($aIdentities) ? $aIdentities : array() - )) - )); - } - - /** - * @throws \MailSo\Base\Exceptions\Exception - */ - public function DoAccountsAndIdentities() : array - { - $oAccount = $this->getAccountFromToken(); - - $mAccounts = false; - - if ($this->GetCapa(false, false, Enumerations\Capa::ADDITIONAL_ACCOUNTS, $oAccount)) - { - $mAccounts = $this->GetAccounts($oAccount); - $mAccounts = \array_keys($mAccounts); - - foreach ($mAccounts as $iIndex => $sName) - { - $mAccounts[$iIndex] = \MailSo\Base\Utils::IdnToUtf8($sName); - } - } - - return $this->DefaultResponse(__FUNCTION__, array( - 'Accounts' => $mAccounts, - 'Identities' => $this->GetIdentities($oAccount) - )); - } - - /** - * @throws \MailSo\Base\Exceptions\Exception - */ - public function DoTemplates() : array - { - $oAccount = $this->getAccountFromToken(); - - if (!$this->GetCapa(false, false, Enumerations\Capa::TEMPLATES, $oAccount)) - { - return $this->FalseResponse(__FUNCTION__); - } - - return $this->DefaultResponse(__FUNCTION__, array( - 'Templates' => $this->GetTemplates($oAccount) - )); - } - /** * @throws \MailSo\Base\Exceptions\Exception */ @@ -2751,111 +2174,6 @@ NewThemeLink IncludeCss TemplatesLink LangLink IncludeBackground PluginsLink Aut } } - public function DoLogout() : array - { - $oAccount = $this->getAccountFromToken(false); - if ($oAccount) - { - if ($oAccount->SignMe()) - { - $this->ClearSignMeData($oAccount); - } - - if (!$oAccount->IsAdditionalAccount()) - { - Utils::ClearCookie(Actions::AUTH_SPEC_TOKEN_KEY); - } - } - - return $this->TrueResponse(__FUNCTION__); - } - - public function DoAppDelayStart() : array - { - $this->Plugins()->RunHook('service.app-delay-start-begin'); - - Utils::UpdateConnectionToken(); - - $bMainCache = false; - $bFilesCache = false; - $bVersionsCache = false; - - $iOneDay1 = 60 * 60 * 23; - $iOneDay2 = 60 * 60 * 25; - $iOneDay3 = 60 * 60 * 30; - - $sTimers = $this->StorageProvider()->Get(null, - Providers\Storage\Enumerations\StorageType::NOBODY, 'Cache/Timers', ''); - - $aTimers = \explode(',', $sTimers); - - $iMainCacheTime = !empty($aTimers[0]) && \is_numeric($aTimers[0]) ? (int) $aTimers[0] : 0; - $iFilesCacheTime = !empty($aTimers[1]) && \is_numeric($aTimers[1]) ? (int) $aTimers[1] : 0; - $iVersionsCacheTime = !empty($aTimers[2]) && \is_numeric($aTimers[2]) ? (int) $aTimers[2] : 0; - - if (0 === $iMainCacheTime || $iMainCacheTime + $iOneDay1 < \time()) - { - $bMainCache = true; - $iMainCacheTime = \time(); - } - - if (0 === $iFilesCacheTime || $iFilesCacheTime + $iOneDay2 < \time()) - { - $bFilesCache = true; - $iFilesCacheTime = \time(); - } - - if (0 === $iVersionsCacheTime || $iVersionsCacheTime + $iOneDay3 < \time()) - { - $bVersionsCache = true; - $iVersionsCacheTime = \time(); - } - - if ($bMainCache || $bFilesCache || $bVersionsCache) - { - if (!$this->StorageProvider()->Put(null, - Providers\Storage\Enumerations\StorageType::NOBODY, 'Cache/Timers', - \implode(',', array($iMainCacheTime, $iFilesCacheTime, $iVersionsCacheTime)))) - { - $bMainCache = $bFilesCache = $bVersionsCache = false; - } - } - - if ($bMainCache) - { - $this->Logger()->Write('Cacher GC: Begin'); - $this->Cacher()->GC(48); - $this->Logger()->Write('Cacher GC: End'); - } - else if ($bFilesCache) - { - $this->Logger()->Write('Files GC: Begin'); - $this->FilesProvider()->GC(48); - $this->Logger()->Write('Files GC: End'); - } - - $this->Plugins()->RunHook('service.app-delay-start-end'); - - return $this->TrueResponse(__FUNCTION__); - } - - public function DoSystemFoldersUpdate() : array - { - $oAccount = $this->getAccountFromToken(); - - $oSettingsLocal = $this->SettingsProvider(true)->Load($oAccount); - - $oSettingsLocal->SetConf('SentFolder', $this->GetActionParam('SentFolder', '')); - $oSettingsLocal->SetConf('DraftFolder', $this->GetActionParam('DraftFolder', '')); - $oSettingsLocal->SetConf('SpamFolder', $this->GetActionParam('SpamFolder', '')); - $oSettingsLocal->SetConf('TrashFolder', $this->GetActionParam('TrashFolder', '')); - $oSettingsLocal->SetConf('ArchiveFolder', $this->GetActionParam('ArchiveFolder', '')); - $oSettingsLocal->SetConf('NullFolder', $this->GetActionParam('NullFolder', '')); - - return $this->DefaultResponse(__FUNCTION__, - $this->SettingsProvider(true)->Save($oAccount, $oSettingsLocal)); - } - public function setConfigFromParams(Config\Application $oConfig, string $sParamName, string $sConfigSector, string $sConfigName, string $sType = 'string', ?callable $mStringCallback = null) : void { $sValue = $this->GetActionParam($sParamName, ''); @@ -2924,71 +2242,6 @@ NewThemeLink IncludeCss TemplatesLink LangLink IncludeBackground PluginsLink Aut } } - public function DoSettingsUpdate() : array - { - $oAccount = $this->getAccountFromToken(); - if (!$this->GetCapa(false, false, Enumerations\Capa::SETTINGS, $oAccount)) - { - return $this->FalseResponse(__FUNCTION__); - } - - $self = $this; - $oConfig = $this->Config(); - - $oSettings = $this->SettingsProvider()->Load($oAccount); - $oSettingsLocal = $this->SettingsProvider(true)->Load($oAccount); - - if ($oConfig->Get('webmail', 'allow_languages_on_settings', true)) - { - $this->setSettingsFromParams($oSettings, 'Language', 'string', function ($sLanguage) use ($self) { - return $self->ValidateLanguage($sLanguage); - }); - } - else - { - $oSettings->SetConf('Language', $this->ValidateLanguage($oConfig->Get('webmail', 'language', 'en'))); - } - - if ($this->GetCapa(false, false, Enumerations\Capa::THEMES, $oAccount)) - { - $this->setSettingsFromParams($oSettingsLocal, 'Theme', 'string', function ($sTheme) use ($self) { - return $self->ValidateTheme($sTheme); - }); - } - else - { - $oSettingsLocal->SetConf('Theme', $this->ValidateTheme($oConfig->Get('webmail', 'theme', 'Default'))); - } - - $this->setSettingsFromParams($oSettings, 'MPP', 'int', function ($iValue) { - return (int) (\in_array($iValue, array(10, 20, 30, 50, 100, 150, 200, 300)) ? $iValue : 20); - }); - - $this->setSettingsFromParams($oSettings, 'Layout', 'int', function ($iValue) { - return (int) (\in_array((int) $iValue, array(Enumerations\Layout::NO_PREVIW, - Enumerations\Layout::SIDE_PREVIEW, Enumerations\Layout::BOTTOM_PREVIEW)) ? - $iValue : Enumerations\Layout::SIDE_PREVIEW); - }); - - $this->setSettingsFromParams($oSettings, 'EditorDefaultType', 'string'); - $this->setSettingsFromParams($oSettings, 'ShowImages', 'bool'); - $this->setSettingsFromParams($oSettings, 'ContactsAutosave', 'bool'); - $this->setSettingsFromParams($oSettings, 'DesktopNotifications', 'bool'); - $this->setSettingsFromParams($oSettings, 'SoundNotification', 'bool'); - $this->setSettingsFromParams($oSettings, 'UseCheckboxesInList', 'bool'); - $this->setSettingsFromParams($oSettings, 'AllowDraftAutosave', 'bool'); - $this->setSettingsFromParams($oSettings, 'AutoLogout', 'int'); - - $this->setSettingsFromParams($oSettings, 'EnableTwoFactor', 'bool'); - - $this->setSettingsFromParams($oSettingsLocal, 'UseThreads', 'bool'); - $this->setSettingsFromParams($oSettingsLocal, 'ReplySameFolder', 'bool'); - - return $this->DefaultResponse(__FUNCTION__, - $this->SettingsProvider()->Save($oAccount, $oSettings) && - $this->SettingsProvider(true)->Save($oAccount, $oSettingsLocal)); - } - public function DoNoop() : array { $this->initMailClientConnection(); @@ -3164,513 +2417,6 @@ NewThemeLink IncludeCss TemplatesLink LangLink IncludeBackground PluginsLink Aut } } - public function DoFolders() : array - { - $oAccount = $this->initMailClientConnection(); - - $oFolderCollection = null; - $this->Plugins()->RunHook('filter.folders-before', array($oAccount, $oFolderCollection)); - - $bUseFolders = $this->GetCapa(false, false, Enumerations\Capa::FOLDERS, $oAccount); - - if (null === $oFolderCollection) - { - $oFolderCollection = $this->MailClient()->Folders('', - $bUseFolders ? '*' : 'INBOX', - !!$this->Config()->Get('labs', 'use_imap_list_subscribe', true), - (int) $this->Config()->Get('labs', 'imap_folder_list_limit', 200) - ); - } - - $this->Plugins()->RunHook('filter.folders-post', array($oAccount, $oFolderCollection)); - - if ($oFolderCollection instanceof \MailSo\Mail\FolderCollection) - { - $oSettingsLocal = $this->SettingsProvider(true)->Load($oAccount); - - $aSystemFolders = array(); - $this->recFoldersTypes($oAccount, $oFolderCollection, $aSystemFolders); - $oFolderCollection->SystemFolders = $aSystemFolders; - - if ($bUseFolders && $this->Config()->Get('labs', 'autocreate_system_folders', true)) - { - $bDoItAgain = false; - - $sNamespace = $oFolderCollection->GetNamespace(); - $sParent = empty($sNamespace) ? '' : \substr($sNamespace, 0, -1); - - $sDelimiter = $oFolderCollection->FindDelimiter(); - - $aList = array(); - $aMap = $this->systemFoldersNames($oAccount); - - if ('' === $oSettingsLocal->GetConf('SentFolder', '')) - { - $aList[] = \MailSo\Imap\Enumerations\FolderType::SENT; - } - - if ('' === $oSettingsLocal->GetConf('DraftFolder', '')) - { - $aList[] = \MailSo\Imap\Enumerations\FolderType::DRAFTS; - } - - if ('' === $oSettingsLocal->GetConf('SpamFolder', '')) - { - $aList[] = \MailSo\Imap\Enumerations\FolderType::JUNK; - } - - if ('' === $oSettingsLocal->GetConf('TrashFolder', '')) - { - $aList[] = \MailSo\Imap\Enumerations\FolderType::TRASH; - } - - if ('' === $oSettingsLocal->GetConf('ArchiveFolder', '')) - { - $aList[] = \MailSo\Imap\Enumerations\FolderType::ALL; - } - - $this->Plugins()->RunHook('filter.folders-system-types', array($oAccount, &$aList)); - - foreach ($aList as $iType) - { - if (!isset($aSystemFolders[$iType])) - { - $mFolderNameToCreate = \array_search($iType, $aMap); - if (!empty($mFolderNameToCreate)) - { - $iPos = \strrpos($mFolderNameToCreate, $sDelimiter); - if (false !== $iPos) - { - $mNewParent = \substr($mFolderNameToCreate, 0, $iPos); - $mNewFolderNameToCreate = \substr($mFolderNameToCreate, $iPos + 1); - if (0 < \strlen($mNewFolderNameToCreate)) - { - $mFolderNameToCreate = $mNewFolderNameToCreate; - } - - if (0 < \strlen($mNewParent)) - { - $sParent = 0 < \strlen($sParent) ? $sParent.$sDelimiter.$mNewParent : $mNewParent; - } - } - - $sFullNameToCheck = \MailSo\Base\Utils::ConvertEncoding($mFolderNameToCreate, - \MailSo\Base\Enumerations\Charset::UTF_8, \MailSo\Base\Enumerations\Charset::UTF_7_IMAP); - - if (0 < \strlen(\trim($sParent))) - { - $sFullNameToCheck = $sParent.$sDelimiter.$sFullNameToCheck; - } - - if (!$oFolderCollection->GetByFullNameRaw($sFullNameToCheck)) - { - try - { - if ($this->MailClient()->FolderCreate($mFolderNameToCreate, $sParent, true, $sDelimiter)) - { - $bDoItAgain = true; - } - } - catch (\Throwable $oException) - { - $this->Logger()->WriteException($oException); - } - } - } - } - } - - if ($bDoItAgain) - { - $oFolderCollection = $this->MailClient()->Folders('', '*', - !!$this->Config()->Get('labs', 'use_imap_list_subscribe', true), - (int) $this->Config()->Get('labs', 'imap_folder_list_limit', 200) - ); - - if ($oFolderCollection) - { - $aSystemFolders = array(); - $this->recFoldersTypes($oAccount, $oFolderCollection, $aSystemFolders); - $oFolderCollection->SystemFolders = $aSystemFolders; - } - } - } - - if ($oFolderCollection) - { - $oFolderCollection->FoldersHash = \md5(\implode("\x0", $this->recFoldersNames($oFolderCollection))); - } - } - - $this->Plugins()->RunHook('filter.folders-complete', array($oAccount, $oFolderCollection)); - - return $this->DefaultResponse(__FUNCTION__, $oFolderCollection); - } - - public function DoFolderCreate() : array - { - $oAccount = $this->initMailClientConnection(); - - if (!$this->GetCapa(false, false, Enumerations\Capa::FOLDERS, $oAccount)) - { - return $this->FalseResponse(__FUNCTION__); - } - - try - { - $sFolderNameInUtf = $this->GetActionParam('Folder', ''); - $sFolderParentFullNameRaw = $this->GetActionParam('Parent', ''); - - $this->MailClient()->FolderCreate($sFolderNameInUtf, $sFolderParentFullNameRaw, - !!$this->Config()->Get('labs', 'use_imap_list_subscribe', true)); - } - catch (\Throwable $oException) - { - throw new Exceptions\ClientException(Notifications::CantCreateFolder, $oException); - } - - return $this->TrueResponse(__FUNCTION__); - } - - public function DoFolderSubscribe() : array - { - $oAccount = $this->initMailClientConnection(); - - if (!$this->GetCapa(false, false, Enumerations\Capa::FOLDERS, $oAccount)) - { - return $this->FalseResponse(__FUNCTION__); - } - - $sFolderFullNameRaw = $this->GetActionParam('Folder', ''); - $bSubscribe = '1' === (string) $this->GetActionParam('Subscribe', '0'); - - try - { - $this->MailClient()->FolderSubscribe($sFolderFullNameRaw, !!$bSubscribe); - } - catch (\Throwable $oException) - { - if ($bSubscribe) - { - throw new Exceptions\ClientException(Notifications::CantSubscribeFolder, $oException); - } - else - { - throw new Exceptions\ClientException(Notifications::CantUnsubscribeFolder, $oException); - } - } - - return $this->TrueResponse(__FUNCTION__); - } - - public function DoFolderCheckable() : array - { - $oAccount = $this->getAccountFromToken(); - - if (!$this->GetCapa(false, false, Enumerations\Capa::FOLDERS, $oAccount)) - { - return $this->FalseResponse(__FUNCTION__); - } - - $sFolderFullNameRaw = $this->GetActionParam('Folder', ''); - $bCheckable = '1' === (string) $this->GetActionParam('Checkable', '0'); - - $oSettingsLocal = $this->SettingsProvider(true)->Load($oAccount); - - $sCheckableFolder = $oSettingsLocal->GetConf('CheckableFolder', '[]'); - $aCheckableFolder = \json_decode($sCheckableFolder); - - if (!\is_array($aCheckableFolder)) - { - $aCheckableFolder = array(); - } - - if ($bCheckable) - { - $aCheckableFolder[] = $sFolderFullNameRaw; - } - else - { - $aCheckableFolderNew = array(); - foreach ($aCheckableFolder as $sFolder) - { - if ($sFolder !== $sFolderFullNameRaw) - { - $aCheckableFolderNew[] = $sFolder; - } - } - $aCheckableFolder = $aCheckableFolderNew; - } - - $aCheckableFolder = \array_unique($aCheckableFolder); - - $oSettingsLocal->SetConf('CheckableFolder', \json_encode($aCheckableFolder)); - - return $this->DefaultResponse(__FUNCTION__, - $this->SettingsProvider(true)->Save($oAccount, $oSettingsLocal)); - } - - /** - * @throws \MailSo\Base\Exceptions\Exception - */ - public function DoFolderRename() : array - { - $oAccount = $this->initMailClientConnection(); - - if (!$this->GetCapa(false, false, Enumerations\Capa::FOLDERS, $oAccount)) - { - return $this->FalseResponse(__FUNCTION__); - } - - $sPrevFolderFullNameRaw = $this->GetActionParam('Folder', ''); - $sNewTopFolderNameInUtf = $this->GetActionParam('NewFolderName', ''); - - try - { - $this->MailClient()->FolderRename($sPrevFolderFullNameRaw, $sNewTopFolderNameInUtf, - !!$this->Config()->Get('labs', 'use_imap_list_subscribe', true)); - } - catch (\Throwable $oException) - { - throw new Exceptions\ClientException(Notifications::CantRenameFolder, $oException); - } - - return $this->TrueResponse(__FUNCTION__); - } - - /** - * @throws \MailSo\Base\Exceptions\Exception - */ - public function DoFolderDelete() : array - { - $oAccount = $this->initMailClientConnection(); - - if (!$this->GetCapa(false, false, Enumerations\Capa::FOLDERS, $oAccount)) - { - return $this->FalseResponse(__FUNCTION__); - } - - $sFolderFullNameRaw = $this->GetActionParam('Folder', ''); - - try - { - $this->MailClient()->FolderDelete($sFolderFullNameRaw, - !!$this->Config()->Get('labs', 'use_imap_list_subscribe', true)); - } - catch (\MailSo\Mail\Exceptions\NonEmptyFolder $oException) - { - throw new Exceptions\ClientException(Notifications::CantDeleteNonEmptyFolder, $oException); - } - catch (\Throwable $oException) - { - throw new Exceptions\ClientException(Notifications::CantDeleteFolder, $oException); - } - - return $this->TrueResponse(__FUNCTION__); - } - - /** - * @throws \MailSo\Base\Exceptions\Exception - */ - public function DoFolderClear() : array - { - $this->initMailClientConnection(); - - $sFolderFullNameRaw = $this->GetActionParam('Folder', ''); - - try - { - $this->MailClient()->FolderClear($sFolderFullNameRaw); - } - catch (\Throwable $oException) - { - throw new Exceptions\ClientException(Notifications::MailServerError, $oException); - } - - return $this->TrueResponse(__FUNCTION__); - } - - /** - * @throws \MailSo\Base\Exceptions\Exception - */ - public function DoFolderInformation() : array - { - $sFolder = $this->GetActionParam('Folder', ''); - $sPrevUidNext = $this->GetActionParam('UidNext', ''); - $aFlagsUids = array(); - $sFlagsUids = (string) $this->GetActionParam('FlagsUids', ''); - - $aFlagsFilteredUids = array(); - if (0 < strlen($sFlagsUids)) - { - $aFlagsUids = explode(',', $sFlagsUids); - $aFlagsFilteredUids = array_filter($aFlagsUids, function (&$sUid) { - $sUid = (int) trim($sUid); - return 0 < (int) trim($sUid); - }); - } - - $this->initMailClientConnection(); - - $sForwardedFlag = $this->Config()->Get('labs', 'imap_forwarded_flag', ''); - $sReadReceiptFlag = $this->Config()->Get('labs', 'imap_read_receipt_flag', ''); - try - { - $aInboxInformation = $this->MailClient()->FolderInformation( - $sFolder, $sPrevUidNext, $aFlagsFilteredUids - ); - - if (isset($aInboxInformation['Flags']) && \is_array($aInboxInformation['Flags'])) - { - foreach ($aInboxInformation['Flags'] as $iUid => $aFlags) - { - $aLowerFlags = array_map('strtolower', $aFlags); - $aInboxInformation['Flags'][$iUid] = array( - 'IsSeen' => in_array('\\seen', $aLowerFlags), - 'IsFlagged' => in_array('\\flagged', $aLowerFlags), - 'IsAnswered' => in_array('\\answered', $aLowerFlags), - 'IsDeleted' => in_array('\\deleted', $aLowerFlags), - 'IsForwarded' => 0 < strlen($sForwardedFlag) && in_array(strtolower($sForwardedFlag), $aLowerFlags), - 'IsReadReceipt' => 0 < strlen($sReadReceiptFlag) && in_array(strtolower($sReadReceiptFlag), $aLowerFlags) - ); - } - } - } - catch (\Throwable $oException) - { - throw new Exceptions\ClientException(Notifications::MailServerError, $oException); - } - - $aInboxInformation['Version'] = APP_VERSION; - - return $this->DefaultResponse(__FUNCTION__, $aInboxInformation); - } - - /** - * @throws \MailSo\Base\Exceptions\Exception - */ - public function DoFolderInformationMultiply() : array - { - $aResult = array( - 'List' => array(), - 'Version' => APP_VERSION - ); - - $aFolders = $this->GetActionParam('Folders', null); - if (\is_array($aFolders)) - { - $this->initMailClientConnection(); - - $aFolders = \array_unique($aFolders); - foreach ($aFolders as $sFolder) - { - if (0 < \strlen(\trim($sFolder)) && 'INBOX' !== \strtoupper($sFolder)) - { - try - { - $aInboxInformation = $this->MailClient()->FolderInformation($sFolder, '', array()); - if (isset($aInboxInformation['Folder'])) - { - $aResult['List'][] = $aInboxInformation; - } - } - catch (\Throwable $oException) - { - $this->Logger()->WriteException($oException); - } - } - } - } - - return $this->DefaultResponse(__FUNCTION__, $aResult); - } - - /** - * @throws \MailSo\Base\Exceptions\Exception - */ - public function DoMessageList() : array - { -// \sleep(1); -// throw new Exceptions\ClientException(Notifications::CantGetMessageList); - - $sFolder = ''; - $iOffset = 0; - $iLimit = 20; - $sSearch = ''; - $sUidNext = ''; - $bUseThreads = false; - $sThreadUid = ''; - - $sRawKey = $this->GetActionParam('RawKey', ''); - $aValues = $this->getDecodedClientRawKeyValue($sRawKey, 9); - - if ($aValues && 7 < \count($aValues)) - { - $sFolder =(string) $aValues[0]; - $iOffset = (int) $aValues[1]; - $iLimit = (int) $aValues[2]; - $sSearch = (string) $aValues[3]; - $sUidNext = (string) $aValues[6]; - $bUseThreads = (bool) $aValues[7]; - - if ($bUseThreads) - { - $sThreadUid = isset($aValues[8]) ? (string) $aValues[8] : ''; - } - - $this->verifyCacheByKey($sRawKey); - } - else - { - $sFolder = $this->GetActionParam('Folder', ''); - $iOffset = (int) $this->GetActionParam('Offset', 0); - $iLimit = (int) $this->GetActionParam('Limit', 10); - $sSearch = $this->GetActionParam('Search', ''); - $sUidNext = $this->GetActionParam('UidNext', ''); - $bUseThreads = '1' === (string) $this->GetActionParam('UseThreads', '0'); - - if ($bUseThreads) - { - $sThreadUid = (string) $this->GetActionParam('ThreadUid', ''); - } - } - - if (0 === strlen($sFolder)) - { - throw new Exceptions\ClientException(Notifications::CantGetMessageList); - } - - $this->initMailClientConnection(); - - try - { - if (!$this->Config()->Get('labs', 'use_imap_thread', false)) - { - $bUseThreads = false; - } - - $oMessageList = $this->MailClient()->MessageList( - $sFolder, $iOffset, $iLimit, $sSearch, $sUidNext, - $this->cacherForUids(), - !!$this->Config()->Get('labs', 'use_imap_sort', false), - $bUseThreads, - $sThreadUid, - '' - ); - } - catch (\Throwable $oException) - { - throw new Exceptions\ClientException(Notifications::CantGetMessageList, $oException); - } - - if ($oMessageList) - { - $this->cacheByKey($sRawKey); - } - - return $this->DefaultResponse(__FUNCTION__, $oMessageList); - } - private function buildMessage(Model\Account $oAccount, bool $bWithDraftInfo = true) : \MailSo\Mime\Message { $sIdentityID = $this->GetActionParam('IdentityID', ''); @@ -3910,73 +2656,6 @@ NewThemeLink IncludeCss TemplatesLink LangLink IncludeBackground PluginsLink Aut return $oMessage; } - public function DoSaveMessage() : array - { - $oAccount = $this->initMailClientConnection(); - - if (!$this->GetCapa(false, false, Enumerations\Capa::COMPOSER, $oAccount)) - { - return $this->FalseResponse(__FUNCTION__); - } - - $sMessageFolder = $this->GetActionParam('MessageFolder', ''); - $sMessageUid = $this->GetActionParam('MessageUid', ''); - - $sDraftFolder = $this->GetActionParam('SaveFolder', ''); - if (0 === strlen($sDraftFolder)) - { - throw new Exceptions\ClientException(Notifications::UnknownError); - } - - $oMessage = $this->buildMessage($oAccount, true); - - $this->Plugins()->RunHook('filter.save-message', array($oMessage)); - - $mResult = false; - if ($oMessage) - { - $rMessageStream = \MailSo\Base\ResourceRegistry::CreateMemoryResource(); - - $iMessageStreamSize = \MailSo\Base\Utils::MultipleStreamWriter( - $oMessage->ToStream(false), array($rMessageStream), 8192, true, true); - - if (false !== $iMessageStreamSize) - { - $sMessageId = $oMessage->MessageId(); - - \rewind($rMessageStream); - - $iNewUid = 0; - $this->MailClient()->MessageAppendStream( - $rMessageStream, $iMessageStreamSize, $sDraftFolder, array( - \MailSo\Imap\Enumerations\MessageFlag::SEEN - ), $iNewUid); - - if (!empty($sMessageId) && (null === $iNewUid || 0 === $iNewUid)) - { - $iNewUid = $this->MailClient()->FindMessageUidByMessageId($sDraftFolder, $sMessageId); - } - - $mResult = true; - - if (0 < strlen($sMessageFolder) && 0 < strlen($sMessageUid)) - { - $this->MailClient()->MessageDelete($sMessageFolder, array($sMessageUid), true, true); - } - - if (null !== $iNewUid && 0 < $iNewUid) - { - $mResult = array( - 'NewFolder' => $sDraftFolder, - 'NewUid' => $iNewUid - ); - } - } - } - - return $this->DefaultResponse(__FUNCTION__, $mResult); - } - /** * @throws \RainLoop\Exceptions\ClientException * @throws \MailSo\Net\Exceptions\ConnectionException @@ -4132,281 +2811,6 @@ NewThemeLink IncludeCss TemplatesLink LangLink IncludeBackground PluginsLink Aut } } - public function DoSendMessage() : array - { - $oAccount = $this->initMailClientConnection(); - - if (!$this->GetCapa(false, false, Enumerations\Capa::COMPOSER, $oAccount)) - { - return $this->FalseResponse(__FUNCTION__); - } - - $oConfig = $this->Config(); - - $sDraftFolder = $this->GetActionParam('MessageFolder', ''); - $sDraftUid = $this->GetActionParam('MessageUid', ''); - $sSentFolder = $this->GetActionParam('SaveFolder', ''); - $aDraftInfo = $this->GetActionParam('DraftInfo', null); - $bDsn = '1' === (string) $this->GetActionParam('Dsn', '0'); - - $oMessage = $this->buildMessage($oAccount, false); - - $this->Plugins()->RunHook('filter.send-message', array($oMessage)); - - $mResult = false; - try - { - if ($oMessage) - { - $rMessageStream = \MailSo\Base\ResourceRegistry::CreateMemoryResource(); - - $iMessageStreamSize = \MailSo\Base\Utils::MultipleStreamWriter( - $oMessage->ToStream(true), array($rMessageStream), 8192, true, true, true); - - if (false !== $iMessageStreamSize) - { - $this->smtpSendMessage($oAccount, $oMessage, $rMessageStream, $iMessageStreamSize, $bDsn, true); - - if (\is_array($aDraftInfo) && 3 === \count($aDraftInfo)) - { - $sDraftInfoType = $aDraftInfo[0]; - $sDraftInfoUid = $aDraftInfo[1]; - $sDraftInfoFolder = $aDraftInfo[2]; - - try - { - switch (\strtolower($sDraftInfoType)) - { - case 'reply': - case 'reply-all': - $this->MailClient()->MessageSetFlag($sDraftInfoFolder, array($sDraftInfoUid), true, - \MailSo\Imap\Enumerations\MessageFlag::ANSWERED, true); - break; - case 'forward': - $sForwardedFlag = $this->Config()->Get('labs', 'imap_forwarded_flag', ''); - if (0 < strlen($sForwardedFlag)) - { - $this->MailClient()->MessageSetFlag($sDraftInfoFolder, array($sDraftInfoUid), true, - $sForwardedFlag, true); - } - break; - } - } - catch (\Throwable $oException) - { - $this->Logger()->WriteException($oException, \MailSo\Log\Enumerations\Type::ERROR); - } - } - - if (0 < \strlen($sSentFolder)) - { - try - { - if (!$oMessage->GetBcc()) - { - if (\is_resource($rMessageStream)) - { - \rewind($rMessageStream); - } - - $this->Plugins()->RunHook('filter.send-message-stream', - array($oAccount, &$rMessageStream, &$iMessageStreamSize)); - - $this->MailClient()->MessageAppendStream( - $rMessageStream, $iMessageStreamSize, $sSentFolder, array( - \MailSo\Imap\Enumerations\MessageFlag::SEEN - ) - ); - } - else - { - $rAppendMessageStream = \MailSo\Base\ResourceRegistry::CreateMemoryResource(); - - $iAppendMessageStreamSize = \MailSo\Base\Utils::MultipleStreamWriter( - $oMessage->ToStream(false), array($rAppendMessageStream), 8192, true, true, true); - - $this->Plugins()->RunHook('filter.send-message-stream', - array($oAccount, &$rAppendMessageStream, &$iAppendMessageStreamSize)); - - $this->MailClient()->MessageAppendStream( - $rAppendMessageStream, $iAppendMessageStreamSize, $sSentFolder, array( - \MailSo\Imap\Enumerations\MessageFlag::SEEN - )); - - if (\is_resource($rAppendMessageStream)) - { - fclose($rAppendMessageStream); - } - } - } - catch (\Throwable $oException) - { - throw new Exceptions\ClientException(Notifications::CantSaveMessage, $oException); - } - } - - if (\is_resource($rMessageStream)) - { - \fclose($rMessageStream); - } - - $this->deleteMessageAttachmnets($oAccount); - - if (0 < \strlen($sDraftFolder) && 0 < \strlen($sDraftUid)) - { - try - { - $this->MailClient()->MessageDelete($sDraftFolder, array($sDraftUid), true, true); - } - catch (\Throwable $oException) - { - $this->Logger()->WriteException($oException, \MailSo\Log\Enumerations\Type::ERROR); - } - } - - $mResult = true; - } - } - } - catch (Exceptions\ClientException $oException) - { - throw $oException; - } - catch (\Throwable $oException) - { - throw new Exceptions\ClientException(Notifications::CantSendMessage, $oException); - } - - if (false === $mResult) - { - throw new Exceptions\ClientException(Notifications::CantSendMessage); - } - - try - { - if ($oMessage && $this->AddressBookProvider($oAccount)->IsActive()) - { - $aArrayToFrec = array(); - $oToCollection = $oMessage->GetTo(); - if ($oToCollection) - { - foreach ($oToCollection as /* @var $oEmail \MailSo\Mime\Email */ $oEmail) - { - $aArrayToFrec[$oEmail->GetEmail(true)] = $oEmail->ToString(false, true); - } - } - - if (0 < \count($aArrayToFrec)) - { - $oSettings = $this->SettingsProvider()->Load($oAccount); - - $this->AddressBookProvider($oAccount)->IncFrec( - $oAccount->ParentEmailHelper(), \array_values($aArrayToFrec), - !!$oSettings->GetConf('ContactsAutosave', - !!$oConfig->Get('defaults', 'contacts_autosave', true))); - } - } - } - catch (\Throwable $oException) - { - $this->Logger()->WriteException($oException); - } - - return $this->TrueResponse(__FUNCTION__); - } - - public function DoSendReadReceiptMessage() : array - { - $oAccount = $this->initMailClientConnection(); - - if (!$this->GetCapa(false, false, Enumerations\Capa::COMPOSER, $oAccount)) - { - return $this->FalseResponse(__FUNCTION__); - } - - $oMessage = $this->buildReadReceiptMessage($oAccount); - - $this->Plugins()->RunHook('filter.send-read-receipt-message', array($oMessage, $oAccount)); - - $mResult = false; - try - { - if ($oMessage) - { - $rMessageStream = \MailSo\Base\ResourceRegistry::CreateMemoryResource(); - - $iMessageStreamSize = \MailSo\Base\Utils::MultipleStreamWriter( - $oMessage->ToStream(true), array($rMessageStream), 8192, true, true, true); - - if (false !== $iMessageStreamSize) - { - $this->smtpSendMessage($oAccount, $oMessage, $rMessageStream, $iMessageStreamSize, false, false); - - if (\is_resource($rMessageStream)) - { - \fclose($rMessageStream); - } - - $mResult = true; - - $sReadReceiptFlag = $this->Config()->Get('labs', 'imap_read_receipt_flag', ''); - if (!empty($sReadReceiptFlag)) - { - $sFolderFullName = $this->GetActionParam('MessageFolder', ''); - $sUid = $this->GetActionParam('MessageUid', ''); - - $this->Cacher($oAccount)->Set(KeyPathHelper::ReadReceiptCache($oAccount->Email(), $sFolderFullName, $sUid), '1'); - - if (0 < \strlen($sFolderFullName) && 0 < \strlen($sUid)) - { - try - { - $this->MailClient()->MessageSetFlag($sFolderFullName, array($sUid), true, $sReadReceiptFlag, true, true); - } - catch (\Throwable $oException) {} - } - } - } - } - } - catch (Exceptions\ClientException $oException) - { - throw $oException; - } - catch (\Throwable $oException) - { - throw new Exceptions\ClientException(Notifications::CantSendMessage, $oException); - } - - if (false === $mResult) - { - throw new Exceptions\ClientException(Notifications::CantSendMessage); - } - - return $this->TrueResponse(__FUNCTION__); - } - - public function DoQuota() : array - { - $oAccount = $this->initMailClientConnection(); - - if (!$this->GetCapa(false, false, Enumerations\Capa::QUOTA, $oAccount)) - { - return $this->DefaultResponse(__FUNCTION__, array(0, 0, 0, 0)); - } - - try - { - $aQuota = $this->MailClient()->Quota(); - } - catch (\Throwable $oException) - { - throw new Exceptions\ClientException(Notifications::MailServerError, $oException); - } - - return $this->DefaultResponse(__FUNCTION__, $aQuota); - } - private function getContactsSyncData(Model\Account $oAccount) : ?array { $mResult = null; @@ -4433,63 +2837,6 @@ NewThemeLink IncludeCss TemplatesLink LangLink IncludeBackground PluginsLink Aut return $mResult; } - public function DoSaveContactsSyncData() : array - { - $oAccount = $this->getAccountFromToken(); - - $oAddressBookProvider = $this->AddressBookProvider($oAccount); - if (!$oAddressBookProvider || !$oAddressBookProvider->IsActive()) - { - return $this->FalseResponse(__FUNCTION__); - } - - $bEnabled = '1' === (string) $this->GetActionParam('Enable', '0'); - $sUrl = $this->GetActionParam('Url', ''); - $sUser = $this->GetActionParam('User', ''); - $sPassword = $this->GetActionParam('Password', ''); - - $mData = $this->getContactsSyncData($oAccount); - - $bResult = $this->StorageProvider()->Put($oAccount, - Providers\Storage\Enumerations\StorageType::CONFIG, - 'contacts_sync', - Utils::EncodeKeyValues(array( - 'Enable' => $bEnabled, - 'User' => $sUser, - 'Password' => APP_DUMMY === $sPassword && isset($mData['Password']) ? - $mData['Password'] : (APP_DUMMY === $sPassword ? '' : $sPassword), - 'Url' => $sUrl - )) - ); - - return $this->DefaultResponse(__FUNCTION__, $bResult); - } - - public function DoContactsSync() : array - { - $bResult = false; - $oAccount = $this->getAccountFromToken(); - - $oAddressBookProvider = $this->AddressBookProvider($oAccount); - if ($oAddressBookProvider && $oAddressBookProvider->IsActive()) - { - $mData = $this->getContactsSyncData($oAccount); - if (isset($mData['Enable'], $mData['User'], $mData['Password'], $mData['Url']) && $mData['Enable']) - { - $bResult = $oAddressBookProvider->Sync( - $oAccount->ParentEmailHelper(), - $mData['Url'], $mData['User'], $mData['Password']); - } - } - - if (!$bResult) - { - throw new Exceptions\ClientException(Notifications::ContactsSyncError); - } - - return $this->TrueResponse(__FUNCTION__); - } - private function getTwoFactorInfo(Model\Account $oAccount, bool $bRemoveSecret = false) : array { $sEmail = $oAccount->ParentEmailHelper(); @@ -4586,335 +2933,6 @@ NewThemeLink IncludeCss TemplatesLink LangLink IncludeBackground PluginsLink Aut return false; } - public function DoGetTwoFactorInfo() : array - { - $oAccount = $this->getAccountFromToken(); - - if (!$this->TwoFactorAuthProvider()->IsActive() || - !$this->GetCapa(false, false, Enumerations\Capa::TWO_FACTOR, $oAccount)) - { - return $this->FalseResponse(__FUNCTION__); - } - - return $this->DefaultResponse(__FUNCTION__, - $this->getTwoFactorInfo($oAccount, true)); - } - - public function DoCreateTwoFactorSecret() : array - { - $oAccount = $this->getAccountFromToken(); - - if (!$this->TwoFactorAuthProvider()->IsActive() || - !$this->GetCapa(false, false, Enumerations\Capa::TWO_FACTOR, $oAccount)) - { - return $this->FalseResponse(__FUNCTION__); - } - - $sEmail = $oAccount->ParentEmailHelper(); - - $sSecret = $this->TwoFactorAuthProvider()->CreateSecret(); - - $aCodes = array(); - for ($iIndex = 9; $iIndex > 0; $iIndex--) - { - $aCodes[] = \rand(100000000, 900000000); - } - - $this->StorageProvider()->Put($oAccount, - Providers\Storage\Enumerations\StorageType::CONFIG, - 'two_factor', - Utils::EncodeKeyValues(array( - 'User' => $sEmail, - 'Enable' => false, - 'Secret' => $sSecret, - 'BackupCodes' => \implode(' ', $aCodes) - )) - ); - - $this->requestSleep(); - - return $this->DefaultResponse(__FUNCTION__, - $this->getTwoFactorInfo($oAccount)); - } - - public function DoShowTwoFactorSecret() : array - { - $oAccount = $this->getAccountFromToken(); - - if (!$this->TwoFactorAuthProvider()->IsActive() || - !$this->GetCapa(false, false, Enumerations\Capa::TWO_FACTOR, $oAccount)) - { - return $this->FalseResponse(__FUNCTION__); - } - - $aResult = $this->getTwoFactorInfo($oAccount); - unset($aResult['BackupCodes']); - - return $this->DefaultResponse(__FUNCTION__, $aResult); - } - - public function DoEnableTwoFactor() : array - { - $oAccount = $this->getAccountFromToken(); - - if (!$this->TwoFactorAuthProvider()->IsActive() || - !$this->GetCapa(false, false, Enumerations\Capa::TWO_FACTOR, $oAccount)) - { - return $this->FalseResponse(__FUNCTION__); - } - - $sEmail = $oAccount->ParentEmailHelper(); - - $bResult = false; - $mData = $this->getTwoFactorInfo($oAccount); - if (isset($mData['Secret'], $mData['BackupCodes'])) - { - $bResult = $this->StorageProvider()->Put($oAccount, - Providers\Storage\Enumerations\StorageType::CONFIG, - 'two_factor', - Utils::EncodeKeyValues(array( - 'User' => $sEmail, - 'Enable' => '1' === \trim($this->GetActionParam('Enable', '0')), - 'Secret' => $mData['Secret'], - 'BackupCodes' => $mData['BackupCodes'] - )) - ); - } - - return $this->DefaultResponse(__FUNCTION__, $bResult); - } - - public function DoTestTwoFactorInfo() : array - { - $oAccount = $this->getAccountFromToken(); - - if (!$this->TwoFactorAuthProvider()->IsActive() || - !$this->GetCapa(false, false, Enumerations\Capa::TWO_FACTOR, $oAccount)) - { - return $this->FalseResponse(__FUNCTION__); - } - - $sCode = \trim($this->GetActionParam('Code', '')); - - $aData = $this->getTwoFactorInfo($oAccount); - $sSecret = !empty($aData['Secret']) ? $aData['Secret'] : ''; - -// $this->Logger()->WriteDump(array( -// $sCode, $sSecret, $aData, -// $this->TwoFactorAuthProvider()->VerifyCode($sSecret, $sCode) -// )); - - $this->requestSleep(); - - return $this->DefaultResponse(__FUNCTION__, - $this->TwoFactorAuthProvider()->VerifyCode($sSecret, $sCode)); - } - - public function DoClearTwoFactorInfo() : array - { - $oAccount = $this->getAccountFromToken(); - - if (!$this->TwoFactorAuthProvider()->IsActive() || - !$this->GetCapa(false, false, Enumerations\Capa::TWO_FACTOR, $oAccount)) - { - return $this->FalseResponse(__FUNCTION__); - } - - $this->StorageProvider()->Clear($oAccount, - Providers\Storage\Enumerations\StorageType::CONFIG, - 'two_factor' - ); - - return $this->DefaultResponse(__FUNCTION__, - $this->getTwoFactorInfo($oAccount, true)); - } - - public function DoContacts() : array - { - $oAccount = $this->getAccountFromToken(); - - $sSearch = \trim($this->GetActionParam('Search', '')); - $iOffset = (int) $this->GetActionParam('Offset', 0); - $iLimit = (int) $this->GetActionParam('Limit', 20); - $iOffset = 0 > $iOffset ? 0 : $iOffset; - $iLimit = 0 > $iLimit ? 20 : $iLimit; - - $iResultCount = 0; - $mResult = array(); - - $oAbp = $this->AddressBookProvider($oAccount); - if ($oAbp->IsActive()) - { - $iResultCount = 0; - $mResult = $oAbp->GetContacts($oAccount->ParentEmailHelper(), - $iOffset, $iLimit, $sSearch, $iResultCount); - } - - return $this->DefaultResponse(__FUNCTION__, array( - 'Offset' => $iOffset, - 'Limit' => $iLimit, - 'Count' => $iResultCount, - 'Search' => $sSearch, - 'List' => $mResult - )); - } - - public function DoContactsDelete() : array - { - $oAccount = $this->getAccountFromToken(); - $aUids = \explode(',', (string) $this->GetActionParam('Uids', '')); - - $aFilteredUids = \array_filter($aUids, function (&$mUid) { - $mUid = (int) \trim($mUid); - return 0 < $mUid; - }); - - $bResult = false; - if (0 < \count($aFilteredUids) && $this->AddressBookProvider($oAccount)->IsActive()) - { - $bResult = $this->AddressBookProvider($oAccount)->DeleteContacts($oAccount->ParentEmailHelper(), $aFilteredUids); - } - - return $this->DefaultResponse(__FUNCTION__, $bResult); - } - - public function DoContactSave() : array - { - $oAccount = $this->getAccountFromToken(); - - $bResult = false; - - $oAddressBookProvider = $this->AddressBookProvider($oAccount); - $sRequestUid = \trim($this->GetActionParam('RequestUid', '')); - if ($oAddressBookProvider && $oAddressBookProvider->IsActive() && 0 < \strlen($sRequestUid)) - { - $sUid = \trim($this->GetActionParam('Uid', '')); - - $oContact = null; - if (0 < \strlen($sUid)) - { - $oContact = $oAddressBookProvider->GetContactByID($oAccount->ParentEmailHelper(), $sUid); - } - - if (!$oContact) - { - $oContact = new Providers\AddressBook\Classes\Contact(); - if (0 < \strlen($sUid)) - { - $oContact->IdContact = $sUid; - } - } - - $oContact->Properties = array(); - $aProperties = $this->GetActionParam('Properties', array()); - if (\is_array($aProperties)) - { - foreach ($aProperties as $aItem) - { - if ($aItem && isset($aItem[0], $aItem[1]) && \is_numeric($aItem[0])) - { - $oProp = new Providers\AddressBook\Classes\Property(); - $oProp->Type = (int) $aItem[0]; - $oProp->Value = $aItem[1]; - $oProp->TypeStr = empty($aItem[2]) ? '': $aItem[2]; - - $oContact->Properties[] = $oProp; - } - } - } - - if (!empty($oContact->Etag)) - { - $oContact->Etag = \md5($oContact->ToVCard()); - } - - $oContact->PopulateDisplayAndFullNameValue(true); - - $bResult = $oAddressBookProvider->ContactSave($oAccount->ParentEmailHelper(), $oContact); - } - - return $this->DefaultResponse(__FUNCTION__, array( - 'RequestUid' => $sRequestUid, - 'ResultID' => $bResult ? $oContact->IdContact : '', - 'Result' => $bResult - )); - } - - public function DoSuggestions() : array - { - $oAccount = $this->getAccountFromToken(); - - $sQuery = \trim($this->GetActionParam('Query', '')); - $iLimit = (int) $this->Config()->Get('contacts', 'suggestions_limit', 20); - - $aResult = array(); - - $this->Plugins()->RunHook('ajax.suggestions-input-parameters', array(&$sQuery, &$iLimit, $oAccount)); - - $iLimit = (int) $iLimit; - if (5 > $iLimit) - { - $iLimit = 5; - } - - $this->Plugins()->RunHook('ajax.suggestions-pre', array(&$aResult, $sQuery, $oAccount, $iLimit)); - - if ($iLimit > \count($aResult) && 0 < \strlen($sQuery)) - { - try - { - // Address Book - $oAddressBookProvider = $this->AddressBookProvider($oAccount); - if ($oAddressBookProvider && $oAddressBookProvider->IsActive()) - { - $aSuggestions = $oAddressBookProvider->GetSuggestions($oAccount->ParentEmailHelper(), $sQuery, $iLimit); - if (0 === \count($aResult)) - { - $aResult = $aSuggestions; - } - else - { - $aResult = \array_merge($aResult, $aSuggestions); - } - } - } - catch (\Throwable $oException) - { - $this->Logger()->WriteException($oException); - } - } - - if ($iLimit > \count($aResult) && 0 < \strlen($sQuery)) - { - $oSuggestionsProvider = $this->SuggestionsProvider(); - if ($oSuggestionsProvider && $oSuggestionsProvider->IsActive()) - { - $aSuggestionsProviderResult = $oSuggestionsProvider->Process($oAccount, $sQuery, $iLimit); - if (\is_array($aSuggestionsProviderResult) && 0 < \count($aSuggestionsProviderResult)) - { - $aResult = \array_merge($aResult, $aSuggestionsProviderResult); - } - } - - } - - $aResult = Utils::RemoveSuggestionDuplicates($aResult); - if ($iLimit < \count($aResult)) - { - $aResult = \array_slice($aResult, 0, $iLimit); - } - - $this->Plugins()->RunHook('ajax.suggestions-post', array(&$aResult, $sQuery, $oAccount, $iLimit)); - - $aResult = Utils::RemoveSuggestionDuplicates($aResult); - if ($iLimit < \count($aResult)) - { - $aResult = \array_slice($aResult, 0, $iLimit); - } - - return $this->DefaultResponse(__FUNCTION__, $aResult); - } - private function messageSetFlag(string $sActionFunction, string $sResponseFunction) : array { $this->initMailClientConnection(); @@ -4939,235 +2957,6 @@ NewThemeLink IncludeCss TemplatesLink LangLink IncludeBackground PluginsLink Aut return $this->TrueResponse($sResponseFunction); } - public function DoMessageSetSeen() : array - { - return $this->messageSetFlag('MessageSetSeen', __FUNCTION__); - } - - public function DoMessageSetSeenToAll() : array - { - $this->initMailClientConnection(); - - $sFolder = $this->GetActionParam('Folder', ''); - $bSetAction = '1' === (string) $this->GetActionParam('SetAction', '0'); - $sThreadUids = \trim($this->GetActionParam('ThreadUids', '')); - - try - { - $this->MailClient()->MessageSetSeenToAll($sFolder, $bSetAction, - !empty($sThreadUids) ? explode(',', $sThreadUids) : null); - } - catch (\Throwable $oException) - { - throw new Exceptions\ClientException(Notifications::MailServerError, $oException); - } - - return $this->TrueResponse(__FUNCTION__); - } - - public function DoMessageSetFlagged() : array - { - return $this->messageSetFlag('MessageSetFlagged', __FUNCTION__); - } - - /** - * @throws \MailSo\Base\Exceptions\Exception - */ - public function DoMessage() : array - { - $sRawKey = (string) $this->GetActionParam('RawKey', ''); - - $sFolder = ''; - $iUid = 0; - - $aValues = $this->getDecodedClientRawKeyValue($sRawKey, 4); - if ($aValues && 4 === count($aValues)) - { - $sFolder = (string) $aValues[0]; - $iUid = (int) $aValues[1]; - - $this->verifyCacheByKey($sRawKey); - } - else - { - $sFolder = $this->GetActionParam('Folder', ''); - $iUid = (int) $this->GetActionParam('Uid', 0); - } - - $oAccount = $this->initMailClientConnection(); - - try - { - $oMessage = $this->MailClient()->Message($sFolder, $iUid, true, - $this->cacherForThreads(), - (int) $this->Config()->Get('labs', 'imap_body_text_limit', 0) - ); - } - catch (\Throwable $oException) - { - throw new Exceptions\ClientException(Notifications::CantGetMessage, $oException); - } - - if ($oMessage) - { - $this->Plugins()->RunHook('filter.result-message', array($oMessage)); - - $this->cacheByKey($sRawKey); - } - - return $this->DefaultResponse(__FUNCTION__, $oMessage); - } - - /** - * @throws \MailSo\Base\Exceptions\Exception - */ - public function DoMessageDelete() : array - { - $this->initMailClientConnection(); - - $sFolder = $this->GetActionParam('Folder', ''); - $aUids = \explode(',', (string) $this->GetActionParam('Uids', '')); - - $aFilteredUids = \array_filter($aUids, function (&$sUid) { - $sUid = (int) \trim($sUid); - return 0 < $sUid; - }); - - try - { - $this->MailClient()->MessageDelete($sFolder, $aFilteredUids, true, true, - !!$this->Config()->Get('labs', 'use_imap_expunge_all_on_delete', false)); - } - catch (\Throwable $oException) - { - throw new Exceptions\ClientException(Notifications::CantDeleteMessage, $oException); - } - - if ($this->Config()->Get('labs', 'use_imap_unselect', true)) - { - try - { - $this->MailClient()->FolderUnSelect(); - } - catch (\Throwable $oException) - { - unset($oException); - } - } - - $sHash = ''; - - try - { - $sHash = $this->MailClient()->FolderHash($sFolder); - } - catch (\Throwable $oException) - { - unset($oException); - } - - return $this->DefaultResponse(__FUNCTION__, '' === $sHash ? false : array($sFolder, $sHash)); - } - - /** - * @throws \MailSo\Base\Exceptions\Exception - */ - public function DoMessageMove() : array - { - $this->initMailClientConnection(); - - $sFromFolder = $this->GetActionParam('FromFolder', ''); - $sToFolder = $this->GetActionParam('ToFolder', ''); - $aUids = \explode(',', (string) $this->GetActionParam('Uids', '')); - $bMarkAsRead = '1' === (string) $this->GetActionParam('MarkAsRead', '0'); - - $aFilteredUids = \array_filter($aUids, function (&$mUid) { - $mUid = (int) \trim($mUid); - return 0 < $mUid; - }); - - if ($bMarkAsRead) - { - try - { - $this->MailClient()->MessageSetSeen($sFromFolder, $aFilteredUids, true, true); - } - catch (\Throwable $oException) - { - unset($oException); - } - } - - try - { - $this->MailClient()->MessageMove($sFromFolder, $sToFolder, $aFilteredUids, true, - !!$this->Config()->Get('labs', 'use_imap_move', true), - !!$this->Config()->Get('labs', 'use_imap_expunge_all_on_delete', false) - ); - } - catch (\Throwable $oException) - { - throw new Exceptions\ClientException(Notifications::CantMoveMessage, $oException); - } - - if ($this->Config()->Get('labs', 'use_imap_unselect', true)) - { - try - { - $this->MailClient()->FolderUnSelect(); - } - catch (\Throwable $oException) - { - unset($oException); - } - } - - $sHash = ''; - - try - { - $sHash = $this->MailClient()->FolderHash($sFromFolder); - } - catch (\Throwable $oException) - { - unset($oException); - } - - return $this->DefaultResponse(__FUNCTION__, '' === $sHash ? false : array($sFromFolder, $sHash)); - } - - /** - * @throws \MailSo\Base\Exceptions\Exception - */ - public function DoMessageCopy() : array - { - $this->initMailClientConnection(); - - $sFromFolder = $this->GetActionParam('FromFolder', ''); - $sToFolder = $this->GetActionParam('ToFolder', ''); - $aUids = \explode(',', (string) $this->GetActionParam('Uids', '')); - - $aFilteredUids = \array_filter($aUids, function (&$mUid) { - $mUid = (int) \trim($mUid); - return 0 < $mUid; - }); - - try - { - $this->MailClient()->MessageCopy($sFromFolder, $sToFolder, - $aFilteredUids, true); - - $sHash = $this->MailClient()->FolderHash($sFromFolder); - } - catch (\Throwable $oException) - { - throw new Exceptions\ClientException(Notifications::CantCopyMessage, $oException); - } - - return $this->DefaultResponse(__FUNCTION__, - '' === $sHash ? false : array($sFromFolder, $sHash)); - } - public function MainClearFileName(string $sFileName, string $sContentType, string $sMimeIndex, int $iMaxLength = 250) : string { $sFileName = 0 === \strlen($sFileName) ? \preg_replace('/[^a-zA-Z0-9]/', '.', (empty($sMimeIndex) ? '' : $sMimeIndex.'.').$sContentType) : $sFileName; @@ -5182,97 +2971,6 @@ NewThemeLink IncludeCss TemplatesLink LangLink IncludeBackground PluginsLink Aut return \MailSo\Base\Utils::ClearFileName(\MailSo\Base\Utils::Utf8Clear($sClearedFileName)); } - public function DoMessageUploadAttachments() : array - { - $oAccount = $this->initMailClientConnection(); - - $mResult = false; - $self = $this; - - try - { - $aAttachments = $this->GetActionParam('Attachments', array()); - if (\is_array($aAttachments) && 0 < \count($aAttachments)) - { - $mResult = array(); - foreach ($aAttachments as $sAttachment) - { - if ($aValues = Utils::DecodeKeyValuesQ($sAttachment)) - { - $sFolder = isset($aValues['Folder']) ? $aValues['Folder'] : ''; - $iUid = (int) isset($aValues['Uid']) ? $aValues['Uid'] : 0; - $sMimeIndex = (string) isset($aValues['MimeIndex']) ? $aValues['MimeIndex'] : ''; - - $sTempName = \md5($sAttachment); - if (!$this->FilesProvider()->FileExists($oAccount, $sTempName)) - { - $this->MailClient()->MessageMimeStream( - function($rResource, $sContentType, $sFileName, $sMimeIndex = '') use ($oAccount, &$mResult, $sTempName, $sAttachment, $self) { - if (is_resource($rResource)) - { - $sContentType = (empty($sFileName)) ? 'text/plain' : \MailSo\Base\Utils::MimeContentType($sFileName); - $sFileName = $self->MainClearFileName($sFileName, $sContentType, $sMimeIndex); - - if ($self->FilesProvider()->PutFile($oAccount, $sTempName, $rResource)) - { - $mResult[$sTempName] = $sAttachment; - } - } - }, $sFolder, $iUid, true, $sMimeIndex); - } - else - { - $mResult[$sTempName] = $sAttachment; - } - } - } - } - } - catch (\Throwable $oException) - { - throw new Exceptions\ClientException(Notifications::MailServerError, $oException); - } - - return $this->DefaultResponse(__FUNCTION__, $mResult); - } - - public function DoComposeUploadExternals() : array - { - $oAccount = $this->getAccountFromToken(); - - $mResult = false; - $aExternals = $this->GetActionParam('Externals', array()); - if (\is_array($aExternals) && 0 < \count($aExternals)) - { - $oHttp = \MailSo\Base\Http::SingletonInstance(); - - $mResult = array(); - foreach ($aExternals as $sUrl) - { - $mResult[$sUrl] = ''; - - $sTempName = \md5($sUrl); - - $iCode = 0; - $sContentType = ''; - - $rFile = $this->FilesProvider()->GetFile($oAccount, $sTempName, 'wb+'); - if ($rFile && $oHttp->SaveUrlToFile($sUrl, $rFile, '', $sContentType, $iCode, $this->Logger(), 60, - $this->Config()->Get('labs', 'curl_proxy', ''), $this->Config()->Get('labs', 'curl_proxy_auth', ''))) - { - $mResult[$sUrl] = $sTempName; - } - - if (\is_resource($rFile)) - { - \fclose($rFile); - } - } - } - - return $this->DefaultResponse(__FUNCTION__, $mResult); - } - private function getUploadErrorMessageByCode(int $iError, int &$iClientError) : string { $sError = ''; @@ -5366,31 +3064,6 @@ NewThemeLink IncludeCss TemplatesLink LangLink IncludeBackground PluginsLink Aut return $this->DefaultResponse(__FUNCTION__, $aResponse); } - public function DoClearUserBackground() : array - { - $oAccount = $this->getAccountFromToken(); - - if (!$this->GetCapa(false, false, Enumerations\Capa::USER_BACKGROUND, $oAccount)) - { - return $this->FalseResponse(__FUNCTION__); - } - - $oSettings = $this->SettingsProvider()->Load($oAccount); - if ($oAccount && $oSettings) - { - $this->StorageProvider()->Clear($oAccount, - Providers\Storage\Enumerations\StorageType::CONFIG, - 'background' - ); - - $oSettings->SetConf('UserBackgroundName', ''); - $oSettings->SetConf('UserBackgroundHash', ''); - } - - return $this->DefaultResponse(__FUNCTION__, $oAccount && $oSettings ? - $this->SettingsProvider()->Save($oAccount, $oSettings) : false); - } - public function UploadBackground() : array { $oAccount = $this->getAccountFromToken(); @@ -5655,48 +3328,6 @@ NewThemeLink IncludeCss TemplatesLink LangLink IncludeBackground PluginsLink Aut }, $sFolder, $iUid, true, $sMimeIndex); } - public function RawFramedView() : string - { - $oAccount = $this->getAccountFromToken(false); - if ($oAccount) - { - $sRawKey = (string) $this->GetActionParam('RawKey', ''); - $aParams = $this->GetActionParam('Params', null); - $this->Http()->ServerNoCache(); - - $aData = Utils::DecodeKeyValuesQ($sRawKey); - if (isset($aParams[0], $aParams[1], $aParams[2]) && - 'Raw' === $aParams[0] && 'FramedView' === $aParams[2] && isset($aData['Framed']) && $aData['Framed'] && $aData['FileName']) - { - if ($this->isFileHasFramedPreview($aData['FileName'])) - { - $sNewSpecAuthToken = $this->GetShortLifeSpecAuthToken(); - if (!empty($sNewSpecAuthToken)) - { - $aParams[1] = '_'.$sNewSpecAuthToken; - $aParams[2] = 'View'; - - \array_shift($aParams); - $sLast = \array_pop($aParams); - - $sUrl = $this->Http()->GetFullUrl().'?/Raw/&q[]=/'.\implode('/', $aParams).'/&q[]=/'.$sLast; - $sFullUrl = 'https://docs.google.com/viewer?embedded=true&url='.\urlencode($sUrl); - - \header('Content-Type: text/html; charset=utf-8'); - echo ''. - ''. - ''. - ''; - } - } - } - } - - - return true; - - } - /** * @throws \MailSo\Base\Exceptions\Exception */ @@ -6338,46 +3969,6 @@ NewThemeLink IncludeCss TemplatesLink LangLink IncludeBackground PluginsLink Aut return false; } - public function isFileHasFramedPreview(string $sFileName) : bool - { - $sExt = \MailSo\Base\Utils::GetFileExtension($sFileName); - return \in_array($sExt, array('doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx')); - } - - public function isFileHasThumbnail(string $sFileName) : bool - { - static $aCache = array(); - - $sExt = \MailSo\Base\Utils::GetFileExtension($sFileName); - if (isset($aCache[$sExt])) - { - return $aCache[$sExt]; - } - - $bResult = \function_exists('gd_info'); - if ($bResult) - { - $bResult = false; - switch ($sExt) - { - case 'png': - $bResult = \function_exists('imagecreatefrompng'); - break; - case 'gif': - $bResult = \function_exists('imagecreatefromgif'); - break; - case 'jpg': - case 'jpeg': - $bResult = \function_exists('imagecreatefromjpeg'); - break; - } - } - - $aCache[$sExt] = $bResult; - - return $bResult; - } - public function RawContactsVcf() : bool { $oAccount = $this->getAccountFromToken(); @@ -6678,109 +4269,6 @@ NewThemeLink IncludeCss TemplatesLink LangLink IncludeBackground PluginsLink Aut return Utils::ClearHtmlOutput($sHtml); } - /** - * @param mixed $mResult = false - */ - private function mainDefaultResponse(string $sActionName, $mResult = false, array $aAdditionalParams = array()) : array - { - $sActionName = 'Do' === \substr($sActionName, 0, 2) ? \substr($sActionName, 2) : $sActionName; - $sActionName = \preg_replace('/[^a-zA-Z0-9_]+/', '', $sActionName); - - $aResult = array( - 'Action' => $sActionName, - 'Result' => $this->responseObject($mResult, $sActionName) - ); - - foreach ($aAdditionalParams as $sKey => $mValue) - { - $aResult[$sKey] = $mValue; - } - - return $aResult; - } - /** - * @param mixed $mResult = false - */ - public function DefaultResponse(string $sActionName, $mResult = false, array $aAdditionalParams = array()) : array - { - $this->Plugins()->RunHook('main.default-response-data', array($sActionName, &$mResult)); - $aResponseItem = $this->mainDefaultResponse($sActionName, $mResult, $aAdditionalParams); - $this->Plugins()->RunHook('main.default-response', array($sActionName, &$aResponseItem)); - return $aResponseItem; - } - - public function TrueResponse(string $sActionName, array $aAdditionalParams = array()) : array - { - $mResult = true; - $this->Plugins()->RunHook('main.default-response-data', array($sActionName, &$mResult)); - $aResponseItem = $this->mainDefaultResponse($sActionName, $mResult, $aAdditionalParams); - $this->Plugins()->RunHook('main.default-response', array($sActionName, &$aResponseItem)); - return $aResponseItem; - } - - public function FalseResponse(string $sActionName, ?int $iErrorCode = null, ?string $sErrorMessage = null, ?string $sAdditionalErrorMessage = null) : array - { - $mResult = false; - $this->Plugins() - ->RunHook('main.default-response-data', array($sActionName, &$mResult)) - ->RunHook('main.default-response-error-data', array($sActionName, &$iErrorCode, &$sErrorMessage)) - ; - - $aAdditionalParams = array(); - if (null !== $iErrorCode) - { - $aAdditionalParams['ErrorCode'] = (int) $iErrorCode; - $aAdditionalParams['ErrorMessage'] = null === $sErrorMessage ? '' : (string) $sErrorMessage; - $aAdditionalParams['ErrorMessageAdditional'] = null === $sAdditionalErrorMessage ? '' : (string) $sAdditionalErrorMessage; - } - - $aResponseItem = $this->mainDefaultResponse($sActionName, $mResult, $aAdditionalParams); - - $this->Plugins()->RunHook('main.default-response', array($sActionName, &$aResponseItem)); - return $aResponseItem; - } - - public function ExceptionResponse(string $sActionName, \Throwable $oException) : array - { - $iErrorCode = null; - $sErrorMessage = null; - $sErrorMessageAdditional = null; - - if ($oException instanceof Exceptions\ClientException) - { - $iErrorCode = $oException->getCode(); - $sErrorMessage = null; - - if ($iErrorCode === Notifications::ClientViewError) - { - $sErrorMessage = $oException->getMessage(); - } - - $sErrorMessageAdditional = $oException->getAdditionalMessage(); - if (empty($sErrorMessageAdditional)) - { - $sErrorMessageAdditional = null; - } - } - else - { - $iErrorCode = Notifications::UnknownError; - $sErrorMessage = $oException->getCode().' - '.$oException->getMessage(); - } - - $oPrevious = $oException->getPrevious(); - if ($oPrevious) - { - $this->Logger()->WriteException($oPrevious); - } - else - { - $this->Logger()->WriteException($oException); - } - - return $this->FalseResponse($sActionName, $iErrorCode, $sErrorMessage, $sErrorMessageAdditional); - } - public function SetActionParams(array $aCurrentActionParams, string $sMethodName = '') : self { $this->Plugins()->RunHook('filter.action-params', array($sMethodName, &$aCurrentActionParams)); @@ -6833,38 +4321,6 @@ NewThemeLink IncludeCss TemplatesLink LangLink IncludeBackground PluginsLink Aut \header('Location: '.$sUrl); } - private function objectData(object $oData, string $sParent, array $aParameters = array()) : array - { - $mResult = array(); - if (is_object($oData)) - { - $aNames = explode('\\', get_class($oData)); - $mResult = array( - '@Object' => end($aNames) - ); - - if ($oData instanceof \MailSo\Base\Collection) - { - $mResult['@Object'] = 'Collection/'.$mResult['@Object']; - $mResult['@Count'] = $oData->Count(); - $mResult['@Collection'] = $this->responseObject($oData->getArrayCopy(), $sParent, $aParameters); - } - else - { - $mResult['@Object'] = 'Object/'.$mResult['@Object']; - } - } - - return $mResult; - } - - private function hashFolderFullName(string $sFolderFullName) : string - { - return \in_array(\strtolower($sFolderFullName), array('inbox', 'sent', 'send', 'drafts', - 'spam', 'junk', 'bin', 'trash', 'archive', 'allmail', 'all')) ? - $sFolderFullName : \md5($sFolderFullName); - } - public function GetLanguageAndTheme(bool $bAdmin = false, bool $bMobile = false) : array { $sTheme = $this->Config()->Get('webmail', 'theme', 'Default'); @@ -6995,436 +4451,4 @@ NewThemeLink IncludeCss TemplatesLink LangLink IncludeBackground PluginsLink Aut return $aResult; } - /** - * @param mixed $mResponse - * - * @return mixed - */ - protected $oAccount = null; - protected $aCheckableFolder = null; - protected function responseObject($mResponse, string $sParent = '', array $aParameters = array()) - { - if (\is_object($mResponse)) - { - $bHook = true; - $sClassName = \get_class($mResponse); - if (\method_exists($mResponse, 'ToSimpleJSON')) - { - $mResult = \array_merge($this->objectData($mResponse, $sParent, $aParameters), $mResponse->ToSimpleJSON(true)); - } - else if ('MailSo\Mail\Message' === $sClassName) - { - if (null === $this->oAccount) { - $this->oAccount = $this->getAccountFromToken(false); - } - $oAccount = $this->oAccount; - - $iDateTimeStampInUTC = $mResponse->InternalTimeStampInUTC(); - if (0 === $iDateTimeStampInUTC || !!$this->Config()->Get('labs', 'date_from_headers', false)) - { - $iDateTimeStampInUTC = $mResponse->HeaderTimeStampInUTC(); - if (0 === $iDateTimeStampInUTC) - { - $iDateTimeStampInUTC = $mResponse->InternalTimeStampInUTC(); - } - } - - $mResult = \array_merge($this->objectData($mResponse, $sParent, $aParameters), array( - 'Folder' => $mResponse->Folder(), - 'Uid' => (string) $mResponse->Uid(), - 'Subject' => \trim(\MailSo\Base\Utils::Utf8Clear($mResponse->Subject())), - 'MessageId' => $mResponse->MessageId(), - 'Size' => $mResponse->Size(), - 'DateTimeStampInUTC' => $iDateTimeStampInUTC, - 'ReplyTo' => $this->responseObject($mResponse->ReplyTo(), $sParent, $aParameters), - 'From' => $this->responseObject($mResponse->From(), $sParent, $aParameters), - 'To' => $this->responseObject($mResponse->To(), $sParent, $aParameters), - 'Cc' => $this->responseObject($mResponse->Cc(), $sParent, $aParameters), - 'Bcc' => $this->responseObject($mResponse->Bcc(), $sParent, $aParameters), - 'Sender' => $this->responseObject($mResponse->Sender(), $sParent, $aParameters), - 'DeliveredTo' => $this->responseObject($mResponse->DeliveredTo(), $sParent, $aParameters), - 'Priority' => $mResponse->Priority(), - 'Threads' => $mResponse->Threads(), - 'Sensitivity' => $mResponse->Sensitivity(), - 'UnsubsribeLinks' => $mResponse->UnsubsribeLinks(), - 'ExternalProxy' => false, - 'ReadReceipt' => '' - )); - - $mResult['SubjectParts'] = $this->explodeSubject($mResult['Subject']); - - $oAttachments = $mResponse->Attachments(); - $iAttachmentsCount = $oAttachments ? $oAttachments->Count() : 0; - - $mResult['HasAttachments'] = 0 < $iAttachmentsCount; - $mResult['AttachmentsSpecData'] = $mResult['HasAttachments'] ? $oAttachments->SpecData() : array(); - - $sSubject = $mResult['Subject']; - $mResult['Hash'] = \md5($mResult['Folder'].$mResult['Uid']); - $mResult['RequestHash'] = Utils::EncodeKeyValuesQ(array( - 'V' => APP_VERSION, - 'Account' => $oAccount ? \md5($oAccount->Hash()) : '', - 'Folder' => $mResult['Folder'], - 'Uid' => $mResult['Uid'], - 'MimeType' => 'message/rfc822', - 'FileName' => (0 === \strlen($sSubject) ? 'message-'.$mResult['Uid'] : \MailSo\Base\Utils::ClearXss($sSubject)).'.eml' - )); - - // Flags - $aFlags = $mResponse->FlagsLowerCase(); - $mResult['IsSeen'] = \in_array('\\seen', $aFlags); - $mResult['IsFlagged'] = \in_array('\\flagged', $aFlags); - $mResult['IsAnswered'] = \in_array('\\answered', $aFlags); - $mResult['IsDeleted'] = \in_array('\\deleted', $aFlags); - - $sForwardedFlag = $this->Config()->Get('labs', 'imap_forwarded_flag', ''); - $sReadReceiptFlag = $this->Config()->Get('labs', 'imap_read_receipt_flag', ''); - - $mResult['IsForwarded'] = 0 < \strlen($sForwardedFlag) && \in_array(\strtolower($sForwardedFlag), $aFlags); - $mResult['IsReadReceipt'] = 0 < \strlen($sReadReceiptFlag) && \in_array(\strtolower($sReadReceiptFlag), $aFlags); - - if (!$this->GetCapa(false, false, Enumerations\Capa::COMPOSER, $oAccount)) - { - $mResult['IsReadReceipt'] = true; - } - - $mResult['TextPartIsTrimmed'] = false; - - if ('Message' === $sParent) - { - $oAttachments = /* @var \MailSo\Mail\AttachmentCollection */ $mResponse->Attachments(); - - $bHasExternals = false; - $mFoundedCIDs = array(); - $aContentLocationUrls = array(); - $mFoundedContentLocationUrls = array(); - - if ($oAttachments && 0 < $oAttachments->Count()) - { - foreach ($oAttachments as /* @var \MailSo\Mail\Attachment */ $oAttachment) - { - if ($oAttachment) - { - $sContentLocation = $oAttachment->ContentLocation(); - if ($sContentLocation && 0 < \strlen($sContentLocation)) - { - $aContentLocationUrls[] = $oAttachment->ContentLocation(); - } - } - } - } - - $sPlain = ''; - $sHtml = \trim($mResponse->Html()); - - if (0 === \strlen($sHtml)) - { - $sPlain = \trim($mResponse->Plain()); - } - - $mResult['DraftInfo'] = $mResponse->DraftInfo(); - $mResult['InReplyTo'] = $mResponse->InReplyTo(); - $mResult['UnsubsribeLinks'] = $mResponse->UnsubsribeLinks(); - $mResult['References'] = $mResponse->References(); - - $fAdditionalExternalFilter = null; - if (!!$this->Config()->Get('labs', 'use_local_proxy_for_external_images', false)) - { - $fAdditionalExternalFilter = function ($sUrl) { - return './?/ProxyExternal/'.Utils::EncodeKeyValuesQ(array( - 'Rnd' => \md5(\microtime(true)), - 'Token' => Utils::GetConnectionToken(), - 'Url' => $sUrl - )).'/'; - }; - } - - $sHtml = \preg_replace_callback('/(]*>)([\s\S\r\n\t]*?)(<\/pre>)/mi', function ($aMatches) { - return \preg_replace('/[\r\n]+/', '
', $aMatches[1].\trim($aMatches[2]).$aMatches[3]); - }, $sHtml); - - $mResult['Html'] = 0 === \strlen($sHtml) ? '' : \MailSo\Base\HtmlUtils::ClearHtml( - $sHtml, $bHasExternals, $mFoundedCIDs, $aContentLocationUrls, $mFoundedContentLocationUrls, false, false, - $fAdditionalExternalFilter, null, !!$this->Config()->Get('labs', 'try_to_detect_hidden_images', false) - ); - - $mResult['ExternalProxy'] = null !== $fAdditionalExternalFilter; - - $mResult['Plain'] = $sPlain; -// $mResult['Plain'] = 0 === \strlen($sPlain) ? '' : \MailSo\Base\HtmlUtils::ConvertPlainToHtml($sPlain); - - $mResult['TextHash'] = \md5($mResult['Html'].$mResult['Plain']); - - $mResult['TextPartIsTrimmed'] = $mResponse->TextPartIsTrimmed(); - - $mResult['PgpSigned'] = $mResponse->PgpSigned(); - $mResult['PgpEncrypted'] = $mResponse->PgpEncrypted(); - $mResult['PgpSignature'] = $mResponse->PgpSignature(); - - unset($sHtml, $sPlain); - - $mResult['HasExternals'] = $bHasExternals; - $mResult['HasInternals'] = (\is_array($mFoundedCIDs) && 0 < \count($mFoundedCIDs)) || - (\is_array($mFoundedContentLocationUrls) && 0 < \count($mFoundedContentLocationUrls)); - $mResult['FoundedCIDs'] = $mFoundedCIDs; - $mResult['Attachments'] = $this->responseObject($oAttachments, $sParent, \array_merge($aParameters, array( - 'FoundedCIDs' => $mFoundedCIDs, - 'FoundedContentLocationUrls' => $mFoundedContentLocationUrls - ))); - - $mResult['ReadReceipt'] = $mResponse->ReadReceipt(); - if (0 < \strlen($mResult['ReadReceipt']) && !$mResult['IsReadReceipt']) - { - if (0 < \strlen($mResult['ReadReceipt'])) - { - try - { - $oReadReceipt = \MailSo\Mime\Email::Parse($mResult['ReadReceipt']); - if (!$oReadReceipt) - { - $mResult['ReadReceipt'] = ''; - } - } - catch (\Throwable $oException) { unset($oException); } - } - - if (0 < \strlen($mResult['ReadReceipt']) && '1' === $this->Cacher($oAccount)->Get( - KeyPathHelper::ReadReceiptCache($oAccount->Email(), $mResult['Folder'], $mResult['Uid']), '0')) - { - $mResult['ReadReceipt'] = ''; - } - } - } - } - else if ('MailSo\Mime\Email' === $sClassName) - { - $mResult = \array_merge($this->objectData($mResponse, $sParent, $aParameters), array( - 'Name' => \MailSo\Base\Utils::Utf8Clear($mResponse->GetDisplayName()), - 'Email' => \MailSo\Base\Utils::Utf8Clear($mResponse->GetEmail(true)), - 'DkimStatus' => $mResponse->GetDkimStatus(), - 'DkimValue' => $mResponse->GetDkimValue() - )); - } - else if ('RainLoop\Providers\AddressBook\Classes\Contact' === $sClassName) - { - $mResult = \array_merge($this->objectData($mResponse, $sParent, $aParameters), array( - /* @var $mResponse Providers\AddressBook\Classes\Contact */ - 'IdContact' => $mResponse->IdContact, - 'Display' => \MailSo\Base\Utils::Utf8Clear($mResponse->Display), - 'ReadOnly' => $mResponse->ReadOnly, - 'IdPropertyFromSearch' => $mResponse->IdPropertyFromSearch, - 'Properties' => $this->responseObject($mResponse->Properties, $sParent, $aParameters) - )); - } - else if ('RainLoop\Providers\AddressBook\Classes\Tag' === $sClassName) - { - $mResult = \array_merge($this->objectData($mResponse, $sParent, $aParameters), array( - /* @var $mResponse Providers\AddressBook\Classes\Tag */ - 'IdContactTag' => $mResponse->IdContactTag, - 'Name' => \MailSo\Base\Utils::Utf8Clear($mResponse->Name), - 'ReadOnly' => $mResponse->ReadOnly - )); - } - else if ('RainLoop\Providers\AddressBook\Classes\Property' === $sClassName) - { - // Simple hack - if ($mResponse && $mResponse->IsWeb()) - { - $mResponse->Value = \preg_replace('/(skype|ftp|http[s]?)\\\:\/\//i', '$1://', $mResponse->Value); - } - - $mResult = \array_merge($this->objectData($mResponse, $sParent, $aParameters), array( - /* @var $mResponse Providers\AddressBook\Classes\Property */ - 'IdProperty' => $mResponse->IdProperty, - 'Type' => $mResponse->Type, - 'TypeStr' => $mResponse->TypeStr, - 'Value' => \MailSo\Base\Utils::Utf8Clear($mResponse->Value) - )); - } - else if ('MailSo\Mail\Attachment' === $sClassName) - { - if (null === $this->oAccount) { - $this->oAccount = $this->getAccountFromToken(false); - } - - $mFoundedCIDs = isset($aParameters['FoundedCIDs']) && \is_array($aParameters['FoundedCIDs']) && - 0 < \count($aParameters['FoundedCIDs']) ? - $aParameters['FoundedCIDs'] : null; - - $mFoundedContentLocationUrls = isset($aParameters['FoundedContentLocationUrls']) && - \is_array($aParameters['FoundedContentLocationUrls']) && - 0 < \count($aParameters['FoundedContentLocationUrls']) ? - $aParameters['FoundedContentLocationUrls'] : null; - - if ($mFoundedCIDs || $mFoundedContentLocationUrls) - { - $mFoundedCIDs = \array_merge($mFoundedCIDs ? $mFoundedCIDs : array(), - $mFoundedContentLocationUrls ? $mFoundedContentLocationUrls : array()); - - $mFoundedCIDs = 0 < \count($mFoundedCIDs) ? $mFoundedCIDs : null; - } - - $mResult = \array_merge($this->objectData($mResponse, $sParent, $aParameters), array( - 'Folder' => $mResponse->Folder(), - 'Uid' => (string) $mResponse->Uid(), - 'Framed' => false, - 'MimeIndex' => (string) $mResponse->MimeIndex(), - 'MimeType' => $mResponse->MimeType(), - 'FileName' => \MailSo\Base\Utils::ClearFileName( - \MailSo\Base\Utils::ClearXss($mResponse->FileName(true))), - 'EstimatedSize' => $mResponse->EstimatedSize(), - 'CID' => $mResponse->Cid(), - 'ContentLocation' => $mResponse->ContentLocation(), - 'IsInline' => $mResponse->IsInline(), - 'IsThumbnail' => $this->GetCapa(false, false, Enumerations\Capa::ATTACHMENT_THUMBNAILS), - 'IsLinked' => ($mFoundedCIDs && \in_array(\trim(\trim($mResponse->Cid()), '<>'), $mFoundedCIDs)) || - ($mFoundedContentLocationUrls && \in_array(\trim($mResponse->ContentLocation()), $mFoundedContentLocationUrls)) - )); - - $mResult['Framed'] = $this->isFileHasFramedPreview($mResult['FileName']); - - if ($mResult['IsThumbnail']) - { - $mResult['IsThumbnail'] = $this->isFileHasThumbnail($mResult['FileName']); - } - - $mResult['Download'] = Utils::EncodeKeyValuesQ(array( - 'V' => APP_VERSION, - 'Account' => $this->oAccount ? \md5($this->oAccount->Hash()) : '', - 'Folder' => $mResult['Folder'], - 'Uid' => $mResult['Uid'], - 'MimeIndex' => $mResult['MimeIndex'], - 'MimeType' => $mResult['MimeType'], - 'FileName' => $mResult['FileName'], - 'Framed' => $mResult['Framed'] - )); - } - else if ('MailSo\Mail\Folder' === $sClassName) - { - $aExtended = null; - -// $mStatus = $mResponse->Status(); -// if (\is_array($mStatus) && isset($mStatus['MESSAGES'], $mStatus['UNSEEN'], $mStatus['UIDNEXT'])) -// { -// $aExtended = array( -// 'MessageCount' => (int) $mStatus['MESSAGES'], -// 'MessageUnseenCount' => (int) $mStatus['UNSEEN'], -// 'UidNext' => (string) $mStatus['UIDNEXT'], -// 'Hash' => $this->MailClient()->GenerateFolderHash( -// $mResponse->FullNameRaw(), $mStatus['MESSAGES'], $mStatus['UNSEEN'], $mStatus['UIDNEXT'], -// empty($mStatus['HIGHESTMODSEQ']) ? '' : $mStatus['HIGHESTMODSEQ']) -// ); -// } - - if (null === $this->aCheckableFolder) - { - if (null === $this->oAccount) { - $this->oAccount = $this->getAccountFromToken(false); - } - $aCheckable = \json_decode( - $this->SettingsProvider(true) - ->Load($this->oAccount) - ->GetConf('CheckableFolder', '[]') - ); - $this->aCheckableFolder = \is_array($aCheckable) ? $aCheckable : array(); - } - - $mResult = \array_merge($this->objectData($mResponse, $sParent, $aParameters), array( - 'Name' => $mResponse->Name(), - 'FullName' => $mResponse->FullName(), - 'FullNameRaw' => $mResponse->FullNameRaw(), - 'FullNameHash' => $this->hashFolderFullName($mResponse->FullNameRaw(), $mResponse->FullName()), - 'Delimiter' => (string) $mResponse->Delimiter(), - 'HasVisibleSubFolders' => $mResponse->HasVisibleSubFolders(), - 'IsSubscribed' => $mResponse->IsSubscribed(), - 'IsExists' => $mResponse->IsExists(), - 'IsSelectable' => $mResponse->IsSelectable(), - 'Flags' => $mResponse->FlagsLowerCase(), - 'Checkable' => \in_array($mResponse->FullNameRaw(), $this->aCheckableFolder), - 'Extended' => $aExtended, - 'SubFolders' => $this->responseObject($mResponse->SubFolders(), $sParent, $aParameters) - )); - } - else if ('MailSo\Mail\MessageCollection' === $sClassName) - { - $mResult = \array_merge($this->objectData($mResponse, $sParent, $aParameters), array( - 'MessageCount' => $mResponse->MessageCount, - 'MessageUnseenCount' => $mResponse->MessageUnseenCount, - 'MessageResultCount' => $mResponse->MessageResultCount, - 'Folder' => $mResponse->FolderName, - 'FolderHash' => $mResponse->FolderHash, - 'UidNext' => $mResponse->UidNext, - 'ThreadUid' => $mResponse->ThreadUid, - 'NewMessages' => $this->responseObject($mResponse->NewMessages), - 'Filtered' => $mResponse->Filtered, - 'Offset' => $mResponse->Offset, - 'Limit' => $mResponse->Limit, - 'Search' => $mResponse->Search - )); - } - else if ('MailSo\Mail\AttachmentCollection' === $sClassName) - { - $mResult = \array_merge($this->objectData($mResponse, $sParent, $aParameters), array( - 'InlineCount' => $mResponse->InlineCount() - )); - } - else if ('MailSo\Mail\FolderCollection' === $sClassName) - { - $mResult = \array_merge($this->objectData($mResponse, $sParent, $aParameters), array( - 'Namespace' => $mResponse->GetNamespace(), - 'FoldersHash' => isset($mResponse->FoldersHash) ? $mResponse->FoldersHash : '', - 'IsThreadsSupported' => $mResponse->IsThreadsSupported, - 'Optimized' => $mResponse->Optimized, - 'CountRec' => $mResponse->CountRec(), - 'SystemFolders' => isset($mResponse->SystemFolders) && \is_array($mResponse->SystemFolders) ? - $mResponse->SystemFolders : array() - )); - } - else if ('MailSo\Mime\EmailCollection' === $sClassName) - { - $mResult = array(); - if (100 < \count($mResponse)) { - $mResponse = $mResponse->Slice(0, 100); - } - foreach ($mResponse as $iKey => $oItem) { - $mResult[$iKey] = $this->responseObject($oItem, $sParent, $aParameters); - } - $bHook = false; - } - else if ($mResponse instanceof \MailSo\Base\Collection) - { - $mResult = array(); - foreach ($mResponse as $iKey => $oItem) { - $mResult[$iKey] = $this->responseObject($oItem, $sParent, $aParameters); - } - $bHook = false; - } - else - { - $mResult = '["'.$sClassName.'"]'; - $bHook = false; - } - - if ($bHook) - { - $this->Plugins()->RunHook('filter.response-object', array($sClassName, $mResult), false); - } - } - else if (\is_array($mResponse)) - { - foreach ($mResponse as $iKey => $oItem) - { - $mResponse[$iKey] = $this->responseObject($oItem, $sParent, $aParameters); - } - - $mResult = $mResponse; - } - else - { - $mResult = $mResponse; - } - - unset($mResponse); - return $mResult; - } } diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Accounts.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Accounts.php new file mode 100644 index 000000000..2588f1a2c --- /dev/null +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Accounts.php @@ -0,0 +1,217 @@ +getAccountFromToken(); + + if (!$this->GetCapa(false, false, Capa::ADDITIONAL_ACCOUNTS, $oAccount)) + { + return $this->FalseResponse(__FUNCTION__); + } + + $sParentEmail = $oAccount->ParentEmailHelper(); + + $aAccounts = $this->GetAccounts($oAccount); + + $sEmail = \trim($this->GetActionParam('Email', '')); + $sPassword = $this->GetActionParam('Password', ''); + $bNew = '1' === (string) $this->GetActionParam('New', '1'); + + $sEmail = \MailSo\Base\Utils::IdnToAscii($sEmail, true); + if ($bNew && ($oAccount->Email() === $sEmail || $sParentEmail === $sEmail || isset($aAccounts[$sEmail]))) + { + throw new ClientException(Notifications::AccountAlreadyExists); + } + else if (!$bNew && !isset($aAccounts[$sEmail])) + { + throw new ClientException(Notifications::AccountDoesNotExist); + } + + $oNewAccount = $this->LoginProcess($sEmail, $sPassword, '', '', false, true); + $oNewAccount->SetParentEmail($sParentEmail); + + $aAccounts[$oNewAccount->Email()] = $oNewAccount->GetAuthToken(); + if (!$oAccount->IsAdditionalAccount()) + { + $aAccounts[$oAccount->Email()] = $oAccount->GetAuthToken(); + } + + $this->SetAccounts($oAccount, $aAccounts); + return $this->TrueResponse(__FUNCTION__); + } + + /** + * @throws \MailSo\Base\Exceptions\Exception + */ + public function DoAccountDelete() : array + { + $oAccount = $this->getAccountFromToken(); + + if (!$this->GetCapa(false, false, Capa::ADDITIONAL_ACCOUNTS, $oAccount)) + { + return $this->FalseResponse(__FUNCTION__); + } + + $sParentEmail = $oAccount->ParentEmailHelper(); + $sEmailToDelete = \trim($this->GetActionParam('EmailToDelete', '')); + $sEmailToDelete = \MailSo\Base\Utils::IdnToAscii($sEmailToDelete, true); + + $aAccounts = $this->GetAccounts($oAccount); + + if (0 < \strlen($sEmailToDelete) && $sEmailToDelete !== $sParentEmail && isset($aAccounts[$sEmailToDelete])) + { + unset($aAccounts[$sEmailToDelete]); + + $oAccountToChange = null; + if ($oAccount->Email() === $sEmailToDelete && !empty($aAccounts[$sParentEmail])) + { + $oAccountToChange = $this->GetAccountFromCustomToken($aAccounts[$sParentEmail], false, false); + if ($oAccountToChange) + { + $this->AuthToken($oAccountToChange); + } + } + + $this->SetAccounts($oAccount, $aAccounts); + return $this->TrueResponse(__FUNCTION__, array('Reload' => !!$oAccountToChange)); + } + + return $this->FalseResponse(__FUNCTION__); + } + + /** + * @throws \MailSo\Base\Exceptions\Exception + */ + public function DoIdentityUpdate() : array + { + $oAccount = $this->getAccountFromToken(); + + $oIdentity = new \RainLoop\Model\Identity(); + if (!$oIdentity->FromJSON($this->GetActionParams(), true)) + { + throw new ClientException(Notifications::InvalidInputArgument); + } + + $aIdentities = $this->GetIdentities($oAccount); + + $bAdded = false; + $aIdentitiesForSave = array(); + foreach ($aIdentities as $oItem) + { + if ($oItem) + { + if ($oItem->Id() === $oIdentity->Id()) + { + $aIdentitiesForSave[] = $oIdentity; + $bAdded = true; + } + else + { + $aIdentitiesForSave[] = $oItem; + } + } + } + + if (!$bAdded) + { + $aIdentitiesForSave[] = $oIdentity; + } + + return $this->DefaultResponse(__FUNCTION__, $this->SetIdentities($oAccount, $aIdentitiesForSave)); + } + + /** + * @throws \MailSo\Base\Exceptions\Exception + */ + public function DoIdentityDelete() : array + { + $oAccount = $this->getAccountFromToken(); + + if (!$this->GetCapa(false, false, Capa::IDENTITIES, $oAccount)) + { + return $this->FalseResponse(__FUNCTION__); + } + + $sId = \trim($this->GetActionParam('IdToDelete', '')); + if (empty($sId)) + { + throw new ClientException(Notifications::UnknownError); + } + + $aNew = array(); + $aIdentities = $this->GetIdentities($oAccount); + + foreach ($aIdentities as $oItem) + { + if ($oItem && $sId !== $oItem->Id()) + { + $aNew[] = $oItem; + } + } + + return $this->DefaultResponse(__FUNCTION__, $this->SetIdentities($oAccount, $aNew)); + } + + /** + * @throws \MailSo\Base\Exceptions\Exception + */ + public function DoAccountsAndIdentitiesSortOrder() : array + { + $oAccount = $this->getAccountFromToken(); + + $aAccounts = $this->GetActionParam('Accounts', null); + $aIdentities = $this->GetActionParam('Identities', null); + + if (!\is_array($aAccounts) && !\is_array($aIdentities)) + { + return $this->FalseResponse(__FUNCTION__); + } + + return $this->DefaultResponse(__FUNCTION__, $this->StorageProvider()->Put($oAccount, + \RainLoop\Providers\Storage\Enumerations\StorageType::CONFIG, 'accounts_identities_order', + \json_encode(array( + 'Accounts' => \is_array($aAccounts) ? $aAccounts : array(), + 'Identities' => \is_array($aIdentities) ? $aIdentities : array() + )) + )); + } + + /** + * @throws \MailSo\Base\Exceptions\Exception + */ + public function DoAccountsAndIdentities() : array + { + $oAccount = $this->getAccountFromToken(); + + $mAccounts = false; + + if ($this->GetCapa(false, false, Capa::ADDITIONAL_ACCOUNTS, $oAccount)) + { + $mAccounts = $this->GetAccounts($oAccount); + $mAccounts = \array_keys($mAccounts); + + foreach ($mAccounts as $iIndex => $sName) + { + $mAccounts[$iIndex] = \MailSo\Base\Utils::IdnToUtf8($sName); + } + } + + return $this->DefaultResponse(__FUNCTION__, array( + 'Accounts' => $mAccounts, + 'Identities' => $this->GetIdentities($oAccount) + )); + } + +} diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Contacts.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Contacts.php new file mode 100644 index 000000000..44aca9f4d --- /dev/null +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Contacts.php @@ -0,0 +1,176 @@ +getAccountFromToken(); + + $oAddressBookProvider = $this->AddressBookProvider($oAccount); + if (!$oAddressBookProvider || !$oAddressBookProvider->IsActive()) + { + return $this->FalseResponse(__FUNCTION__); + } + + $bEnabled = '1' === (string) $this->GetActionParam('Enable', '0'); + $sUrl = $this->GetActionParam('Url', ''); + $sUser = $this->GetActionParam('User', ''); + $sPassword = $this->GetActionParam('Password', ''); + + $mData = $this->getContactsSyncData($oAccount); + + $bResult = $this->StorageProvider()->Put($oAccount, + \RainLoop\Providers\Storage\Enumerations\StorageType::CONFIG, + 'contacts_sync', + \RainLoop\Utils::EncodeKeyValues(array( + 'Enable' => $bEnabled, + 'User' => $sUser, + 'Password' => APP_DUMMY === $sPassword && isset($mData['Password']) ? + $mData['Password'] : (APP_DUMMY === $sPassword ? '' : $sPassword), + 'Url' => $sUrl + )) + ); + + return $this->DefaultResponse(__FUNCTION__, $bResult); + } + + public function DoContactsSync() : array + { + $bResult = false; + $oAccount = $this->getAccountFromToken(); + + $oAddressBookProvider = $this->AddressBookProvider($oAccount); + if ($oAddressBookProvider && $oAddressBookProvider->IsActive()) + { + $mData = $this->getContactsSyncData($oAccount); + if (isset($mData['Enable'], $mData['User'], $mData['Password'], $mData['Url']) && $mData['Enable']) + { + $bResult = $oAddressBookProvider->Sync( + $oAccount->ParentEmailHelper(), + $mData['Url'], $mData['User'], $mData['Password']); + } + } + + if (!$bResult) + { + throw new \RainLoop\Exceptions\ClientException(\RainLoop\Notifications::ContactsSyncError); + } + + return $this->TrueResponse(__FUNCTION__); + } + + public function DoContacts() : array + { + $oAccount = $this->getAccountFromToken(); + + $sSearch = \trim($this->GetActionParam('Search', '')); + $iOffset = (int) $this->GetActionParam('Offset', 0); + $iLimit = (int) $this->GetActionParam('Limit', 20); + $iOffset = 0 > $iOffset ? 0 : $iOffset; + $iLimit = 0 > $iLimit ? 20 : $iLimit; + + $iResultCount = 0; + $mResult = array(); + + $oAbp = $this->AddressBookProvider($oAccount); + if ($oAbp->IsActive()) + { + $iResultCount = 0; + $mResult = $oAbp->GetContacts($oAccount->ParentEmailHelper(), + $iOffset, $iLimit, $sSearch, $iResultCount); + } + + return $this->DefaultResponse(__FUNCTION__, array( + 'Offset' => $iOffset, + 'Limit' => $iLimit, + 'Count' => $iResultCount, + 'Search' => $sSearch, + 'List' => $mResult + )); + } + + public function DoContactsDelete() : array + { + $oAccount = $this->getAccountFromToken(); + $aUids = \explode(',', (string) $this->GetActionParam('Uids', '')); + + $aFilteredUids = \array_filter($aUids, function (&$mUid) { + $mUid = (int) \trim($mUid); + return 0 < $mUid; + }); + + $bResult = false; + if (0 < \count($aFilteredUids) && $this->AddressBookProvider($oAccount)->IsActive()) + { + $bResult = $this->AddressBookProvider($oAccount)->DeleteContacts($oAccount->ParentEmailHelper(), $aFilteredUids); + } + + return $this->DefaultResponse(__FUNCTION__, $bResult); + } + + public function DoContactSave() : array + { + $oAccount = $this->getAccountFromToken(); + + $bResult = false; + + $oAddressBookProvider = $this->AddressBookProvider($oAccount); + $sRequestUid = \trim($this->GetActionParam('RequestUid', '')); + if ($oAddressBookProvider && $oAddressBookProvider->IsActive() && 0 < \strlen($sRequestUid)) + { + $sUid = \trim($this->GetActionParam('Uid', '')); + + $oContact = null; + if (0 < \strlen($sUid)) + { + $oContact = $oAddressBookProvider->GetContactByID($oAccount->ParentEmailHelper(), $sUid); + } + + if (!$oContact) + { + $oContact = new \RainLoop\Providers\AddressBook\Classes\Contact(); + if (0 < \strlen($sUid)) + { + $oContact->IdContact = $sUid; + } + } + + $oContact->Properties = array(); + $aProperties = $this->GetActionParam('Properties', array()); + if (\is_array($aProperties)) + { + foreach ($aProperties as $aItem) + { + if ($aItem && isset($aItem[0], $aItem[1]) && \is_numeric($aItem[0])) + { + $oProp = new \RainLoop\Providers\AddressBook\Classes\Property(); + $oProp->Type = (int) $aItem[0]; + $oProp->Value = $aItem[1]; + $oProp->TypeStr = empty($aItem[2]) ? '': $aItem[2]; + + $oContact->Properties[] = $oProp; + } + } + } + + if (!empty($oContact->Etag)) + { + $oContact->Etag = \md5($oContact->ToVCard()); + } + + $oContact->PopulateDisplayAndFullNameValue(true); + + $bResult = $oAddressBookProvider->ContactSave($oAccount->ParentEmailHelper(), $oContact); + } + + return $this->DefaultResponse(__FUNCTION__, array( + 'RequestUid' => $sRequestUid, + 'ResultID' => $bResult ? $oContact->IdContact : '', + 'Result' => $bResult + )); + } + +} diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Filters.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Filters.php new file mode 100644 index 000000000..54736e307 --- /dev/null +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Filters.php @@ -0,0 +1,74 @@ +getAccountFromToken(); + + if (!$this->GetCapa(false, false, Capa::FILTERS, $oAccount)) + { + return $this->FalseResponse(__FUNCTION__); + } + + $aFakeFilters = null; + + $this->Plugins() + ->RunHook('filter.filters-fake', array($oAccount, &$aFakeFilters)) + ; + + if ($aFakeFilters) + { + return $this->DefaultResponse(__FUNCTION__, $aFakeFilters); + } + + return $this->DefaultResponse(__FUNCTION__, + $this->FiltersProvider()->Load($oAccount, $oAccount->DomainSieveAllowRaw())); + } + + /** + * @throws \MailSo\Base\Exceptions\Exception + */ + public function DoFiltersSave() : array + { + $oAccount = $this->getAccountFromToken(); + + if (!$this->GetCapa(false, false, Capa::FILTERS, $oAccount)) + { + return $this->FalseResponse(__FUNCTION__); + } + + $aIncFilters = $this->GetActionParam('Filters', array()); + + $sRaw = $this->GetActionParam('Raw', ''); + $bRawIsActive = '1' === (string) $this->GetActionParam('RawIsActive', '0'); + + $aFilters = array(); + foreach ($aIncFilters as $aFilter) + { + if (is_array($aFilter)) + { + $oFilter = new \RainLoop\Providers\Filters\Classes\Filter(); + if ($oFilter->FromJSON($aFilter)) + { + $aFilters[] = $oFilter; + } + } + } + + $this->Plugins() + ->RunHook('filter.filters-save', array($oAccount, &$aFilters, &$sRaw, &$bRawIsActive)) + ; + + return $this->DefaultResponse(__FUNCTION__, $this->FiltersProvider()->Save($oAccount, + $aFilters, $sRaw, $bRawIsActive)); + } + +} diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Folders.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Folders.php new file mode 100644 index 000000000..9e06caa7f --- /dev/null +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Folders.php @@ -0,0 +1,451 @@ +initMailClientConnection(); + + $oFolderCollection = null; + $this->Plugins()->RunHook('filter.folders-before', array($oAccount, $oFolderCollection)); + + $bUseFolders = $this->GetCapa(false, false, Capa::FOLDERS, $oAccount); + + if (null === $oFolderCollection) + { + $oFolderCollection = $this->MailClient()->Folders('', + $bUseFolders ? '*' : 'INBOX', + !!$this->Config()->Get('labs', 'use_imap_list_subscribe', true), + (int) $this->Config()->Get('labs', 'imap_folder_list_limit', 200) + ); + } + + $this->Plugins()->RunHook('filter.folders-post', array($oAccount, $oFolderCollection)); + + if ($oFolderCollection instanceof \MailSo\Mail\FolderCollection) + { + $oSettingsLocal = $this->SettingsProvider(true)->Load($oAccount); + + $aSystemFolders = array(); + $this->recFoldersTypes($oAccount, $oFolderCollection, $aSystemFolders); + $oFolderCollection->SystemFolders = $aSystemFolders; + + if ($bUseFolders && $this->Config()->Get('labs', 'autocreate_system_folders', true)) + { + $bDoItAgain = false; + + $sNamespace = $oFolderCollection->GetNamespace(); + $sParent = empty($sNamespace) ? '' : \substr($sNamespace, 0, -1); + + $sDelimiter = $oFolderCollection->FindDelimiter(); + + $aList = array(); + $aMap = $this->systemFoldersNames($oAccount); + + if ('' === $oSettingsLocal->GetConf('SentFolder', '')) + { + $aList[] = FolderType::SENT; + } + + if ('' === $oSettingsLocal->GetConf('DraftFolder', '')) + { + $aList[] = FolderType::DRAFTS; + } + + if ('' === $oSettingsLocal->GetConf('SpamFolder', '')) + { + $aList[] = FolderType::JUNK; + } + + if ('' === $oSettingsLocal->GetConf('TrashFolder', '')) + { + $aList[] = FolderType::TRASH; + } + + if ('' === $oSettingsLocal->GetConf('ArchiveFolder', '')) + { + $aList[] = FolderType::ALL; + } + + $this->Plugins()->RunHook('filter.folders-system-types', array($oAccount, &$aList)); + + foreach ($aList as $iType) + { + if (!isset($aSystemFolders[$iType])) + { + $mFolderNameToCreate = \array_search($iType, $aMap); + if (!empty($mFolderNameToCreate)) + { + $iPos = \strrpos($mFolderNameToCreate, $sDelimiter); + if (false !== $iPos) + { + $mNewParent = \substr($mFolderNameToCreate, 0, $iPos); + $mNewFolderNameToCreate = \substr($mFolderNameToCreate, $iPos + 1); + if (0 < \strlen($mNewFolderNameToCreate)) + { + $mFolderNameToCreate = $mNewFolderNameToCreate; + } + + if (0 < \strlen($mNewParent)) + { + $sParent = 0 < \strlen($sParent) ? $sParent.$sDelimiter.$mNewParent : $mNewParent; + } + } + + $sFullNameToCheck = \MailSo\Base\Utils::ConvertEncoding($mFolderNameToCreate, + \MailSo\Base\Enumerations\Charset::UTF_8, \MailSo\Base\Enumerations\Charset::UTF_7_IMAP); + + if (0 < \strlen(\trim($sParent))) + { + $sFullNameToCheck = $sParent.$sDelimiter.$sFullNameToCheck; + } + + if (!$oFolderCollection->GetByFullNameRaw($sFullNameToCheck)) + { + try + { + if ($this->MailClient()->FolderCreate($mFolderNameToCreate, $sParent, true, $sDelimiter)) + { + $bDoItAgain = true; + } + } + catch (\Throwable $oException) + { + $this->Logger()->WriteException($oException); + } + } + } + } + } + + if ($bDoItAgain) + { + $oFolderCollection = $this->MailClient()->Folders('', '*', + !!$this->Config()->Get('labs', 'use_imap_list_subscribe', true), + (int) $this->Config()->Get('labs', 'imap_folder_list_limit', 200) + ); + + if ($oFolderCollection) + { + $aSystemFolders = array(); + $this->recFoldersTypes($oAccount, $oFolderCollection, $aSystemFolders); + $oFolderCollection->SystemFolders = $aSystemFolders; + } + } + } + + if ($oFolderCollection) + { + $oFolderCollection->FoldersHash = \md5(\implode("\x0", $this->recFoldersNames($oFolderCollection))); + } + } + + $this->Plugins()->RunHook('filter.folders-complete', array($oAccount, $oFolderCollection)); + + return $this->DefaultResponse(__FUNCTION__, $oFolderCollection); + } + + public function DoFolderCreate() : array + { + $oAccount = $this->initMailClientConnection(); + + if (!$this->GetCapa(false, false, Capa::FOLDERS, $oAccount)) + { + return $this->FalseResponse(__FUNCTION__); + } + + try + { + $sFolderNameInUtf = $this->GetActionParam('Folder', ''); + $sFolderParentFullNameRaw = $this->GetActionParam('Parent', ''); + + $this->MailClient()->FolderCreate($sFolderNameInUtf, $sFolderParentFullNameRaw, + !!$this->Config()->Get('labs', 'use_imap_list_subscribe', true)); + } + catch (\Throwable $oException) + { + throw new ClientException(Notifications::CantCreateFolder, $oException); + } + + return $this->TrueResponse(__FUNCTION__); + } + + public function DoFolderSubscribe() : array + { + $oAccount = $this->initMailClientConnection(); + + if (!$this->GetCapa(false, false, Capa::FOLDERS, $oAccount)) + { + return $this->FalseResponse(__FUNCTION__); + } + + $sFolderFullNameRaw = $this->GetActionParam('Folder', ''); + $bSubscribe = '1' === (string) $this->GetActionParam('Subscribe', '0'); + + try + { + $this->MailClient()->FolderSubscribe($sFolderFullNameRaw, !!$bSubscribe); + } + catch (\Throwable $oException) + { + if ($bSubscribe) + { + throw new ClientException(Notifications::CantSubscribeFolder, $oException); + } + else + { + throw new ClientException(Notifications::CantUnsubscribeFolder, $oException); + } + } + + return $this->TrueResponse(__FUNCTION__); + } + + public function DoFolderCheckable() : array + { + $oAccount = $this->getAccountFromToken(); + + if (!$this->GetCapa(false, false, Capa::FOLDERS, $oAccount)) + { + return $this->FalseResponse(__FUNCTION__); + } + + $sFolderFullNameRaw = $this->GetActionParam('Folder', ''); + $bCheckable = '1' === (string) $this->GetActionParam('Checkable', '0'); + + $oSettingsLocal = $this->SettingsProvider(true)->Load($oAccount); + + $sCheckableFolder = $oSettingsLocal->GetConf('CheckableFolder', '[]'); + $aCheckableFolder = \json_decode($sCheckableFolder); + + if (!\is_array($aCheckableFolder)) + { + $aCheckableFolder = array(); + } + + if ($bCheckable) + { + $aCheckableFolder[] = $sFolderFullNameRaw; + } + else + { + $aCheckableFolderNew = array(); + foreach ($aCheckableFolder as $sFolder) + { + if ($sFolder !== $sFolderFullNameRaw) + { + $aCheckableFolderNew[] = $sFolder; + } + } + $aCheckableFolder = $aCheckableFolderNew; + } + + $aCheckableFolder = \array_unique($aCheckableFolder); + + $oSettingsLocal->SetConf('CheckableFolder', \json_encode($aCheckableFolder)); + + return $this->DefaultResponse(__FUNCTION__, + $this->SettingsProvider(true)->Save($oAccount, $oSettingsLocal)); + } + + /** + * @throws \MailSo\Base\Exceptions\Exception + */ + public function DoFolderRename() : array + { + $oAccount = $this->initMailClientConnection(); + + if (!$this->GetCapa(false, false, Capa::FOLDERS, $oAccount)) + { + return $this->FalseResponse(__FUNCTION__); + } + + $sPrevFolderFullNameRaw = $this->GetActionParam('Folder', ''); + $sNewTopFolderNameInUtf = $this->GetActionParam('NewFolderName', ''); + + try + { + $this->MailClient()->FolderRename($sPrevFolderFullNameRaw, $sNewTopFolderNameInUtf, + !!$this->Config()->Get('labs', 'use_imap_list_subscribe', true)); + } + catch (\Throwable $oException) + { + throw new ClientException(Notifications::CantRenameFolder, $oException); + } + + return $this->TrueResponse(__FUNCTION__); + } + + /** + * @throws \MailSo\Base\Exceptions\Exception + */ + public function DoFolderDelete() : array + { + $oAccount = $this->initMailClientConnection(); + + if (!$this->GetCapa(false, false, Capa::FOLDERS, $oAccount)) + { + return $this->FalseResponse(__FUNCTION__); + } + + $sFolderFullNameRaw = $this->GetActionParam('Folder', ''); + + try + { + $this->MailClient()->FolderDelete($sFolderFullNameRaw, + !!$this->Config()->Get('labs', 'use_imap_list_subscribe', true)); + } + catch (\MailSo\Mail\Exceptions\NonEmptyFolder $oException) + { + throw new ClientException(Notifications::CantDeleteNonEmptyFolder, $oException); + } + catch (\Throwable $oException) + { + throw new ClientException(Notifications::CantDeleteFolder, $oException); + } + + return $this->TrueResponse(__FUNCTION__); + } + + /** + * @throws \MailSo\Base\Exceptions\Exception + */ + public function DoFolderClear() : array + { + $this->initMailClientConnection(); + + $sFolderFullNameRaw = $this->GetActionParam('Folder', ''); + + try + { + $this->MailClient()->FolderClear($sFolderFullNameRaw); + } + catch (\Throwable $oException) + { + throw new ClientException(Notifications::MailServerError, $oException); + } + + return $this->TrueResponse(__FUNCTION__); + } + + /** + * @throws \MailSo\Base\Exceptions\Exception + */ + public function DoFolderInformation() : array + { + $sFolder = $this->GetActionParam('Folder', ''); + $sPrevUidNext = $this->GetActionParam('UidNext', ''); + $aFlagsUids = array(); + $sFlagsUids = (string) $this->GetActionParam('FlagsUids', ''); + + $aFlagsFilteredUids = array(); + if (0 < strlen($sFlagsUids)) + { + $aFlagsUids = explode(',', $sFlagsUids); + $aFlagsFilteredUids = array_filter($aFlagsUids, function (&$sUid) { + $sUid = (int) trim($sUid); + return 0 < (int) trim($sUid); + }); + } + + $this->initMailClientConnection(); + + $sForwardedFlag = $this->Config()->Get('labs', 'imap_forwarded_flag', ''); + $sReadReceiptFlag = $this->Config()->Get('labs', 'imap_read_receipt_flag', ''); + try + { + $aInboxInformation = $this->MailClient()->FolderInformation( + $sFolder, $sPrevUidNext, $aFlagsFilteredUids + ); + + if (isset($aInboxInformation['Flags']) && \is_array($aInboxInformation['Flags'])) + { + foreach ($aInboxInformation['Flags'] as $iUid => $aFlags) + { + $aLowerFlags = array_map('strtolower', $aFlags); + $aInboxInformation['Flags'][$iUid] = array( + 'IsSeen' => in_array('\\seen', $aLowerFlags), + 'IsFlagged' => in_array('\\flagged', $aLowerFlags), + 'IsAnswered' => in_array('\\answered', $aLowerFlags), + 'IsDeleted' => in_array('\\deleted', $aLowerFlags), + 'IsForwarded' => 0 < strlen($sForwardedFlag) && in_array(strtolower($sForwardedFlag), $aLowerFlags), + 'IsReadReceipt' => 0 < strlen($sReadReceiptFlag) && in_array(strtolower($sReadReceiptFlag), $aLowerFlags) + ); + } + } + } + catch (\Throwable $oException) + { + throw new ClientException(Notifications::MailServerError, $oException); + } + + $aInboxInformation['Version'] = APP_VERSION; + + return $this->DefaultResponse(__FUNCTION__, $aInboxInformation); + } + + /** + * @throws \MailSo\Base\Exceptions\Exception + */ + public function DoFolderInformationMultiply() : array + { + $aResult = array( + 'List' => array(), + 'Version' => APP_VERSION + ); + + $aFolders = $this->GetActionParam('Folders', null); + if (\is_array($aFolders)) + { + $this->initMailClientConnection(); + + $aFolders = \array_unique($aFolders); + foreach ($aFolders as $sFolder) + { + if (0 < \strlen(\trim($sFolder)) && 'INBOX' !== \strtoupper($sFolder)) + { + try + { + $aInboxInformation = $this->MailClient()->FolderInformation($sFolder, '', array()); + if (isset($aInboxInformation['Folder'])) + { + $aResult['List'][] = $aInboxInformation; + } + } + catch (\Throwable $oException) + { + $this->Logger()->WriteException($oException); + } + } + } + } + + return $this->DefaultResponse(__FUNCTION__, $aResult); + } + + public function DoSystemFoldersUpdate() : array + { + $oAccount = $this->getAccountFromToken(); + + $oSettingsLocal = $this->SettingsProvider(true)->Load($oAccount); + + $oSettingsLocal->SetConf('SentFolder', $this->GetActionParam('SentFolder', '')); + $oSettingsLocal->SetConf('DraftFolder', $this->GetActionParam('DraftFolder', '')); + $oSettingsLocal->SetConf('SpamFolder', $this->GetActionParam('SpamFolder', '')); + $oSettingsLocal->SetConf('TrashFolder', $this->GetActionParam('TrashFolder', '')); + $oSettingsLocal->SetConf('ArchiveFolder', $this->GetActionParam('ArchiveFolder', '')); + $oSettingsLocal->SetConf('NullFolder', $this->GetActionParam('NullFolder', '')); + + return $this->DefaultResponse(__FUNCTION__, + $this->SettingsProvider(true)->Save($oAccount, $oSettingsLocal)); + } + +} 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 new file mode 100644 index 000000000..86a66bf21 --- /dev/null +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Messages.php @@ -0,0 +1,701 @@ +GetActionParam('RawKey', ''); + $aValues = $this->getDecodedClientRawKeyValue($sRawKey, 9); + + if ($aValues && 7 < \count($aValues)) + { + $sFolder =(string) $aValues[0]; + $iOffset = (int) $aValues[1]; + $iLimit = (int) $aValues[2]; + $sSearch = (string) $aValues[3]; + $sUidNext = (string) $aValues[6]; + $bUseThreads = (bool) $aValues[7]; + + if ($bUseThreads) + { + $sThreadUid = isset($aValues[8]) ? (string) $aValues[8] : ''; + } + + $this->verifyCacheByKey($sRawKey); + } + else + { + $sFolder = $this->GetActionParam('Folder', ''); + $iOffset = (int) $this->GetActionParam('Offset', 0); + $iLimit = (int) $this->GetActionParam('Limit', 10); + $sSearch = $this->GetActionParam('Search', ''); + $sUidNext = $this->GetActionParam('UidNext', ''); + $bUseThreads = '1' === (string) $this->GetActionParam('UseThreads', '0'); + + if ($bUseThreads) + { + $sThreadUid = (string) $this->GetActionParam('ThreadUid', ''); + } + } + + if (0 === strlen($sFolder)) + { + throw new ClientException(Notifications::CantGetMessageList); + } + + $this->initMailClientConnection(); + + try + { + if (!$this->Config()->Get('labs', 'use_imap_thread', false)) + { + $bUseThreads = false; + } + + $oMessageList = $this->MailClient()->MessageList( + $sFolder, $iOffset, $iLimit, $sSearch, $sUidNext, + $this->cacherForUids(), + !!$this->Config()->Get('labs', 'use_imap_sort', false), + $bUseThreads, + $sThreadUid, + '' + ); + } + catch (\Throwable $oException) + { + throw new ClientException(Notifications::CantGetMessageList, $oException); + } + + if ($oMessageList) + { + $this->cacheByKey($sRawKey); + } + + return $this->DefaultResponse(__FUNCTION__, $oMessageList); + } + + public function DoSaveMessage() : array + { + $oAccount = $this->initMailClientConnection(); + + if (!$this->GetCapa(false, false, Capa::COMPOSER, $oAccount)) + { + return $this->FalseResponse(__FUNCTION__); + } + + $sMessageFolder = $this->GetActionParam('MessageFolder', ''); + $sMessageUid = $this->GetActionParam('MessageUid', ''); + + $sDraftFolder = $this->GetActionParam('SaveFolder', ''); + if (0 === strlen($sDraftFolder)) + { + throw new ClientException(Notifications::UnknownError); + } + + $oMessage = $this->buildMessage($oAccount, true); + + $this->Plugins()->RunHook('filter.save-message', array($oMessage)); + + $mResult = false; + if ($oMessage) + { + $rMessageStream = \MailSo\Base\ResourceRegistry::CreateMemoryResource(); + + $iMessageStreamSize = \MailSo\Base\Utils::MultipleStreamWriter( + $oMessage->ToStream(false), array($rMessageStream), 8192, true, true); + + if (false !== $iMessageStreamSize) + { + $sMessageId = $oMessage->MessageId(); + + \rewind($rMessageStream); + + $iNewUid = 0; + $this->MailClient()->MessageAppendStream( + $rMessageStream, $iMessageStreamSize, $sDraftFolder, array( + \MailSo\Imap\Enumerations\MessageFlag::SEEN + ), $iNewUid); + + if (!empty($sMessageId) && (null === $iNewUid || 0 === $iNewUid)) + { + $iNewUid = $this->MailClient()->FindMessageUidByMessageId($sDraftFolder, $sMessageId); + } + + $mResult = true; + + if (0 < strlen($sMessageFolder) && 0 < strlen($sMessageUid)) + { + $this->MailClient()->MessageDelete($sMessageFolder, array($sMessageUid), true, true); + } + + if (null !== $iNewUid && 0 < $iNewUid) + { + $mResult = array( + 'NewFolder' => $sDraftFolder, + 'NewUid' => $iNewUid + ); + } + } + } + + return $this->DefaultResponse(__FUNCTION__, $mResult); + } + + public function DoSendMessage() : array + { + $oAccount = $this->initMailClientConnection(); + + if (!$this->GetCapa(false, false, Capa::COMPOSER, $oAccount)) + { + return $this->FalseResponse(__FUNCTION__); + } + + $oConfig = $this->Config(); + + $sDraftFolder = $this->GetActionParam('MessageFolder', ''); + $sDraftUid = $this->GetActionParam('MessageUid', ''); + $sSentFolder = $this->GetActionParam('SaveFolder', ''); + $aDraftInfo = $this->GetActionParam('DraftInfo', null); + $bDsn = '1' === (string) $this->GetActionParam('Dsn', '0'); + + $oMessage = $this->buildMessage($oAccount, false); + + $this->Plugins()->RunHook('filter.send-message', array($oMessage)); + + $mResult = false; + try + { + if ($oMessage) + { + $rMessageStream = \MailSo\Base\ResourceRegistry::CreateMemoryResource(); + + $iMessageStreamSize = \MailSo\Base\Utils::MultipleStreamWriter( + $oMessage->ToStream(true), array($rMessageStream), 8192, true, true, true); + + if (false !== $iMessageStreamSize) + { + $this->smtpSendMessage($oAccount, $oMessage, $rMessageStream, $iMessageStreamSize, $bDsn, true); + + if (\is_array($aDraftInfo) && 3 === \count($aDraftInfo)) + { + $sDraftInfoType = $aDraftInfo[0]; + $sDraftInfoUid = $aDraftInfo[1]; + $sDraftInfoFolder = $aDraftInfo[2]; + + try + { + switch (\strtolower($sDraftInfoType)) + { + case 'reply': + case 'reply-all': + $this->MailClient()->MessageSetFlag($sDraftInfoFolder, array($sDraftInfoUid), true, + \MailSo\Imap\Enumerations\MessageFlag::ANSWERED, true); + break; + case 'forward': + $sForwardedFlag = $this->Config()->Get('labs', 'imap_forwarded_flag', ''); + if (0 < strlen($sForwardedFlag)) + { + $this->MailClient()->MessageSetFlag($sDraftInfoFolder, array($sDraftInfoUid), true, + $sForwardedFlag, true); + } + break; + } + } + catch (\Throwable $oException) + { + $this->Logger()->WriteException($oException, \MailSo\Log\Enumerations\Type::ERROR); + } + } + + if (0 < \strlen($sSentFolder)) + { + try + { + if (!$oMessage->GetBcc()) + { + if (\is_resource($rMessageStream)) + { + \rewind($rMessageStream); + } + + $this->Plugins()->RunHook('filter.send-message-stream', + array($oAccount, &$rMessageStream, &$iMessageStreamSize)); + + $this->MailClient()->MessageAppendStream( + $rMessageStream, $iMessageStreamSize, $sSentFolder, array( + \MailSo\Imap\Enumerations\MessageFlag::SEEN + ) + ); + } + else + { + $rAppendMessageStream = \MailSo\Base\ResourceRegistry::CreateMemoryResource(); + + $iAppendMessageStreamSize = \MailSo\Base\Utils::MultipleStreamWriter( + $oMessage->ToStream(false), array($rAppendMessageStream), 8192, true, true, true); + + $this->Plugins()->RunHook('filter.send-message-stream', + array($oAccount, &$rAppendMessageStream, &$iAppendMessageStreamSize)); + + $this->MailClient()->MessageAppendStream( + $rAppendMessageStream, $iAppendMessageStreamSize, $sSentFolder, array( + \MailSo\Imap\Enumerations\MessageFlag::SEEN + )); + + if (\is_resource($rAppendMessageStream)) + { + fclose($rAppendMessageStream); + } + } + } + catch (\Throwable $oException) + { + throw new ClientException(Notifications::CantSaveMessage, $oException); + } + } + + if (\is_resource($rMessageStream)) + { + \fclose($rMessageStream); + } + + $this->deleteMessageAttachmnets($oAccount); + + if (0 < \strlen($sDraftFolder) && 0 < \strlen($sDraftUid)) + { + try + { + $this->MailClient()->MessageDelete($sDraftFolder, array($sDraftUid), true, true); + } + catch (\Throwable $oException) + { + $this->Logger()->WriteException($oException, \MailSo\Log\Enumerations\Type::ERROR); + } + } + + $mResult = true; + } + } + } + catch (Exceptions\ClientException $oException) + { + throw $oException; + } + catch (\Throwable $oException) + { + throw new ClientException(Notifications::CantSendMessage, $oException); + } + + if (false === $mResult) + { + throw new ClientException(Notifications::CantSendMessage); + } + + try + { + if ($oMessage && $this->AddressBookProvider($oAccount)->IsActive()) + { + $aArrayToFrec = array(); + $oToCollection = $oMessage->GetTo(); + if ($oToCollection) + { + foreach ($oToCollection as /* @var $oEmail \MailSo\Mime\Email */ $oEmail) + { + $aArrayToFrec[$oEmail->GetEmail(true)] = $oEmail->ToString(false, true); + } + } + + if (0 < \count($aArrayToFrec)) + { + $oSettings = $this->SettingsProvider()->Load($oAccount); + + $this->AddressBookProvider($oAccount)->IncFrec( + $oAccount->ParentEmailHelper(), \array_values($aArrayToFrec), + !!$oSettings->GetConf('ContactsAutosave', + !!$oConfig->Get('defaults', 'contacts_autosave', true))); + } + } + } + catch (\Throwable $oException) + { + $this->Logger()->WriteException($oException); + } + + return $this->TrueResponse(__FUNCTION__); + } + + public function DoSendReadReceiptMessage() : array + { + $oAccount = $this->initMailClientConnection(); + + if (!$this->GetCapa(false, false, Capa::COMPOSER, $oAccount)) + { + return $this->FalseResponse(__FUNCTION__); + } + + $oMessage = $this->buildReadReceiptMessage($oAccount); + + $this->Plugins()->RunHook('filter.send-read-receipt-message', array($oMessage, $oAccount)); + + $mResult = false; + try + { + if ($oMessage) + { + $rMessageStream = \MailSo\Base\ResourceRegistry::CreateMemoryResource(); + + $iMessageStreamSize = \MailSo\Base\Utils::MultipleStreamWriter( + $oMessage->ToStream(true), array($rMessageStream), 8192, true, true, true); + + if (false !== $iMessageStreamSize) + { + $this->smtpSendMessage($oAccount, $oMessage, $rMessageStream, $iMessageStreamSize, false, false); + + if (\is_resource($rMessageStream)) + { + \fclose($rMessageStream); + } + + $mResult = true; + + $sReadReceiptFlag = $this->Config()->Get('labs', 'imap_read_receipt_flag', ''); + if (!empty($sReadReceiptFlag)) + { + $sFolderFullName = $this->GetActionParam('MessageFolder', ''); + $sUid = $this->GetActionParam('MessageUid', ''); + + $this->Cacher($oAccount)->Set(\RainLoop\KeyPathHelper::ReadReceiptCache($oAccount->Email(), $sFolderFullName, $sUid), '1'); + + if (0 < \strlen($sFolderFullName) && 0 < \strlen($sUid)) + { + try + { + $this->MailClient()->MessageSetFlag($sFolderFullName, array($sUid), true, $sReadReceiptFlag, true, true); + } + catch (\Throwable $oException) {} + } + } + } + } + } + catch (Exceptions\ClientException $oException) + { + throw $oException; + } + catch (\Throwable $oException) + { + throw new ClientException(Notifications::CantSendMessage, $oException); + } + + if (false === $mResult) + { + throw new ClientException(Notifications::CantSendMessage); + } + + return $this->TrueResponse(__FUNCTION__); + } + + public function DoMessageSetSeen() : array + { + return $this->messageSetFlag('MessageSetSeen', __FUNCTION__); + } + + public function DoMessageSetSeenToAll() : array + { + $this->initMailClientConnection(); + + $sFolder = $this->GetActionParam('Folder', ''); + $bSetAction = '1' === (string) $this->GetActionParam('SetAction', '0'); + $sThreadUids = \trim($this->GetActionParam('ThreadUids', '')); + + try + { + $this->MailClient()->MessageSetSeenToAll($sFolder, $bSetAction, + !empty($sThreadUids) ? explode(',', $sThreadUids) : null); + } + catch (\Throwable $oException) + { + throw new ClientException(Notifications::MailServerError, $oException); + } + + return $this->TrueResponse(__FUNCTION__); + } + + public function DoMessageSetFlagged() : array + { + return $this->messageSetFlag('MessageSetFlagged', __FUNCTION__); + } + + /** + * @throws \MailSo\Base\Exceptions\Exception + */ + public function DoMessage() : array + { + $sRawKey = (string) $this->GetActionParam('RawKey', ''); + + $sFolder = ''; + $iUid = 0; + + $aValues = $this->getDecodedClientRawKeyValue($sRawKey, 4); + if ($aValues && 4 === count($aValues)) + { + $sFolder = (string) $aValues[0]; + $iUid = (int) $aValues[1]; + + $this->verifyCacheByKey($sRawKey); + } + else + { + $sFolder = $this->GetActionParam('Folder', ''); + $iUid = (int) $this->GetActionParam('Uid', 0); + } + + $oAccount = $this->initMailClientConnection(); + + try + { + $oMessage = $this->MailClient()->Message($sFolder, $iUid, true, + $this->cacherForThreads(), + (int) $this->Config()->Get('labs', 'imap_body_text_limit', 0) + ); + } + catch (\Throwable $oException) + { + throw new ClientException(Notifications::CantGetMessage, $oException); + } + + if ($oMessage) + { + $this->Plugins()->RunHook('filter.result-message', array($oMessage)); + + $this->cacheByKey($sRawKey); + } + + return $this->DefaultResponse(__FUNCTION__, $oMessage); + } + + /** + * @throws \MailSo\Base\Exceptions\Exception + */ + public function DoMessageDelete() : array + { + $this->initMailClientConnection(); + + $sFolder = $this->GetActionParam('Folder', ''); + $aUids = \explode(',', (string) $this->GetActionParam('Uids', '')); + + $aFilteredUids = \array_filter($aUids, function (&$sUid) { + $sUid = (int) \trim($sUid); + return 0 < $sUid; + }); + + try + { + $this->MailClient()->MessageDelete($sFolder, $aFilteredUids, true, true, + !!$this->Config()->Get('labs', 'use_imap_expunge_all_on_delete', false)); + } + catch (\Throwable $oException) + { + throw new ClientException(Notifications::CantDeleteMessage, $oException); + } + + if ($this->Config()->Get('labs', 'use_imap_unselect', true)) + { + try + { + $this->MailClient()->FolderUnSelect(); + } + catch (\Throwable $oException) + { + unset($oException); + } + } + + $sHash = ''; + + try + { + $sHash = $this->MailClient()->FolderHash($sFolder); + } + catch (\Throwable $oException) + { + unset($oException); + } + + return $this->DefaultResponse(__FUNCTION__, '' === $sHash ? false : array($sFolder, $sHash)); + } + + /** + * @throws \MailSo\Base\Exceptions\Exception + */ + public function DoMessageMove() : array + { + $this->initMailClientConnection(); + + $sFromFolder = $this->GetActionParam('FromFolder', ''); + $sToFolder = $this->GetActionParam('ToFolder', ''); + $aUids = \explode(',', (string) $this->GetActionParam('Uids', '')); + $bMarkAsRead = '1' === (string) $this->GetActionParam('MarkAsRead', '0'); + + $aFilteredUids = \array_filter($aUids, function (&$mUid) { + $mUid = (int) \trim($mUid); + return 0 < $mUid; + }); + + if ($bMarkAsRead) + { + try + { + $this->MailClient()->MessageSetSeen($sFromFolder, $aFilteredUids, true, true); + } + catch (\Throwable $oException) + { + unset($oException); + } + } + + try + { + $this->MailClient()->MessageMove($sFromFolder, $sToFolder, $aFilteredUids, true, + !!$this->Config()->Get('labs', 'use_imap_move', true), + !!$this->Config()->Get('labs', 'use_imap_expunge_all_on_delete', false) + ); + } + catch (\Throwable $oException) + { + throw new ClientException(Notifications::CantMoveMessage, $oException); + } + + if ($this->Config()->Get('labs', 'use_imap_unselect', true)) + { + try + { + $this->MailClient()->FolderUnSelect(); + } + catch (\Throwable $oException) + { + unset($oException); + } + } + + $sHash = ''; + + try + { + $sHash = $this->MailClient()->FolderHash($sFromFolder); + } + catch (\Throwable $oException) + { + unset($oException); + } + + return $this->DefaultResponse(__FUNCTION__, '' === $sHash ? false : array($sFromFolder, $sHash)); + } + + /** + * @throws \MailSo\Base\Exceptions\Exception + */ + public function DoMessageCopy() : array + { + $this->initMailClientConnection(); + + $sFromFolder = $this->GetActionParam('FromFolder', ''); + $sToFolder = $this->GetActionParam('ToFolder', ''); + $aUids = \explode(',', (string) $this->GetActionParam('Uids', '')); + + $aFilteredUids = \array_filter($aUids, function (&$mUid) { + $mUid = (int) \trim($mUid); + return 0 < $mUid; + }); + + try + { + $this->MailClient()->MessageCopy($sFromFolder, $sToFolder, + $aFilteredUids, true); + + $sHash = $this->MailClient()->FolderHash($sFromFolder); + } + catch (\Throwable $oException) + { + throw new ClientException(Notifications::CantCopyMessage, $oException); + } + + return $this->DefaultResponse(__FUNCTION__, + '' === $sHash ? false : array($sFromFolder, $sHash)); + } + + public function DoMessageUploadAttachments() : array + { + $oAccount = $this->initMailClientConnection(); + + $mResult = false; + $self = $this; + + try + { + $aAttachments = $this->GetActionParam('Attachments', array()); + if (\is_array($aAttachments) && 0 < \count($aAttachments)) + { + $mResult = array(); + foreach ($aAttachments as $sAttachment) + { + if ($aValues = \RainLoop\Utils::DecodeKeyValuesQ($sAttachment)) + { + $sFolder = isset($aValues['Folder']) ? $aValues['Folder'] : ''; + $iUid = (int) isset($aValues['Uid']) ? $aValues['Uid'] : 0; + $sMimeIndex = (string) isset($aValues['MimeIndex']) ? $aValues['MimeIndex'] : ''; + + $sTempName = \md5($sAttachment); + if (!$this->FilesProvider()->FileExists($oAccount, $sTempName)) + { + $this->MailClient()->MessageMimeStream( + function($rResource, $sContentType, $sFileName, $sMimeIndex = '') use ($oAccount, &$mResult, $sTempName, $sAttachment, $self) { + if (is_resource($rResource)) + { + $sContentType = (empty($sFileName)) ? 'text/plain' : \MailSo\Base\Utils::MimeContentType($sFileName); + $sFileName = $self->MainClearFileName($sFileName, $sContentType, $sMimeIndex); + + if ($self->FilesProvider()->PutFile($oAccount, $sTempName, $rResource)) + { + $mResult[$sTempName] = $sAttachment; + } + } + }, $sFolder, $iUid, true, $sMimeIndex); + } + else + { + $mResult[$sTempName] = $sAttachment; + } + } + } + } + } + catch (\Throwable $oException) + { + throw new ClientException(Notifications::MailServerError, $oException); + } + + return $this->DefaultResponse(__FUNCTION__, $mResult); + } + +} diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Response.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Response.php new file mode 100644 index 000000000..22c4fc294 --- /dev/null +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Response.php @@ -0,0 +1,636 @@ +Plugins()->RunHook('main.default-response-data', array($sActionName, &$mResult)); + $aResponseItem = $this->mainDefaultResponse($sActionName, $mResult, $aAdditionalParams); + $this->Plugins()->RunHook('main.default-response', array($sActionName, &$aResponseItem)); + return $aResponseItem; + } + + public function TrueResponse(string $sActionName, array $aAdditionalParams = array()) : array + { + $mResult = true; + $this->Plugins()->RunHook('main.default-response-data', array($sActionName, &$mResult)); + $aResponseItem = $this->mainDefaultResponse($sActionName, $mResult, $aAdditionalParams); + $this->Plugins()->RunHook('main.default-response', array($sActionName, &$aResponseItem)); + return $aResponseItem; + } + + public function FalseResponse(string $sActionName, ?int $iErrorCode = null, ?string $sErrorMessage = null, ?string $sAdditionalErrorMessage = null) : array + { + $mResult = false; + $this->Plugins() + ->RunHook('main.default-response-data', array($sActionName, &$mResult)) + ->RunHook('main.default-response-error-data', array($sActionName, &$iErrorCode, &$sErrorMessage)) + ; + + $aAdditionalParams = array(); + if (null !== $iErrorCode) + { + $aAdditionalParams['ErrorCode'] = (int) $iErrorCode; + $aAdditionalParams['ErrorMessage'] = null === $sErrorMessage ? '' : (string) $sErrorMessage; + $aAdditionalParams['ErrorMessageAdditional'] = null === $sAdditionalErrorMessage ? '' : (string) $sAdditionalErrorMessage; + } + + $aResponseItem = $this->mainDefaultResponse($sActionName, $mResult, $aAdditionalParams); + + $this->Plugins()->RunHook('main.default-response', array($sActionName, &$aResponseItem)); + return $aResponseItem; + } + + public function ExceptionResponse(string $sActionName, \Throwable $oException) : array + { + $iErrorCode = null; + $sErrorMessage = null; + $sErrorMessageAdditional = null; + + if ($oException instanceof \RainLoop\Exceptions\ClientException) + { + $iErrorCode = $oException->getCode(); + $sErrorMessage = null; + + if ($iErrorCode === Notifications::ClientViewError) + { + $sErrorMessage = $oException->getMessage(); + } + + $sErrorMessageAdditional = $oException->getAdditionalMessage(); + if (empty($sErrorMessageAdditional)) + { + $sErrorMessageAdditional = null; + } + } + else + { + $iErrorCode = Notifications::UnknownError; + $sErrorMessage = $oException->getCode().' - '.$oException->getMessage(); + } + + $oPrevious = $oException->getPrevious(); + if ($oPrevious) + { + $this->Logger()->WriteException($oPrevious); + } + else + { + $this->Logger()->WriteException($oException); + } + + return $this->FalseResponse($sActionName, $iErrorCode, $sErrorMessage, $sErrorMessageAdditional); + } + + /** + * @param mixed $mResult = false + */ + private function mainDefaultResponse(string $sActionName, $mResult = false, array $aAdditionalParams = array()) : array + { + $sActionName = 'Do' === \substr($sActionName, 0, 2) ? \substr($sActionName, 2) : $sActionName; + $sActionName = \preg_replace('/[^a-zA-Z0-9_]+/', '', $sActionName); + + $aResult = array( + 'Action' => $sActionName, + 'Result' => $this->responseObject($mResult, $sActionName) + ); + + foreach ($aAdditionalParams as $sKey => $mValue) + { + $aResult[$sKey] = $mValue; + } + + return $aResult; + } + +/* + $this->Cacher( + $this->Config( + $this->getAccountFromToken( + $this->GetCapa( + $this->MailClient( + $this->SettingsProvider( + $this->Plugins( +*/ + private function isFileHasFramedPreview(string $sFileName) : bool + { + $sExt = \MailSo\Base\Utils::GetFileExtension($sFileName); + return \in_array($sExt, array('doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx')); + } + + private function isFileHasThumbnail(string $sFileName) : bool + { + static $aCache = array(); + + $sExt = \MailSo\Base\Utils::GetFileExtension($sFileName); + if (isset($aCache[$sExt])) + { + return $aCache[$sExt]; + } + + $bResult = \function_exists('gd_info'); + if ($bResult) + { + $bResult = false; + switch ($sExt) + { + case 'png': + $bResult = \function_exists('imagecreatefrompng'); + break; + case 'gif': + $bResult = \function_exists('imagecreatefromgif'); + break; + case 'jpg': + case 'jpeg': + $bResult = \function_exists('imagecreatefromjpeg'); + break; + } + } + + $aCache[$sExt] = $bResult; + + return $bResult; + } + + private function hashFolderFullName(string $sFolderFullName) : string + { + return \in_array(\strtolower($sFolderFullName), array('inbox', 'sent', 'send', 'drafts', + 'spam', 'junk', 'bin', 'trash', 'archive', 'allmail', 'all')) ? + $sFolderFullName : \md5($sFolderFullName); + } + +/* + public function RawFramedView() : string + { + $oAccount = $this->getAccountFromToken(false); + if ($oAccount) + { + $sRawKey = (string) $this->GetActionParam('RawKey', ''); + $aParams = $this->GetActionParam('Params', null); + $this->Http()->ServerNoCache(); + + $aData = Utils::DecodeKeyValuesQ($sRawKey); + if (isset($aParams[0], $aParams[1], $aParams[2]) && + 'Raw' === $aParams[0] && 'FramedView' === $aParams[2] && isset($aData['Framed']) && $aData['Framed'] && $aData['FileName']) + { + if ($this->isFileHasFramedPreview($aData['FileName'])) + { + $sNewSpecAuthToken = $this->GetShortLifeSpecAuthToken(); + if (!empty($sNewSpecAuthToken)) + { + $aParams[1] = '_'.$sNewSpecAuthToken; + $aParams[2] = 'View'; + + \array_shift($aParams); + $sLast = \array_pop($aParams); + + $sUrl = $this->Http()->GetFullUrl().'?/Raw/&q[]=/'.\implode('/', $aParams).'/&q[]=/'.$sLast; + $sFullUrl = 'https://docs.google.com/viewer?embedded=true&url='.\urlencode($sUrl); + + \header('Content-Type: text/html; charset=utf-8'); + echo ''. + ''. + ''. + ''; + } + } + } + } + + + return true; + + } +*/ + + private function objectData(object $oData, string $sParent, array $aParameters = array()) : array + { + $mResult = array(); + if (\is_object($oData)) + { + $aNames = \explode('\\', \get_class($oData)); + $mResult = array( + '@Object' => \end($aNames) + ); + + if ($oData instanceof \MailSo\Base\Collection) + { + $mResult['@Object'] = 'Collection/'.$mResult['@Object']; + $mResult['@Count'] = $oData->Count(); + $mResult['@Collection'] = $this->responseObject($oData->getArrayCopy(), $sParent, $aParameters); + } + else + { + $mResult['@Object'] = 'Object/'.$mResult['@Object']; + } + } + + return $mResult; + } + + /** + * @param mixed $mResponse + * + * @return mixed + */ + private $oAccount = null; + private $aCheckableFolder = null; + private function responseObject($mResponse, string $sParent = '', array $aParameters = array()) + { + /** + \MailSo\Mime\Email + \RainLoop\Model\Domain + \RainLoop\Model\Identity + \RainLoop\Model\Template + \RainLoop\Providers\AddressBook\Classes\Contact + \RainLoop\Providers\AddressBook\Classes\Property + \RainLoop\Providers\AddressBook\Classes\Tag + \RainLoop\Providers\Filters\Classes\Filter + \RainLoop\Providers\Filters\Classes\FilterCondition + */ + if ($mResponse instanceof \JsonSerializable) { +// return $mResponse->jsonSerialize(); + return $mResponse; + } + + if (\is_object($mResponse)) + { + $sClassName = \get_class($mResponse); + + if ('MailSo\Mail\Message' === $sClassName) + { + if (null === $this->oAccount) { + $this->oAccount = $this->getAccountFromToken(false); + } + $oAccount = $this->oAccount; + + $iDateTimeStampInUTC = $mResponse->InternalTimeStampInUTC(); + if (0 === $iDateTimeStampInUTC || !!$this->Config()->Get('labs', 'date_from_headers', false)) + { + $iDateTimeStampInUTC = $mResponse->HeaderTimeStampInUTC(); + if (0 === $iDateTimeStampInUTC) + { + $iDateTimeStampInUTC = $mResponse->InternalTimeStampInUTC(); + } + } + + $mResult = \array_merge($this->objectData($mResponse, $sParent, $aParameters), array( + 'Folder' => $mResponse->Folder(), + 'Uid' => (string) $mResponse->Uid(), + 'Subject' => \trim(\MailSo\Base\Utils::Utf8Clear($mResponse->Subject())), + 'MessageId' => $mResponse->MessageId(), + 'Size' => $mResponse->Size(), + 'DateTimeStampInUTC' => $iDateTimeStampInUTC, + 'ReplyTo' => $this->responseObject($mResponse->ReplyTo(), $sParent, $aParameters), + 'From' => $this->responseObject($mResponse->From(), $sParent, $aParameters), + 'To' => $this->responseObject($mResponse->To(), $sParent, $aParameters), + 'Cc' => $this->responseObject($mResponse->Cc(), $sParent, $aParameters), + 'Bcc' => $this->responseObject($mResponse->Bcc(), $sParent, $aParameters), + 'Sender' => $this->responseObject($mResponse->Sender(), $sParent, $aParameters), + 'DeliveredTo' => $this->responseObject($mResponse->DeliveredTo(), $sParent, $aParameters), + 'Priority' => $mResponse->Priority(), + 'Threads' => $mResponse->Threads(), + 'Sensitivity' => $mResponse->Sensitivity(), + 'UnsubsribeLinks' => $mResponse->UnsubsribeLinks(), + 'ExternalProxy' => false, + 'ReadReceipt' => '' + )); + + $mResult['SubjectParts'] = $this->explodeSubject($mResult['Subject']); + + $oAttachments = $mResponse->Attachments(); + $iAttachmentsCount = $oAttachments ? $oAttachments->Count() : 0; + + $mResult['HasAttachments'] = 0 < $iAttachmentsCount; + $mResult['AttachmentsSpecData'] = $mResult['HasAttachments'] ? $oAttachments->SpecData() : array(); + + $sSubject = $mResult['Subject']; + $mResult['Hash'] = \md5($mResult['Folder'].$mResult['Uid']); + $mResult['RequestHash'] = Utils::EncodeKeyValuesQ(array( + 'V' => APP_VERSION, + 'Account' => $oAccount ? \md5($oAccount->Hash()) : '', + 'Folder' => $mResult['Folder'], + 'Uid' => $mResult['Uid'], + 'MimeType' => 'message/rfc822', + 'FileName' => (0 === \strlen($sSubject) ? 'message-'.$mResult['Uid'] : \MailSo\Base\Utils::ClearXss($sSubject)).'.eml' + )); + + // Flags + $aFlags = $mResponse->FlagsLowerCase(); + $mResult['IsSeen'] = \in_array('\\seen', $aFlags); + $mResult['IsFlagged'] = \in_array('\\flagged', $aFlags); + $mResult['IsAnswered'] = \in_array('\\answered', $aFlags); + $mResult['IsDeleted'] = \in_array('\\deleted', $aFlags); + + $sForwardedFlag = $this->Config()->Get('labs', 'imap_forwarded_flag', ''); + $sReadReceiptFlag = $this->Config()->Get('labs', 'imap_read_receipt_flag', ''); + + $mResult['IsForwarded'] = 0 < \strlen($sForwardedFlag) && \in_array(\strtolower($sForwardedFlag), $aFlags); + $mResult['IsReadReceipt'] = 0 < \strlen($sReadReceiptFlag) && \in_array(\strtolower($sReadReceiptFlag), $aFlags); + + if (!$this->GetCapa(false, false, Capa::COMPOSER, $oAccount)) + { + $mResult['IsReadReceipt'] = true; + } + + $mResult['TextPartIsTrimmed'] = false; + + if ('Message' === $sParent) + { + $oAttachments = /* @var \MailSo\Mail\AttachmentCollection */ $mResponse->Attachments(); + + $bHasExternals = false; + $mFoundedCIDs = array(); + $aContentLocationUrls = array(); + $mFoundedContentLocationUrls = array(); + + if ($oAttachments && 0 < $oAttachments->Count()) + { + foreach ($oAttachments as /* @var \MailSo\Mail\Attachment */ $oAttachment) + { + if ($oAttachment) + { + $sContentLocation = $oAttachment->ContentLocation(); + if ($sContentLocation && 0 < \strlen($sContentLocation)) + { + $aContentLocationUrls[] = $oAttachment->ContentLocation(); + } + } + } + } + + $sPlain = ''; + $sHtml = \trim($mResponse->Html()); + + if (0 === \strlen($sHtml)) + { + $sPlain = \trim($mResponse->Plain()); + } + + $mResult['DraftInfo'] = $mResponse->DraftInfo(); + $mResult['InReplyTo'] = $mResponse->InReplyTo(); + $mResult['UnsubsribeLinks'] = $mResponse->UnsubsribeLinks(); + $mResult['References'] = $mResponse->References(); + + $fAdditionalExternalFilter = null; + if (!!$this->Config()->Get('labs', 'use_local_proxy_for_external_images', false)) + { + $fAdditionalExternalFilter = function ($sUrl) { + return './?/ProxyExternal/'.Utils::EncodeKeyValuesQ(array( + 'Rnd' => \md5(\microtime(true)), + 'Token' => Utils::GetConnectionToken(), + 'Url' => $sUrl + )).'/'; + }; + } + + $sHtml = \preg_replace_callback('/(]*>)([\s\S\r\n\t]*?)(<\/pre>)/mi', function ($aMatches) { + return \preg_replace('/[\r\n]+/', '
', $aMatches[1].\trim($aMatches[2]).$aMatches[3]); + }, $sHtml); + + $mResult['Html'] = 0 === \strlen($sHtml) ? '' : \MailSo\Base\HtmlUtils::ClearHtml( + $sHtml, $bHasExternals, $mFoundedCIDs, $aContentLocationUrls, $mFoundedContentLocationUrls, false, false, + $fAdditionalExternalFilter, null, !!$this->Config()->Get('labs', 'try_to_detect_hidden_images', false) + ); + + $mResult['ExternalProxy'] = null !== $fAdditionalExternalFilter; + + $mResult['Plain'] = $sPlain; +// $mResult['Plain'] = 0 === \strlen($sPlain) ? '' : \MailSo\Base\HtmlUtils::ConvertPlainToHtml($sPlain); + + $mResult['TextHash'] = \md5($mResult['Html'].$mResult['Plain']); + + $mResult['TextPartIsTrimmed'] = $mResponse->TextPartIsTrimmed(); + + $mResult['PgpSigned'] = $mResponse->PgpSigned(); + $mResult['PgpEncrypted'] = $mResponse->PgpEncrypted(); + $mResult['PgpSignature'] = $mResponse->PgpSignature(); + + unset($sHtml, $sPlain); + + $mResult['HasExternals'] = $bHasExternals; + $mResult['HasInternals'] = (\is_array($mFoundedCIDs) && 0 < \count($mFoundedCIDs)) || + (\is_array($mFoundedContentLocationUrls) && 0 < \count($mFoundedContentLocationUrls)); + $mResult['FoundedCIDs'] = $mFoundedCIDs; + $mResult['Attachments'] = $this->responseObject($oAttachments, $sParent, \array_merge($aParameters, array( + 'FoundedCIDs' => $mFoundedCIDs, + 'FoundedContentLocationUrls' => $mFoundedContentLocationUrls + ))); + + $mResult['ReadReceipt'] = $mResponse->ReadReceipt(); + if (0 < \strlen($mResult['ReadReceipt']) && !$mResult['IsReadReceipt']) + { + if (0 < \strlen($mResult['ReadReceipt'])) + { + try + { + $oReadReceipt = \MailSo\Mime\Email::Parse($mResult['ReadReceipt']); + if (!$oReadReceipt) + { + $mResult['ReadReceipt'] = ''; + } + } + catch (\Throwable $oException) { unset($oException); } + } + + if (0 < \strlen($mResult['ReadReceipt']) && '1' === $this->Cacher($oAccount)->Get( + \RainLoop\KeyPathHelper::ReadReceiptCache($oAccount->Email(), $mResult['Folder'], $mResult['Uid']), '0')) + { + $mResult['ReadReceipt'] = ''; + } + } + } + return $mResult; + } + + if ('MailSo\Mail\Attachment' === $sClassName) + { + if (null === $this->oAccount) { + $this->oAccount = $this->getAccountFromToken(false); + } + + $mFoundedCIDs = isset($aParameters['FoundedCIDs']) && \is_array($aParameters['FoundedCIDs']) && + 0 < \count($aParameters['FoundedCIDs']) ? + $aParameters['FoundedCIDs'] : null; + + $mFoundedContentLocationUrls = isset($aParameters['FoundedContentLocationUrls']) && + \is_array($aParameters['FoundedContentLocationUrls']) && + 0 < \count($aParameters['FoundedContentLocationUrls']) ? + $aParameters['FoundedContentLocationUrls'] : null; + + if ($mFoundedCIDs || $mFoundedContentLocationUrls) + { + $mFoundedCIDs = \array_merge($mFoundedCIDs ? $mFoundedCIDs : array(), + $mFoundedContentLocationUrls ? $mFoundedContentLocationUrls : array()); + + $mFoundedCIDs = 0 < \count($mFoundedCIDs) ? $mFoundedCIDs : null; + } + + $mResult = \array_merge($this->objectData($mResponse, $sParent, $aParameters), array( + 'Folder' => $mResponse->Folder(), + 'Uid' => (string) $mResponse->Uid(), + 'Framed' => false, + 'MimeIndex' => (string) $mResponse->MimeIndex(), + 'MimeType' => $mResponse->MimeType(), + 'FileName' => \MailSo\Base\Utils::ClearFileName( + \MailSo\Base\Utils::ClearXss($mResponse->FileName(true))), + 'EstimatedSize' => $mResponse->EstimatedSize(), + 'CID' => $mResponse->Cid(), + 'ContentLocation' => $mResponse->ContentLocation(), + 'IsInline' => $mResponse->IsInline(), + 'IsThumbnail' => $this->GetCapa(false, false, Capa::ATTACHMENT_THUMBNAILS), + 'IsLinked' => ($mFoundedCIDs && \in_array(\trim(\trim($mResponse->Cid()), '<>'), $mFoundedCIDs)) || + ($mFoundedContentLocationUrls && \in_array(\trim($mResponse->ContentLocation()), $mFoundedContentLocationUrls)) + )); + + $mResult['Framed'] = $this->isFileHasFramedPreview($mResult['FileName']); + + if ($mResult['IsThumbnail']) + { + $mResult['IsThumbnail'] = $this->isFileHasThumbnail($mResult['FileName']); + } + + $mResult['Download'] = Utils::EncodeKeyValuesQ(array( + 'V' => APP_VERSION, + 'Account' => $this->oAccount ? \md5($this->oAccount->Hash()) : '', + 'Folder' => $mResult['Folder'], + 'Uid' => $mResult['Uid'], + 'MimeIndex' => $mResult['MimeIndex'], + 'MimeType' => $mResult['MimeType'], + 'FileName' => $mResult['FileName'], + 'Framed' => $mResult['Framed'] + )); + return $mResult; + } + + if ('MailSo\Mail\Folder' === $sClassName) + { + $aExtended = null; + +// $mStatus = $mResponse->Status(); +// if (\is_array($mStatus) && isset($mStatus['MESSAGES'], $mStatus['UNSEEN'], $mStatus['UIDNEXT'])) +// { +// $aExtended = array( +// 'MessageCount' => (int) $mStatus['MESSAGES'], +// 'MessageUnseenCount' => (int) $mStatus['UNSEEN'], +// 'UidNext' => (string) $mStatus['UIDNEXT'], +// 'Hash' => $this->MailClient()->GenerateFolderHash( +// $mResponse->FullNameRaw(), $mStatus['MESSAGES'], $mStatus['UNSEEN'], $mStatus['UIDNEXT'], +// empty($mStatus['HIGHESTMODSEQ']) ? '' : $mStatus['HIGHESTMODSEQ']) +// ); +// } + + if (null === $this->aCheckableFolder) + { + if (null === $this->oAccount) { + $this->oAccount = $this->getAccountFromToken(false); + } + $aCheckable = \json_decode( + $this->SettingsProvider(true) + ->Load($this->oAccount) + ->GetConf('CheckableFolder', '[]') + ); + $this->aCheckableFolder = \is_array($aCheckable) ? $aCheckable : array(); + } + + return \array_merge($this->objectData($mResponse, $sParent, $aParameters), array( + 'Name' => $mResponse->Name(), + 'FullName' => $mResponse->FullName(), + 'FullNameRaw' => $mResponse->FullNameRaw(), + 'FullNameHash' => $this->hashFolderFullName($mResponse->FullNameRaw(), $mResponse->FullName()), + 'Delimiter' => (string) $mResponse->Delimiter(), + 'HasVisibleSubFolders' => $mResponse->HasVisibleSubFolders(), + 'IsSubscribed' => $mResponse->IsSubscribed(), + 'IsExists' => $mResponse->IsExists(), + 'IsSelectable' => $mResponse->IsSelectable(), + 'Flags' => $mResponse->FlagsLowerCase(), + 'Checkable' => \in_array($mResponse->FullNameRaw(), $this->aCheckableFolder), + 'Extended' => $aExtended, + 'SubFolders' => $this->responseObject($mResponse->SubFolders(), $sParent, $aParameters) + )); + } + + if ('MailSo\Mail\MessageCollection' === $sClassName) + { + return \array_merge($this->objectData($mResponse, $sParent, $aParameters), array( + 'MessageCount' => $mResponse->MessageCount, + 'MessageUnseenCount' => $mResponse->MessageUnseenCount, + 'MessageResultCount' => $mResponse->MessageResultCount, + 'Folder' => $mResponse->FolderName, + 'FolderHash' => $mResponse->FolderHash, + 'UidNext' => $mResponse->UidNext, + 'ThreadUid' => $mResponse->ThreadUid, + 'NewMessages' => $this->responseObject($mResponse->NewMessages), + 'Filtered' => $mResponse->Filtered, + 'Offset' => $mResponse->Offset, + 'Limit' => $mResponse->Limit, + 'Search' => $mResponse->Search + )); + } + + if ('MailSo\Mail\AttachmentCollection' === $sClassName) + { + return \array_merge($this->objectData($mResponse, $sParent, $aParameters), array( + 'InlineCount' => $mResponse->InlineCount() + )); + } + + if ('MailSo\Mail\FolderCollection' === $sClassName) + { + return \array_merge($this->objectData($mResponse, $sParent, $aParameters), array( + 'Namespace' => $mResponse->GetNamespace(), + 'FoldersHash' => isset($mResponse->FoldersHash) ? $mResponse->FoldersHash : '', + 'IsThreadsSupported' => $mResponse->IsThreadsSupported, + 'Optimized' => $mResponse->Optimized, + 'CountRec' => $mResponse->CountRec(), + 'SystemFolders' => isset($mResponse->SystemFolders) && \is_array($mResponse->SystemFolders) ? + $mResponse->SystemFolders : array() + )); + } + + if ('MailSo\Mime\EmailCollection' === $sClassName) + { + $mResult = array(); + if (100 < \count($mResponse)) { + $mResponse = $mResponse->Slice(0, 100); + } + foreach ($mResponse as $iKey => $oItem) { + $mResult[$iKey] = $this->responseObject($oItem, $sParent, $aParameters); + } + return $mResult; + } + + if ($mResponse instanceof \MailSo\Base\Collection) + { + $mResult = array(); + foreach ($mResponse as $iKey => $oItem) { + $mResult[$iKey] = $this->responseObject($oItem, $sParent, $aParameters); + } + return $mResult; + } + + return '["'.$sClassName.'"]'; + } + + if (\is_array($mResponse)) + { + foreach ($mResponse as $iKey => $oItem) + { + $mResponse[$iKey] = $this->responseObject($oItem, $sParent, $aParameters); + } + + return $mResponse; + } + + return $mResponse; + } +} diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Templates.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Templates.php new file mode 100644 index 000000000..158372a62 --- /dev/null +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Templates.php @@ -0,0 +1,133 @@ +getAccountFromToken(); + + if (!$this->GetCapa(false, false, Capa::TEMPLATES, $oAccount)) + { + return $this->FalseResponse(__FUNCTION__); + } + + $oTemplate = new \RainLoop\Model\Template(); + if (!$oTemplate->FromJSON($this->GetActionParams(), true)) + { + throw new ClientException(Notifications::InvalidInputArgument); + } + + if ('' === $oTemplate->Id()) + { + $oTemplate->GenerateID(); + } + + $aTemplatesForSave = array(); + $aTemplates = $this->GetTemplates($oAccount); + + + foreach ($aTemplates as $oItem) + { + if ($oItem && $oItem->Id() !== $oTemplate->Id()) + { + $aTemplatesForSave[] = $oItem; + } + } + + $aTemplatesForSave[] = $oTemplate; + + return $this->DefaultResponse(__FUNCTION__, $this->SetTemplates($oAccount, $aTemplatesForSave)); + } + + /** + * @throws \MailSo\Base\Exceptions\Exception + */ + public function DoTemplateDelete() : array + { + $oAccount = $this->getAccountFromToken(); + + if (!$this->GetCapa(false, false, Capa::TEMPLATES, $oAccount)) + { + return $this->FalseResponse(__FUNCTION__); + } + + $sId = \trim($this->GetActionParam('IdToDelete', '')); + if (empty($sId)) + { + throw new ClientException(Notifications::UnknownError); + } + + $aNew = array(); + $aTemplates = $this->GetTemplates($oAccount); + foreach ($aTemplates as $oItem) + { + if ($oItem && $sId !== $oItem->Id()) + { + $aNew[] = $oItem; + } + } + + return $this->DefaultResponse(__FUNCTION__, $this->SetTemplates($oAccount, $aNew)); + } + + /** + * @throws \MailSo\Base\Exceptions\Exception + */ + public function DoTemplateGetByID() : array + { + $oAccount = $this->getAccountFromToken(); + + if (!$this->GetCapa(false, false, Capa::TEMPLATES, $oAccount)) + { + return $this->FalseResponse(__FUNCTION__); + } + + $sId = \trim($this->GetActionParam('ID', '')); + if (empty($sId)) + { + throw new ClientException(Notifications::UnknownError); + } + + $oTemplate = false; + $aTemplates = $this->GetTemplates($oAccount); + + foreach ($aTemplates as $oItem) + { + if ($oItem && $sId === $oItem->Id()) + { + $oTemplate = $oItem; + break; + } + } + + $oTemplate->SetPopulateAlways(true); + return $this->DefaultResponse(__FUNCTION__, $oTemplate); + } + + /** + * @throws \MailSo\Base\Exceptions\Exception + */ + public function DoTemplates() : array + { + $oAccount = $this->getAccountFromToken(); + + if (!$this->GetCapa(false, false, Capa::TEMPLATES, $oAccount)) + { + return $this->FalseResponse(__FUNCTION__); + } + + return $this->DefaultResponse(__FUNCTION__, array( + 'Templates' => $this->GetTemplates($oAccount) + )); + } +} diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/TwoFactor.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/TwoFactor.php new file mode 100644 index 000000000..adfbe8df9 --- /dev/null +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/TwoFactor.php @@ -0,0 +1,151 @@ +getAccountFromToken(); + + if (!$this->TwoFactorAuthProvider()->IsActive() || + !$this->GetCapa(false, false, \RainLoop\Enumerations\Capa::TWO_FACTOR, $oAccount)) + { + return $this->FalseResponse(__FUNCTION__); + } + + return $this->DefaultResponse(__FUNCTION__, + $this->getTwoFactorInfo($oAccount, true)); + } + + public function DoCreateTwoFactorSecret() : array + { + $oAccount = $this->getAccountFromToken(); + + if (!$this->TwoFactorAuthProvider()->IsActive() || + !$this->GetCapa(false, false, \RainLoop\Enumerations\Capa::TWO_FACTOR, $oAccount)) + { + return $this->FalseResponse(__FUNCTION__); + } + + $sEmail = $oAccount->ParentEmailHelper(); + + $sSecret = $this->TwoFactorAuthProvider()->CreateSecret(); + + $aCodes = array(); + for ($iIndex = 9; $iIndex > 0; $iIndex--) + { + $aCodes[] = \rand(100000000, 900000000); + } + + $this->StorageProvider()->Put($oAccount, + \RainLoop\Providers\Storage\Enumerations\StorageType::CONFIG, + 'two_factor', + \RainLoop\Utils::EncodeKeyValues(array( + 'User' => $sEmail, + 'Enable' => false, + 'Secret' => $sSecret, + 'BackupCodes' => \implode(' ', $aCodes) + )) + ); + + $this->requestSleep(); + + return $this->DefaultResponse(__FUNCTION__, + $this->getTwoFactorInfo($oAccount)); + } + + public function DoShowTwoFactorSecret() : array + { + $oAccount = $this->getAccountFromToken(); + + if (!$this->TwoFactorAuthProvider()->IsActive() || + !$this->GetCapa(false, false, \RainLoop\Enumerations\Capa::TWO_FACTOR, $oAccount)) + { + return $this->FalseResponse(__FUNCTION__); + } + + $aResult = $this->getTwoFactorInfo($oAccount); + unset($aResult['BackupCodes']); + + return $this->DefaultResponse(__FUNCTION__, $aResult); + } + + public function DoEnableTwoFactor() : array + { + $oAccount = $this->getAccountFromToken(); + + if (!$this->TwoFactorAuthProvider()->IsActive() || + !$this->GetCapa(false, false, \RainLoop\Enumerations\Capa::TWO_FACTOR, $oAccount)) + { + return $this->FalseResponse(__FUNCTION__); + } + + $sEmail = $oAccount->ParentEmailHelper(); + + $bResult = false; + $mData = $this->getTwoFactorInfo($oAccount); + if (isset($mData['Secret'], $mData['BackupCodes'])) + { + $bResult = $this->StorageProvider()->Put($oAccount, + \RainLoop\Providers\Storage\Enumerations\StorageType::CONFIG, + 'two_factor', + \RainLoop\Utils::EncodeKeyValues(array( + 'User' => $sEmail, + 'Enable' => '1' === \trim($this->GetActionParam('Enable', '0')), + 'Secret' => $mData['Secret'], + 'BackupCodes' => $mData['BackupCodes'] + )) + ); + } + + return $this->DefaultResponse(__FUNCTION__, $bResult); + } + + public function DoTestTwoFactorInfo() : array + { + $oAccount = $this->getAccountFromToken(); + + if (!$this->TwoFactorAuthProvider()->IsActive() || + !$this->GetCapa(false, false, \RainLoop\Enumerations\Capa::TWO_FACTOR, $oAccount)) + { + return $this->FalseResponse(__FUNCTION__); + } + + $sCode = \trim($this->GetActionParam('Code', '')); + + $aData = $this->getTwoFactorInfo($oAccount); + $sSecret = !empty($aData['Secret']) ? $aData['Secret'] : ''; + +// $this->Logger()->WriteDump(array( +// $sCode, $sSecret, $aData, +// $this->TwoFactorAuthProvider()->VerifyCode($sSecret, $sCode) +// )); + + $this->requestSleep(); + + return $this->DefaultResponse(__FUNCTION__, + $this->TwoFactorAuthProvider()->VerifyCode($sSecret, $sCode)); + } + + public function DoClearTwoFactorInfo() : array + { + $oAccount = $this->getAccountFromToken(); + + if (!$this->TwoFactorAuthProvider()->IsActive() || + !$this->GetCapa(false, false, \RainLoop\Enumerations\Capa::TWO_FACTOR, $oAccount)) + { + return $this->FalseResponse(__FUNCTION__); + } + + $this->StorageProvider()->Clear($oAccount, + \RainLoop\Providers\Storage\Enumerations\StorageType::CONFIG, + 'two_factor' + ); + + return $this->DefaultResponse(__FUNCTION__, + $this->getTwoFactorInfo($oAccount, true)); + } + +} diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/User.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/User.php new file mode 100644 index 000000000..3df9f4405 --- /dev/null +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/User.php @@ -0,0 +1,513 @@ +GetActionParam('Email', '')); + $sPassword = $this->GetActionParam('Password', ''); + $sLanguage = $this->GetActionParam('Language', ''); + $bSignMe = '1' === (string) $this->GetActionParam('SignMe', '0'); + + $sAdditionalCode = $this->GetActionParam('AdditionalCode', ''); + $bAdditionalCodeSignMe = '1' === (string) $this->GetActionParam('AdditionalCodeSignMe', '0'); + + $oAccount = null; + + $this->Logger()->AddSecret($sPassword); + + if ('sleep@sleep.dev' === $sEmail && 0 < \strlen($sPassword) && + \is_numeric($sPassword) && $this->Config()->Get('debug', 'enable', false) && + 0 < (int) $sPassword && 30 > (int) $sPassword + ) + { + \sleep((int) $sPassword); + throw new Exceptions\ClientException(Notifications::AuthError); + } + + try + { + $oAccount = $this->LoginProcess($sEmail, $sPassword, + $bSignMe ? $this->generateSignMeToken($sEmail) : '', + $sAdditionalCode, $bAdditionalCodeSignMe); + } + catch (Exceptions\ClientException $oException) + { + if ($oException && + Notifications::AccountTwoFactorAuthRequired === $oException->getCode()) + { + return $this->DefaultResponse(__FUNCTION__, true, array( + 'TwoFactorAuth' => true + )); + } + else + { + throw $oException; + } + } + + $this->AuthToken($oAccount); + + if ($oAccount && 0 < \strlen($sLanguage)) + { + $oSettings = $this->SettingsProvider()->Load($oAccount); + if ($oSettings) + { + $sLanguage = $this->ValidateLanguage($sLanguage); + $sCurrentLanguage = $oSettings->GetConf('Language', ''); + + if ($sCurrentLanguage !== $sLanguage) + { + $oSettings->SetConf('Language', $sLanguage); + $this->SettingsProvider()->Save($oAccount, $oSettings); + } + } + } + + return $this->TrueResponse(__FUNCTION__); + } + + /** + * @throws \MailSo\Base\Exceptions\Exception + */ + public function DoAttachmentsActions() : array + { + if (!$this->GetCapa(false, false, Enumerations\Capa::ATTACHMENTS_ACTIONS)) + { + return $this->FalseResponse(__FUNCTION__); + } + + $oAccount = $this->initMailClientConnection(); + + $sAction = $this->GetActionParam('Do', ''); + $aHashes = $this->GetActionParam('Hashes', null); + + $mResult = false; + $bError = false; + $aData = false; + + if (\is_array($aHashes) && 0 < \count($aHashes)) + { + $aData = array(); + foreach ($aHashes as $sZipHash) + { + $aResult = $this->getMimeFileByHash($oAccount, $sZipHash); + if (!empty($aResult['FileHash'])) + { + $aData[] = $aResult; + } + else + { + $bError = true; + break; + } + } + } + + $oFilesProvider = $this->FilesProvider(); + if (!empty($sAction) && !$bError && \is_array($aData) && 0 < \count($aData) && + $oFilesProvider && $oFilesProvider->IsActive()) + { + $bError = false; + switch (\strtolower($sAction)) + { + case 'zip': + + if (\class_exists('ZipArchive')) + { + $sZipHash = \MailSo\Base\Utils::Md5Rand(); + $sZipFileName = $oFilesProvider->GenerateLocalFullFileName($oAccount, $sZipHash); + + if (!empty($sZipFileName)) + { + $oZip = new \ZipArchive(); + $oZip->open($sZipFileName, \ZIPARCHIVE::CREATE | \ZIPARCHIVE::OVERWRITE); + $oZip->setArchiveComment('SnappyMail/'.APP_VERSION); + + foreach ($aData as $aItem) + { + $sFileName = (string) (isset($aItem['FileName']) ? $aItem['FileName'] : 'file.dat'); + $sFileHash = (string) (isset($aItem['FileHash']) ? $aItem['FileHash'] : ''); + + if (!empty($sFileHash)) + { + $sFullFileNameHash = $oFilesProvider->GetFileName($oAccount, $sFileHash); + if (!$oZip->addFile($sFullFileNameHash, $sFileName)) + { + $bError = true; + } + } + } + + if (!$bError) + { + $bError = !$oZip->close(); + } + else + { + $oZip->close(); + } + } + + foreach ($aData as $aItem) + { + $sFileHash = (string) (isset($aItem['FileHash']) ? $aItem['FileHash'] : ''); + if (!empty($sFileHash)) + { + $oFilesProvider->Clear($oAccount, $sFileHash); + } + } + + if (!$bError) + { + $mResult = array( + 'Files' => array(array( + 'FileName' => 'attachments.zip', + 'Hash' => Utils::EncodeKeyValuesQ(array( + 'V' => APP_VERSION, + 'Account' => $oAccount ? \md5($oAccount->Hash()) : '', + 'FileName' => 'attachments.zip', + 'MimeType' => 'application/zip', + 'FileHash' => $sZipHash + )) + )) + ); + } + } + break; + } + } + else + { + $bError = true; + } + + $this->requestSleep(); + return $this->DefaultResponse(__FUNCTION__, $bError ? false : $mResult); + } + + public function DoLogout() : array + { + $oAccount = $this->getAccountFromToken(false); + if ($oAccount) + { + if ($oAccount->SignMe()) + { + $this->ClearSignMeData($oAccount); + } + + if (!$oAccount->IsAdditionalAccount()) + { + Utils::ClearCookie(Actions::AUTH_SPEC_TOKEN_KEY); + } + } + + return $this->TrueResponse(__FUNCTION__); + } + + public function DoAppDelayStart() : array + { + $this->Plugins()->RunHook('service.app-delay-start-begin'); + + Utils::UpdateConnectionToken(); + + $bMainCache = false; + $bFilesCache = false; + $bVersionsCache = false; + + $iOneDay1 = 60 * 60 * 23; + $iOneDay2 = 60 * 60 * 25; + $iOneDay3 = 60 * 60 * 30; + + $sTimers = $this->StorageProvider()->Get(null, + Providers\Storage\Enumerations\StorageType::NOBODY, 'Cache/Timers', ''); + + $aTimers = \explode(',', $sTimers); + + $iMainCacheTime = !empty($aTimers[0]) && \is_numeric($aTimers[0]) ? (int) $aTimers[0] : 0; + $iFilesCacheTime = !empty($aTimers[1]) && \is_numeric($aTimers[1]) ? (int) $aTimers[1] : 0; + $iVersionsCacheTime = !empty($aTimers[2]) && \is_numeric($aTimers[2]) ? (int) $aTimers[2] : 0; + + if (0 === $iMainCacheTime || $iMainCacheTime + $iOneDay1 < \time()) + { + $bMainCache = true; + $iMainCacheTime = \time(); + } + + if (0 === $iFilesCacheTime || $iFilesCacheTime + $iOneDay2 < \time()) + { + $bFilesCache = true; + $iFilesCacheTime = \time(); + } + + if (0 === $iVersionsCacheTime || $iVersionsCacheTime + $iOneDay3 < \time()) + { + $bVersionsCache = true; + $iVersionsCacheTime = \time(); + } + + if ($bMainCache || $bFilesCache || $bVersionsCache) + { + if (!$this->StorageProvider()->Put(null, + Providers\Storage\Enumerations\StorageType::NOBODY, 'Cache/Timers', + \implode(',', array($iMainCacheTime, $iFilesCacheTime, $iVersionsCacheTime)))) + { + $bMainCache = $bFilesCache = $bVersionsCache = false; + } + } + + if ($bMainCache) + { + $this->Logger()->Write('Cacher GC: Begin'); + $this->Cacher()->GC(48); + $this->Logger()->Write('Cacher GC: End'); + } + else if ($bFilesCache) + { + $this->Logger()->Write('Files GC: Begin'); + $this->FilesProvider()->GC(48); + $this->Logger()->Write('Files GC: End'); + } + + $this->Plugins()->RunHook('service.app-delay-start-end'); + + return $this->TrueResponse(__FUNCTION__); + } + + public function DoSettingsUpdate() : array + { + $oAccount = $this->getAccountFromToken(); + if (!$this->GetCapa(false, false, Enumerations\Capa::SETTINGS, $oAccount)) + { + return $this->FalseResponse(__FUNCTION__); + } + + $self = $this; + $oConfig = $this->Config(); + + $oSettings = $this->SettingsProvider()->Load($oAccount); + $oSettingsLocal = $this->SettingsProvider(true)->Load($oAccount); + + if ($oConfig->Get('webmail', 'allow_languages_on_settings', true)) + { + $this->setSettingsFromParams($oSettings, 'Language', 'string', function ($sLanguage) use ($self) { + return $self->ValidateLanguage($sLanguage); + }); + } + else + { + $oSettings->SetConf('Language', $this->ValidateLanguage($oConfig->Get('webmail', 'language', 'en'))); + } + + if ($this->GetCapa(false, false, Enumerations\Capa::THEMES, $oAccount)) + { + $this->setSettingsFromParams($oSettingsLocal, 'Theme', 'string', function ($sTheme) use ($self) { + return $self->ValidateTheme($sTheme); + }); + } + else + { + $oSettingsLocal->SetConf('Theme', $this->ValidateTheme($oConfig->Get('webmail', 'theme', 'Default'))); + } + + $this->setSettingsFromParams($oSettings, 'MPP', 'int', function ($iValue) { + return (int) (\in_array($iValue, array(10, 20, 30, 50, 100, 150, 200, 300)) ? $iValue : 20); + }); + + $this->setSettingsFromParams($oSettings, 'Layout', 'int', function ($iValue) { + return (int) (\in_array((int) $iValue, array(Enumerations\Layout::NO_PREVIW, + Enumerations\Layout::SIDE_PREVIEW, Enumerations\Layout::BOTTOM_PREVIEW)) ? + $iValue : Enumerations\Layout::SIDE_PREVIEW); + }); + + $this->setSettingsFromParams($oSettings, 'EditorDefaultType', 'string'); + $this->setSettingsFromParams($oSettings, 'ShowImages', 'bool'); + $this->setSettingsFromParams($oSettings, 'ContactsAutosave', 'bool'); + $this->setSettingsFromParams($oSettings, 'DesktopNotifications', 'bool'); + $this->setSettingsFromParams($oSettings, 'SoundNotification', 'bool'); + $this->setSettingsFromParams($oSettings, 'UseCheckboxesInList', 'bool'); + $this->setSettingsFromParams($oSettings, 'AllowDraftAutosave', 'bool'); + $this->setSettingsFromParams($oSettings, 'AutoLogout', 'int'); + + $this->setSettingsFromParams($oSettings, 'EnableTwoFactor', 'bool'); + + $this->setSettingsFromParams($oSettingsLocal, 'UseThreads', 'bool'); + $this->setSettingsFromParams($oSettingsLocal, 'ReplySameFolder', 'bool'); + + return $this->DefaultResponse(__FUNCTION__, + $this->SettingsProvider()->Save($oAccount, $oSettings) && + $this->SettingsProvider(true)->Save($oAccount, $oSettingsLocal)); + } + + public function DoQuota() : array + { + $oAccount = $this->initMailClientConnection(); + + if (!$this->GetCapa(false, false, Enumerations\Capa::QUOTA, $oAccount)) + { + return $this->DefaultResponse(__FUNCTION__, array(0, 0, 0, 0)); + } + + try + { + $aQuota = $this->MailClient()->Quota(); + } + catch (\Throwable $oException) + { + throw new Exceptions\ClientException(Notifications::MailServerError, $oException); + } + + return $this->DefaultResponse(__FUNCTION__, $aQuota); + } + + public function DoSuggestions() : array + { + $oAccount = $this->getAccountFromToken(); + + $sQuery = \trim($this->GetActionParam('Query', '')); + $iLimit = (int) $this->Config()->Get('contacts', 'suggestions_limit', 20); + + $aResult = array(); + + $this->Plugins()->RunHook('ajax.suggestions-input-parameters', array(&$sQuery, &$iLimit, $oAccount)); + + $iLimit = (int) $iLimit; + if (5 > $iLimit) + { + $iLimit = 5; + } + + $this->Plugins()->RunHook('ajax.suggestions-pre', array(&$aResult, $sQuery, $oAccount, $iLimit)); + + if ($iLimit > \count($aResult) && 0 < \strlen($sQuery)) + { + try + { + // Address Book + $oAddressBookProvider = $this->AddressBookProvider($oAccount); + if ($oAddressBookProvider && $oAddressBookProvider->IsActive()) + { + $aSuggestions = $oAddressBookProvider->GetSuggestions($oAccount->ParentEmailHelper(), $sQuery, $iLimit); + if (0 === \count($aResult)) + { + $aResult = $aSuggestions; + } + else + { + $aResult = \array_merge($aResult, $aSuggestions); + } + } + } + catch (\Throwable $oException) + { + $this->Logger()->WriteException($oException); + } + } + + if ($iLimit > \count($aResult) && 0 < \strlen($sQuery)) + { + $oSuggestionsProvider = $this->SuggestionsProvider(); + if ($oSuggestionsProvider && $oSuggestionsProvider->IsActive()) + { + $aSuggestionsProviderResult = $oSuggestionsProvider->Process($oAccount, $sQuery, $iLimit); + if (\is_array($aSuggestionsProviderResult) && 0 < \count($aSuggestionsProviderResult)) + { + $aResult = \array_merge($aResult, $aSuggestionsProviderResult); + } + } + + } + + $aResult = Utils::RemoveSuggestionDuplicates($aResult); + if ($iLimit < \count($aResult)) + { + $aResult = \array_slice($aResult, 0, $iLimit); + } + + $this->Plugins()->RunHook('ajax.suggestions-post', array(&$aResult, $sQuery, $oAccount, $iLimit)); + + $aResult = Utils::RemoveSuggestionDuplicates($aResult); + if ($iLimit < \count($aResult)) + { + $aResult = \array_slice($aResult, 0, $iLimit); + } + + return $this->DefaultResponse(__FUNCTION__, $aResult); + } + + public function DoComposeUploadExternals() : array + { + $oAccount = $this->getAccountFromToken(); + + $mResult = false; + $aExternals = $this->GetActionParam('Externals', array()); + if (\is_array($aExternals) && 0 < \count($aExternals)) + { + $oHttp = \MailSo\Base\Http::SingletonInstance(); + + $mResult = array(); + foreach ($aExternals as $sUrl) + { + $mResult[$sUrl] = ''; + + $sTempName = \md5($sUrl); + + $iCode = 0; + $sContentType = ''; + + $rFile = $this->FilesProvider()->GetFile($oAccount, $sTempName, 'wb+'); + if ($rFile && $oHttp->SaveUrlToFile($sUrl, $rFile, '', $sContentType, $iCode, $this->Logger(), 60, + $this->Config()->Get('labs', 'curl_proxy', ''), $this->Config()->Get('labs', 'curl_proxy_auth', ''))) + { + $mResult[$sUrl] = $sTempName; + } + + if (\is_resource($rFile)) + { + \fclose($rFile); + } + } + } + + return $this->DefaultResponse(__FUNCTION__, $mResult); + } + + public function DoClearUserBackground() : array + { + $oAccount = $this->getAccountFromToken(); + + if (!$this->GetCapa(false, false, Enumerations\Capa::USER_BACKGROUND, $oAccount)) + { + return $this->FalseResponse(__FUNCTION__); + } + + $oSettings = $this->SettingsProvider()->Load($oAccount); + if ($oAccount && $oSettings) + { + $this->StorageProvider()->Clear($oAccount, + Providers\Storage\Enumerations\StorageType::CONFIG, + 'background' + ); + + $oSettings->SetConf('UserBackgroundName', ''); + $oSettings->SetConf('UserBackgroundHash', ''); + } + + return $this->DefaultResponse(__FUNCTION__, $oAccount && $oSettings ? + $this->SettingsProvider()->Save($oAccount, $oSettings) : false); + } + +} diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Model/Domain.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Model/Domain.php index 5cb635245..2cf460a42 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Model/Domain.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Model/Domain.php @@ -4,7 +4,7 @@ namespace RainLoop\Model; use MailSo\Net\Enumerations\ConnectionSecurityType; -class Domain +class Domain implements \JsonSerializable { const DEFAULT_FORWARDED_FLAG = '$Forwarded'; @@ -401,20 +401,45 @@ class Domain return true; } - public function ToSimpleJSON(bool $bAjax = false) : array + public function ToSimpleJSON() : array { return array( - 'Name' => $bAjax ? \MailSo\Base\Utils::IdnToUtf8($this->Name()) : $this->Name(), - 'IncHost' => $bAjax ? \MailSo\Base\Utils::IdnToUtf8($this->IncHost()) : $this->IncHost(), + 'Name' => $this->Name(), + 'IncHost' => $this->IncHost(), 'IncPort' => $this->IncPort(), 'IncSecure' => $this->IncSecure(), 'IncShortLogin' => $this->IncShortLogin(), 'UseSieve' => $this->UseSieve(), - 'SieveHost' => $bAjax ? \MailSo\Base\Utils::IdnToUtf8($this->SieveHost()) : $this->SieveHost(), + 'SieveHost' => $this->SieveHost(), 'SievePort' => $this->SievePort(), 'SieveSecure' => $this->SieveSecure(), 'SieveAllowRaw' => $this->SieveAllowRaw(), - 'OutHost' => $bAjax ? \MailSo\Base\Utils::IdnToUtf8($this->OutHost()) : $this->OutHost(), + 'OutHost' => $this->OutHost(), + 'OutPort' => $this->OutPort(), + 'OutSecure' => $this->OutSecure(), + 'OutShortLogin' => $this->OutShortLogin(), + 'OutAuth' => $this->OutAuth(), + 'OutUsePhpMail' => $this->OutUsePhpMail(), + 'WhiteList' => $this->WhiteList(), + 'AliasName' => $this->AliasName() + ); + } + + public function jsonSerialize() + { + return array( +// '@Object' => 'Object/Domain', + 'Name' => \MailSo\Base\Utils::IdnToUtf8($this->Name()), + 'IncHost' => \MailSo\Base\Utils::IdnToUtf8($this->IncHost()), + 'IncPort' => $this->IncPort(), + 'IncSecure' => $this->IncSecure(), + 'IncShortLogin' => $this->IncShortLogin(), + 'UseSieve' => $this->UseSieve(), + 'SieveHost' => \MailSo\Base\Utils::IdnToUtf8($this->SieveHost()), + 'SievePort' => $this->SievePort(), + 'SieveSecure' => $this->SieveSecure(), + 'SieveAllowRaw' => $this->SieveAllowRaw(), + 'OutHost' => \MailSo\Base\Utils::IdnToUtf8($this->OutHost()), 'OutPort' => $this->OutPort(), 'OutSecure' => $this->OutSecure(), 'OutShortLogin' => $this->OutShortLogin(), diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Model/Identity.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Model/Identity.php index 9dda1c114..54023f46b 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Model/Identity.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Model/Identity.php @@ -2,7 +2,7 @@ namespace RainLoop\Model; -class Identity +class Identity implements \JsonSerializable { /** * @var string @@ -111,11 +111,25 @@ class Identity return false; } - public function ToSimpleJSON(bool $bAjax = false) : array + public function ToSimpleJSON() : array { return array( 'Id' => $this->Id(), - 'Email' => $bAjax ? \MailSo\Base\Utils::IdnToUtf8($this->Email()) : $this->Email(), + 'Email' => $this->Email(), + 'Name' => $this->Name(), + 'ReplyTo' => $this->ReplyTo(), + 'Bcc' => $this->Bcc(), + 'Signature' => $this->Signature(), + 'SignatureInsertBefore' => $this->SignatureInsertBefore() + ); + } + + public function jsonSerialize() + { + return array( +// '@Object' => 'Object/Identity', + 'Id' => $this->Id(), + 'Email' => \MailSo\Base\Utils::IdnToUtf8($this->Email()), 'Name' => $this->Name(), 'ReplyTo' => $this->ReplyTo(), 'Bcc' => $this->Bcc(), diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Model/Template.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Model/Template.php index c710a44bb..951225431 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Model/Template.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Model/Template.php @@ -2,7 +2,7 @@ namespace RainLoop\Model; -class Template +class Template implements \JsonSerializable { /** * @var string @@ -66,32 +66,32 @@ class Template return false; } - public function ToSimpleJSON(bool $bAjax = false) : array + public function ToSimpleJSON() : array + { + return array( + 'ID' => $this->Id(), + 'Name' => $this->Name(), + 'Body' => $this->Body() + ); + } + + public function jsonSerialize() { $sBody = $this->Body(); $bPopulated = true; - - if ($bAjax && $bPopulated && !$this->bPopulateAlways) - { - if (1024 * 5 < \strlen($sBody) || true) - { + if ($bPopulated && !$this->bPopulateAlways) { + if (1024 * 5 < \strlen($sBody) || true) { $bPopulated = false; $sBody = ''; } } - - $aResult = array( + return array( +// '@Object' => 'Object/Template', 'ID' => $this->Id(), 'Name' => $this->Name(), - 'Body' => $sBody + 'Body' => $sBody, + 'Populated' => $bPopulated ); - - if ($bAjax) - { - $aResult['Populated'] = $bPopulated; - } - - return $aResult; } public function GenerateID() : bool diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Providers/AddressBook/Classes/Contact.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Providers/AddressBook/Classes/Contact.php index 7a355f5ec..75117998c 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Providers/AddressBook/Classes/Contact.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Providers/AddressBook/Classes/Contact.php @@ -7,7 +7,7 @@ use RainLoop\Providers\AddressBook\Classes\Property ; -class Contact +class Contact implements \JsonSerializable { /** * @var string @@ -636,4 +636,19 @@ class Contact return true; } + + public function jsonSerialize() + { + $properties = array(); + foreach ($this->Properties as $property) { + $properties[] = $property->jsonSerialize(); + } + return array( + '@Object' => 'Object/Contact', + 'display' => \MailSo\Base\Utils::Utf8Clear($this->Display), + 'readOnly' => $this->ReadOnly, + 'IdPropertyFromSearch' => $this->IdPropertyFromSearch, + 'properties' => $properties + ); + } } diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Providers/AddressBook/Classes/Property.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Providers/AddressBook/Classes/Property.php index 4b26c4bb9..316421c82 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Providers/AddressBook/Classes/Property.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Providers/AddressBook/Classes/Property.php @@ -4,7 +4,7 @@ namespace RainLoop\Providers\AddressBook\Classes; use RainLoop\Providers\AddressBook\Enumerations\PropertyType; -class Property +class Property implements \JsonSerializable { /** * @var int @@ -145,4 +145,20 @@ class Property } } } + + public function jsonSerialize() + { + // Simple hack + if ($this && $this->IsWeb()) + { + $this->Value = \preg_replace('/(skype|ftp|http[s]?)\\\:\/\//i', '$1://', $this->Value); + } + return array( + '@Object' => 'Object/Property', + 'IdProperty' => $this->IdProperty, + 'Type' => $this->Type, + 'TypeStr' => $this->TypeStr, + 'Value' => \MailSo\Base\Utils::Utf8Clear($this->Value) + )); + } } diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Providers/AddressBook/Classes/Tag.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Providers/AddressBook/Classes/Tag.php index 83c2b7cc1..c5973a680 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Providers/AddressBook/Classes/Tag.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Providers/AddressBook/Classes/Tag.php @@ -2,7 +2,7 @@ namespace RainLoop\Providers\AddressBook\Classes; -class Tag +class Tag implements \JsonSerializable { /** * @var string @@ -30,4 +30,14 @@ class Tag $this->Name = ''; $this->ReadOnly = false; } + + public function jsonSerialize() + { + return array( + '@Object' => 'Object/Tag', + 'id' => $this->IdContactTag, + 'name' => \MailSo\Base\Utils::Utf8Clear($mResponse->Name), + 'readOnly' => $this->ReadOnly + ); + } } diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Providers/Filters/Classes/Filter.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Providers/Filters/Classes/Filter.php index b4b8a9344..0e70d480b 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Providers/Filters/Classes/Filter.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Providers/Filters/Classes/Filter.php @@ -2,7 +2,7 @@ namespace RainLoop\Providers\Filters\Classes; -class Filter +class Filter implements \JsonSerializable { /** * @var string @@ -166,11 +166,6 @@ class Filter return $this->bKeep; } - public function serializeToJson() : string - { - return \json_encode($this->ToSimpleJSON()); - } - public function unserializeFromJson(string $sFilterJson) { $aFilterJson = \json_decode(\trim($sFilterJson), true); @@ -210,22 +205,14 @@ class Filter return true; } - public function ToSimpleJSON(bool $bAjax = false) : array + public function jsonSerialize() { - $aConditions = array(); - foreach ($this->Conditions() as $oItem) - { - if ($oItem) - { - $aConditions[] = $oItem->ToSimpleJSON($bAjax); - } - } - return array( + '@Object' => 'Object/Filter', 'ID' => $this->ID(), 'Enabled' => $this->Enabled(), 'Name' => $this->Name(), - 'Conditions' => $aConditions, + 'Conditions' => $this->Conditions(), 'ConditionsType' => $this->ConditionsType(), 'ActionType' => $this->ActionType(), 'ActionValue' => $this->ActionValue(), diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Providers/Filters/Classes/FilterCondition.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Providers/Filters/Classes/FilterCondition.php index 3f7623253..396d4f913 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Providers/Filters/Classes/FilterCondition.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Providers/Filters/Classes/FilterCondition.php @@ -2,7 +2,7 @@ namespace RainLoop\Providers\Filters\Classes; -class FilterCondition +class FilterCondition implements \JsonSerializable { /** * @var string @@ -71,16 +71,6 @@ class FilterCondition return true; } - public function ToSimpleJSON(bool $bAjax = false) : array - { - return array( - 'Field' => $this->Field(), - 'Type' => $this->Type(), - 'Value' => $this->Value(), - 'ValueSecond' => $this->ValueSecond() - ); - } - public static function CollectionFromJSON(array $aCollection) : array { $aResult = array(); @@ -97,4 +87,15 @@ class FilterCondition } return $aResult; } + + public function jsonSerialize() + { + return array( +// '@Object' => 'Object/FilterCondition', + 'Field' => $this->Field(), + 'Type' => $this->Type(), + 'Value' => $this->Value(), + 'ValueSecond' => $this->ValueSecond() + ); + } } diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Providers/Filters/SieveStorage.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Providers/Filters/SieveStorage.php index 7836f13af..45877fdd5 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Providers/Filters/SieveStorage.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Providers/Filters/SieveStorage.php @@ -472,7 +472,7 @@ class SieveStorage implements \RainLoop\Providers\Filters\FiltersInterface $aData[] = '/*'; $aData[] = 'BEGIN:FILTER:'.$oItem->ID(); $aData[] = 'BEGIN:HEADER'; - $aData[] = \chunk_split(\base64_encode($oItem->serializeToJson()), 74, $sNL).'END:HEADER'; + $aData[] = \chunk_split(\base64_encode(\json_encode($oItem)), 74, $sNL).'END:HEADER'; $aData[] = '*/'; $aData[] = $oItem->Enabled() ? '' : '/* @Filter is disabled '; $aData[] = $this->filterToSieveScript($oItem, $aCapa);