generated from Nodarx/template
refactor: use custom imap client
Signed-off-by: Sebastian Krupinski <krupinski01@gmail.com>
This commit is contained in:
@@ -11,10 +11,8 @@ namespace KTXM\ProviderImap\Providers;
|
||||
|
||||
use DateTimeImmutable;
|
||||
use DateTimeInterface;
|
||||
use Gricob\IMAP\Protocol\Response\Line\Data\Fetch\BodyStructure\MultiPart;
|
||||
use Gricob\IMAP\Protocol\Response\Line\Data\Fetch\BodyStructure\Part;
|
||||
use Gricob\IMAP\Protocol\Response\Line\Data\Fetch\BodyStructure\SinglePart;
|
||||
use Gricob\IMAP\Protocol\Response\Line\Data\FetchData;
|
||||
use KTXM\ProviderImap\Client\Message;
|
||||
use KTXM\ProviderImap\Client\MessagePart as ClientMessagePart;
|
||||
use KTXF\Mail\Object\MessagePropertiesMutableAbstract;
|
||||
|
||||
/**
|
||||
@@ -23,117 +21,111 @@ use KTXF\Mail\Object\MessagePropertiesMutableAbstract;
|
||||
class MessageProperties extends MessagePropertiesMutableAbstract {
|
||||
|
||||
/**
|
||||
* Convert IMAP data to mail message properties object
|
||||
*
|
||||
* @param FetchData $fetchData result from IMAP FETCH command
|
||||
* Convert IMAP data to mail message properties object.
|
||||
*/
|
||||
public function fromImap(FetchData $fetchData): static {
|
||||
public function fromImap(Message $message): static
|
||||
{
|
||||
$this->data['size'] = $message->size();
|
||||
|
||||
// ── Size ──────────────────────────────────────────────────────
|
||||
$this->data['size'] = $fetchData->rfc822Size ?? 0;
|
||||
|
||||
// ── Flags ─────────────────────────────────────────────────────
|
||||
$this->data['flags'] = [];
|
||||
foreach ($fetchData->flags ?? [] as $flag) {
|
||||
foreach ($message->flags() as $flag) {
|
||||
$flag = ltrim($flag, '\\');
|
||||
$normalized = match (strtolower($flag)) {
|
||||
'seen' => 'read',
|
||||
'flagged' => 'flagged',
|
||||
'seen' => 'read',
|
||||
'flagged' => 'flagged',
|
||||
'answered' => 'answered',
|
||||
'draft' => 'draft',
|
||||
'deleted' => 'deleted',
|
||||
default => strtolower($flag),
|
||||
'draft' => 'draft',
|
||||
'deleted' => 'deleted',
|
||||
default => strtolower($flag),
|
||||
};
|
||||
$this->data['flags'][$normalized] = true;
|
||||
}
|
||||
|
||||
// ── Envelope ──────────────────────────────────────────────────
|
||||
if ($fetchData->envelope !== null) {
|
||||
$envelope = $fetchData->envelope;
|
||||
|
||||
if ($envelope->messageId !== null) {
|
||||
$this->data['urid'] = trim($envelope->messageId, '<>');
|
||||
}
|
||||
|
||||
if ($envelope->subject !== null) {
|
||||
// Decode MIME encoded-word in subject
|
||||
$this->data['subject'] = mb_decode_mimeheader($envelope->subject);
|
||||
}
|
||||
|
||||
if ($envelope->date !== null) {
|
||||
$date = $envelope->date instanceof DateTimeImmutable
|
||||
? $envelope->date
|
||||
: new DateTimeImmutable($envelope->date);
|
||||
$this->data['date'] = $date->format(DateTimeInterface::ATOM);
|
||||
}
|
||||
|
||||
if ($envelope->inReplyTo !== null) {
|
||||
$this->data['inReplyTo'] = $envelope->inReplyTo;
|
||||
}
|
||||
|
||||
$addressToArray = static function ($addr): array {
|
||||
$email = '';
|
||||
if ($addr->mailboxName !== null && $addr->hostName !== null) {
|
||||
$email = $addr->mailboxName . '@' . $addr->hostName;
|
||||
} elseif ($addr->mailboxName !== null) {
|
||||
$email = $addr->mailboxName;
|
||||
}
|
||||
return [
|
||||
'address' => $email,
|
||||
'label' => $addr->displayName ?? null,
|
||||
];
|
||||
};
|
||||
|
||||
if (!empty($envelope->from)) {
|
||||
$this->data['from'] = $addressToArray($envelope->from[0]);
|
||||
}
|
||||
|
||||
if (!empty($envelope->sender)) {
|
||||
$this->data['sender'] = $addressToArray($envelope->sender[0]);
|
||||
}
|
||||
|
||||
foreach (['to', 'cc', 'bcc', 'replyTo'] as $field) {
|
||||
$envField = $field === 'replyTo' ? 'replyTo' : $field;
|
||||
if (!empty($envelope->$envField)) {
|
||||
$this->data[$field] = [];
|
||||
foreach ($envelope->$envField as $addr) {
|
||||
$this->data[$field][] = $addressToArray($addr);
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($message->messageId() !== null) {
|
||||
$this->data['urid'] = $message->messageId();
|
||||
}
|
||||
|
||||
// ── Body Structure ────────────────────────────────────────────
|
||||
if ($fetchData->bodyStructure !== null) {
|
||||
$bodyStructure = $fetchData->bodyStructure;
|
||||
// Root multipart containers have no fetchable section ID; their
|
||||
// children are numbered "1", "2", … to match IMAP section IDs.
|
||||
$isRootMultipart = $bodyStructure->part instanceof MultiPart;
|
||||
$rootPartId = $isRootMultipart ? '' : '1';
|
||||
$rootPart = (new MessagePart())->fromImap($bodyStructure->part, $rootPartId);
|
||||
if ($message->subject() !== null) {
|
||||
$this->data['subject'] = $message->subject();
|
||||
}
|
||||
|
||||
// ── Body Content: inject decoded content onto part nodes ──────
|
||||
if (!empty($fetchData->bodySections)) {
|
||||
$sectionMap = [];
|
||||
foreach ($fetchData->bodySections as $bs) {
|
||||
$sectionMap[$bs->section] = $bs->text;
|
||||
}
|
||||
$rootPart->injectSections($sectionMap);
|
||||
if ($message->sentAt() !== null) {
|
||||
$date = new DateTimeImmutable($message->sentAt());
|
||||
$this->data['date'] = $date->format(DateTimeInterface::ATOM);
|
||||
}
|
||||
|
||||
if ($message->inReplyTo() !== null) {
|
||||
$this->data['inReplyTo'] = $message->inReplyTo();
|
||||
}
|
||||
|
||||
if ($message->from() !== []) {
|
||||
$this->data['from'] = $message->from()[0]->toArray();
|
||||
}
|
||||
|
||||
if ($message->sender() !== []) {
|
||||
$this->data['sender'] = $message->sender()[0]->toArray();
|
||||
}
|
||||
|
||||
foreach (['to', 'cc', 'bcc', 'replyTo'] as $field) {
|
||||
$addresses = $message->{$field}();
|
||||
if ($addresses === []) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->data['body'] = $rootPart->toStore();
|
||||
$this->data[$field] = array_map(
|
||||
static fn ($address): array => $address->toArray(),
|
||||
$addresses,
|
||||
);
|
||||
}
|
||||
|
||||
if ($message->bodyStructure() !== null) {
|
||||
$this->data['body'] = $message->bodyStructure()->toArray();
|
||||
|
||||
// Collect attachments: non-body parts with name or attachment disposition
|
||||
$attachments = [];
|
||||
self::collectAttachments($bodyStructure->part, $rootPartId, $attachments);
|
||||
if (!empty($attachments)) {
|
||||
self::collectAttachments($message->bodyStructure(), $attachments);
|
||||
if ($attachments !== []) {
|
||||
$this->data['attachments'] = $attachments;
|
||||
}
|
||||
}
|
||||
|
||||
if ($message->bodyStructure() !== null) {
|
||||
$this->data['body'] = $message->bodyStructure()->toArray();
|
||||
// Recursively add content from bodyValues to matching parts
|
||||
if (is_array($message->bodySections())) {
|
||||
$addContentToParts = function(&$structure, $bodyValues) use (&$addContentToParts) {
|
||||
// If this part has a partId and matching bodyValue, add content
|
||||
if (isset($structure['partId']) && isset($bodyValues[$structure['partId']])) {
|
||||
$structure['content'] = $bodyValues[$structure['partId']] ?? null;
|
||||
}
|
||||
// Recursively process subParts
|
||||
if (isset($structure['subParts']) && is_array($structure['subParts'])) {
|
||||
foreach ($structure['subParts'] as &$subPart) {
|
||||
$addContentToParts($subPart, $bodyValues);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
$addContentToParts($this->data['body'], $message->bodySections());
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
private static function normalizeFlag(string $flag): string
|
||||
{
|
||||
$flag = ltrim($flag, '\\');
|
||||
|
||||
return match (strtolower($flag)) {
|
||||
'seen' => 'read',
|
||||
'flagged' => 'flagged',
|
||||
'answered' => 'answered',
|
||||
'draft' => 'draft',
|
||||
'deleted' => 'deleted',
|
||||
default => strtolower($flag),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a string to UTF-8 from the given charset.
|
||||
*
|
||||
@@ -165,42 +157,28 @@ class MessageProperties extends MessagePropertiesMutableAbstract {
|
||||
/**
|
||||
* Recursively collect attachment parts from body structure
|
||||
*/
|
||||
private static function collectAttachments(
|
||||
Part $part,
|
||||
string $partId,
|
||||
array &$attachments,
|
||||
): void {
|
||||
if ($part instanceof SinglePart) {
|
||||
$type = strtolower($part->type ?? '');
|
||||
$subtype = strtolower($part->subtype ?? '');
|
||||
$disposition = strtolower($part->disposition?->type ?? '');
|
||||
$name = null;
|
||||
if (!empty($part->attributes)) {
|
||||
foreach ($part->attributes as $k => $v) {
|
||||
if (strtolower($k) === 'name') {
|
||||
$name = $v;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!empty($part->disposition?->attributes)) {
|
||||
foreach ($part->disposition->attributes as $k => $v) {
|
||||
if (strtolower($k) === 'filename') {
|
||||
$name = $name ?? $v;
|
||||
}
|
||||
}
|
||||
}
|
||||
$isInlineText = ($type === 'text' && ($subtype === 'plain' || $subtype === 'html') && $disposition !== 'attachment');
|
||||
if (!$isInlineText && ($disposition !== '' || $name !== null)) {
|
||||
$mp = (new MessagePart())->fromImap($part, $partId);
|
||||
$attachments[] = $mp->toStore();
|
||||
}
|
||||
} elseif ($part instanceof MultiPart) {
|
||||
foreach ($part->parts as $index => $subPart) {
|
||||
$subPartId = ($partId === '') ? (string)($index + 1) : $partId . '.' . ($index + 1);
|
||||
self::collectAttachments($subPart, $subPartId, $attachments);
|
||||
private static function collectAttachments(ClientMessagePart $part, array &$attachments): void
|
||||
{
|
||||
$children = $part->parts();
|
||||
if ($children !== []) {
|
||||
foreach ($children as $childPart) {
|
||||
self::collectAttachments($childPart, $attachments);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
$mimeType = strtolower($part->mimeType());
|
||||
$disposition = strtolower($part->disposition() ?? '');
|
||||
$name = $part->parameters()['name'] ?? $part->dispositionParameters()['filename'] ?? null;
|
||||
$isInlineText = str_starts_with($mimeType, 'text/')
|
||||
&& in_array($mimeType, ['text/plain', 'text/html'], true)
|
||||
&& $disposition !== 'attachment';
|
||||
|
||||
if ($isInlineText || ($disposition === '' && $name === null)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$attachments[] = $part->toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user