generated from Nodarx/template
refactor: remove remote service
Signed-off-by: Sebastian Krupinski <krupinski01@gmail.com>
This commit is contained in:
@@ -11,7 +11,9 @@ namespace KTXM\ProviderImap\Service\Remote;
|
||||
|
||||
use DateTimeImmutable;
|
||||
use Generator;
|
||||
use KTXM\ProviderImap\Client\Client;
|
||||
use KTXC\Server;
|
||||
use KTXC\Logger\PlainFileLogger;
|
||||
use KTXM\ProviderImap\Client\Client as ImapClient;
|
||||
use KTXM\ProviderImap\Client\Command\AppendCommand;
|
||||
use KTXM\ProviderImap\Client\Command\FetchManyCommand;
|
||||
use KTXM\ProviderImap\Client\Command\FetchOneCommand;
|
||||
@@ -48,6 +50,9 @@ use KTXF\Resource\Range\RangeAnchorType;
|
||||
use KTXF\Resource\Range\RangeTally;
|
||||
use KTXF\Resource\Sort\ISort;
|
||||
use KTXF\Resource\BinaryResource;
|
||||
use KTXM\ProviderImap\Providers\Service;
|
||||
use KTXM\ProviderImap\Smtp\Client as SmtpClient;
|
||||
use RuntimeException;
|
||||
|
||||
/**
|
||||
* IMAP Remote Mail Service
|
||||
@@ -58,10 +63,80 @@ class RemoteMailService
|
||||
private const COLLECTION_FILTER_OPTIONS = ['name', 'role', 'subscription'];
|
||||
private const DEFAULT_MAILBOX_STATUS_ITEMS = ['MESSAGES', 'UNSEEN', 'RECENT', 'UIDNEXT', 'UIDVALIDITY'];
|
||||
|
||||
private ?ImapClient $imapClient = null;
|
||||
private ?SmtpClient $smtpClient = null;
|
||||
|
||||
public function __construct(
|
||||
private readonly Client $client,
|
||||
private readonly Service $service,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* The connected IMAP client, established on first use.
|
||||
*/
|
||||
public function imapClient(): ImapClient
|
||||
{
|
||||
if ($this->imapClient instanceof ImapClient) {
|
||||
return $this->imapClient;
|
||||
}
|
||||
|
||||
$location = $this->service->getLocation();
|
||||
if ($location === null || $location->getInboundHost() === '') {
|
||||
throw new RuntimeException('No IMAP (inbound) host is configured for this service.');
|
||||
}
|
||||
$identity = $this->service->getIdentity();
|
||||
|
||||
$config = $location->toConnectionConfig(
|
||||
$identity?->getIdentity(),
|
||||
$identity?->getSecret(),
|
||||
);
|
||||
|
||||
$client = new ImapClient(logger: $this->logger('imap'));
|
||||
$client->connect($config);
|
||||
|
||||
return $this->imapClient = $client;
|
||||
}
|
||||
|
||||
/**
|
||||
* The connected SMTP client, established on first use.
|
||||
*/
|
||||
public function smtpClient(): SmtpClient
|
||||
{
|
||||
if ($this->smtpClient instanceof SmtpClient) {
|
||||
return $this->smtpClient;
|
||||
}
|
||||
|
||||
$location = $this->service->getLocation();
|
||||
if ($location === null || $location->getOutboundHost() === '') {
|
||||
throw new RuntimeException('No SMTP submission (outbound) host is configured for this service.');
|
||||
}
|
||||
|
||||
$identity = $this->service->getIdentity();
|
||||
|
||||
$config = $location->toSmtpConnectionConfig(
|
||||
$identity?->getIdentity(),
|
||||
$identity?->getSecret(),
|
||||
);
|
||||
|
||||
$client = new SmtpClient(logger: $this->logger('smtp'));
|
||||
$client->connect($config);
|
||||
|
||||
return $this->smtpClient = $client;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a per-protocol file logger when the service has debug enabled.
|
||||
*/
|
||||
private function logger(string $channel): ?PlainFileLogger
|
||||
{
|
||||
if (!$this->service->getDebug()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$logDir = Server::getInstance()?->logDir() ?? __DIR__ . '/../../../../../var/log';
|
||||
|
||||
return new PlainFileLogger($logDir . '/' . $channel, $this->service->identifier());
|
||||
}
|
||||
|
||||
/**
|
||||
* list of collections in remote storage
|
||||
*
|
||||
@@ -79,7 +154,7 @@ class RemoteMailService
|
||||
$location = '';
|
||||
}
|
||||
// construct the most efficient LIST command based on server capabilities
|
||||
if ($this->client->hasCapability('LIST-STATUS') && !empty($depth)) {
|
||||
if ($this->imapClient()->hasCapability('LIST-STATUS') && !empty($depth)) {
|
||||
$command = new ListCommand($location, $depth, null, ListReturnOptions::status(...self::DEFAULT_MAILBOX_STATUS_ITEMS));
|
||||
$rfc5258 = true;
|
||||
} else {
|
||||
@@ -88,7 +163,7 @@ class RemoteMailService
|
||||
}
|
||||
// retrieve list of mailboxes from remote
|
||||
$mailboxes = [];
|
||||
foreach ($this->client->perform($command) as $mailbox) {
|
||||
foreach ($this->imapClient()->perform($command) as $mailbox) {
|
||||
// apply filter
|
||||
if ($filter === null || $this->mailboxFilter($mailbox, $filter)) {
|
||||
if ($rfc5258) {
|
||||
@@ -106,7 +181,7 @@ class RemoteMailService
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
$status = $this->client->perform(new StatusCommand($mailbox->name(), self::DEFAULT_MAILBOX_STATUS_ITEMS));
|
||||
$status = $this->imapClient()->perform(new StatusCommand($mailbox->name(), self::DEFAULT_MAILBOX_STATUS_ITEMS));
|
||||
$mailbox = $mailbox->fromStatus($status);
|
||||
} catch (ImapException) {
|
||||
// do nothing
|
||||
@@ -126,13 +201,13 @@ class RemoteMailService
|
||||
public function collectionFetch(string $identifier): ?Mailbox
|
||||
{
|
||||
// retrieve mailbox from remote
|
||||
$mailbox = iterator_to_array($this->client->perform(new ListCommand('', $identifier, null, ListReturnOptions::status(...self::DEFAULT_MAILBOX_STATUS_ITEMS))));
|
||||
$mailbox = iterator_to_array($this->imapClient()->perform(new ListCommand('', $identifier, null, ListReturnOptions::status(...self::DEFAULT_MAILBOX_STATUS_ITEMS))));
|
||||
if (empty($mailbox)) {
|
||||
return null;
|
||||
}
|
||||
$mailbox = reset($mailbox);
|
||||
// enrich with STATUS
|
||||
$status = $this->client->perform(new StatusCommand($mailbox->name(), self::DEFAULT_MAILBOX_STATUS_ITEMS));
|
||||
$status = $this->imapClient()->perform(new StatusCommand($mailbox->name(), self::DEFAULT_MAILBOX_STATUS_ITEMS));
|
||||
$mailbox = $mailbox->fromStatus($status);
|
||||
|
||||
return $mailbox;
|
||||
@@ -146,7 +221,7 @@ class RemoteMailService
|
||||
*/
|
||||
public function collectionCreate(string $name): Mailbox
|
||||
{
|
||||
$result = $this->client->perform(new CreateCommand($name));
|
||||
$result = $this->imapClient()->perform(new CreateCommand($name));
|
||||
|
||||
if (!$result->isOk()) {
|
||||
throw new ImapException('Failed to create mailbox: ' . $name);
|
||||
@@ -166,7 +241,7 @@ class RemoteMailService
|
||||
*/
|
||||
public function collectionRename(string $oldName, string $newName): Mailbox
|
||||
{
|
||||
$result = $this->client->perform(new RenameCommand($oldName, $newName));
|
||||
$result = $this->imapClient()->perform(new RenameCommand($oldName, $newName));
|
||||
|
||||
if (!$result->isOk()) {
|
||||
throw new ImapException('Failed to rename mailbox: ' . $oldName . ' to ' . $newName);
|
||||
@@ -186,7 +261,7 @@ class RemoteMailService
|
||||
*/
|
||||
public function collectionDestroy(string $name): bool
|
||||
{
|
||||
$result = $this->client->perform(new DeleteCommand($name));
|
||||
$result = $this->imapClient()->perform(new DeleteCommand($name));
|
||||
|
||||
if (!$result->isOk()) {
|
||||
throw new ImapException('Failed to delete mailbox: ' . $name);
|
||||
@@ -207,18 +282,18 @@ class RemoteMailService
|
||||
$nativeFilter = $this->buildEntitySearchCriteria($filter);
|
||||
$nativeSort = $sort !== null ? $this->entitySortCriteria($sort) : [];
|
||||
|
||||
$this->client->perform(new SelectCommand($collection, true));
|
||||
$rfc5258 = $this->client->hasCapability('SORT');
|
||||
$this->imapClient()->perform(new SelectCommand($collection, true));
|
||||
$rfc5258 = $this->imapClient()->hasCapability('SORT');
|
||||
|
||||
$uids = [];
|
||||
if ($nativeSort !== [] && $rfc5258) {
|
||||
$uids = $this->client->perform(new SortCommand(
|
||||
$uids = $this->imapClient()->perform(new SortCommand(
|
||||
$nativeSort,
|
||||
$nativeFilter,
|
||||
IdentifierMode::Uid,
|
||||
))->matches();
|
||||
} else {
|
||||
$uids = $this->client->perform(new SearchCommand(
|
||||
$uids = $this->imapClient()->perform(new SearchCommand(
|
||||
$nativeFilter,
|
||||
IdentifierMode::Uid,
|
||||
))->matches();
|
||||
@@ -246,13 +321,13 @@ class RemoteMailService
|
||||
|
||||
// fast path: fetch all messages without filtering, sorting or pagination
|
||||
if ($filter === null && $sort === null && $range === null) {
|
||||
$mailbox = $this->client->perform(new SelectCommand($collection, true));
|
||||
$mailbox = $this->imapClient()->perform(new SelectCommand($collection, true));
|
||||
|
||||
if ($mailbox === null) {
|
||||
return [];
|
||||
}
|
||||
|
||||
yield from $this->client->perform(new FetchManyCommand(
|
||||
yield from $this->imapClient()->perform(new FetchManyCommand(
|
||||
FetchTarget::all(),
|
||||
$options,
|
||||
));
|
||||
@@ -283,14 +358,14 @@ class RemoteMailService
|
||||
}
|
||||
|
||||
$options ??= FetchOptions::message()->withBodyText();
|
||||
$this->client->perform(new SelectCommand($collection, true));
|
||||
$this->imapClient()->perform(new SelectCommand($collection, true));
|
||||
|
||||
$request = new FetchManyCommand(
|
||||
FetchTarget::uid(SequenceSet::items(...array_values($uids))),
|
||||
$options,
|
||||
);
|
||||
|
||||
foreach ($this->client->perform($request) as $message) {
|
||||
foreach ($this->imapClient()->perform($request) as $message) {
|
||||
$uid = $message->uid() ?: $message->sequence();
|
||||
yield $uid => $message;
|
||||
}
|
||||
@@ -308,7 +383,7 @@ class RemoteMailService
|
||||
*/
|
||||
public function entityDownload(string $collection, int $uid, ?string $partId = null): BinaryResource
|
||||
{
|
||||
$this->client->perform(new SelectCommand($collection, true));
|
||||
$this->imapClient()->perform(new SelectCommand($collection, true));
|
||||
|
||||
$encoding = null;
|
||||
|
||||
@@ -317,7 +392,7 @@ class RemoteMailService
|
||||
$mimeType = 'message/rfc822';
|
||||
} else {
|
||||
// Fetch BODYSTRUCTURE first to determine metadata (no body bytes transferred)
|
||||
$message = $this->client->perform(new FetchOneCommand(
|
||||
$message = $this->imapClient()->perform(new FetchOneCommand(
|
||||
FetchTarget::uid(SequenceSet::items($uid)),
|
||||
FetchOptions::of('BODYSTRUCTURE'),
|
||||
));
|
||||
@@ -333,7 +408,7 @@ class RemoteMailService
|
||||
|
||||
// Start download stream
|
||||
$stream = $this->decodeStream(
|
||||
$this->client->download(FetchTarget::uid(SequenceSet::items($uid)), $partId ?? ''),
|
||||
$this->imapClient()->download(FetchTarget::uid(SequenceSet::items($uid)), $partId ?? ''),
|
||||
$encoding
|
||||
);
|
||||
|
||||
@@ -413,7 +488,7 @@ class RemoteMailService
|
||||
*/
|
||||
public function entityCreate(string $collection, string $rawMessage, array $flags = []): ?int
|
||||
{
|
||||
return $this->client->perform(new AppendCommand($collection, $rawMessage, $flags));
|
||||
return $this->imapClient()->perform(new AppendCommand($collection, $rawMessage, $flags));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -429,8 +504,8 @@ class RemoteMailService
|
||||
return;
|
||||
}
|
||||
|
||||
$this->client->perform(new SelectCommand($collection, false));
|
||||
$this->client->perform(new StoreCommand(
|
||||
$this->imapClient()->perform(new SelectCommand($collection, false));
|
||||
$this->imapClient()->perform(new StoreCommand(
|
||||
FetchTarget::uid(SequenceSet::items(...array_values($uids))),
|
||||
$flags,
|
||||
$action,
|
||||
@@ -448,9 +523,9 @@ class RemoteMailService
|
||||
|
||||
$target = FetchTarget::uid(SequenceSet::items(...array_values($uids)));
|
||||
|
||||
$this->client->perform(new SelectCommand($collection, false));
|
||||
$this->client->perform(new StoreCommand($target, ['\\Deleted'], '+'));
|
||||
$this->client->perform(new ExpungeCommand($target));
|
||||
$this->imapClient()->perform(new SelectCommand($collection, false));
|
||||
$this->imapClient()->perform(new StoreCommand($target, ['\\Deleted'], '+'));
|
||||
$this->imapClient()->perform(new ExpungeCommand($target));
|
||||
|
||||
// TODO: find a way to determine which actual UID's were deleted
|
||||
return array_fill_keys($uids, true);
|
||||
@@ -462,13 +537,13 @@ class RemoteMailService
|
||||
return;
|
||||
}
|
||||
|
||||
$this->client->perform(new SelectCommand($collection, false));
|
||||
$this->imapClient()->perform(new SelectCommand($collection, false));
|
||||
|
||||
$flagsToAdd = $this->normalizeFlags($flagsToAdd);
|
||||
$flagsToRemove = $this->normalizeFlags($flagsToRemove);
|
||||
|
||||
if (!empty($flagsToAdd)) {
|
||||
$this->client->perform(new StoreCommand(
|
||||
$this->imapClient()->perform(new StoreCommand(
|
||||
FetchTarget::uid(SequenceSet::items(...array_values($uids))),
|
||||
$flagsToAdd,
|
||||
'+',
|
||||
@@ -476,7 +551,7 @@ class RemoteMailService
|
||||
}
|
||||
|
||||
if (!empty($flagsToRemove)) {
|
||||
$this->client->perform(new StoreCommand(
|
||||
$this->imapClient()->perform(new StoreCommand(
|
||||
FetchTarget::uid(SequenceSet::items(...array_values($uids))),
|
||||
$flagsToRemove,
|
||||
'-',
|
||||
@@ -490,28 +565,28 @@ class RemoteMailService
|
||||
return [];
|
||||
}
|
||||
|
||||
$rfc6851 = $this->client->hasCapability('MOVE');
|
||||
$rfc6851 = $this->imapClient()->hasCapability('MOVE');
|
||||
|
||||
// if MOVE is supported, use it; otherwise, fall back to COPY + EXPUNGE
|
||||
if ($rfc6851) {
|
||||
$this->client->perform(new SelectCommand($sourceCollection, false));
|
||||
$response = $this->client->perform(new MoveCommand(
|
||||
$this->imapClient()->perform(new SelectCommand($sourceCollection, false));
|
||||
$response = $this->imapClient()->perform(new MoveCommand(
|
||||
FetchTarget::uid(SequenceSet::items(...array_values($uids))),
|
||||
$targetCollection,
|
||||
));
|
||||
} else {
|
||||
$this->client->perform(new SelectCommand($sourceCollection, false));
|
||||
$response = $this->client->perform(new CopyCommand(
|
||||
$this->imapClient()->perform(new SelectCommand($sourceCollection, false));
|
||||
$response = $this->imapClient()->perform(new CopyCommand(
|
||||
FetchTarget::uid(SequenceSet::items(...array_values($uids))),
|
||||
$targetCollection,
|
||||
));
|
||||
if ($response->isOk()) {
|
||||
$this->client->perform(new StoreCommand(
|
||||
$this->imapClient()->perform(new StoreCommand(
|
||||
FetchTarget::uid(SequenceSet::items(...array_values($uids))),
|
||||
['\\Deleted'],
|
||||
'+',
|
||||
));
|
||||
$this->client->perform(new ExpungeCommand(
|
||||
$this->imapClient()->perform(new ExpungeCommand(
|
||||
FetchTarget::uid(SequenceSet::items(...array_values($uids))),
|
||||
));
|
||||
}
|
||||
@@ -542,8 +617,8 @@ class RemoteMailService
|
||||
return [];
|
||||
}
|
||||
|
||||
$this->client->perform(new SelectCommand($sourceCollection, false));
|
||||
$response = $this->client->perform(new CopyCommand(
|
||||
$this->imapClient()->perform(new SelectCommand($sourceCollection, false));
|
||||
$response = $this->imapClient()->perform(new CopyCommand(
|
||||
FetchTarget::uid(SequenceSet::items(...array_values($uids))),
|
||||
$targetCollection,
|
||||
));
|
||||
@@ -768,7 +843,7 @@ class RemoteMailService
|
||||
}
|
||||
}
|
||||
|
||||
$messages = iterator_to_array($this->client->perform(new FetchManyCommand(
|
||||
$messages = iterator_to_array($this->imapClient()->perform(new FetchManyCommand(
|
||||
FetchTarget::uid(SequenceSet::items(...array_values($uids))),
|
||||
$options,
|
||||
)));
|
||||
|
||||
Reference in New Issue
Block a user