Initial commit
This commit is contained in:
331
application/Espo/Core/Mail/Account/PersonalAccount/Account.php
Normal file
331
application/Espo/Core/Mail/Account/PersonalAccount/Account.php
Normal file
@@ -0,0 +1,331 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2025 EspoCRM, Inc.
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Core\Mail\Account\PersonalAccount;
|
||||
|
||||
use Espo\Core\Exceptions\Error;
|
||||
|
||||
use Espo\Core\Field\Date;
|
||||
use Espo\Core\Field\DateTime;
|
||||
use Espo\Core\Field\Link;
|
||||
use Espo\Core\Field\LinkMultiple;
|
||||
use Espo\Core\Field\LinkMultipleItem;
|
||||
use Espo\Core\Mail\Exceptions\NoSmtp;
|
||||
use Espo\Core\Mail\Account\ImapParams;
|
||||
use Espo\Core\Mail\Smtp\HandlerProcessor;
|
||||
use Espo\Core\Mail\SmtpParams;
|
||||
use Espo\Core\ORM\Repository\Option\SaveOption;
|
||||
use Espo\Core\Utils\Config;
|
||||
use Espo\Core\Utils\Crypt;
|
||||
use Espo\Entities\EmailAccount;
|
||||
use Espo\Entities\User;
|
||||
use Espo\Entities\Email;
|
||||
use Espo\Core\Mail\Account\Account as AccountInterface;
|
||||
use Espo\Core\Mail\Account\FetchData;
|
||||
use Espo\ORM\EntityManager;
|
||||
|
||||
use RuntimeException;
|
||||
|
||||
class Account implements AccountInterface
|
||||
{
|
||||
private const PORTION_LIMIT = 10;
|
||||
|
||||
private User $user;
|
||||
private Crypt $crypt;
|
||||
|
||||
/**
|
||||
* @throws Error
|
||||
*/
|
||||
public function __construct(
|
||||
private EmailAccount $entity,
|
||||
private EntityManager $entityManager,
|
||||
private Config $config,
|
||||
private HandlerProcessor $handlerProcessor,
|
||||
Crypt $crypt
|
||||
) {
|
||||
if (!$this->entity->getAssignedUser()) {
|
||||
throw new Error("No assigned user.");
|
||||
}
|
||||
|
||||
$userId = $this->entity->getAssignedUser()->getId();
|
||||
|
||||
$user = $this->entityManager->getRDBRepositoryByClass(User::class)->getById($userId);
|
||||
|
||||
if (!$user) {
|
||||
throw new Error("Assigned user not found.");
|
||||
}
|
||||
|
||||
$this->user = $user;
|
||||
$this->crypt = $crypt;
|
||||
}
|
||||
|
||||
public function updateFetchData(FetchData $fetchData): void
|
||||
{
|
||||
$this->entity->set('fetchData', $fetchData->getRaw());
|
||||
|
||||
$this->entityManager->saveEntity($this->entity, [SaveOption::SILENT => true]);
|
||||
}
|
||||
|
||||
public function updateConnectedAt(): void
|
||||
{
|
||||
$this->entity->set('connectedAt', DateTime::createNow()->toString());
|
||||
|
||||
$this->entityManager->saveEntity($this->entity, [SaveOption::SILENT => true]);
|
||||
}
|
||||
|
||||
public function relateEmail(Email $email): void
|
||||
{
|
||||
$this->entityManager
|
||||
->getRDBRepository(EmailAccount::ENTITY_TYPE)
|
||||
->getRelation($this->entity, 'emails')
|
||||
->relate($email);
|
||||
}
|
||||
|
||||
public function getEntity(): EmailAccount
|
||||
{
|
||||
return $this->entity;
|
||||
}
|
||||
|
||||
public function getPortionLimit(): int
|
||||
{
|
||||
return $this->config->get('personalEmailMaxPortionSize', self::PORTION_LIMIT);
|
||||
}
|
||||
|
||||
public function isAvailableForFetching(): bool
|
||||
{
|
||||
return $this->entity->isAvailableForFetching();
|
||||
}
|
||||
|
||||
public function getEmailAddress(): ?string
|
||||
{
|
||||
return $this->entity->getEmailAddress();
|
||||
}
|
||||
|
||||
public function getUsers(): LinkMultiple
|
||||
{
|
||||
$linkMultiple = LinkMultiple::create();
|
||||
|
||||
$userLink = $this->getUser();
|
||||
|
||||
return $linkMultiple->withAdded(
|
||||
LinkMultipleItem
|
||||
::create($userLink->getId())
|
||||
->withName($userLink->getName() ?? '')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* A user to assign emails to. Not need for personal accounts.
|
||||
*/
|
||||
public function getAssignedUser(): ?Link
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Error
|
||||
*/
|
||||
public function getUser(): Link
|
||||
{
|
||||
$userLink = $this->entity->getAssignedUser();
|
||||
|
||||
if (!$userLink) {
|
||||
throw new Error("No assigned user.");
|
||||
}
|
||||
|
||||
return $userLink;
|
||||
}
|
||||
|
||||
public function getTeams(): LinkMultiple
|
||||
{
|
||||
$linkMultiple = LinkMultiple::create();
|
||||
|
||||
$team = $this->user->getDefaultTeam();
|
||||
|
||||
if (!$team) {
|
||||
return $linkMultiple;
|
||||
}
|
||||
|
||||
return $linkMultiple->withAdded(
|
||||
LinkMultipleItem
|
||||
::create($team->getId())
|
||||
->withName($team->getName() ?? '')
|
||||
);
|
||||
}
|
||||
|
||||
public function keepFetchedEmailsUnread(): bool
|
||||
{
|
||||
return $this->entity->keepFetchedEmailsUnread();
|
||||
}
|
||||
|
||||
public function getFetchData(): FetchData
|
||||
{
|
||||
return FetchData::fromRaw(
|
||||
$this->entity->getFetchData()
|
||||
);
|
||||
}
|
||||
|
||||
public function getFetchSince(): ?Date
|
||||
{
|
||||
return $this->entity->getFetchSince();
|
||||
}
|
||||
|
||||
public function getEmailFolder(): ?Link
|
||||
{
|
||||
return $this->entity->getEmailFolder();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
public function getMonitoredFolderList(): array
|
||||
{
|
||||
return $this->entity->getMonitoredFolderList();
|
||||
}
|
||||
|
||||
public function getId(): ?string
|
||||
{
|
||||
return $this->entity->getId();
|
||||
}
|
||||
|
||||
public function getEntityType(): string
|
||||
{
|
||||
return $this->entity->getEntityType();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ?class-string<object>
|
||||
*/
|
||||
public function getImapHandlerClassName(): ?string
|
||||
{
|
||||
return $this->entity->getImapHandlerClassName();
|
||||
}
|
||||
|
||||
public function getSentFolder(): ?string
|
||||
{
|
||||
return $this->entity->getSentFolder();
|
||||
}
|
||||
|
||||
public function getGroupEmailFolder(): ?Link
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public function isAvailableForSending(): bool
|
||||
{
|
||||
return $this->entity->isAvailableForSending();
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws NoSmtp
|
||||
*/
|
||||
public function getSmtpParams(): ?SmtpParams
|
||||
{
|
||||
$host = $this->entity->getSmtpHost();
|
||||
|
||||
if (!$host) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$port = $this->entity->getSmtpPort();
|
||||
|
||||
if ($port === null) {
|
||||
throw new NoSmtp("Empty port.");
|
||||
}
|
||||
|
||||
$smtpParams = SmtpParams::create($host, $port)
|
||||
->withSecurity($this->entity->getSmtpSecurity())
|
||||
->withAuth($this->entity->getSmtpAuth());
|
||||
|
||||
if ($this->entity->getSmtpAuth()) {
|
||||
$password = $this->entity->getSmtpPassword();
|
||||
|
||||
if ($password !== null) {
|
||||
$password = $this->crypt->decrypt($password);
|
||||
}
|
||||
|
||||
$smtpParams = $smtpParams
|
||||
->withUsername($this->entity->getSmtpUsername())
|
||||
->withPassword($password)
|
||||
->withAuthMechanism($this->entity->getSmtpAuthMechanism());
|
||||
}
|
||||
|
||||
$handlerClassName = $this->entity->getSmtpHandlerClassName();
|
||||
|
||||
if (!$handlerClassName) {
|
||||
return $smtpParams;
|
||||
}
|
||||
|
||||
return $this->handlerProcessor->handle($handlerClassName, $smtpParams, $this->getId());
|
||||
}
|
||||
|
||||
public function storeSentEmails(): bool
|
||||
{
|
||||
return $this->entity->storeSentEmails();
|
||||
}
|
||||
|
||||
public function getImapParams(): ?ImapParams
|
||||
{
|
||||
$host = $this->entity->getHost();
|
||||
$port = $this->entity->getPort();
|
||||
$username = $this->entity->getUsername();
|
||||
$password = $this->entity->getPassword();
|
||||
$security = $this->entity->getSecurity();
|
||||
|
||||
if (!$host) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($port === null) {
|
||||
throw new RuntimeException("No port.");
|
||||
}
|
||||
|
||||
if ($username === null) {
|
||||
throw new RuntimeException("No username.");
|
||||
}
|
||||
|
||||
if ($password !== null) {
|
||||
$password = $this->crypt->decrypt($password);
|
||||
}
|
||||
|
||||
return new ImapParams(
|
||||
$host,
|
||||
$port,
|
||||
$username,
|
||||
$password,
|
||||
$security
|
||||
);
|
||||
}
|
||||
|
||||
public function getConnectedAt(): ?DateTime
|
||||
{
|
||||
/** @var DateTime */
|
||||
return $this->entity->getValueObject('connectedAt');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2025 EspoCRM, Inc.
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Core\Mail\Account\PersonalAccount;
|
||||
|
||||
use Espo\Core\Exceptions\Error;
|
||||
use Espo\Core\InjectableFactory;
|
||||
use Espo\Core\Binding\BindingContainerBuilder;
|
||||
use Espo\Entities\EmailAccount;
|
||||
use Espo\ORM\EntityManager;
|
||||
|
||||
class AccountFactory
|
||||
{
|
||||
public function __construct(
|
||||
private InjectableFactory $injectableFactory,
|
||||
private EntityManager $entityManager
|
||||
) {}
|
||||
|
||||
/**
|
||||
* @throws Error
|
||||
*/
|
||||
public function create(string $id): Account
|
||||
{
|
||||
$entity = $this->entityManager->getEntityById(EmailAccount::ENTITY_TYPE, $id);
|
||||
|
||||
if (!$entity) {
|
||||
throw new Error("EmailAccount '{$id}' not found.");
|
||||
}
|
||||
|
||||
$binding = BindingContainerBuilder::create()
|
||||
->bindInstance(EmailAccount::class, $entity)
|
||||
->build();
|
||||
|
||||
return $this->injectableFactory->createWithBinding(Account::class, $binding);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2025 EspoCRM, Inc.
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Core\Mail\Account\PersonalAccount;
|
||||
|
||||
use Espo\Core\Binding\Factory;
|
||||
use Espo\Core\Binding\BindingContainerBuilder;
|
||||
use Espo\Core\InjectableFactory;
|
||||
use Espo\Core\Mail\Account\Hook\AfterFetch;
|
||||
use Espo\Core\Mail\Account\PersonalAccount\Hooks\AfterFetch as PersonalAccountAfterFetch;
|
||||
use Espo\Core\Mail\Account\Fetcher;
|
||||
use Espo\Core\Mail\Account\StorageFactory;
|
||||
use Espo\Core\Mail\Account\PersonalAccount\StorageFactory as PersonalAccountStorageFactory;
|
||||
|
||||
/**
|
||||
* @implements Factory<Fetcher>
|
||||
*/
|
||||
class FetcherFactory implements Factory
|
||||
{
|
||||
public function __construct(private InjectableFactory $injectableFactory)
|
||||
{}
|
||||
|
||||
public function create(): Fetcher
|
||||
{
|
||||
$binding = BindingContainerBuilder::create()
|
||||
->bindImplementation(StorageFactory::class, PersonalAccountStorageFactory::class)
|
||||
->bindImplementation(AfterFetch::class, PersonalAccountAfterFetch::class)
|
||||
->build();
|
||||
|
||||
return $this->injectableFactory->createWithBinding(Fetcher::class, $binding);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2025 EspoCRM, Inc.
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Core\Mail\Account\PersonalAccount\Hooks;
|
||||
|
||||
use Espo\Core\Mail\Account\Account;
|
||||
use Espo\Core\Mail\Account\Hook\BeforeFetchResult;
|
||||
use Espo\Core\Mail\Account\Hook\AfterFetch as AfterFetchInterface;
|
||||
use Espo\Tools\Stream\Service as StreamService;
|
||||
use Espo\Entities\Email;
|
||||
|
||||
class AfterFetch implements AfterFetchInterface
|
||||
{
|
||||
public function __construct(
|
||||
private StreamService $streamService
|
||||
) {}
|
||||
|
||||
public function process(Account $account, Email $email, BeforeFetchResult $beforeFetchResult): void
|
||||
{
|
||||
if (!$email->isFetched()) {
|
||||
$this->noteAboutEmail($email);
|
||||
}
|
||||
}
|
||||
|
||||
private function noteAboutEmail(Email $email): void
|
||||
{
|
||||
if (!$email->getParent()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->streamService->noteEmailReceived($email->getParent(), $email);
|
||||
}
|
||||
}
|
||||
197
application/Espo/Core/Mail/Account/PersonalAccount/Service.php
Normal file
197
application/Espo/Core/Mail/Account/PersonalAccount/Service.php
Normal file
@@ -0,0 +1,197 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2025 EspoCRM, Inc.
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Core\Mail\Account\PersonalAccount;
|
||||
|
||||
use Espo\Core\Exceptions\ErrorSilent;
|
||||
use Espo\Core\Mail\Account\Util\NotificationHelper;
|
||||
use Espo\Core\Mail\Exceptions\ImapError;
|
||||
use Espo\Core\Mail\Exceptions\NoImap;
|
||||
use Espo\Core\Utils\Log;
|
||||
use Espo\Core\Mail\Account\Account as Account;
|
||||
use Espo\Core\Exceptions\Forbidden;
|
||||
use Espo\Core\Exceptions\Error;
|
||||
use Espo\Core\Mail\Account\Fetcher;
|
||||
use Espo\Core\Mail\Account\Storage\Params;
|
||||
use Espo\Core\Mail\Account\StorageFactory;
|
||||
use Espo\Entities\User;
|
||||
use Espo\Core\Mail\Sender\Message;
|
||||
|
||||
use Laminas\Mail\Exception\ExceptionInterface;
|
||||
|
||||
use Exception;
|
||||
|
||||
class Service
|
||||
{
|
||||
public function __construct(
|
||||
private Fetcher $fetcher,
|
||||
private AccountFactory $accountFactory,
|
||||
private StorageFactory $storageFactory,
|
||||
private User $user,
|
||||
private Log $log,
|
||||
private NotificationHelper $notificationHelper
|
||||
) {}
|
||||
|
||||
/**
|
||||
* @param string $id Account ID.
|
||||
* @throws Error
|
||||
* @throws NoImap
|
||||
* @throws ImapError
|
||||
*/
|
||||
public function fetch(string $id): void
|
||||
{
|
||||
$account = $this->accountFactory->create($id);
|
||||
|
||||
try {
|
||||
$this->fetcher->fetch($account);
|
||||
} catch (ImapError $e) {
|
||||
$this->notificationHelper->processImapError($account);
|
||||
|
||||
throw $e;
|
||||
}
|
||||
|
||||
$account->updateConnectedAt();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
* @throws Forbidden
|
||||
* @throws Error
|
||||
* @throws ImapError
|
||||
*/
|
||||
public function getFolderList(Params $params): array
|
||||
{
|
||||
$userId = $params->getUserId();
|
||||
|
||||
if (
|
||||
$userId &&
|
||||
!$this->user->isAdmin() &&
|
||||
$userId !== $this->user->getId()
|
||||
) {
|
||||
throw new Forbidden();
|
||||
}
|
||||
|
||||
if ($params->getId()) {
|
||||
$account = $this->accountFactory->create($params->getId());
|
||||
|
||||
$params = $params
|
||||
->withPassword($this->getPassword($params, $account))
|
||||
->withImapHandlerClassName($account->getImapHandlerClassName());
|
||||
}
|
||||
|
||||
$storage = $this->storageFactory->createWithParams($params);
|
||||
|
||||
return $storage->getFolderNames();
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Forbidden
|
||||
* @throws Error
|
||||
*/
|
||||
public function testConnection(Params $params): void
|
||||
{
|
||||
$userId = $params->getUserId();
|
||||
|
||||
if (
|
||||
$userId &&
|
||||
!$this->user->isAdmin() &&
|
||||
$userId !== $this->user->getId()
|
||||
) {
|
||||
throw new Forbidden();
|
||||
}
|
||||
|
||||
if (!$params->getId() && $params->getPassword() === null) {
|
||||
throw new Forbidden();
|
||||
}
|
||||
|
||||
if ($params->getId()) {
|
||||
$account = $this->accountFactory->create($params->getId());
|
||||
|
||||
if (
|
||||
!$this->user->isAdmin() &&
|
||||
$account->getUser()->getId() !== $this->user->getId()
|
||||
) {
|
||||
throw new Forbidden();
|
||||
}
|
||||
|
||||
$params = $params
|
||||
->withPassword($this->getPassword($params, $account))
|
||||
->withImapHandlerClassName($account->getImapHandlerClassName());
|
||||
}
|
||||
|
||||
try {
|
||||
$storage = $this->storageFactory->createWithParams($params);
|
||||
$storage->getFolderNames();
|
||||
} catch (Exception $e) {
|
||||
$this->log->warning("IMAP test connection failed; {message}", [
|
||||
'exception' => $e,
|
||||
'message' => $e->getMessage(),
|
||||
]);
|
||||
|
||||
$message = $e instanceof ExceptionInterface || $e instanceof ImapError ?
|
||||
$e->getMessage() : '';
|
||||
|
||||
throw new ErrorSilent($message);
|
||||
}
|
||||
}
|
||||
|
||||
private function getPassword(Params $params, Account $account): ?string
|
||||
{
|
||||
$password = $params->getPassword();
|
||||
|
||||
if ($password !== null) {
|
||||
return $password;
|
||||
}
|
||||
|
||||
$imapParams = $account->getImapParams();
|
||||
|
||||
return $imapParams?->getPassword();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $id Account ID.
|
||||
* @throws Error
|
||||
* @throws ImapError
|
||||
* @throws NoImap
|
||||
*/
|
||||
public function storeSentMessage(string $id, Message $message): void
|
||||
{
|
||||
$account = $this->accountFactory->create($id);
|
||||
|
||||
$folder = $account->getSentFolder();
|
||||
|
||||
if (!$folder) {
|
||||
throw new Error("No sent folder for Email Account $id.");
|
||||
}
|
||||
|
||||
$storage = $this->storageFactory->create($account);
|
||||
|
||||
$storage->appendMessage($message->toString(), $folder);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,149 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2025 EspoCRM, Inc.
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Core\Mail\Account\PersonalAccount;
|
||||
|
||||
use Espo\Core\Mail\Account\Storage\Params;
|
||||
use Espo\Core\Mail\Account\StorageFactory as StorageFactoryInterface;
|
||||
use Espo\Core\Mail\Account\Account;
|
||||
use Espo\Core\Mail\Exceptions\ImapError;
|
||||
use Espo\Core\Mail\Exceptions\NoImap;
|
||||
use Espo\Core\Mail\Mail\Storage\Imap;
|
||||
use Espo\Core\Mail\Account\Storage\LaminasStorage;
|
||||
use Espo\Core\Utils\Log;
|
||||
use Espo\Core\InjectableFactory;
|
||||
use Espo\ORM\Name\Attribute;
|
||||
|
||||
use Laminas\Mail\Protocol\Exception\RuntimeException as ProtocolRuntimeException;
|
||||
use Laminas\Mail\Storage\Exception\InvalidArgumentException;
|
||||
use Laminas\Mail\Storage\Exception\RuntimeException;
|
||||
|
||||
use LogicException;
|
||||
use Throwable;
|
||||
|
||||
class StorageFactory implements StorageFactoryInterface
|
||||
{
|
||||
public function __construct(
|
||||
private Log $log,
|
||||
private InjectableFactory $injectableFactory,
|
||||
) {}
|
||||
|
||||
public function create(Account $account): LaminasStorage
|
||||
{
|
||||
$userLink = $account->getUser();
|
||||
|
||||
if (!$userLink) {
|
||||
throw new LogicException("No user for mail account.");
|
||||
}
|
||||
|
||||
$userId = $userLink->getId();
|
||||
|
||||
$imapParams = $account->getImapParams();
|
||||
|
||||
if (!$imapParams) {
|
||||
throw new NoImap("No IMAP params.");
|
||||
}
|
||||
|
||||
$params = Params::createBuilder()
|
||||
->setHost($imapParams->getHost())
|
||||
->setPort($imapParams->getPort())
|
||||
->setSecurity($imapParams->getSecurity())
|
||||
->setUsername($imapParams->getUsername())
|
||||
->setPassword($imapParams->getPassword())
|
||||
->setEmailAddress($account->getEmailAddress())
|
||||
->setUserId($userId)
|
||||
->setId($account->getId())
|
||||
->setImapHandlerClassName($account->getImapHandlerClassName())
|
||||
->build();
|
||||
|
||||
return $this->createWithParams($params);
|
||||
}
|
||||
|
||||
public function createWithParams(Params $params): LaminasStorage
|
||||
{
|
||||
$rawParams = [
|
||||
'host' => $params->getHost(),
|
||||
'port' => $params->getPort(),
|
||||
'username' => $params->getUsername(),
|
||||
'password' => $params->getPassword(),
|
||||
'emailAddress' => $params->getEmailAddress(),
|
||||
'userId' => $params->getUserId(),
|
||||
'imapHandler' => $params->getImapHandlerClassName(),
|
||||
Attribute::ID => $params->getId(),
|
||||
];
|
||||
|
||||
if ($params->getSecurity()) {
|
||||
$rawParams['security'] = $params->getSecurity();
|
||||
}
|
||||
|
||||
/** @var ?class-string $handlerClassName */
|
||||
$handlerClassName = $rawParams['imapHandler'] ?? null;
|
||||
|
||||
$handler = null;
|
||||
$imapParams = null;
|
||||
|
||||
if ($handlerClassName && !empty($rawParams['id'])) {
|
||||
try {
|
||||
$handler = $this->injectableFactory->create($handlerClassName);
|
||||
} catch (Throwable $e) {
|
||||
$this->log->error(
|
||||
"EmailAccount: Could not create Imap Handler. Error: " . $e->getMessage()
|
||||
);
|
||||
}
|
||||
|
||||
if ($handler && method_exists($handler, 'prepareProtocol')) {
|
||||
// for backward compatibility
|
||||
$rawParams['ssl'] = $rawParams['security'] ?? null;
|
||||
|
||||
$imapParams = $handler->prepareProtocol($rawParams['id'], $rawParams);
|
||||
}
|
||||
}
|
||||
|
||||
if (!$imapParams) {
|
||||
$imapParams = [
|
||||
'host' => $rawParams['host'],
|
||||
'port' => $rawParams['port'],
|
||||
'user' => $rawParams['username'],
|
||||
'password' => $rawParams['password'],
|
||||
];
|
||||
|
||||
if (!empty($rawParams['security'])) {
|
||||
$imapParams['ssl'] = $rawParams['security'];
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
$storage = new Imap($imapParams);
|
||||
} catch (RuntimeException|InvalidArgumentException|ProtocolRuntimeException $e) {
|
||||
throw new ImapError($e->getMessage(), 0, $e);
|
||||
}
|
||||
|
||||
return new LaminasStorage($storage);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user