generated from Nodarx/template
feat: harmonize mailbox messages
Signed-off-by: Sebastian Krupinski <krupinski01@gmail.com>
This commit is contained in:
@@ -65,6 +65,8 @@ class LiveMailService
|
||||
|
||||
private ?ImapClient $imapClient = null;
|
||||
private ?SmtpClient $smtpClient = null;
|
||||
/** Result of the last SELECT on the current connection, reused while still selected */
|
||||
private ?Mailbox $selected = null;
|
||||
|
||||
public function __construct(
|
||||
private readonly Service $service,
|
||||
@@ -200,16 +202,25 @@ class LiveMailService
|
||||
*/
|
||||
public function collectionFetch(string $identifier): ?Mailbox
|
||||
{
|
||||
// LIST-STATUS (RFC 5819) returns the status with the LIST response in one round trip
|
||||
$listStatus = $this->imapClient()->hasCapability('LIST-STATUS');
|
||||
$command = $listStatus
|
||||
? new ListCommand('', $identifier, null, ListReturnOptions::status(...self::DEFAULT_MAILBOX_STATUS_ITEMS))
|
||||
: new ListCommand('', $identifier);
|
||||
|
||||
// retrieve mailbox from remote
|
||||
$mailbox = iterator_to_array($this->imapClient()->perform(new ListCommand('', $identifier, null, ListReturnOptions::status(...self::DEFAULT_MAILBOX_STATUS_ITEMS))));
|
||||
$mailbox = iterator_to_array($this->imapClient()->perform($command));
|
||||
if (empty($mailbox)) {
|
||||
return null;
|
||||
}
|
||||
$mailbox = reset($mailbox);
|
||||
// enrich with STATUS
|
||||
$status = $this->imapClient()->perform(new StatusCommand($mailbox->name(), self::DEFAULT_MAILBOX_STATUS_ITEMS));
|
||||
$mailbox = $mailbox->fromStatus($status);
|
||||
|
||||
|
||||
// enrich with STATUS when LIST could not provide it
|
||||
if (!$listStatus && $mailbox->isSelectable()) {
|
||||
$status = $this->imapClient()->perform(new StatusCommand($mailbox->name(), self::DEFAULT_MAILBOX_STATUS_ITEMS));
|
||||
$mailbox = $mailbox->fromStatus($status);
|
||||
}
|
||||
|
||||
return $mailbox;
|
||||
}
|
||||
|
||||
@@ -270,7 +281,7 @@ class LiveMailService
|
||||
$nativeFilter = $this->buildEntitySearchCriteria($filter);
|
||||
$nativeSort = $sort !== null ? $this->entitySortCriteria($sort) : [];
|
||||
|
||||
$this->imapClient()->perform(new SelectCommand($collection, true));
|
||||
$this->select($collection);
|
||||
$rfc5258 = $this->imapClient()->hasCapability('SORT');
|
||||
|
||||
$uids = [];
|
||||
@@ -298,6 +309,31 @@ class LiveMailService
|
||||
return $this->entityApplyRange($uids, $range);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stream the flags of every message in a mailbox.
|
||||
*
|
||||
* The stream also lists every UID that exists in the mailbox.
|
||||
*
|
||||
* @return Generator<int, list<string>> IMAP flags keyed by UID
|
||||
*/
|
||||
public function entityFlags(string $collection): Generator
|
||||
{
|
||||
// fresh SELECT: the message count decides whether "1:*" may be sent
|
||||
$mailbox = $this->select($collection, refresh: true);
|
||||
|
||||
// "1:*" on an empty mailbox is rejected by some servers
|
||||
if ($mailbox->messages() === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($this->imapClient()->perform(new FetchManyCommand(
|
||||
MessageTarget::uid('1:*'),
|
||||
FetchOptions::of('FLAGS'),
|
||||
)) as $message) {
|
||||
yield $message->uid() => $message->flags();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine which of the given UIDs exist in a mailbox.
|
||||
*
|
||||
@@ -309,7 +345,7 @@ class LiveMailService
|
||||
return [];
|
||||
}
|
||||
|
||||
$this->imapClient()->perform(new SelectCommand($collection, true));
|
||||
$this->select($collection);
|
||||
|
||||
return $this->imapClient()->perform(new SearchCommand(
|
||||
SearchCriteriaBuilder::create()->uid(SequenceSet::items(...$uids)),
|
||||
@@ -329,9 +365,10 @@ class LiveMailService
|
||||
|
||||
// fast path: fetch all messages without filtering, sorting or pagination
|
||||
if ($filter === null && $sort === null && $range === null) {
|
||||
$mailbox = $this->imapClient()->perform(new SelectCommand($collection, true));
|
||||
$mailbox = $this->select($collection, refresh: true);
|
||||
|
||||
if ($mailbox === null) {
|
||||
// "1:*" on an empty mailbox is rejected by some servers
|
||||
if ($mailbox->messages() === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
@@ -366,7 +403,7 @@ class LiveMailService
|
||||
}
|
||||
|
||||
$options ??= FetchOptions::message()->withBodyText();
|
||||
$this->imapClient()->perform(new SelectCommand($collection, true));
|
||||
$this->select($collection);
|
||||
|
||||
$request = new FetchManyCommand(
|
||||
MessageTarget::uid(SequenceSet::items(...array_values($uids))),
|
||||
@@ -391,7 +428,7 @@ class LiveMailService
|
||||
*/
|
||||
public function entityDownload(string $collection, int $uid, ?string $partId = null): BinaryResource
|
||||
{
|
||||
$this->imapClient()->perform(new SelectCommand($collection, true));
|
||||
$this->select($collection);
|
||||
|
||||
$encoding = null;
|
||||
|
||||
@@ -550,7 +587,7 @@ class LiveMailService
|
||||
return;
|
||||
}
|
||||
|
||||
$this->imapClient()->perform(new SelectCommand($collection, false));
|
||||
$this->select($collection, readOnly: false);
|
||||
$this->imapClient()->perform(new StoreCommand(
|
||||
MessageTarget::uid(SequenceSet::items(...array_values($uids))),
|
||||
$flags,
|
||||
@@ -569,7 +606,7 @@ class LiveMailService
|
||||
|
||||
$target = MessageTarget::uid(SequenceSet::items(...array_values($uids)));
|
||||
|
||||
$this->imapClient()->perform(new SelectCommand($collection, false));
|
||||
$this->select($collection, readOnly: false);
|
||||
$this->imapClient()->perform(new StoreCommand($target, ['\\Deleted'], '+'));
|
||||
$this->imapClient()->perform(new ExpungeCommand($target));
|
||||
|
||||
@@ -583,7 +620,7 @@ class LiveMailService
|
||||
return;
|
||||
}
|
||||
|
||||
$this->imapClient()->perform(new SelectCommand($collection, false));
|
||||
$this->select($collection, readOnly: false);
|
||||
|
||||
$flagsToAdd = $this->normalizeFlags($flagsToAdd);
|
||||
$flagsToRemove = $this->normalizeFlags($flagsToRemove);
|
||||
@@ -615,13 +652,13 @@ class LiveMailService
|
||||
|
||||
// if MOVE is supported, use it; otherwise, fall back to COPY + EXPUNGE
|
||||
if ($rfc6851) {
|
||||
$this->imapClient()->perform(new SelectCommand($sourceCollection, false));
|
||||
$this->select($sourceCollection, readOnly: false);
|
||||
$response = $this->imapClient()->perform(new MoveCommand(
|
||||
MessageTarget::uid(SequenceSet::items(...array_values($uids))),
|
||||
$targetCollection,
|
||||
));
|
||||
} else {
|
||||
$this->imapClient()->perform(new SelectCommand($sourceCollection, false));
|
||||
$this->select($sourceCollection, readOnly: false);
|
||||
$response = $this->imapClient()->perform(new CopyCommand(
|
||||
MessageTarget::uid(SequenceSet::items(...array_values($uids))),
|
||||
$targetCollection,
|
||||
@@ -657,7 +694,7 @@ class LiveMailService
|
||||
return [];
|
||||
}
|
||||
|
||||
$this->imapClient()->perform(new SelectCommand($sourceCollection, false));
|
||||
$this->select($sourceCollection, readOnly: false);
|
||||
$response = $this->imapClient()->perform(new CopyCommand(
|
||||
MessageTarget::uid(SequenceSet::items(...array_values($uids))),
|
||||
$targetCollection,
|
||||
@@ -1143,4 +1180,28 @@ class LiveMailService
|
||||
}
|
||||
return $normalized;
|
||||
}
|
||||
|
||||
/**
|
||||
* Select a mailbox, reusing the current selection when possible.
|
||||
*
|
||||
* The current selection is reused when the same mailbox is still selected on the
|
||||
* session and its access mode suffices (read-write covers read-only). A refresh
|
||||
* forces a new SELECT, e.g. when an up-to-date message count is needed.
|
||||
*/
|
||||
private function select(string $collection, bool $readOnly = true, bool $refresh = false): Mailbox
|
||||
{
|
||||
if (!$refresh
|
||||
&& $this->selected !== null
|
||||
&& $this->selected->name() === $collection
|
||||
&& $this->imapClient()->session()->selectedMailbox() === $collection
|
||||
&& ($readOnly || !$this->selected->readOnly())
|
||||
) {
|
||||
return $this->selected;
|
||||
}
|
||||
|
||||
$this->selected = null;
|
||||
$this->selected = $this->imapClient()->perform(new SelectCommand($collection, $readOnly));
|
||||
|
||||
return $this->selected;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user