More splitting of RainLoop/Actions.php

This commit is contained in:
djmaze 2020-10-20 12:23:53 +02:00
parent 2470f1add6
commit 0e3275599e
12 changed files with 1524 additions and 1599 deletions

File diff suppressed because it is too large Load diff

View file

@ -214,4 +214,26 @@ trait Accounts
));
}
private function SetIdentities(\RainLoop\Model\Account $oAccount, array $aIdentities = array()) : bool
{
$bAllowIdentities = $this->GetCapa(false, false, Capa::IDENTITIES, $oAccount);
$aResult = array();
foreach ($aIdentities as $oItem)
{
if (!$bAllowIdentities && $oItem && !$oItem->IsAccountIdentities())
{
continue;
}
$aResult[] = $oItem->ToSimpleJSON();
}
return $this->StorageProvider(true)->Put($oAccount,
\RainLoop\Providers\Storage\Enumerations\StorageType::CONFIG,
'identities',
\json_encode($aResult)
);
}
}

View file

@ -1005,4 +1005,16 @@ trait Admin
return $this->DefaultResponse(__FUNCTION__, true);
}
private function HasOneOfActionParams(array $aKeys) : bool
{
foreach ($aKeys as $sKey)
{
if ($this->HasActionParam($sKey))
{
return true;
}
}
return false;
}
}

View file

@ -173,4 +173,90 @@ trait Contacts
));
}
public function UploadContacts() : array
{
$oAccount = $this->getAccountFromToken();
$mResponse = false;
$aFile = $this->GetActionParam('File', null);
$iError = $this->GetActionParam('Error', \RainLoop\Enumerations\UploadError::UNKNOWN);
if ($oAccount && UPLOAD_ERR_OK === $iError && \is_array($aFile))
{
$sSavedName = 'upload-post-'.\md5($aFile['name'].$aFile['tmp_name']);
if (!$this->FilesProvider()->MoveUploadedFile($oAccount, $sSavedName, $aFile['tmp_name']))
{
$iError = \RainLoop\Enumerations\UploadError::ON_SAVING;
}
else
{
\ini_set('auto_detect_line_endings', true);
$mData = $this->FilesProvider()->GetFile($oAccount, $sSavedName);
if ($mData)
{
$sFileStart = \fread($mData, 20);
\rewind($mData);
if (false !== $sFileStart)
{
$sFileStart = \trim($sFileStart);
if (false !== \strpos($sFileStart, 'BEGIN:VCARD'))
{
$mResponse = $this->importContactsFromVcfFile($oAccount, $mData);
}
else if (false !== \strpos($sFileStart, ',') || false !== \strpos($sFileStart, ';'))
{
$mResponse = $this->importContactsFromCsvFile($oAccount, $mData, $sFileStart);
}
}
}
if (\is_resource($mData))
{
\fclose($mData);
}
unset($mData);
$this->FilesProvider()->Clear($oAccount, $sSavedName);
\ini_set('auto_detect_line_endings', false);
}
}
if (UPLOAD_ERR_OK !== $iError)
{
$iClientError = \RainLoop\Enumerations\UploadClientError::NORMAL;
$sError = $this->getUploadErrorMessageByCode($iError, $iClientError);
if (!empty($sError))
{
return $this->FalseResponse(__FUNCTION__, $iClientError, $sError);
}
}
return $this->DefaultResponse(__FUNCTION__, $mResponse);
}
protected function getContactsSyncData(\RainLoop\Model\Account $oAccount) : ?array
{
$mResult = null;
$sData = $this->StorageProvider()->Get($oAccount,
\RainLoop\Providers\Storage\Enumerations\StorageType::CONFIG,
'contacts_sync'
);
if (!empty($sData)) {
$aData = \RainLoop\Utils::DecodeKeyValues($sData);
if ($aData) {
$mResult = array(
'Enable' => isset($aData['Enable']) ? !!$aData['Enable'] : false,
'Url' => isset($aData['Url']) ? \trim($aData['Url']) : '',
'User' => isset($aData['User']) ? \trim($aData['User']) : '',
'Password' => isset($aData['Password']) ? $aData['Password'] : ''
);
}
}
return $mResult;
}
}

View file

@ -6,6 +6,11 @@ use \RainLoop\Enumerations\Capa;
trait Filters
{
/**
* @var \RainLoop\Providers\Filters
*/
private $oFiltersProvider;
/**
* @throws \MailSo\Base\Exceptions\Exception
*/
@ -71,4 +76,11 @@ trait Filters
$aFilters, $sRaw, $bRawIsActive));
}
protected function FiltersProvider() : \RainLoop\Providers\Filters
{
if (!$this->oFiltersProvider) {
$this->oFiltersProvider = new \RainLoop\Providers\Filters($this->fabrica('filters'));
}
return $this->oFiltersProvider;
}
}

View file

@ -448,4 +448,161 @@ trait Folders
$this->SettingsProvider(true)->Save($oAccount, $oSettingsLocal));
}
private function recFoldersNames(\MailSo\Mail\FolderCollection $oFolders) : array
{
$aResult = array();
if ($oFolders)
{
foreach ($oFolders as $oFolder)
{
$aResult[] = $oFolder->FullNameRaw()."|".
implode("|", $oFolder->Flags()).($oFolder->IsSubscribed() ? '1' : '0');
$oSub = $oFolder->SubFolders();
if ($oSub && 0 < $oSub->Count())
{
$aResult = \array_merge($aResult, $this->recFoldersNames($oSub));
}
}
}
return $aResult;
}
private function recFoldersTypes(\RainLoop\Model\Account $oAccount, \MailSo\Mail\FolderCollection $oFolders, array &$aResult, bool $bListFolderTypes = true) : void
{
if ($oFolders && $oFolders->Count())
{
if ($bListFolderTypes)
{
foreach ($oFolders as $oFolder)
{
$iFolderListType = $oFolder->GetFolderListType();
if (!isset($aResult[$iFolderListType]) && \in_array($iFolderListType, array(
FolderType::INBOX,
FolderType::SENT,
FolderType::DRAFTS,
FolderType::JUNK,
FolderType::TRASH,
FolderType::ALL
)))
{
$aResult[$iFolderListType] = $oFolder->FullNameRaw();
}
}
foreach ($oFolders as $oFolder)
{
$oSub = $oFolder->SubFolders();
if ($oSub && 0 < $oSub->Count())
{
$this->recFoldersTypes($oAccount, $oSub, $aResult, true);
}
}
}
$aMap = $this->systemFoldersNames($oAccount);
foreach ($oFolders as $oFolder)
{
$sName = $oFolder->Name();
$sFullName = $oFolder->FullName();
if (isset($aMap[$sName]) || isset($aMap[$sFullName]))
{
$iFolderType = isset($aMap[$sName]) ? $aMap[$sName] : $aMap[$sFullName];
if (!isset($aResult[$iFolderType]) && \in_array($iFolderType, array(
FolderType::INBOX,
FolderType::SENT,
FolderType::DRAFTS,
FolderType::JUNK,
FolderType::TRASH,
FolderType::ALL
)))
{
$aResult[$iFolderType] = $oFolder->FullNameRaw();
}
}
}
foreach ($oFolders as $oFolder)
{
$oSub = $oFolder->SubFolders();
if ($oSub && 0 < $oSub->Count())
{
$this->recFoldersTypes($oAccount, $oSub, $aResult, false);
}
}
}
}
/**
* @staticvar array $aCache
*/
private function systemFoldersNames(\RainLoop\Model\Account $oAccount) : array
{
static $aCache = null;
if (null === $aCache)
{
$aCache = array(
'Sent' => FolderType::SENT,
'Send' => FolderType::SENT,
'Outbox' => FolderType::SENT,
'Out box' => FolderType::SENT,
'Sent Item' => FolderType::SENT,
'Sent Items' => FolderType::SENT,
'Send Item' => FolderType::SENT,
'Send Items' => FolderType::SENT,
'Sent Mail' => FolderType::SENT,
'Sent Mails' => FolderType::SENT,
'Send Mail' => FolderType::SENT,
'Send Mails' => FolderType::SENT,
'Drafts' => FolderType::DRAFTS,
'Draft' => FolderType::DRAFTS,
'Draft Mail' => FolderType::DRAFTS,
'Draft Mails' => FolderType::DRAFTS,
'Drafts Mail' => FolderType::DRAFTS,
'Drafts Mails' => FolderType::DRAFTS,
'Junk E-mail' => FolderType::JUNK,
'Spam' => FolderType::JUNK,
'Spams' => FolderType::JUNK,
'Junk' => FolderType::JUNK,
'Bulk Mail' => FolderType::JUNK,
'Bulk Mails' => FolderType::JUNK,
'Deleted Items' => FolderType::TRASH,
'Trash' => FolderType::TRASH,
'Deleted' => FolderType::TRASH,
'Bin' => FolderType::TRASH,
'Archive' => FolderType::ALL,
'Archives' => FolderType::ALL,
'All' => FolderType::ALL,
'All Mail' => FolderType::ALL,
'All Mails' => FolderType::ALL,
);
$aNewCache = array();
foreach ($aCache as $sKey => $iType)
{
$aNewCache[$sKey] = $iType;
$aNewCache[\str_replace(' ', '', $sKey)] = $iType;
}
$aCache = $aNewCache;
$this->Plugins()->RunHook('filter.system-folders-names', array($oAccount, &$aCache));
}
return $aCache;
}
}

