diff --git a/application/Espo/Core/Mail/Account/Fetcher.php b/application/Espo/Core/Mail/Account/Fetcher.php index eb2f095c02..97a86bed1b 100644 --- a/application/Espo/Core/Mail/Account/Fetcher.php +++ b/application/Espo/Core/Mail/Account/Fetcher.php @@ -29,12 +29,12 @@ namespace Espo\Core\Mail\Account; -use Laminas\Mail\Storage; - use Espo\Core\Exceptions\Error; + +use Espo\Core\Mail\Account\Storage\Flag; +use Espo\Core\Mail\Account\Storage; use Espo\Core\Mail\Importer; use Espo\Core\Mail\Importer\Data as ImporterData; -use Espo\Core\Mail\Mail\Storage\Imap; use Espo\Core\Mail\ParserFactory; use Espo\Core\Mail\MessageWrapper; use Espo\Core\Mail\Account\Hook\BeforeFetch as BeforeFetchHook; @@ -44,6 +44,7 @@ use Espo\Core\Mail\Account\Hook\BeforeFetchResult as BeforeFetchHookResult; use Espo\Core\Utils\DateTime as DateTimeUtil; use Espo\Core\Utils\Config; use Espo\Core\Utils\Log; +use Espo\Core\Field\DateTime as DateTimeField; use Espo\Entities\EmailFilter; use Espo\Entities\Email; @@ -121,7 +122,7 @@ class Fetcher private function fetchFolder( Account $account, string $folderOriginal, - Imap $storage, + Storage $storage, Collection $filterList ): void { @@ -130,7 +131,7 @@ class Fetcher $folder = mb_convert_encoding($folderOriginal, 'UTF7-IMAP', 'UTF-8'); try { - $storage->selectFolder($folder); + $storage->selectFolder($folderOriginal); } catch (Throwable $e) { $this->log->error( @@ -211,7 +212,7 @@ class Fetcher if ($forceByDate) { if ($previousLastUID) { - $idList = $storage->getIdsFromUID($previousLastUID); + $idList = $storage->getIdsFromUniqueId($previousLastUID); if (count($idList)) { $uid1 = $storage->getUniqueId($idList[0]); @@ -234,14 +235,14 @@ class Fetcher */ private function getIdList( Account $account, - Imap $storage, + Storage $storage, ?string $lastUID, ?string $lastDate, bool $forceByDate ): array { if (!empty($lastUID) && !$forceByDate) { - return $storage->getIdsFromUID($lastUID); + return $storage->getIdsFromUniqueId($lastUID); } if ($lastDate) { @@ -255,8 +256,8 @@ class Fetcher $fetchSince = $account->getFetchSince()->getString(); } - return $storage->getIdsFromDate( - (new DateTime($fetchSince))->format('d-M-Y') + return $storage->getIdsSinceDate( + DateTimeField::fromString($fetchSince) ); } @@ -265,7 +266,7 @@ class Fetcher */ private function fetchEmail( Account $account, - Imap $storage, + Storage $storage, int $id, Collection $filterList ): ?Email { @@ -297,7 +298,7 @@ class Fetcher ->withAssignedUserId($assignedUserId); try { - $message = new MessageWrapper($storage, (string) $id, $parser); + $message = new MessageWrapper($id, $storage, $parser); $hookResult = null; @@ -318,9 +319,9 @@ class Fetcher if ( $account->keepFetchedEmailsUnread() && is_array($flags) && - empty($flags[Storage::FLAG_SEEN]) + empty($flags[Flag::SEEN]) ) { - unset($flags[Storage::FLAG_RECENT]); + unset($flags[Flag::RECENT]); $storage->setFlags($id, $flags); } @@ -395,7 +396,7 @@ class Fetcher ->find(); } - private function checkFetchOnlyHeader(Imap $storage, int $id): bool + private function checkFetchOnlyHeader(Storage $storage, int $id): bool { $maxSize = $this->config->get('emailMessageMaxSize'); diff --git a/application/Espo/Core/Mail/Account/GroupAccount/Service.php b/application/Espo/Core/Mail/Account/GroupAccount/Service.php index 06f41c7b7f..197022f0f8 100644 --- a/application/Espo/Core/Mail/Account/GroupAccount/Service.php +++ b/application/Espo/Core/Mail/Account/GroupAccount/Service.php @@ -39,8 +39,6 @@ use Espo\Core\Mail\Account\StorageFactory; use Espo\Core\Utils\Crypt; -use RecursiveIteratorIterator; - class Service { private Fetcher $fetcher; @@ -90,18 +88,7 @@ class Service $storage = $this->storageFactory->createWithParams($params); - $folderIterator = new RecursiveIteratorIterator( - $storage->getFolders(), - RecursiveIteratorIterator::SELF_FIRST - ); - - $list = []; - - foreach ($folderIterator as $folder) { - $list[] = mb_convert_encoding($folder->getGlobalName(), 'UTF-8', 'UTF7-IMAP'); - } - - return $list; + return $storage->getFolderNames(); } public function testConnection(Params $params): void @@ -118,7 +105,7 @@ class Service $storage = $this->storageFactory->createWithParams($params); - $storage->getFolders(); + $storage->getFolderNames(); } /** diff --git a/application/Espo/Core/Mail/Account/GroupAccount/StorageFactory.php b/application/Espo/Core/Mail/Account/GroupAccount/StorageFactory.php index 5e951c5482..3f7c5e6bde 100644 --- a/application/Espo/Core/Mail/Account/GroupAccount/StorageFactory.php +++ b/application/Espo/Core/Mail/Account/GroupAccount/StorageFactory.php @@ -32,7 +32,7 @@ namespace Espo\Core\Mail\Account\GroupAccount; use Espo\Core\Mail\Account\Storage\Params; use Espo\Core\Mail\Account\Account; use Espo\Core\Mail\Account\StorageFactory as StorageFactoryInterface; - +use Espo\Core\Mail\Account\Storage\LaminasStorage; use Espo\Core\Mail\Mail\Storage\Imap; use Espo\Core\Utils\Crypt; @@ -49,8 +49,6 @@ class StorageFactory implements StorageFactoryInterface private InjectableFactory $injectableFactory; - private string $storageClassName = Imap::class; - public function __construct(Crypt $crypt, Log $log, InjectableFactory $injectableFactory) { $this->crypt = $crypt; @@ -58,7 +56,7 @@ class StorageFactory implements StorageFactoryInterface $this->injectableFactory = $injectableFactory; } - public function create(Account $account): Imap + public function create(Account $account): LaminasStorage { $params = Params::createBuilder() ->setHost($account->getHost()) @@ -75,7 +73,7 @@ class StorageFactory implements StorageFactoryInterface return $this->createWithParams($params); } - public function createWithParams(Params $params): Imap + public function createWithParams(Params $params): LaminasStorage { $rawParams = [ 'host' => $params->getHost(), @@ -127,6 +125,8 @@ class StorageFactory implements StorageFactoryInterface } } - return new $this->storageClassName($imapParams); + return new LaminasStorage( + new Imap($imapParams) + ); } } diff --git a/application/Espo/Core/Mail/Account/PersonalAccount/Service.php b/application/Espo/Core/Mail/Account/PersonalAccount/Service.php index d002d4d85f..8b8f1a13b8 100644 --- a/application/Espo/Core/Mail/Account/PersonalAccount/Service.php +++ b/application/Espo/Core/Mail/Account/PersonalAccount/Service.php @@ -42,8 +42,6 @@ use Espo\Core\Utils\Crypt; use Espo\Entities\User; -use RecursiveIteratorIterator; - class Service { private Fetcher $fetcher; @@ -107,18 +105,7 @@ class Service $storage = $this->storageFactory->createWithParams($params); - $folderIterator = new RecursiveIteratorIterator( - $storage->getFolders(), - RecursiveIteratorIterator::SELF_FIRST - ); - - $list = []; - - foreach ($folderIterator as $folder) { - $list[] = mb_convert_encoding($folder->getGlobalName(), 'UTF-8', 'UTF7-IMAP'); - } - - return $list; + return $storage->getFolderNames(); } public function testConnection(Params $params): void @@ -156,7 +143,7 @@ class Service $storage = $this->storageFactory->createWithParams($params); - $storage->getFolders(); + $storage->getFolderNames(); } /** diff --git a/application/Espo/Core/Mail/Account/PersonalAccount/StorageFactory.php b/application/Espo/Core/Mail/Account/PersonalAccount/StorageFactory.php index 0d74c318e7..150609ce87 100644 --- a/application/Espo/Core/Mail/Account/PersonalAccount/StorageFactory.php +++ b/application/Espo/Core/Mail/Account/PersonalAccount/StorageFactory.php @@ -34,6 +34,7 @@ use Espo\Core\Mail\Account\StorageFactory as StorageFactoryInterface; use Espo\Core\Mail\Account\Account; use Espo\Core\Mail\Mail\Storage\Imap; +use Espo\Core\Mail\Account\Storage\LaminasStorage; use Espo\Core\Utils\Crypt; use Espo\Core\Utils\Log; @@ -56,8 +57,6 @@ class StorageFactory implements StorageFactoryInterface private EntityManager $entityManager; - private string $storageClassName = Imap::class; - public function __construct( Crypt $crypt, Log $log, @@ -70,7 +69,7 @@ class StorageFactory implements StorageFactoryInterface $this->entityManager = $entityManager; } - public function create(Account $account): Imap + public function create(Account $account): LaminasStorage { $params = Params::createBuilder() ->setHost($account->getHost()) @@ -89,7 +88,7 @@ class StorageFactory implements StorageFactoryInterface return $this->createWithParams($params); } - public function createWithParams(Params $params): Imap + public function createWithParams(Params $params): LaminasStorage { $rawParams = [ 'host' => $params->getHost(), @@ -172,7 +171,9 @@ class StorageFactory implements StorageFactoryInterface } } - return new $this->storageClassName($imapParams); + return new LaminasStorage( + new Imap($imapParams) + ); } private function getUserDataRepository(): UserDataRepository diff --git a/application/Espo/Core/Mail/Account/Storage.php b/application/Espo/Core/Mail/Account/Storage.php new file mode 100644 index 0000000000..562096da7d --- /dev/null +++ b/application/Espo/Core/Mail/Account/Storage.php @@ -0,0 +1,98 @@ +imap = $imap; + } + + /** + * @param string[] $flags + */ + public function setFlags(int $id, array $flags): void + { + $this->imap->setFlags($id, $flags); + } + + public function getSize(int $id): int + { + return $this->imap->getSize($id); + } + + public function getRawContent(int $id): string + { + return $this->imap->getRawContent($id); + } + + public function getUniqueId(int $id): string + { + return $this->imap->getUniqueId($id); + } + + /** + * @return int[] + */ + public function getIdsFromUniqueId(string $uniqueId): array + { + return $this->imap->getIdsFromUniqueId($uniqueId); + } + + /** + * @return int[] + */ + public function getIdsSinceDate(DateTime $since): array + { + return $this->imap->getIdsSinceDate( + $since->getDateTime()->format('d-M-Y') + ); + } + + /** + * @return array{header: string, flags: string[]} + */ + public function getHeaderAndFlags(int $id): array + { + return $this->imap->getHeaderAndFlags($id); + } + + public function close(): void + { + $this->imap->close(); + } + + /** + * @retrun string[] + */ + public function getFolderNames(): array + { + $folderIterator = new RecursiveIteratorIterator( + $this->imap->getFolders(), + RecursiveIteratorIterator::SELF_FIRST + ); + + $list = []; + + foreach ($folderIterator as $folder) { + $list[] = mb_convert_encoding($folder->getGlobalName(), 'UTF-8', 'UTF7-IMAP'); + } + + return $list; + } + + public function selectFolder(string $name): void + { + $nameConverted = mb_convert_encoding($name, 'UTF7-IMAP', 'UTF-8'); + + $this->imap->selectFolder($nameConverted); + } + + public function appendMessage(string $content, ?string $folder = null): void + { + $this->imap->appendMessage($content, $folder); + } +} diff --git a/application/Espo/Core/Mail/Account/StorageFactory.php b/application/Espo/Core/Mail/Account/StorageFactory.php index 8ebcce00f8..7305425885 100644 --- a/application/Espo/Core/Mail/Account/StorageFactory.php +++ b/application/Espo/Core/Mail/Account/StorageFactory.php @@ -29,12 +29,12 @@ namespace Espo\Core\Mail\Account; -use Espo\Core\Mail\Mail\Storage\Imap; use Espo\Core\Mail\Account\Storage\Params; +use Espo\Core\Mail\Account\Storage; interface StorageFactory { - public function create(Account $account): Imap; + public function create(Account $account): Storage; - public function createWithParams(Params $params): Imap; + public function createWithParams(Params $params): Storage; } diff --git a/application/Espo/Core/Mail/Importer.php b/application/Espo/Core/Mail/Importer.php index 40e1566e12..4823772ec3 100644 --- a/application/Espo/Core/Mail/Importer.php +++ b/application/Espo/Core/Mail/Importer.php @@ -102,7 +102,9 @@ class Importer $folderData = $data->getFolderData(); $parser = $message instanceof MessageWrapper ? - $message->getParser() : + ( + $message->getParser() ?? $this->parserFactory->create() + ) : $this->parserFactory->create(); /** @var Email $email */ diff --git a/application/Espo/Core/Mail/Mail/Storage/Imap.php b/application/Espo/Core/Mail/Mail/Storage/Imap.php index 1d2c984036..344e69c257 100644 --- a/application/Espo/Core/Mail/Mail/Storage/Imap.php +++ b/application/Espo/Core/Mail/Mail/Storage/Imap.php @@ -31,31 +31,43 @@ namespace Espo\Core\Mail\Mail\Storage; class Imap extends \Laminas\Mail\Storage\Imap { - public function getIdsFromUID($uid) + /** + * @return int[] + */ + public function getIdsFromUniqueId(string $uid): array { - $uid = intval($uid) + 1; - return $this->protocol->search(['UID ' . $uid . ':*']); + $nextUid = strval(intval($uid) + 1); + + return $this->protocol->search(['UID ' . $nextUid . ':*']); } - public function getIdsFromDate($date) + /** + * @return int[] + */ + public function getIdsSinceDate(string $date): array { return $this->protocol->search(['SINCE "' . $date . '"']); } - public function getHeaderAndFlags($id, $part = null) + /** + * @param int $id + * @return array{header: string, flags: string[]} + */ + public function getHeaderAndFlags(int $id): array { $data = $this->protocol->fetch(['FLAGS', 'RFC822.HEADER'], $id); $header = $data['RFC822.HEADER']; $flags = []; + foreach ($data['FLAGS'] as $flag) { $flags[] = isset(static::$knownFlags[$flag]) ? static::$knownFlags[$flag] : $flag; } return [ 'flags' => $flags, - 'header' => $header + 'header' => $header, ]; } } diff --git a/application/Espo/Core/Mail/Message.php b/application/Espo/Core/Mail/Message.php index 42f0752943..5e4162ac39 100644 --- a/application/Espo/Core/Mail/Message.php +++ b/application/Espo/Core/Mail/Message.php @@ -56,11 +56,6 @@ interface Message */ public function getFullRawContent(): string; - /** - * Set a full raw message. - */ - public function setFullRawContent(string $content): void; - /** * Get flags. * diff --git a/application/Espo/Core/Mail/MessageWrapper.php b/application/Espo/Core/Mail/MessageWrapper.php index aabdc27550..b788fff3f6 100644 --- a/application/Espo/Core/Mail/MessageWrapper.php +++ b/application/Espo/Core/Mail/MessageWrapper.php @@ -29,16 +29,16 @@ namespace Espo\Core\Mail; -use Espo\Core\Mail\Mail\Storage\Imap; +use Espo\Core\Mail\Account\Storage; class MessageWrapper implements Message { + private int $id; + private $parser; private $storage; - private $id; - private $rawHeader = null; private $rawContent = null; @@ -47,8 +47,12 @@ class MessageWrapper implements Message private $flagList = null; - public function __construct(?Imap $storage = null, ?string $id = null, ?Parser $parser = null) - { + public function __construct( + int $id, + ?Storage $storage = null, + ?Parser $parser = null, + ?string $fullRawContent = null + ) { if ($storage) { $data = $storage->getHeaderAndFlags($id); @@ -59,11 +63,7 @@ class MessageWrapper implements Message $this->id = $id; $this->storage = $storage; $this->parser = $parser; - } - - public function setFullRawContent(string $content): void - { - $this->fullRawContent = $content; + $this->fullRawContent = $fullRawContent; } public function getRawHeader(): string @@ -89,7 +89,7 @@ class MessageWrapper implements Message public function getRawContent(): string { if (is_null($this->rawContent)) { - $this->rawContent = $this->storage->getRawContent((int) $this->id); + $this->rawContent = $this->storage->getRawContent($this->id); } return $this->rawContent ?? ''; diff --git a/tests/unit/Espo/Core/Mail/ImporterDataTest.php b/tests/unit/Espo/Core/Mail/ImporterDataTest.php index 037b2f66d6..142aa5d87c 100644 --- a/tests/unit/Espo/Core/Mail/ImporterDataTest.php +++ b/tests/unit/Espo/Core/Mail/ImporterDataTest.php @@ -29,13 +29,10 @@ namespace tests\unit\Espo\Core\Mail; -use Espo\Core\{ - Mail\ImporterData, -}; +use Espo\Core\Mail\Importer\Data as ImporterData; use Espo\Entities\EmailFilter; - class ImporterDataTest extends \PHPUnit\Framework\TestCase { diff --git a/tests/unit/Espo/Core/Mail/ImporterTest.php b/tests/unit/Espo/Core/Mail/ImporterTest.php index b67fe8e1f5..e3152b34ea 100644 --- a/tests/unit/Espo/Core/Mail/ImporterTest.php +++ b/tests/unit/Espo/Core/Mail/ImporterTest.php @@ -165,9 +165,7 @@ class ImporterTest extends \PHPUnit\Framework\TestCase $this->linkMultipleSaver ); - $message = new MessageWrapper(); - - $message->setFullRawContent($contents); + $message = new MessageWrapper(0, null, null, $contents); $data = ImporterData ::create()