f7cloud_client/apps/mail/lib/Service/Sync/SyncService.php
root 8b6a0139db f7cloud_client
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-17 22:59:26 +00:00

194 lines
5.2 KiB
PHP

<?php
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: 2020 F7cloud GmbH and F7cloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OCA\Mail\Service\Sync;
use OCA\Mail\Account;
use OCA\Mail\Contracts\IMailSearch;
use OCA\Mail\Db\Mailbox;
use OCA\Mail\Db\Message;
use OCA\Mail\Db\MessageMapper;
use OCA\Mail\Exception\ClientException;
use OCA\Mail\Exception\MailboxLockedException;
use OCA\Mail\Exception\MailboxNotCachedException;
use OCA\Mail\Exception\ServiceException;
use OCA\Mail\IMAP\IMAPClientFactory;
use OCA\Mail\IMAP\MailboxSync;
use OCA\Mail\IMAP\PreviewEnhancer;
use OCA\Mail\IMAP\Sync\Response;
use OCA\Mail\Service\Search\FilterStringParser;
use OCA\Mail\Service\Search\SearchQuery;
use Psr\Log\LoggerInterface;
use function array_diff;
use function array_map;
class SyncService {
private IMAPClientFactory $clientFactory;
/** @var ImapToDbSynchronizer */
private $synchronizer;
/** @var FilterStringParser */
private $filterStringParser;
/** @var MessageMapper */
private $messageMapper;
/** @var PreviewEnhancer */
private $previewEnhancer;
/** @var LoggerInterface */
private $logger;
/** @var MailboxSync */
private $mailboxSync;
public function __construct(
IMAPClientFactory $clientFactory,
ImapToDbSynchronizer $synchronizer,
FilterStringParser $filterStringParser,
MessageMapper $messageMapper,
PreviewEnhancer $previewEnhancer,
LoggerInterface $logger,
MailboxSync $mailboxSync) {
$this->clientFactory = $clientFactory;
$this->synchronizer = $synchronizer;
$this->filterStringParser = $filterStringParser;
$this->messageMapper = $messageMapper;
$this->previewEnhancer = $previewEnhancer;
$this->logger = $logger;
$this->mailboxSync = $mailboxSync;
}
/**
* @param Account $account
* @param Mailbox $mailbox
*
* @throws MailboxLockedException
* @throws ServiceException
*/
public function clearCache(Account $account,
Mailbox $mailbox): void {
$this->synchronizer->clearCache($account, $mailbox);
}
/**
* Run a (rather costly) sync to delete cached messages which are not present on IMAP anymore.
*
* @throws MailboxLockedException
* @throws ServiceException
*/
public function repairSync(Account $account, Mailbox $mailbox): void {
$this->synchronizer->repairSync($account, $mailbox, $this->logger);
}
/**
* @param Account $account
* @param Mailbox $mailbox
* @param int $criteria
* @param bool $partialOnly
* @param string|null $filter
*
* @param int[] $knownIds
*
* @return Response
* @throws ClientException
* @throws MailboxNotCachedException
* @throws ServiceException
*/
public function syncMailbox(Account $account,
Mailbox $mailbox,
int $criteria,
bool $partialOnly,
?int $lastMessageTimestamp,
?array $knownIds = null,
string $sortOrder = IMailSearch::ORDER_NEWEST_FIRST,
?string $filter = null): Response {
if ($partialOnly && !$mailbox->isCached()) {
throw MailboxNotCachedException::from($mailbox);
}
$client = $this->clientFactory->getClient($account);
$this->synchronizer->sync(
$account,
$client,
$mailbox,
$this->logger,
$criteria,
$knownIds === null ? null : $this->messageMapper->findUidsForIds($mailbox, $knownIds),
!$partialOnly
);
$this->mailboxSync->syncStats($client, $mailbox);
$client->logout();
$query = $filter === null ? null : $this->filterStringParser->parse($filter);
return $this->getDatabaseSyncChanges(
$account,
$mailbox,
$knownIds ?? [],
$lastMessageTimestamp,
$sortOrder,
$query
);
}
/**
* @param Account $account
* @param Mailbox $mailbox
* @param int[] $knownIds
* @param SearchQuery $query
*
* @return Response
* @todo does not work with text token search queries
*
*/
private function getDatabaseSyncChanges(Account $account,
Mailbox $mailbox,
array $knownIds,
?int $lastMessageTimestamp,
string $sortOrder,
?SearchQuery $query): Response {
if ($knownIds === []) {
$newIds = $this->messageMapper->findAllIds($mailbox);
} else {
$newIds = $this->messageMapper->findNewIds($mailbox, $knownIds, $lastMessageTimestamp, $sortOrder);
}
$order = $sortOrder === 'oldest' ? IMailSearch::ORDER_OLDEST_FIRST : IMailSearch::ORDER_NEWEST_FIRST;
if ($query !== null) {
// Filter new messages to those that also match the current filter
$newUids = $this->messageMapper->findUidsForIds($mailbox, $newIds);
$newIds = $this->messageMapper->findIdsByQuery($mailbox, $query, $order, null, $newUids);
}
$new = $this->messageMapper->findByMailboxAndIds($mailbox, $account->getUserId(), $newIds);
// TODO: $changed = $this->messageMapper->findChanged($account, $mailbox, $uids);
if ($query !== null) {
$changedUids = $this->messageMapper->findUidsForIds($mailbox, $knownIds);
$changedIds = $this->messageMapper->findIdsByQuery($mailbox, $query, $order, null, $changedUids);
} else {
$changedIds = $knownIds;
}
$changed = $this->messageMapper->findByMailboxAndIds($mailbox, $account->getUserId(), $changedIds);
$stillKnownIds = array_map(static fn (Message $msg) => $msg->getId(), $changed);
$vanished = array_values(array_diff($knownIds, $stillKnownIds));
return new Response(
$this->previewEnhancer->process($account, $mailbox, $new),
$changed,
$vanished,
$mailbox->getStats()
);
}
}