Files
provider_imap/lib/Client/Command/StatusCommand.php
2026-05-08 00:16:43 -04:00

118 lines
3.2 KiB
PHP

<?php
declare(strict_types=1);
namespace KTXM\ProviderImap\Client\Command;
use KTXM\ProviderImap\Client\Command\Result\StatusResult;
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\SessionContext;
use KTXM\ProviderImap\Client\SessionState;
/**
* @implements CommandInterface<StatusResult>
*/
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)',
$this->quote($this->mailbox),
implode(' ', $this->normalizeItems($this->items)),
));
}
public function handle(ResponseStream $responses, SessionContext $context): StatusResult
{
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) {
if (!$response->isOk()) {
throw new ImapException('STATUS failed: ' . $response->text());
}
return new StatusResult($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;
}
private function quote(string $value): string
{
return '"' . addcslashes($value, "\\\"") . '"';
}
}