View file

@ -4,6 +4,7 @@ namespace RainLoop\Actions;
use \RainLoop\Enumerations\Capa;
use \RainLoop\Exceptions\ClientException;
use \RainLoop\Model\Account;
use \RainLoop\Notifications;
trait Messages
@ -297,7 +298,7 @@ trait Messages
}
}
}
catch (Exceptions\ClientException $oException)
catch (ClientException $oException)
{
throw $oException;
}
@ -398,7 +399,7 @@ trait Messages
}
}
}
catch (Exceptions\ClientException $oException)
catch (ClientException $oException)
{
throw $oException;
}
@ -698,4 +699,456 @@ trait Messages
return $this->DefaultResponse(__FUNCTION__, $mResult);
}
/**
* @throws \RainLoop\Exceptions\ClientException
* @throws \MailSo\Net\Exceptions\ConnectionException
*/
private function smtpSendMessage(Account $oAccount, \MailSo\Mime\Message $oMessage,
/*resource*/ &$rMessageStream, int &$iMessageStreamSize, bool $bDsn = false, bool $bAddHiddenRcpt = true)
{
$oRcpt = $oMessage->GetRcpt();
if ($oRcpt && 0 < $oRcpt->Count())
{
$this->Plugins()->RunHook('filter.smtp-message-stream',
array($oAccount, &$rMessageStream, &$iMessageStreamSize));
$this->Plugins()->RunHook('filter.message-rcpt', array($oAccount, $oRcpt));
try
{
$oFrom = $oMessage->GetFrom();
$sFrom = $oFrom instanceof \MailSo\Mime\Email ? $oFrom->GetEmail() : '';
$sFrom = empty($sFrom) ? $oAccount->Email() : $sFrom;
$this->Plugins()->RunHook('filter.smtp-from', array($oAccount, $oMessage, &$sFrom));
$aHiddenRcpt = array();
if ($bAddHiddenRcpt)
{
$this->Plugins()->RunHook('filter.smtp-hidden-rcpt', array($oAccount, $oMessage, &$aHiddenRcpt));
}
$bUsePhpMail = $oAccount->Domain()->OutUsePhpMail();
$oSmtpClient = new \MailSo\Smtp\SmtpClient();
$oSmtpClient->SetLogger($this->Logger());
$oSmtpClient->SetTimeOuts(10, (int) \RainLoop\Api::Config()->Get('labs', 'smtp_timeout', 60));
$bLoggined = $oAccount->OutConnectAndLoginHelper(
$this->Plugins(), $oSmtpClient, $this->Config(), null, $bUsePhpMail
);
if ($bUsePhpMail)
{
if (\MailSo\Base\Utils::FunctionExistsAndEnabled('mail'))
{
$aToCollection = $oMessage->GetTo();
if ($aToCollection && $oFrom)
{
$sRawBody = \stream_get_contents($rMessageStream);
if (!empty($sRawBody))
{
$sMailTo = \trim($aToCollection->ToString(true));
$sMailSubject = \trim($oMessage->GetSubject());
$sMailSubject = 0 === \strlen($sMailSubject) ? '' : \MailSo\Base\Utils::EncodeUnencodedValue(
\MailSo\Base\Enumerations\Encoding::BASE64_SHORT, $sMailSubject);
$sMailHeaders = $sMailBody = '';
list($sMailHeaders, $sMailBody) = \explode("\r\n\r\n", $sRawBody, 2);
unset($sRawBody);
if ($this->Config()->Get('labs', 'mail_func_clear_headers', true))
{
$sMailHeaders = \MailSo\Base\Utils::RemoveHeaderFromHeaders($sMailHeaders, array(
\MailSo\Mime\Enumerations\Header::TO_,
\MailSo\Mime\Enumerations\Header::SUBJECT
));
}
if ($this->Config()->Get('debug', 'enable', false))
{
$this->Logger()->WriteDump(array(
$sMailTo, $sMailSubject, $sMailBody, $sMailHeaders
));
}
$bR = $this->Config()->Get('labs', 'mail_func_additional_parameters', false) ?
\mail($sMailTo, $sMailSubject, $sMailBody, $sMailHeaders, '-f'.$oFrom->GetEmail()) :
\mail($sMailTo, $sMailSubject, $sMailBody, $sMailHeaders);
if (!$bR)
{
throw new ClientException(Notifications::CantSendMessage);
}
}
}
}
else
{
throw new ClientException(Notifications::CantSendMessage);
}
}
else if ($oSmtpClient->IsConnected())
{
if (!empty($sFrom))
{
$oSmtpClient->MailFrom($sFrom, '', $bDsn);
}
foreach ($oRcpt as /* @var $oEmail \MailSo\Mime\Email */ $oEmail)
{
$oSmtpClient->Rcpt($oEmail->GetEmail(), $bDsn);
}
if ($bAddHiddenRcpt && \is_array($aHiddenRcpt) && 0 < \count($aHiddenRcpt))
{
foreach ($aHiddenRcpt as $sEmail)
{
if (\preg_match('/^[^@\s]+@[^@\s]+$/', $sEmail))
{
$oSmtpClient->Rcpt($sEmail);
}
}
}
$oSmtpClient->DataWithStream($rMessageStream);
if ($bLoggined)
{
$oSmtpClient->Logout();
}
$oSmtpClient->Disconnect();
}
}
catch (\MailSo\Net\Exceptions\ConnectionException $oException)
{
if ($this->Config()->Get('labs', 'smtp_show_server_errors'))
{
throw new ClientException(Notifications::ClientViewError, $oException);
}
else
{
throw new ClientException(Notifications::ConnectionError, $oException);
}
}
catch (\MailSo\Smtp\Exceptions\LoginException $oException)
{
throw new ClientException(Notifications::AuthError, $oException);
}
catch (\Throwable $oException)
{
if ($this->Config()->Get('labs', 'smtp_show_server_errors'))
{
throw new ClientException(Notifications::ClientViewError, $oException);
}
else
{
throw $oException;
}
}
}
else
{
throw new ClientException(Notifications::InvalidRecipients);
}
}
private function messageSetFlag(string $sActionFunction, string $sResponseFunction) : array
{
$this->initMailClientConnection();
$sFolder = $this->GetActionParam('Folder', '');
$bSetAction = '1' === (string) $this->GetActionParam('SetAction', '0');
$aUids = \explode(',', (string) $this->GetActionParam('Uids', ''));
$aFilteredUids = \array_filter($aUids, function (&$sUid) {
$sUid = (int) \trim($sUid);
return 0 < $sUid;
});
try
{
$this->MailClient()->{$sActionFunction}($sFolder, $aFilteredUids, true, $bSetAction, true);
}
catch (\Throwable $oException)
{
throw new ClientException(Notifications::MailServerError, $oException);
}
return $this->TrueResponse($sResponseFunction);
}
private function getDecodedClientRawKeyValue(string $sRawKey, ?int $iLenCache = null) : ?array
{
if (!empty($sRawKey))
{
$sRawKey = \MailSo\Base\Utils::UrlSafeBase64Decode($sRawKey);
$aValues = explode("\x0", $sRawKey);
if (null === $iLenCache || $iLenCache === count($aValues))
{
return $aValues;
}
}
return null;
}
private function deleteMessageAttachmnets(Account $oAccount) : void
{
$aAttachments = $this->GetActionParam('Attachments', null);
if (\is_array($aAttachments))
{
foreach (\array_keys($aAttachments) as $sTempName)
{
if ($this->FilesProvider()->FileExists($oAccount, $sTempName))
{
$this->FilesProvider()->Clear($oAccount, $sTempName);
}
}
}
}
/**
* @return MailSo\Cache\CacheClient|null
*/
private function cacherForUids()
{
$oAccount = $this->getAccountFromToken(false);
return ($this->Config()->Get('cache', 'enable', true) &&
$this->Config()->Get('cache', 'server_uids', false)) ? $this->Cacher($oAccount) : null;
}
/**
* @return MailSo\Cache\CacheClient|null
*/
private function cacherForThreads()
{
$oAccount = $this->getAccountFromToken(false);
return !!$this->Config()->Get('labs', 'use_imap_thread', false) ? $this->Cacher($oAccount) : null;
}
private function buildReadReceiptMessage(Account $oAccount) : \MailSo\Mime\Message
{
$sReadReceipt = $this->GetActionParam('ReadReceipt', '');
$sSubject = $this->GetActionParam('Subject', '');
$sText = $this->GetActionParam('Text', '');
$oIdentity = $this->GetIdentityByID($oAccount, '', true);
if (empty($sReadReceipt) || empty($sSubject) || empty($sText) || !$oIdentity)
{
throw new ClientException(Notifications::UnknownError);
}
$oMessage = new \MailSo\Mime\Message();
if (!$this->Config()->Get('security', 'hide_x_mailer_header', true))
{
$oMessage->SetXMailer('SnappyMail/'.APP_VERSION);
}
$oMessage->SetFrom(new \MailSo\Mime\Email($oIdentity->Email(), $oIdentity->Name()));
$oFrom = $oMessage->GetFrom();
$oMessage->RegenerateMessageId($oFrom ? $oFrom->GetDomain() : '');
$sReplyTo = $oIdentity->ReplyTo();
if (!empty($sReplyTo))
{
$oReplyTo = new \MailSo\Mime\EmailCollection($sReplyTo);
if ($oReplyTo && $oReplyTo->Count())
{
$oMessage->SetReplyTo($oReplyTo);
}
}
$oMessage->SetSubject($sSubject);
$oToEmails = new \MailSo\Mime\EmailCollection($sReadReceipt);
if ($oToEmails && $oToEmails->Count())
{
$oMessage->SetTo($oToEmails);
}
$this->Plugins()->RunHook('filter.read-receipt-message-plain', array($oAccount, $oMessage, &$sText));
$oMessage->AddText($sText, false);
$this->Plugins()->RunHook('filter.build-read-receipt-message', array($oMessage, $oAccount));
return $oMessage;
}
private function buildMessage(Account $oAccount, bool $bWithDraftInfo = true) : \MailSo\Mime\Message
{
$sIdentityID = $this->GetActionParam('IdentityID', '');
$sTo = $this->GetActionParam('To', '');
$sCc = $this->GetActionParam('Cc', '');
$sBcc = $this->GetActionParam('Bcc', '');
$sReplyTo = $this->GetActionParam('ReplyTo', '');
$sSubject = $this->GetActionParam('Subject', '');
$bTextIsHtml = '1' === $this->GetActionParam('TextIsHtml', '0');
$bReadReceiptRequest = '1' === $this->GetActionParam('ReadReceiptRequest', '0');
$bMarkAsImportant = '1' === $this->GetActionParam('MarkAsImportant', '0');
$sText = $this->GetActionParam('Text', '');
$aAttachments = $this->GetActionParam('Attachments', null);
$aDraftInfo = $this->GetActionParam('DraftInfo', null);
$sInReplyTo = $this->GetActionParam('InReplyTo', '');
$sReferences = $this->GetActionParam('References', '');
$oMessage = new \MailSo\Mime\Message();
if (!$this->Config()->Get('security', 'hide_x_mailer_header', true))
{
$oMessage->SetXMailer('SnappyMail/'.APP_VERSION);
} else {
$oMessage->DoesNotAddDefaultXMailer();
}
$oFromIdentity = $this->GetIdentityByID($oAccount, $sIdentityID);
if ($oFromIdentity)
{
$oMessage->SetFrom(new \MailSo\Mime\Email(
$oFromIdentity->Email(), $oFromIdentity->Name()));
}
else
{
$oMessage->SetFrom(\MailSo\Mime\Email::Parse($oAccount->Email()));
}
$oFrom = $oMessage->GetFrom();
$oMessage->RegenerateMessageId($oFrom ? $oFrom->GetDomain() : '');
if (!empty($sReplyTo))
{
$oReplyTo = new \MailSo\Mime\EmailCollection($sReplyTo);
if ($oReplyTo && 0 < $oReplyTo->Count())
{
$oMessage->SetReplyTo($oReplyTo);
}
}
if ($bReadReceiptRequest)
{
$oMessage->SetReadReceipt($oAccount->Email());
}
if ($bMarkAsImportant)
{
$oMessage->SetPriority(\MailSo\Mime\Enumerations\MessagePriority::HIGH);
}
$oMessage->SetSubject($sSubject);
$oToEmails = new \MailSo\Mime\EmailCollection($sTo);
if ($oToEmails && $oToEmails->Count())
{
$oMessage->SetTo($oToEmails);
}
$oCcEmails = new \MailSo\Mime\EmailCollection($sCc);
if ($oCcEmails && $oCcEmails->Count())
{
$oMessage->SetCc($oCcEmails);
}
$oBccEmails = new \MailSo\Mime\EmailCollection($sBcc);
if ($oBccEmails && $oBccEmails->Count())
{
$oMessage->SetBcc($oBccEmails);
}
if ($bWithDraftInfo && \is_array($aDraftInfo) && !empty($aDraftInfo[0]) && !empty($aDraftInfo[1]) && !empty($aDraftInfo[2]))
{
$oMessage->SetDraftInfo($aDraftInfo[0], $aDraftInfo[1], $aDraftInfo[2]);
}
if (0 < \strlen($sInReplyTo))
{
$oMessage->SetInReplyTo($sInReplyTo);
}
if (0 < \strlen($sReferences))
{
$oMessage->SetReferences($sReferences);
}
$aFoundedCids = array();
$mFoundDataURL = array();
$aFoundedContentLocationUrls = array();
$sTextToAdd = $bTextIsHtml ?
\MailSo\Base\HtmlUtils::BuildHtml($sText, $aFoundedCids, $mFoundDataURL, $aFoundedContentLocationUrls) : $sText;
$this->Plugins()->RunHook($bTextIsHtml ? 'filter.message-html' : 'filter.message-plain',
array($oAccount, $oMessage, &$sTextToAdd));
if ($bTextIsHtml && 0 < \strlen($sTextToAdd))
{
$sTextConverted = \MailSo\Base\HtmlUtils::ConvertHtmlToPlain($sTextToAdd);
$this->Plugins()->RunHook('filter.message-plain', array($oAccount, $oMessage, &$sTextConverted));
$oMessage->AddText($sTextConverted, false);
}
$oMessage->AddText($sTextToAdd, $bTextIsHtml);
if (\is_array($aAttachments))
{
foreach ($aAttachments as $sTempName => $aData)
{
$sFileName = (string) $aData[0];
$bIsInline = (bool) $aData[1];
$sCID = (string) $aData[2];
$sContentLocation = isset($aData[3]) ? (string) $aData[3] : '';
$rResource = $this->FilesProvider()->GetFile($oAccount, $sTempName);
if (\is_resource($rResource))
{
$iFileSize = $this->FilesProvider()->FileSize($oAccount, $sTempName);
$oMessage->Attachments()->append(
new \MailSo\Mime\Attachment($rResource, $sFileName, $iFileSize, $bIsInline,
\in_array(trim(trim($sCID), '<>'), $aFoundedCids),
$sCID, array(), $sContentLocation
)
);
}
}
}
if ($mFoundDataURL && \is_array($mFoundDataURL) && 0 < \count($mFoundDataURL))
{
foreach ($mFoundDataURL as $sCidHash => $sDataUrlString)
{
$aMatch = array();
$sCID = '<'.$sCidHash.'>';
if (\preg_match('/^data:(image\/[a-zA-Z0-9]+);base64,(.+)$/i', $sDataUrlString, $aMatch) &&
!empty($aMatch[1]) && !empty($aMatch[2]))
{
$sRaw = \MailSo\Base\Utils::Base64Decode($aMatch[2]);
$iFileSize = \strlen($sRaw);
if (0 < $iFileSize)
{
$sFileName = \preg_replace('/[^a-z0-9]+/i', '.', $aMatch[1]);
$rResource = \MailSo\Base\ResourceRegistry::CreateMemoryResourceFromString($sRaw);
$sRaw = '';
unset($sRaw);
unset($aMatch);
$oMessage->Attachments()->append(
new \MailSo\Mime\Attachment($rResource, $sFileName, $iFileSize, true, true, $sCID)
);
}
}
}
}
$this->Plugins()->RunHook('filter.build-message', array($oMessage));
return $oMessage;
}
}

