Files
provider_imap/lib/Client/Protocol/Command/StatusCommand.php
T
2026-09-25 12:14:37 -04:00

115 lines
3.3 KiB
PHP

<?php
declare(strict_types=1);
namespace KTXM\ProviderImap\Client\Protocol\Command;
use KTXM\ProviderImap\Client\Protocol\CompletionChecker;
use KTXM\ProviderImap\Client\Protocol\StringEncoder;
use KTXM\ProviderImap\Client\Protocol\Parser\StatusResponseParser;
use KTXM\ProviderImap\Client\Result\MailboxStatusResult;
use KTXM\ProviderImap\Client\ImapException;
use KTXM\ProviderImap\Client\Protocol\RequestFrame;
use KTXM\ProviderImap\Client\Protocol\Response\TaggedResponse;
use KTXM\ProviderImap\Client\Protocol\Response\UntaggedResponse;
use KTXM\ProviderImap\Client\Protocol\ResponseStream;
use KTXM\ProviderImap\Client\Protocol\SessionContext;
use KTXM\ProviderImap\Client\Protocol\SessionState;
/**
* @implements CommandInterface<MailboxStatusResult>
*/
final class StatusCommand implements CommandInterface
{
private readonly StatusResponseParser $statusResponseParser;
/**
* @param list<string> $items
*/
public function __construct(
private readonly string $mailbox,
private readonly array $items = ['MESSAGES', 'UNSEEN'],
?StatusResponseParser $statusResponseParser = null,
) {
$this->statusResponseParser = $statusResponseParser ?? new StatusResponseParser();
}
public function name(): string
{
return 'STATUS';
}
public function allowedStates(): array
{
return [
SessionState::Authenticated,
SessionState::Selected,
];
}
public function encode(string $tag, SessionContext $context): RequestFrame
{
unset($tag, $context);
return new RequestFrame(sprintf(
'STATUS %s (%s)',
StringEncoder::quote($this->mailbox),
implode(' ', $this->normalizeItems($this->items)),
));
}
public function handle(ResponseStream $responses, SessionContext $context): MailboxStatusResult
{
unset($context);
$items = [];
$mailbox = $this->mailbox;
foreach ($responses as $response) {
if ($response instanceof UntaggedResponse && $response->label() === 'STATUS') {
[$mailbox, $items] = $this->statusResponseParser->parse($response->payload());
continue;
}
if ($response instanceof TaggedResponse) {
CompletionChecker::assertSuccess($this->name(), $response);
return new MailboxStatusResult($mailbox, $items);
}
}
throw new ImapException('STATUS did not receive a tagged completion response.');
}
/**
* @param list<string> $items
* @return list<string>
*/
private function normalizeItems(array $items): array
{
$normalized = [];
foreach ($items as $item) {
$item = strtoupper(trim($item));
if ($item === '') {
continue;
}
if (!preg_match('/^[A-Z0-9.-]+$/', $item)) {
throw new ImapException('Invalid STATUS item: ' . $item);
}
if (in_array($item, $normalized, true)) {
continue;
}
$normalized[] = $item;
}
if ($normalized === []) {
throw new ImapException('STATUS requires at least one data item.');
}
return $normalized;
}
}