*/ final class StatusCommand implements CommandInterface { private readonly StatusResponseParser $statusResponseParser; /** * @param list $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 $items * @return list */ 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, "\\\"") . '"'; } }