readToken($payload, $offset); $delimiterToken = $this->readToken($payload, $offset); $nameToken = $this->readToken($payload, $offset); if ($attributesToken === null || $delimiterToken === null || $nameToken === null) { throw new ImapException('Unable to parse LIST response payload: ' . $payload); } $attributeString = trim($attributesToken, '() '); $attributes = $attributeString === '' || strtoupper($attributeString) === 'NIL' ? [] : array_map('strtoupper', preg_split('/\s+/', $attributeString) ?: []); $delimiter = $this->decodeAtom($delimiterToken); $name = $this->decodeMailboxName($nameToken); return new Mailbox($name, $delimiter, $attributes); } private function readToken(string $payload, int &$offset): ?string { $length = strlen($payload); while ($offset < $length && ctype_space($payload[$offset])) { $offset++; } if ($offset >= $length) { return null; } if ($payload[$offset] === '(') { $end = strpos($payload, ')', $offset); if ($end === false) { throw new ImapException('Unterminated LIST attribute block: ' . $payload); } $token = substr($payload, $offset, $end - $offset + 1); $offset = $end + 1; return $token; } if ($payload[$offset] === '"') { $start = $offset; $offset++; while ($offset < $length) { if ($payload[$offset] === '\\') { $offset += 2; continue; } if ($payload[$offset] === '"') { $offset++; return substr($payload, $start, $offset - $start); } $offset++; } throw new ImapException('Unterminated quoted LIST token: ' . $payload); } $start = $offset; while ($offset < $length && !ctype_space($payload[$offset])) { $offset++; } return substr($payload, $start, $offset - $start); } private function decodeAtom(string $value): ?string { $value = trim($value); if (strtoupper($value) === 'NIL') { return null; } if (str_starts_with($value, '"') && str_ends_with($value, '"')) { return stripcslashes(substr($value, 1, -1)); } return $value; } private function decodeMailboxName(string $value): string { $name = $this->decodeAtom($value); // LIST may advertise the root mailbox as an empty quoted string. return $name ?? ''; } }