View file

@ -0,0 +1,420 @@
<?php
namespace RainLoop\Actions;
trait Raw
{
/**
* @throws \MailSo\Base\Exceptions\Exception
*/
public function RawViewAsPlain() : bool
{
$this->initMailClientConnection();
$sRawKey = (string) $this->GetActionParam('RawKey', '');
$aValues = $this->getDecodedRawKeyValue($sRawKey);
$sFolder = isset($aValues['Folder']) ? $aValues['Folder'] : '';
$iUid = (int) (isset($aValues['Uid']) ? $aValues['Uid'] : 0);
$sMimeIndex = (string) (isset($aValues['MimeIndex']) ? $aValues['MimeIndex'] : '');
\header('Content-Type: text/plain', true);
return $this->MailClient()->MessageMimeStream(function ($rResource) {
if (\is_resource($rResource))
{
\MailSo\Base\Utils::FpassthruWithTimeLimitReset($rResource);
}
}, $sFolder, $iUid, true, $sMimeIndex);
}
public function RawDownload() : bool
{
return $this->rawSmart(true);
}
public function RawView() : bool
{
return $this->rawSmart(false);
}
public function RawViewThumbnail() : bool
{
return $this->rawSmart(false, true);
}
public function RawUserBackground() : bool
{
$sRawKey = (string) $this->GetActionParam('RawKey', '');
$this->verifyCacheByKey($sRawKey);
$oAccount = $this->getAccountFromToken();
$sData = $this->StorageProvider()->Get($oAccount,
\RainLoop\Providers\Storage\Enumerations\StorageType::CONFIG,
'background'
);
if (!empty($sData))
{
$aData = \json_decode($sData, true);
unset($sData);
if (!empty($aData['ContentType']) && !empty($aData['Raw']) &&
\in_array($aData['ContentType'], array('image/png', 'image/jpg', 'image/jpeg')))
{
$this->cacheByKey($sRawKey);
\header('Content-Type: '.$aData['ContentType']);
echo \base64_decode($aData['Raw']);
unset($aData);
return true;
}
}
return false;
}
public function RawPublic() : bool
{
$sRawKey = (string) $this->GetActionParam('RawKey', '');
$this->verifyCacheByKey($sRawKey);
$sHash = $sRawKey;
$sData = '';
if (!empty($sHash))
{
$sData = $this->StorageProvider()->Get(null,
\RainLoop\Providers\Storage\Enumerations\StorageType::NOBODY,
\RainLoop\KeyPathHelper::PublicFile($sHash)
);
}
$aMatch = array();
if (!empty($sData) && 0 === \strpos($sData, 'data:') &&
\preg_match('/^data:([^:]+):/', $sData, $aMatch) && !empty($aMatch[1]))
{
$sContentType = \trim($aMatch[1]);
if (\in_array($sContentType, array('image/png', 'image/jpg', 'image/jpeg')))
{
$this->cacheByKey($sRawKey);
\header('Content-Type: '.$sContentType);
echo \preg_replace('/^data:[^:]+:/', '', $sData);
unset($sData);
return true;
}
}
return false;
}
public function RawContactsVcf() : bool
{
$oAccount = $this->getAccountFromToken();
\header('Content-Type: text/x-vcard; charset=UTF-8');
\header('Content-Disposition: attachment; filename="contacts.vcf"', true);
\header('Accept-Ranges: none', true);
\header('Content-Transfer-Encoding: binary');
$this->oHttp->ServerNoCache();
return $this->AddressBookProvider($oAccount)->IsActive() ?
$this->AddressBookProvider($oAccount)->Export($oAccount->ParentEmailHelper(), 'vcf') : false;
}
public function RawContactsCsv() : bool
{
$oAccount = $this->getAccountFromToken();
\header('Content-Type: text/csv; charset=UTF-8');
\header('Content-Disposition: attachment; filename="contacts.csv"', true);
\header('Accept-Ranges: none', true);
\header('Content-Transfer-Encoding: binary');
$this->oHttp->ServerNoCache();
return $this->AddressBookProvider($oAccount)->IsActive() ?
$this->AddressBookProvider($oAccount)->Export($oAccount->ParentEmailHelper(), 'csv') : false;
}
private function rawSmart(bool $bDownload, bool $bThumbnail = false) : bool
{
$sRawKey = (string) $this->GetActionParam('RawKey', '');
$aValues = $this->getDecodedRawKeyValue($sRawKey);
$sRange = $this->Http()->GetHeader('Range');
$aMatch = array();
$sRangeStart = $sRangeEnd = '';
$bIsRangeRequest = false;
if (!empty($sRange) && 'bytes=0-' !== \strtolower($sRange) && \preg_match('/^bytes=([0-9]+)-([0-9]*)/i', \trim($sRange), $aMatch))
{
$sRangeStart = $aMatch[1];
$sRangeEnd = $aMatch[2];
$bIsRangeRequest = true;
}
$sFolder = isset($aValues['Folder']) ? $aValues['Folder'] : '';
$iUid = isset($aValues['Uid']) ? (int) $aValues['Uid'] : 0;
$sMimeIndex = isset($aValues['MimeIndex']) ? (string) $aValues['MimeIndex'] : '';
$sContentTypeIn = isset($aValues['MimeType']) ? (string) $aValues['MimeType'] : '';
$sFileNameIn = isset($aValues['FileName']) ? (string) $aValues['FileName'] : '';
$sFileHashIn = isset($aValues['FileHash']) ? (string) $aValues['FileHash'] : '';
$bDetectImageOrientation = !!$this->Config()->Get('labs', 'detect_image_exif_orientation', true);
if (!empty($sFileHashIn))
{
$this->verifyCacheByKey($sRawKey);
$oAccount = $this->getAccountFromToken();
$sContentTypeOut = empty($sContentTypeIn) ?
\MailSo\Base\Utils::MimeContentType($sFileNameIn) : $sContentTypeIn;
$sFileNameOut = $this->MainClearFileName($sFileNameIn, $sContentTypeIn, $sMimeIndex);
$rResource = $this->FilesProvider()->GetFile($oAccount, $sFileHashIn);
if (\is_resource($rResource))
{
\header('Content-Type: '.$sContentTypeOut);
\header('Content-Disposition: attachment; '.
\trim(\MailSo\Base\Utils::EncodeHeaderUtf8AttributeValue('filename', $sFileNameOut)), true);
\header('Accept-Ranges: none', true);
\header('Content-Transfer-Encoding: binary');
\MailSo\Base\Utils::FpassthruWithTimeLimitReset($rResource);
return true;
}
return false;
}
else
{
if (!empty($sFolder) && 0 < $iUid)
{
$this->verifyCacheByKey($sRawKey);
}
}
$oAccount = $this->initMailClientConnection();
$self = $this;
return $this->MailClient()->MessageMimeStream(
function($rResource, $sContentType, $sFileName, $sMimeIndex = '') use (
$self, $oAccount, $sRawKey, $sContentTypeIn, $sFileNameIn, $bDownload, $bThumbnail, $bDetectImageOrientation,
$bIsRangeRequest, $sRangeStart, $sRangeEnd
) {
if ($oAccount && \is_resource($rResource))
{
\MailSo\Base\Utils::ResetTimeLimit();
$sContentTypeOut = $sContentTypeIn;
if (empty($sContentTypeOut))
{
$sContentTypeOut = $sContentType;
if (empty($sContentTypeOut))
{
$sContentTypeOut = (empty($sFileName)) ? 'text/plain' : \MailSo\Base\Utils::MimeContentType($sFileName);
}
}
$sFileNameOut = $sFileNameIn;
if (empty($sFileNameOut))
{
$sFileNameOut = $sFileName;
}
$sFileNameOut = $self->MainClearFileName($sFileNameOut, $sContentTypeOut, $sMimeIndex);
$self->cacheByKey($sRawKey);
$sLoadedData = null;
if (!$bDownload)
{
if ($bThumbnail)
{
try
{
$oImagine = new \Imagine\Gd\Imagine();
$oImage = $oImagine->load(\stream_get_contents($rResource));
$oImage = $self->correctImageOrientation($oImage, $bDetectImageOrientation, 60);
\header('Content-Disposition: inline; '.
\trim(\MailSo\Base\Utils::EncodeHeaderUtf8AttributeValue('filename', $sFileNameOut.'_thumb60x60.png')), true);
$oImage->show('png');
}
catch (\Throwable $oException)
{
$self->Logger()->WriteExceptionShort($oException);
}
}
else if ($bDetectImageOrientation &&
\in_array($sContentTypeOut, array('image/png', 'image/jpeg', 'image/jpg')) &&
\MailSo\Base\Utils::FunctionExistsAndEnabled('gd_info'))
{
try
{
$oImagine = new \Imagine\Gd\Imagine();
$sLoadedData = \stream_get_contents($rResource);
$oImage = $oImagine->load($sLoadedData);
$oImage = $self->correctImageOrientation($oImage, $bDetectImageOrientation);
\header('Content-Disposition: inline; '.
\trim(\MailSo\Base\Utils::EncodeHeaderUtf8AttributeValue('filename', $sFileNameOut)), true);
$oImage->show($sContentTypeOut === 'image/png' ? 'png' : 'jpg');
}
catch (\Throwable $oException)
{
$self->Logger()->WriteExceptionShort($oException);
}
}
else
{
$sLoadedData = \stream_get_contents($rResource);
}
}
if ($bDownload || $sLoadedData)
{
if (!headers_sent()) {
\header('Content-Type: '.$sContentTypeOut);
\header('Content-Disposition: '.($bDownload ? 'attachment' : 'inline').'; '.
\trim(\MailSo\Base\Utils::EncodeHeaderUtf8AttributeValue('filename', $sFileNameOut)), true);
\header('Accept-Ranges: bytes');
\header('Content-Transfer-Encoding: binary');
}
if ($bIsRangeRequest && !$sLoadedData)
{
$sLoadedData = \stream_get_contents($rResource);
}
\MailSo\Base\Utils::ResetTimeLimit();
if ($sLoadedData)
{
if ($bIsRangeRequest && (0 < \strlen($sRangeStart) || 0 < \strlen($sRangeEnd)))
{
$iFullContentLength = \strlen($sLoadedData);
$self->Http()->StatusHeader(206);
$iRangeStart = (int) $sRangeStart;
$iRangeEnd = (int) $sRangeEnd;
if ('' === $sRangeEnd)
{
$sLoadedData = 0 < $iRangeStart ? \substr($sLoadedData, $iRangeStart) : $sLoadedData;
}
else
{
if ($iRangeStart < $iRangeEnd)
{
$sLoadedData = \substr($sLoadedData, $iRangeStart, $iRangeEnd - $iRangeStart);
}
else
{
$sLoadedData = 0 < $iRangeStart ? \substr($sLoadedData, $iRangeStart) : $sLoadedData;
}
}
$iContentLength = \strlen($sLoadedData);
if (0 < $iContentLength)
{
\header('Content-Length: '.$iContentLength, true);
\header('Content-Range: bytes '.$sRangeStart.'-'.(0 < $iRangeEnd ? $iRangeEnd : $iFullContentLength - 1).'/'.$iFullContentLength);
}
echo $sLoadedData;
}
else
{
echo $sLoadedData;
}
unset($sLoadedData);
}
else
{
\MailSo\Base\Utils::FpassthruWithTimeLimitReset($rResource);
}
}
}
}, $sFolder, $iUid, true, $sMimeIndex);
}
private function correctImageOrientation(\Imagine\Image\AbstractImage $oImage, bool $bDetectImageOrientation = true, int $iThumbnailBoxSize = 0) : \Imagine\Image\AbstractImage
{
$iOrientation = 1;
if ($bDetectImageOrientation && \MailSo\Base\Utils::FunctionExistsAndEnabled('exif_read_data') &&
\MailSo\Base\Utils::FunctionExistsAndEnabled('gd_info'))
{
$oMetadata = $oImage->metadata(new \Imagine\Image\Metadata\ExifMetadataReader());
$iOrientation = isset($oMetadata['ifd0.Orientation']) &&
is_numeric($oMetadata['ifd0.Orientation']) ? (int) $oMetadata['ifd0.Orientation'] : 1;
}
if (0 < $iThumbnailBoxSize) {
$oImage = $oImage->thumbnail(
new \Imagine\Image\Box($iThumbnailBoxSize, $iThumbnailBoxSize),
\Imagine\Image\ImageInterface::THUMBNAIL_OUTBOUND);
}
// rotateImageByOrientation
if (0 < $iOrientation) {
switch ($iOrientation)
{
case 2: // flip horizontal
$oImage->flipHorizontally();
break;
case 3: // rotate 180
$oImage->rotate(180);
break;
case 4: // flip vertical
$oImage->flipVertically();
break;
case 5: // vertical flip + 90 rotate
$oImage->flipVertically();
$oImage->rotate(90);
break;
case 6: // rotate 90
$oImage->rotate(90);
break;
case 7: // horizontal flip + 90 rotate
$oImage->flipHorizontally();
$oImage->rotate(90);
break;
case 8: // rotate 270
$oImage->rotate(270);
break;
}
}
return $oImage;
}
}

