snapshot of https://github.com/espocrm/espocrm.git (master, 2026-09-18; blobs > 131072B omitted)
Some checks failed
Test / Test on PHP 8.3 (push) Failing after 5s
Test / Test on PHP 8.4 (push) Failing after 4s
Test / Test on PHP 8.5 (push) Failing after 3s
TypeScript static analysis / TypeScript check (push) Failing after 14s
Test Integration on MySQL / Test on PHP 8.3 (push) Failing after 2s
Test Integration on MySQL / Test on PHP 8.5 (push) Failing after 0s
Test Integration on PostgreSQL / Test on PHP 8.4 fix (push) Failing after 1s
Test Integration on PostgreSQL / Test on PHP 8.4 master (push) Failing after 1s
Some checks failed
Test / Test on PHP 8.3 (push) Failing after 5s
Test / Test on PHP 8.4 (push) Failing after 4s
Test / Test on PHP 8.5 (push) Failing after 3s
TypeScript static analysis / TypeScript check (push) Failing after 14s
Test Integration on MySQL / Test on PHP 8.3 (push) Failing after 2s
Test Integration on MySQL / Test on PHP 8.5 (push) Failing after 0s
Test Integration on PostgreSQL / Test on PHP 8.4 fix (push) Failing after 1s
Test Integration on PostgreSQL / Test on PHP 8.4 master (push) Failing after 1s
This commit is contained in:
357
application/Espo/Core/Mail/Account/PersonalAccount/Account.php
Normal file
357
application/Espo/Core/Mail/Account/PersonalAccount/Account.php
Normal file
@@ -0,0 +1,357 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2026 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\ORM\Type\FieldType;
|
||||
use Espo\Core\Utils\Config;
|
||||
use Espo\Core\Utils\Crypt;
|
||||
use Espo\Entities\EmailAccount;
|
||||
use Espo\Entities\InboundEmail;
|
||||
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 Espo\ORM\Mapper\Util;
|
||||
use Espo\ORM\Name\Attribute;
|
||||
use Espo\ORM\Query\UpdateBuilder;
|
||||
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
|
||||
{
|
||||
$query = UpdateBuilder::create()
|
||||
->in(EmailAccount::ENTITY_TYPE)
|
||||
->where([
|
||||
InboundEmail::FIELD_FETCH_VALIDITY_NUMBER => $fetchData->getValidityNumber(),
|
||||
Attribute::ID => $this->entity->getId(),
|
||||
Attribute::DELETED => false,
|
||||
])
|
||||
->set([
|
||||
InboundEmail::FIELD_FETCH_DATA => Util::prepareValue(FieldType::JSON_OBJECT, $fetchData->getRaw()),
|
||||
])
|
||||
->build();
|
||||
|
||||
$sth = $this->entityManager->getQueryExecutor()->execute($query);
|
||||
|
||||
if ($sth->rowCount() !== 0) {
|
||||
$this->entity->setFetchData($fetchData);
|
||||
$this->entity->setAsFetched();
|
||||
}
|
||||
}
|
||||
|
||||
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(),
|
||||
$this->entity->getFetchValidityNumber(),
|
||||
);
|
||||
}
|
||||
|
||||
public function getFetchSince(): ?Date
|
||||
{
|
||||
return $this->entity->getFetchSince();
|
||||
}
|
||||
|
||||
public function getEmailFolder(): ?Link
|
||||
{
|
||||
return $this->entity->getEmailFolder();
|
||||
}
|
||||
|
||||
public function getMappedEmailFolder(string $folder): ?Link
|
||||
{
|
||||
return $this->entity->getMappedEmailFolder($folder);
|
||||
}
|
||||
|
||||
/**
|
||||
* @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-2026 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-2026 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-2026 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);
|
||||
}
|
||||
}
|
||||
211
application/Espo/Core/Mail/Account/PersonalAccount/Service.php
Normal file
211
application/Espo/Core/Mail/Account/PersonalAccount/Service.php
Normal file
@@ -0,0 +1,211 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2026 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\AddressUtil;
|
||||
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\Core\Utils\Security\HostCheck;
|
||||
use Espo\Entities\User;
|
||||
use Espo\Core\Mail\Sender\Message;
|
||||
|
||||
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,
|
||||
private HostCheck $hostCheck,
|
||||
private AddressUtil $addressUtil,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* @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
|
||||
{
|
||||
$params = $this->connectPreProcess($params);
|
||||
|
||||
try {
|
||||
$storage = $this->storageFactory->createWithParams($params);
|
||||
|
||||
return $storage->getFolderNames();
|
||||
} catch (Exception $e) {
|
||||
$this->log->warning("IMAP get folder list failed.", [
|
||||
'exception' => $e,
|
||||
]);
|
||||
|
||||
$message = $e instanceof ImapError ? $e->getMessage() : '';
|
||||
|
||||
throw new ErrorSilent($message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Forbidden
|
||||
* @throws Error
|
||||
*/
|
||||
public function testConnection(Params $params): void
|
||||
{
|
||||
$params = $this->connectPreProcess($params);
|
||||
|
||||
try {
|
||||
$storage = $this->storageFactory->createWithParams($params);
|
||||
|
||||
$storage->getFolderNames();
|
||||
} catch (Exception $e) {
|
||||
$this->log->warning("IMAP test connection failed.", [
|
||||
'exception' => $e,
|
||||
]);
|
||||
|
||||
$message = $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);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Error
|
||||
* @throws Forbidden
|
||||
*/
|
||||
private function connectPreProcess(Params $params): Params
|
||||
{
|
||||
$userId = $params->getUserId();
|
||||
|
||||
if (
|
||||
$userId &&
|
||||
!$this->user->isEffectiveAdmin() &&
|
||||
$userId !== $this->user->getId()
|
||||
) {
|
||||
throw new Forbidden();
|
||||
}
|
||||
|
||||
if (!$params->getId() && $params->getPassword() === null) {
|
||||
throw new Forbidden();
|
||||
}
|
||||
|
||||
if (
|
||||
$params->getHost() &&
|
||||
!$this->addressUtil->isAllowedAddress($params) &&
|
||||
!$this->hostCheck->isHostAndNotInternal($params->getHost())
|
||||
) {
|
||||
throw new Forbidden("Not allowed internal host.");
|
||||
}
|
||||
|
||||
if ($params->getId()) {
|
||||
$account = $this->accountFactory->create($params->getId());
|
||||
|
||||
if (
|
||||
!$this->user->isEffectiveAdmin() &&
|
||||
$account->getUser()->getId() !== $this->user->getId()
|
||||
) {
|
||||
throw new Forbidden();
|
||||
}
|
||||
|
||||
$params = $params
|
||||
->withPassword($this->getPassword($params, $account))
|
||||
->withImapHandlerClassName($account->getImapHandlerClassName());
|
||||
}
|
||||
|
||||
return $params;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2026 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\CommonStorageFactory;
|
||||
use Espo\Core\Mail\Account\Storage;
|
||||
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\NoImap;
|
||||
|
||||
use LogicException;
|
||||
|
||||
class StorageFactory implements StorageFactoryInterface
|
||||
{
|
||||
public function __construct(
|
||||
private CommonStorageFactory $commonStorageFactory,
|
||||
) {}
|
||||
|
||||
public function create(Account $account): Storage
|
||||
{
|
||||
$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): Storage
|
||||
{
|
||||
return $this->commonStorageFactory->create($params);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user