*/ final class SelectCommand implements CommandInterface { public function __construct( private readonly string $mailbox, private readonly bool $readOnly = true, ) {} public function name(): string { return $this->readOnly ? 'EXAMINE' : 'SELECT'; } public function allowedStates(): array { return [ SessionState::Authenticated, SessionState::Selected, ]; } public function encode(string $tag, SessionContext $context): RequestFrame { unset($tag, $context); return new RequestFrame(sprintf( '%s %s', $this->name(), StringEncoder::quote($this->mailbox), )); } public function handle(ResponseStream $responses, SessionContext $context): Mailbox { $exists = 0; $recent = 0; $uidValidity = null; $uidNext = null; $highestModSeq = null; $flags = []; $readOnly = $this->readOnly; foreach ($responses as $response) { if ($response instanceof UntaggedResponse) { $raw = $response->raw(); if (preg_match('/^\*\s+(\d+)\s+EXISTS$/i', $raw, $matches)) { $exists = (int) $matches[1]; continue; } if (preg_match('/^\*\s+(\d+)\s+RECENT$/i', $raw, $matches)) { $recent = (int) $matches[1]; continue; } // response codes, e.g. "* OK [UIDVALIDITY 3857529045] UIDs valid" if (preg_match('/^\*\s+OK\s+\[(UIDVALIDITY|UIDNEXT|HIGHESTMODSEQ)\s+(\d+)\]/i', $raw, $matches)) { match (strtoupper($matches[1])) { 'UIDVALIDITY' => $uidValidity = (int) $matches[2], 'UIDNEXT' => $uidNext = (int) $matches[2], 'HIGHESTMODSEQ' => $highestModSeq = (int) $matches[2], }; continue; } if ($response->label() === 'FLAGS' && preg_match('/\(([^)]*)\)/', $response->payload(), $matches)) { $flags = $this->parseFlags($matches[1]); continue; } } if ($response instanceof TaggedResponse) { // a failed SELECT leaves no mailbox selected (RFC 3501 6.3.1) if ($response->status() !== 'OK') { $context->setSelectedMailbox(null); } CompletionChecker::assertSuccess($this->name(), $response); if (str_contains(strtoupper($response->text()), 'READ-ONLY')) { $readOnly = true; } $context->setSelectedMailbox($this->mailbox); $context->setState(SessionState::Selected); return new Mailbox( $this->mailbox, null, [], $exists, 0, $uidValidity, $recent, $flags, $readOnly, $uidNext, $highestModSeq, ); } } throw new ImapException($this->name() . ' did not receive a tagged completion response.'); } /** * @return list */ private function parseFlags(string $flags): array { $flags = trim($flags); if ($flags === '') { return []; } return preg_split('/\s+/', $flags) ?: []; } }