View file

@ -2,9 +2,9 @@
namespace RainLoop\Actions;
use \RainLoop\Enumerations\Capa;
use \RainLoop\Notifications;
use \RainLoop\Utils;
use \RainLoop\Enumerations\Capa;
trait Response
{
@ -112,15 +112,6 @@ trait Response
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);
@ -168,50 +159,6 @@ trait Response
$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 '<html style="height: 100%; width: 100%; margin: 0; padding: 0"><head></head>'.
'<body style="height: 100%; width: 100%; margin: 0; padding: 0">'.
'<iframe style="height: 100%; width: 100%; margin: 0; padding: 0; border: 0" src="'.$sFullUrl.'"></iframe>'.
'</body></html>';
}
}
}
}
return true;
}
*/
private function objectData(object $oData, string $sParent, array $aParameters = array()) : array
{
$mResult = array();
@ -633,4 +580,48 @@ trait Response
return $mResponse;
}
private function explodeSubject(string $sSubject) : array
{
$aResult = array('', '');
if (0 < \strlen($sSubject))
{
$bDrop = false;
$aPrefix = array();
$aSuffix = array();
$aParts = \explode(':', $sSubject);
foreach ($aParts as $sPart)
{
if (!$bDrop &&
(\preg_match('/^(RE|FWD)$/i', \trim($sPart)) || \preg_match('/^(RE|FWD)[\[\(][\d]+[\]\)]$/i', \trim($sPart))))
{
$aPrefix[] = $sPart;
}
else
{
$aSuffix[] = $sPart;
$bDrop = true;
}
}
if (0 < \count($aPrefix))
{
$aResult[0] = \rtrim(\trim(\implode(':', $aPrefix)), ':').': ';
}
if (0 < \count($aSuffix))
{
$aResult[1] = \trim(\implode(':', $aSuffix));
}
if (0 === \strlen($aResult[1]))
{
$aResult = array('', $sSubject);
}
}
return $aResult;
}
}

