diff --git a/src/Connection/ImapCommand.php b/src/Connection/ImapCommand.php index d02b062..3af3107 100644 --- a/src/Connection/ImapCommand.php +++ b/src/Connection/ImapCommand.php @@ -22,6 +22,30 @@ public function __construct( protected array $tokens = [], ) {} + /** + * Get the IMAP tag. + */ + public function tag(): string + { + return $this->tag; + } + + /** + * Get the IMAP command. + */ + public function command(): string + { + return $this->command; + } + + /** + * Get the IMAP tokens. + */ + public function tokens(): array + { + return $this->tokens; + } + /** * Compile the command into lines for transmission. * @@ -62,7 +86,7 @@ public function compile(): array */ public function redacted(): ImapCommand { - return new ImapCommand($this->tag, $this->command, array_map( + return new static($this->tag, $this->command, array_map( function (mixed $token) { return is_array($token) ? array_map(fn () => '[redacted]', $token) diff --git a/src/Exceptions/ImapCommandException.php b/src/Exceptions/ImapCommandException.php index bb5afed..b348893 100644 --- a/src/Exceptions/ImapCommandException.php +++ b/src/Exceptions/ImapCommandException.php @@ -7,11 +7,42 @@ class ImapCommandException extends Exception { + /** + * The IMAP response. + */ + protected Response $response; + + /** + * The failed IMAP command. + */ + protected ImapCommand $command; + /** * Make a new instance from a failed command and response. */ public static function make(ImapCommand $command, Response $response): static { - return new static(sprintf('IMAP command "%s" failed. Response: "%s"', $command, $response)); + $exception = new static(sprintf('IMAP command "%s" failed. Response: "%s"', $command, $response)); + + $exception->command = $command; + $exception->response = $response; + + return $exception; + } + + /** + * Get the failed IMAP command. + */ + public function command(): ImapCommand + { + return $this->command; + } + + /** + * Get the IMAP response. + */ + public function response(): Response + { + return $this->response; } } diff --git a/src/Folder.php b/src/Folder.php index b186473..ac14063 100644 --- a/src/Folder.php +++ b/src/Folder.php @@ -8,6 +8,7 @@ use DirectoryTree\ImapEngine\Exceptions\Exception; use DirectoryTree\ImapEngine\Exceptions\ImapCapabilityException; use Illuminate\Contracts\Support\Arrayable; +use Illuminate\Support\ItemNotFoundException; use JsonSerializable; class Folder implements Arrayable, JsonSerializable @@ -115,8 +116,12 @@ function (int $msgn) use ($callback, $fetch) { try { $message = $fetch($msgn); + } catch (ItemNotFoundException) { + // The message wasn't found. We will skip + // it and continue awaiting new messages. + return; } catch (Exception) { - // If fetching the message fails, we'll attempt + // Something else happened. We will attempt // reconnecting and re-fetching the message. $this->mailbox->reconnect(); diff --git a/src/MessageQuery.php b/src/MessageQuery.php index f0aadee..63a34db 100644 --- a/src/MessageQuery.php +++ b/src/MessageQuery.php @@ -9,6 +9,7 @@ use DirectoryTree\ImapEngine\Connection\Responses\UntaggedResponse; use DirectoryTree\ImapEngine\Connection\Tokens\Atom; use DirectoryTree\ImapEngine\Enums\ImapFetchIdentifier; +use DirectoryTree\ImapEngine\Exceptions\ImapCommandException; use DirectoryTree\ImapEngine\Pagination\LengthAwarePaginator; use DirectoryTree\ImapEngine\Support\ForwardsCalls; use DirectoryTree\ImapEngine\Support\Str; @@ -531,9 +532,10 @@ public function paginate(int $perPage = 5, $page = null, string $pageName = 'pag */ public function findOrFail(int $id, ImapFetchIdentifier $identifier = ImapFetchIdentifier::Uid) { - $uid = $this->uid($id, $identifier) - ->firstOrFail() // Untagged response - ->tokenAt(3) // ListData + /** @var UntaggedResponse $response */ + $response = $this->uid($id, $identifier)->firstOrFail(); + + $uid = $response->tokenAt(3) // ListData ->tokenAt(1) // Atom ->value; // UID @@ -545,6 +547,7 @@ public function findOrFail(int $id, ImapFetchIdentifier $identifier = ImapFetchI */ public function find(int $id, ImapFetchIdentifier $identifier = ImapFetchIdentifier::Uid): ?Message { + /** @var UntaggedResponse $response */ if (! $response = $this->uid($id, $identifier)->first()) { return null; } @@ -557,11 +560,27 @@ public function find(int $id, ImapFetchIdentifier $identifier = ImapFetchIdentif } /** - * Get the UID for theb given identifier. + * Get the UID for the given identifier. */ protected function uid(int $id, ImapFetchIdentifier $identifier = ImapFetchIdentifier::Uid): ResponseCollection { - return $this->connection()->uid([$id], $identifier); + try { + return $this->connection()->uid([$id], $identifier); + } catch (ImapCommandException $e) { + // IMAP servers may return an error if the message number is not found. + // If the identifier being used is a message number, and the message + // number is in the command tokens, we can assume this has occurred + // and safely ignore the error and return an empty collection. + if ( + $identifier === ImapFetchIdentifier::MessageNumber + && in_array($id, $e->command()->tokens()) + ) { + return ResponseCollection::make(); + } + + // Otherwise, re-throw the exception. + throw $e; + } } /**