snappymail/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Folders.php
2022-05-29 10:12:49 +02:00

612 lines
16 KiB
PHP

<?php
namespace RainLoop\Actions;
use RainLoop\Enumerations\Capa;
use RainLoop\Exceptions\ClientException;
use RainLoop\Notifications;
use MailSo\Imap\Enumerations\FolderType;
trait Folders
{
private function getFolderCollection(bool $HideUnsubscribed) : ?\MailSo\Mail\FolderCollection
{
return $this->MailClient()->Folders('', '*',
$HideUnsubscribed,
(int) $this->Config()->Get('labs', 'imap_folder_list_limit', 200),
(bool) $this->Config()->Get('labs', 'imap_use_list_status', true)
);
}
/**
* Appends uploaded rfc822 message to mailbox
* @throws \MailSo\Base\Exceptions\Exception
*/
public function Append(): bool
{
$oAccount = $this->initMailClientConnection();
$sFolderFullName = $this->GetActionParam('Folder', '');
if ($oAccount
&& !empty($sFolderFullName)
&& !empty($_FILES['AppendFile'])
&& \is_uploaded_file($_FILES['AppendFile']['tmp_name'])
&& \UPLOAD_ERR_OK == $_FILES['AppendFile']['error']
&& $this->oConfig->Get('labs', 'allow_message_append', false)
) {
$sSavedName = 'append-post-' . \md5($sFolderFullName . $_FILES['AppendFile']['name'] . $_FILES['AppendFile']['tmp_name']);
if ($this->FilesProvider()->MoveUploadedFile($oAccount, $sSavedName, $_FILES['AppendFile']['tmp_name'])) {
$iMessageStreamSize = $this->FilesProvider()->FileSize($oAccount, $sSavedName);
$rMessageStream = $this->FilesProvider()->GetFile($oAccount, $sSavedName);
$this->MailClient()->MessageAppendStream($rMessageStream, $iMessageStreamSize, $sFolderFullName);
$this->FilesProvider()->Clear($oAccount, $sSavedName);
}
}
return $this->DefaultResponse(__FUNCTION__, true);
}
public function DoFolders() : array
{
$oAccount = $this->initMailClientConnection();
$HideUnsubscribed = false;
$oSettingsLocal = $this->SettingsProvider(true)->Load($oAccount);
if ($oSettingsLocal instanceof \RainLoop\Settings) {
$HideUnsubscribed = (bool) $oSettingsLocal->GetConf('HideUnsubscribed', $HideUnsubscribed);
}
$oFolderCollection = $this->getFolderCollection($HideUnsubscribed);
if ($oFolderCollection)
{
$sNamespace = $this->MailClient()->GetNamespace();
$this->Plugins()->RunHook('filter.folders-post', array($oAccount, $oFolderCollection));
$aSystemFolders = array();
$this->recFoldersTypes($oAccount, $oFolderCollection, $aSystemFolders);
if (!$this->Config()->Get('labs', 'use_imap_sort', true)) {
$oFolderCollection->capabilities = \array_filter($oFolderCollection->capabilities, function($item){
return !\preg_match('/^E?SORT/', $item);
});
}
if ($this->Config()->Get('labs', 'autocreate_system_folders', false))
{
$bDoItAgain = false;
$sParent = \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::ARCHIVE;
}
$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 (\strlen($mNewFolderNameToCreate))
{
$mFolderNameToCreate = $mNewFolderNameToCreate;
}
if (\strlen($mNewParent))
{
$sParent = \strlen($sParent) ? $sParent.$sDelimiter.$mNewParent : $mNewParent;
}
}
$sFullNameToCheck = $mFolderNameToCreate;
if (\strlen($sParent))
{
$sFullNameToCheck = $sParent.$sDelimiter.$sFullNameToCheck;
}
if (!isset($oFolderCollection[$sFullNameToCheck]))
{
try
{
$this->MailClient()->FolderCreate($mFolderNameToCreate, $sParent, true, $sDelimiter);
$bDoItAgain = true;
}
catch (\Throwable $oException)
{
$this->Logger()->WriteException($oException);
}
}
}
}
}
if ($bDoItAgain)
{
$oFolderCollection = $this->getFolderCollection($HideUnsubscribed);
if ($oFolderCollection)
{
$aSystemFolders = array();
$this->recFoldersTypes($oAccount, $oFolderCollection, $aSystemFolders);
}
}
}
if ($oFolderCollection)
{
$this->Plugins()->RunHook('filter.folders-complete', array($oAccount, $oFolderCollection));
$aQuota = null;
if ($this->GetCapa(Capa::QUOTA)) {
try {
// $aQuota = $this->MailClient()->Quota();
$aQuota = $this->MailClient()->QuotaRoot();
} catch (\Throwable $oException) {
// ignore
}
}
$aCapabilities = \array_filter($this->MailClient()->Capabilities(), function($item){
return !\preg_match('/^(IMAP|AUTH|LOGIN|SASL)/', $item);
});
if (!$this->Config()->Get('labs', 'imap_use_list_status', true)) {
$key = \array_search('LIST-STATUS', $aCapabilities);
if (false !== $key) {
unset($aCapabilities[$key]);
}
}
$oFolderCollection = \array_merge(
$oFolderCollection->jsonSerialize(),
array(
'quotaUsage' => $aQuota ? $aQuota[0] * 1024 : null,
'quotaLimit' => $aQuota ? $aQuota[1] * 1024 : null,
'Namespace' => $sNamespace,
'IsThreadsSupported' => $this->MailClient()->IsThreadsSupported(),
'Optimized' => $oFolderCollection->Optimized,
'CountRec' => $oFolderCollection->TotalCount,
'SystemFolders' => empty($aSystemFolders) ? null : $aSystemFolders,
'Capabilities' => \array_values($aCapabilities)
)
);
}
}
return $this->DefaultResponse(__FUNCTION__, $oFolderCollection);
}
public function DoFolderCreate() : array
{
$this->initMailClientConnection();
try
{
$oFolder = $this->MailClient()->FolderCreate(
$this->GetActionParam('Folder', ''),
$this->GetActionParam('Parent', ''),
!!$this->GetActionParam('Subscribe', 1)
);
// FolderInformation(string $sFolderName, int $iPrevUidNext = 0, array $aUids = array())
return $this->DefaultResponse(__FUNCTION__, $oFolder);
}
catch (\Throwable $oException)
{
throw new ClientException(Notifications::CantCreateFolder, $oException);
}
}
public function DoFolderSetMetadata() : array
{
$this->initMailClientConnection();
$sFolderFullName = $this->GetActionParam('Folder');
$sMetadataKey = $this->GetActionParam('Key');
if ($sFolderFullName && $sMetadataKey) {
$this->MailClient()->FolderSetMetadata($sFolderFullName, [
$sMetadataKey => $this->GetActionParam('Value') ?: null
]);
}
return $this->TrueResponse(__FUNCTION__);
}
public function DoFolderSubscribe() : array
{
$this->initMailClientConnection();
$sFolderFullName = $this->GetActionParam('Folder', '');
$bSubscribe = '1' === (string) $this->GetActionParam('Subscribe', '0');
try
{
$this->MailClient()->FolderSubscribe($sFolderFullName, $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();
$sFolderFullName = $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[] = $sFolderFullName;
}
else
{
$aCheckableFolderNew = array();
foreach ($aCheckableFolder as $sFolder)
{
if ($sFolder !== $sFolderFullName)
{
$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 DoFolderMove() : array
{
$this->initMailClientConnection();
try
{
$this->MailClient()->FolderMove(
$this->GetActionParam('Folder', ''),
$this->GetActionParam('NewFolder', ''),
!!$this->GetActionParam('Subscribe', 1)
);
}
catch (\Throwable $oException)
{
throw new ClientException(Notifications::CantRenameFolder, $oException);
}
return $this->TrueResponse(__FUNCTION__);
}
/**
* @throws \MailSo\Base\Exceptions\Exception
*/
public function DoFolderRename() : array
{
$this->initMailClientConnection();
$sName = $this->GetActionParam('NewFolderName', '');
try
{
$sFullName = $this->MailClient()->FolderRename(
$this->GetActionParam('Folder', ''),
$sName,
!!$this->GetActionParam('Subscribe', 1)
);
}
catch (\Throwable $oException)
{
throw new ClientException(Notifications::CantRenameFolder, $oException);
}
// FolderInformation(string $sFolderName, int $iPrevUidNext = 0, array $aUids = array())
return $this->DefaultResponse(__FUNCTION__, array(
'Name' => $sName,
'FullName' => $sFullName,
));
}
/**
* @throws \MailSo\Base\Exceptions\Exception
*/
public function DoFolderDelete() : array
{
$this->initMailClientConnection();
try
{
$this->MailClient()->FolderDelete($this->GetActionParam('Folder', ''));
}
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();
try
{
$this->MailClient()->FolderClear($this->GetActionParam('Folder', ''));
}
catch (\Throwable $oException)
{
throw new ClientException(Notifications::MailServerError, $oException);
}
return $this->TrueResponse(__FUNCTION__);
}
/**
* @throws \MailSo\Base\Exceptions\Exception
*/
public function DoFolderInformation() : array
{
$this->initMailClientConnection();
try
{
$aInboxInformation = $this->MailClient()->FolderInformation(
$this->GetActionParam('Folder', ''),
(int) $this->GetActionParam('UidNext', 0),
new \MailSo\Imap\SequenceSet($this->GetActionParam('FlagsUids', []))
);
$aInboxInformation['Flags'] = $aInboxInformation['MessagesFlags'];
unset($aInboxInformation['MessagesFlags']);
return $this->DefaultResponse(__FUNCTION__, $aInboxInformation);
}
catch (\Throwable $oException)
{
throw new ClientException(Notifications::MailServerError, $oException);
}
}
/**
* @throws \MailSo\Base\Exceptions\Exception
*/
public function DoFolderInformationMultiply() : array
{
$aResult = array();
$aFolders = $this->GetActionParam('Folders', null);
if (\is_array($aFolders))
{
$this->initMailClientConnection();
$aFolders = \array_unique($aFolders);
foreach ($aFolders as $sFolder)
{
if (\strlen($sFolder) && 'INBOX' !== \strtoupper($sFolder))
{
try
{
$aInboxInformation = $this->MailClient()->FolderInformation($sFolder);
if (isset($aInboxInformation['Folder']))
{
$aResult[] = [
'Folder' => $aInboxInformation['Folder'],
'Hash' => $aInboxInformation['Hash'],
'MessageCount' => $aInboxInformation['MessageCount'],
'MessageUnseenCount' => $aInboxInformation['MessageUnseenCount'],
];
}
}
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('Sent', ''));
$oSettingsLocal->SetConf('DraftFolder', $this->GetActionParam('Drafts', ''));
$oSettingsLocal->SetConf('SpamFolder', $this->GetActionParam('Spam', ''));
$oSettingsLocal->SetConf('TrashFolder', $this->GetActionParam('Trash', ''));
$oSettingsLocal->SetConf('ArchiveFolder', $this->GetActionParam('Archive', ''));
return $this->DefaultResponse(__FUNCTION__,
$this->SettingsProvider(true)->Save($oAccount, $oSettingsLocal));
}
private function recFoldersTypes(\RainLoop\Model\Account $oAccount, \MailSo\Mail\FolderCollection $oFolders, array &$aResult, bool $bListFolderTypes = true) : void
{
if ($oFolders->Count())
{
if ($bListFolderTypes)
{
foreach ($oFolders as $oFolder)
{
$iFolderType = $oFolder->GetFolderListType();
if (!isset($aResult[$iFolderType]) && \in_array($iFolderType, array(
FolderType::INBOX,
FolderType::SENT,
FolderType::DRAFTS,
FolderType::JUNK,
FolderType::TRASH,
FolderType::ARCHIVE
)))
{
$aResult[$iFolderType] = $oFolder->FullName();
}
}
}
$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]) || $sName === $sFullName || "INBOX{$oFolder->Delimiter()}{$sName}" === $sFullName) && \in_array($iFolderType, array(
FolderType::INBOX,
FolderType::SENT,
FolderType::DRAFTS,
FolderType::JUNK,
FolderType::TRASH,
FolderType::ARCHIVE
)))
{
$aResult[$iFolderType] = $oFolder->FullName();
}
}
}
}
}
/**
* @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::ARCHIVE,
'Archives' => FolderType::ARCHIVE,
'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;
}
}