View file

@ -4,6 +4,8 @@ namespace RainLoop\Actions;
use \RainLoop\Enumerations\Capa;
use \RainLoop\Exceptions\ClientException;
use \RainLoop\Model\Account;
use \RainLoop\Model\Template;
use \RainLoop\Notifications;
trait Templates
@ -21,7 +23,7 @@ trait Templates
return $this->FalseResponse(__FUNCTION__);
}
$oTemplate = new \RainLoop\Model\Template();
$oTemplate = new Template();
if (!$oTemplate->FromJSON($this->GetActionParams(), true))
{
throw new ClientException(Notifications::InvalidInputArgument);
@ -130,4 +132,84 @@ trait Templates
'Templates' => $this->GetTemplates($oAccount)
));
}
private function GetTemplates(?Account $oAccount) : array
{
$aTemplates = array();
if ($oAccount)
{
$aData = array();
$sData = $this->StorageProvider(true)->Get($oAccount,
\RainLoop\Providers\Storage\Enumerations\StorageType::CONFIG,
'templates'
);
if ('' !== $sData && '[' === \substr($sData, 0, 1))
{
$aData = \json_decode($sData, true);
}
if (\is_array($aData) && 0 < \count($aData))
{
foreach ($aData as $aItem)
{
$oItem = new Template();
$oItem->FromJSON($aItem);
if ($oItem && $oItem->Validate())
{
\array_push($aTemplates, $oItem);
}
}
}
if (1 < \count($aTemplates))
{
$sOrder = $this->StorageProvider()->Get($oAccount,
\RainLoop\Providers\Storage\Enumerations\StorageType::CONFIG,
'templates_order'
);
$aOrder = empty($sOrder) ? array() : \json_decode($sOrder, true);
if (\is_array($aOrder) && 1 < \count($aOrder))
{
\usort($aTemplates, function ($a, $b) use ($aOrder) {
return \array_search($a->Id(), $aOrder) < \array_search($b->Id(), $aOrder) ? -1 : 1;
});
}
}
}
return $aTemplates;
}
private function GetTemplateByID(Account $oAccount, string $sID) : ?\RainLoop\Model\Identity
{
$aTemplates = $this->GetTemplates($oAccount);
foreach ($aTemplates as $oIdentity)
{
if ($oIdentity && $sID === $oIdentity->Id())
{
return $oIdentity;
}
}
return isset($aTemplates[0]) ? $aTemplates[0] : null;
}
private function SetTemplates(Account $oAccount, array $aTemplates = array()) : array
{
$aResult = array();
foreach ($aTemplates as $oItem)
{
$aResult[] = $oItem->ToSimpleJSON();
}
return $this->StorageProvider(true)->Put($oAccount,
\RainLoop\Providers\Storage\Enumerations\StorageType::CONFIG,
'templates',
\json_encode($aResult)
);
}
}

