Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 25 additions & 5 deletions src/Connection/Protocols/ImapProtocol.php
Original file line number Diff line number Diff line change
Expand Up @@ -815,17 +815,31 @@ public function fetch(array|string $items, array|int $from, mixed $to = null, in

// if we only want one item we return that one directly
if (count($items) == 1) {
if ($tokens[2][0] == $items[0]) {
$expectedItem = (string) $items[0];
$itemMatches = static function($actual, $expected): bool {
$actualNorm = strtoupper((string) $actual);
$expectedNorm = strtoupper((string) $expected);
if ($actualNorm === $expectedNorm) {
return true;
}
// IMAP may answer BODY[...] even when request used BODY.PEEK[...].
if (str_replace('.PEEK', '', $actualNorm) === str_replace('.PEEK', '', $expectedNorm)) {
return true;
}
return false;
};

if ($itemMatches($tokens[2][0] ?? '', $expectedItem)) {
$data = $tokens[2][1];
} elseif ($uid === IMAP::ST_UID && $tokens[2][2] == $items[0]) {
} elseif ($uid === IMAP::ST_UID && $itemMatches($tokens[2][2] ?? '', $expectedItem)) {
$data = $tokens[2][3];
} else {
$expectedResponse = 0;
// maybe the server send another field we didn't wanted
$count = count($tokens[2]);
// we start with 2, because 0 was already checked
for ($i = 2; $i < $count; $i += 2) {
if ($tokens[2][$i] != $items[0]) {
if (!$itemMatches($tokens[2][$i] ?? '', $expectedItem)) {
continue;
}
$data = $tokens[2][$i + 1];
Expand Down Expand Up @@ -869,13 +883,19 @@ public function fetch(array|string $items, array|int $from, mixed $to = null, in
* @param string $rfc
* @param int|string $uid set to IMAP::ST_UID or any string representing the UID - set to IMAP::ST_MSGN to use
* message numbers instead.
* @param bool $peek true to fetch body using PEEK (do not set \Seen)
*
* @return Response
* @throws RuntimeException
*/
public function content(int|array $uids, string $rfc = "RFC822", int|string $uid = IMAP::ST_UID): Response {
public function content(int|array $uids, string $rfc = "RFC822", int|string $uid = IMAP::ST_UID, bool $peek = false): Response {
$rfc = $rfc ?? "RFC822";
$item = $rfc === "BODY" ? "BODY[TEXT]" : "$rfc.TEXT";
if ($peek && ($rfc === "RFC822" || $rfc === "BODY")) {
// BODY.PEEK keeps the message unread at protocol level.
$item = "BODY.PEEK[TEXT]";
} else {
$item = $rfc === "BODY" ? "BODY[TEXT]" : "$rfc.TEXT";
}
return $this->fetch([$item], is_array($uids) ? $uids : [$uids], null, $uid);
}

Expand Down
11 changes: 8 additions & 3 deletions src/Connection/Protocols/LegacyProtocol.php
Original file line number Diff line number Diff line change
Expand Up @@ -252,18 +252,23 @@ public function folderStatus(string $folder = 'INBOX', $arguments = ['MESSAGES',
* @param int|array $uids
* @param string $rfc
* @param int|string $uid set to IMAP::ST_UID if you pass message unique identifiers instead of numbers.
* @param bool $peek true to fetch body without setting \Seen
*
* @return Response
*/
public function content(int|array $uids, string $rfc = "RFC822", int|string $uid = IMAP::ST_UID): Response {
return $this->response()->wrap(function($response) use ($uids, $uid) {
public function content(int|array $uids, string $rfc = "RFC822", int|string $uid = IMAP::ST_UID, bool $peek = false): Response {
return $this->response()->wrap(function($response) use ($uids, $uid, $peek) {
/** @var Response $response */

$result = [];
$uids = is_array($uids) ? $uids : [$uids];
$fetchOptions = ($uid === IMAP::ST_UID ? IMAP::ST_UID : IMAP::NIL);
if ($peek) {
$fetchOptions |= IMAP::FT_PEEK;
}
foreach ($uids as $id) {
$response->addCommand("imap_fetchbody");
$result[$id] = \imap_fetchbody($this->stream, $id, "", $uid === IMAP::ST_UID ? IMAP::ST_UID : IMAP::NIL);
$result[$id] = \imap_fetchbody($this->stream, $id, "", $fetchOptions);
}

return $result;
Expand Down
3 changes: 2 additions & 1 deletion src/Connection/Protocols/ProtocolInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -134,11 +134,12 @@ public function folderStatus(string $folder = 'INBOX', $arguments = ['MESSAGES',
* @param string $rfc
* @param int|string $uid set to IMAP::ST_UID or any string representing the UID - set to IMAP::ST_MSGN to use
* message numbers instead.
* @param bool $peek true to fetch body using a non-seen variant when supported
*
* @return Response
* @throws RuntimeException
*/
public function content(int|array $uids, string $rfc = "RFC822", int|string $uid = IMAP::ST_UID): Response;
public function content(int|array $uids, string $rfc = "RFC822", int|string $uid = IMAP::ST_UID, bool $peek = false): Response;

/**
* Fetch message headers
Expand Down
3 changes: 2 additions & 1 deletion src/Message.php
Original file line number Diff line number Diff line change
Expand Up @@ -638,8 +638,9 @@ public function parseBody(): Message {
$this->client->openFolder($this->folder_path);

$sequence_id = $this->getSequenceId();
$usePeek = ($this->fetch_options === IMAP::FT_PEEK);
try {
$contents = $this->client->getConnection()->content([$sequence_id], $this->client->rfc, $this->sequence)->validatedData();
$contents = $this->client->getConnection()->content([$sequence_id], $this->client->rfc, $this->sequence, $usePeek)->validatedData();
} catch (Exceptions\RuntimeException $e) {
throw new MessageContentFetchingException("failed to fetch content", 0, $e);
}
Expand Down
3 changes: 2 additions & 1 deletion src/Query/Query.php
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,8 @@ protected function fetch(Collection $available_messages): array {

$contents = [];
if ($this->getFetchBody()) {
$contents = $this->client->getConnection()->content($uids, $this->client->rfc, $this->sequence)->validatedData();
$usePeek = ($this->getFetchOptions() === IMAP::FT_PEEK);
$contents = $this->client->getConnection()->content($uids, $this->client->rfc, $this->sequence, $usePeek)->validatedData();
}

return [
Expand Down