View file

@ -2,15 +2,21 @@
namespace RainLoop\Actions;
use \RainLoop\Enumerations\Capa;
trait TwoFactor
{
/**
* @var \RainLoop\Providers\TwoFactorAuth
*/
private $oTwoFactorAuthProvider;
public function DoGetTwoFactorInfo() : array
{
$oAccount = $this->getAccountFromToken();
if (!$this->TwoFactorAuthProvider()->IsActive() ||
!$this->GetCapa(false, false, \RainLoop\Enumerations\Capa::TWO_FACTOR, $oAccount))
!$this->GetCapa(false, false, Capa::TWO_FACTOR, $oAccount))
{
return $this->FalseResponse(__FUNCTION__);
}
@ -24,7 +30,7 @@ trait TwoFactor
$oAccount = $this->getAccountFromToken();
if (!$this->TwoFactorAuthProvider()->IsActive() ||
!$this->GetCapa(false, false, \RainLoop\Enumerations\Capa::TWO_FACTOR, $oAccount))
!$this->GetCapa(false, false, Capa::TWO_FACTOR, $oAccount))
{
return $this->FalseResponse(__FUNCTION__);
}
@ -61,7 +67,7 @@ trait TwoFactor
$oAccount = $this->getAccountFromToken();
if (!$this->TwoFactorAuthProvider()->IsActive() ||
!$this->GetCapa(false, false, \RainLoop\Enumerations\Capa::TWO_FACTOR, $oAccount))
!$this->GetCapa(false, false, Capa::TWO_FACTOR, $oAccount))
{
return $this->FalseResponse(__FUNCTION__);
}
@ -77,7 +83,7 @@ trait TwoFactor
$oAccount = $this->getAccountFromToken();
if (!$this->TwoFactorAuthProvider()->IsActive() ||
!$this->GetCapa(false, false, \RainLoop\Enumerations\Capa::TWO_FACTOR, $oAccount))
!$this->GetCapa(false, false, Capa::TWO_FACTOR, $oAccount))
{
return $this->FalseResponse(__FUNCTION__);
}
@ -108,7 +114,7 @@ trait TwoFactor
$oAccount = $this->getAccountFromToken();
if (!$this->TwoFactorAuthProvider()->IsActive() ||
!$this->GetCapa(false, false, \RainLoop\Enumerations\Capa::TWO_FACTOR, $oAccount))
!$this->GetCapa(false, false, Capa::TWO_FACTOR, $oAccount))
{
return $this->FalseResponse(__FUNCTION__);
}
@ -134,7 +140,7 @@ trait TwoFactor
$oAccount = $this->getAccountFromToken();
if (!$this->TwoFactorAuthProvider()->IsActive() ||
!$this->GetCapa(false, false, \RainLoop\Enumerations\Capa::TWO_FACTOR, $oAccount))
!$this->GetCapa(false, false, Capa::TWO_FACTOR, $oAccount))
{
return $this->FalseResponse(__FUNCTION__);
}
@ -148,4 +154,109 @@ trait TwoFactor
$this->getTwoFactorInfo($oAccount, true));
}
protected function TwoFactorAuthProvider() : \RainLoop\Providers\TwoFactorAuth
{
if (!$this->oTwoFactorAuthProvider) {
$this->oTwoFactorAuthProvider = new \RainLoop\Providers\TwoFactorAuth(
$this->Config()->Get('security', 'allow_two_factor_auth', false) ? $this->fabrica('two-factor-auth') : null
);
}
return $this->oTwoFactorAuthProvider;
}
protected function getTwoFactorInfo(\RainLoop\Model\Account $oAccount, bool $bRemoveSecret = false) : array
{
$sEmail = $oAccount->ParentEmailHelper();
$mData = null;
$aResult = array(
'User' => '',
'IsSet' => false,
'Enable' => false,
'Secret' => '',
'UrlTitle' => '',
'BackupCodes' => ''
);
if (!empty($sEmail))
{
$aResult['User'] = $sEmail;
$sData = $this->StorageProvider()->Get($oAccount,
\RainLoop\Providers\Storage\Enumerations\StorageType::CONFIG,
'two_factor'
);
if ($sData)
{
$mData = \RainLoop\Utils::DecodeKeyValues($sData);
}
}
if (!empty($aResult['User']) &&
!empty($mData['User']) && !empty($mData['Secret']) &&
!empty($mData['BackupCodes']) && $sEmail === $mData['User'])
{
$aResult['IsSet'] = true;
$aResult['Enable'] = isset($mData['Enable']) ? !!$mData['Enable'] : false;
$aResult['Secret'] = $mData['Secret'];
$aResult['BackupCodes'] = $mData['BackupCodes'];
$aResult['UrlTitle'] = $this->Config()->Get('webmail', 'title', '');
}
if ($bRemoveSecret)
{
if (isset($aResult['Secret']))
{
unset($aResult['Secret']);
}
if (isset($aResult['UrlTitle']))
{
unset($aResult['UrlTitle']);
}
if (isset($aResult['BackupCodes']))
{
unset($aResult['BackupCodes']);
}
}
return $aResult;
}
protected function removeBackupCodeFromTwoFactorInfo(\RainLoop\Model\Account $oAccount, string $sCode) : bool
{
if (!$oAccount || empty($sCode))
{
return false;
}
$sData = $this->StorageProvider()->Get($oAccount,
\RainLoop\Providers\Storage\Enumerations\StorageType::CONFIG,
'two_factor'
);
if ($sData)
{
$mData = \RainLoop\Utils::DecodeKeyValues($sData);
if (!empty($mData['BackupCodes']))
{
$sBackupCodes = \preg_replace('/[^\d]+/', ' ', ' '.$mData['BackupCodes'].' ');
$sBackupCodes = \str_replace(' '.$sCode.' ', '', $sBackupCodes);
$mData['BackupCodes'] = \trim(\preg_replace('/[^\d]+/', ' ', $sBackupCodes));
return $this->StorageProvider()->Put($oAccount,
\RainLoop\Providers\Storage\Enumerations\StorageType::CONFIG,
'two_factor',
\RainLoop\Utils::EncodeKeyValues($mData)
);
}
}
return false;
}
}

View file

@ -2,6 +2,11 @@
namespace RainLoop\Actions;
use \RainLoop\Enumerations\Capa;
use \RainLoop\Exceptions\ClientException;
use \RainLoop\Notifications;
use \RainLoop\Utils;
trait User
{
use Accounts;
@ -35,7 +40,7 @@ trait User
)
{
\sleep((int) $sPassword);
throw new Exceptions\ClientException(Notifications::AuthError);
throw new ClientException(Notifications::AuthError);
}
try
@ -44,7 +49,7 @@ trait User
$bSignMe ? $this->generateSignMeToken($sEmail) : '',
$sAdditionalCode, $bAdditionalCodeSignMe);
}
catch (Exceptions\ClientException $oException)
catch (ClientException $oException)
{
if ($oException &&
Notifications::AccountTwoFactorAuthRequired === $oException->getCode())
@ -85,7 +90,7 @@ trait User
*/
public function DoAttachmentsActions() : array
{
if (!$this->GetCapa(false, false, Enumerations\Capa::ATTACHMENTS_ACTIONS))
if (!$this->GetCapa(false, false, Capa::ATTACHMENTS_ACTIONS))
{
return $this->FalseResponse(__FUNCTION__);
}
@ -211,7 +216,7 @@ trait User
if (!$oAccount->IsAdditionalAccount())
{
Utils::ClearCookie(Actions::AUTH_SPEC_TOKEN_KEY);
Utils::ClearCookie(self::AUTH_SPEC_TOKEN_KEY);
}
}
@ -233,7 +238,7 @@ trait User
$iOneDay3 = 60 * 60 * 30;
$sTimers = $this->StorageProvider()->Get(null,
Providers\Storage\Enumerations\StorageType::NOBODY, 'Cache/Timers', '');
\RainLoop\Providers\Storage\Enumerations\StorageType::NOBODY, 'Cache/Timers', '');
$aTimers = \explode(',', $sTimers);
@ -262,7 +267,7 @@ trait User
if ($bMainCache || $bFilesCache || $bVersionsCache)
{
if (!$this->StorageProvider()->Put(null,
Providers\Storage\Enumerations\StorageType::NOBODY, 'Cache/Timers',
\RainLoop\Providers\Storage\Enumerations\StorageType::NOBODY, 'Cache/Timers',
\implode(',', array($iMainCacheTime, $iFilesCacheTime, $iVersionsCacheTime))))
{
$bMainCache = $bFilesCache = $bVersionsCache = false;
@ -290,7 +295,7 @@ trait User
public function DoSettingsUpdate() : array
{
$oAccount = $this->getAccountFromToken();
if (!$this->GetCapa(false, false, Enumerations\Capa::SETTINGS, $oAccount))
if (!$this->GetCapa(false, false, Capa::SETTINGS, $oAccount))
{
return $this->FalseResponse(__FUNCTION__);
}
@ -312,7 +317,7 @@ trait User
$oSettings->SetConf('Language', $this->ValidateLanguage($oConfig->Get('webmail', 'language', 'en')));
}
if ($this->GetCapa(false, false, Enumerations\Capa::THEMES, $oAccount))
if ($this->GetCapa(false, false, Capa::THEMES, $oAccount))
{
$this->setSettingsFromParams($oSettingsLocal, 'Theme', 'string', function ($sTheme) use ($self) {
return $self->ValidateTheme($sTheme);
@ -328,9 +333,9 @@ trait User
});
$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);
return (int) (\in_array((int) $iValue, array(\RainLoop\Enumerations\Layout::NO_PREVIW,
\RainLoop\Enumerations\Layout::SIDE_PREVIEW, \RainLoop\Enumerations\Layout::BOTTOM_PREVIEW)) ?
$iValue : \RainLoop\Enumerations\Layout::SIDE_PREVIEW);
});
$this->setSettingsFromParams($oSettings, 'EditorDefaultType', 'string');
@ -356,7 +361,7 @@ trait User
{
$oAccount = $this->initMailClientConnection();
if (!$this->GetCapa(false, false, Enumerations\Capa::QUOTA, $oAccount))
if (!$this->GetCapa(false, false, Capa::QUOTA, $oAccount))
{
return $this->DefaultResponse(__FUNCTION__, array(0, 0, 0, 0));
}
@ -367,7 +372,7 @@ trait User
}
catch (\Throwable $oException)
{
throw new Exceptions\ClientException(Notifications::MailServerError, $oException);
throw new ClientException(Notifications::MailServerError, $oException);
}
return $this->DefaultResponse(__FUNCTION__, $aQuota);
@ -489,7 +494,7 @@ trait User
{
$oAccount = $this->getAccountFromToken();
if (!$this->GetCapa(false, false, Enumerations\Capa::USER_BACKGROUND, $oAccount))
if (!$this->GetCapa(false, false, Capa::USER_BACKGROUND, $oAccount))
{
return $this->FalseResponse(__FUNCTION__);
}
@ -498,7 +503,7 @@ trait User
if ($oAccount && $oSettings)
{
$this->StorageProvider()->Clear($oAccount,
Providers\Storage\Enumerations\StorageType::CONFIG,
\RainLoop\Providers\Storage\Enumerations\StorageType::CONFIG,
'background'
);
@ -510,4 +515,96 @@ trait User
$this->SettingsProvider()->Save($oAccount, $oSettings) : false);
}
protected function ClearSignMeData(Model\Account $oAccount) : void
{
if ($oAccount) {
Utils::ClearCookie(self::AUTH_SIGN_ME_TOKEN_KEY);
$this->StorageProvider()->Clear($oAccount,
\RainLoop\Providers\Storage\Enumerations\StorageType::CONFIG,
'sign_me'
);
}
}
private function generateSignMeToken(string $sEmail) : string
{
return \MailSo\Base\Utils::Md5Rand(APP_SALT.$sEmail);
}
private function getMimeFileByHash(\RainLoop\Model\Account $oAccount, string $sHash) : array
{
$aValues = $this->getDecodedRawKeyValue($sHash);
$sFolder = isset($aValues['Folder']) ? $aValues['Folder'] : '';
$iUid = (int) isset($aValues['Uid']) ? $aValues['Uid'] : 0;
$sMimeIndex = (string) isset($aValues['MimeIndex']) ? $aValues['MimeIndex'] : '';
$sContentTypeIn = (string) isset($aValues['MimeType']) ? $aValues['MimeType'] : '';
$sFileNameIn = (string) isset($aValues['FileName']) ? $aValues['FileName'] : '';
$oFileProvider = $this->FilesProvider();
$sResultHash = '';
$mResult = $this->MailClient()->MessageMimeStream(function($rResource, $sContentType, $sFileName, $sMimeIndex = '')
use ($oAccount, $oFileProvider, $sFileNameIn, $sContentTypeIn, &$sResultHash) {
unset($sContentType, $sFileName, $sMimeIndex);
if ($oAccount && \is_resource($rResource))
{
$sHash = \MailSo\Base\Utils::Md5Rand($sFileNameIn.'~'.$sContentTypeIn);
$rTempResource = $oFileProvider->GetFile($oAccount, $sHash, 'wb+');
if (\is_resource($rTempResource))
{
if (false !== \MailSo\Base\Utils::MultipleStreamWriter($rResource, array($rTempResource)))
{
$sResultHash = $sHash;
}
\fclose($rTempResource);
}
}
}, $sFolder, $iUid, true, $sMimeIndex);
$aValues['FileHash'] = '';
if ($mResult)
{
$aValues['FileHash'] = $sResultHash;
}
return $aValues;
}
private function setSettingsFromParams(\RainLoop\Settings $oSettings, string $sConfigName, string $sType = 'string', ?callable $mStringCallback = null) : void
{
if ($this->HasActionParam($sConfigName))
{
$sValue = $this->GetActionParam($sConfigName, '');
switch ($sType)
{
default:
case 'string':
$sValue = (string) $sValue;
if ($mStringCallback && is_callable($mStringCallback))
{
$sValue = call_user_func($mStringCallback, $sValue);
}
$oSettings->SetConf($sConfigName, (string) $sValue);
break;
case 'int':
$iValue = (int) $sValue;
$oSettings->SetConf($sConfigName, $iValue);
break;
case 'bool':
$oSettings->SetConf($sConfigName, '1' === (string) $sValue);
break;
}
}
}
}