* SPDX-License-Identifier: AGPL-3.0-or-later */ namespace KTXM\ProviderImap\Providers; use DateTimeImmutable; use DateTimeInterface; use KTXM\ProviderImap\Client\Message; use KTXM\ProviderImap\Client\MessagePart as ClientMessagePart; use KTXF\Mail\Object\MessagePropertiesMutableAbstract; /** * Mail Message Properties Implementation */ class MessageProperties extends MessagePropertiesMutableAbstract { /** * Convert IMAP data to mail message properties object. */ public function fromImap(Message $message): static { $this->data[static::PROPERTY_SIZE] = $message->size(); if ($message->messageId() !== null) { $this->data[static::PROPERTY_URID] = $message->messageId(); } if ($message->inReplyTo() !== null) { $this->data[static::PROPERTY_IN_REPLY_TO] = $message->inReplyTo(); } //if ($message->references() !== []) { // $this->data[static::PROPERTY_REFERENCES] = $message->references(); //} $receivedAt = $message->receivedAt() ?? $message->internalDate(); if ($receivedAt !== null) { $date = new DateTimeImmutable($receivedAt); $this->data[static::PROPERTY_RECEIVED] = $date->format(DateTimeInterface::ATOM); } if ($message->sentAt() !== null) { $date = new DateTimeImmutable($message->sentAt()); $this->data[static::PROPERTY_SENT] = $date->format(DateTimeInterface::ATOM); } if ($message->sender() !== []) { $this->data[static::PROPERTY_SENDER] = $message->sender()[0]->toArray(); } if ($message->from() !== []) { $this->data[static::PROPERTY_FROM] = $message->from()[0]->toArray(); } $addressProperties = [ 'to' => static::PROPERTY_TO, 'cc' => static::PROPERTY_CC, 'bcc' => static::PROPERTY_BCC, 'replyTo' => static::PROPERTY_REPLY_TO, ]; foreach ($addressProperties as $field => $property) { $addresses = $message->{$field}(); if ($addresses === []) { continue; } $this->data[$property] = array_map( static fn ($address): array => $address->toArray(), $addresses, ); } if ($message->subject() !== null) { $this->data[static::PROPERTY_SUBJECT] = $message->subject(); } if ($message->bodyStructure() !== null) { $body = $message->bodyStructure()->withInjectedSections($message->bodySections() ?? [])->toArray(); $this->data[static::PROPERTY_BODY] = $body; $attachments = []; $this->collectAttachments($body, $attachments); if ($attachments !== []) { $this->data[static::PROPERTY_ATTACHMENTS] = $attachments; } } $this->data[static::PROPERTY_FLAGS] = []; foreach ($message->flags() as $flag) { $flag = ltrim($flag, '\\'); $normalized = match (strtolower($flag)) { 'seen' => 'seen', 'flagged' => 'flagged', 'answered' => 'answered', 'draft' => 'draft', 'deleted' => 'deleted', default => strtolower($flag), }; $this->data[static::PROPERTY_FLAGS][$normalized] = true; } return $this; } /** * Recursively collect attachment parts from body structure */ private function collectAttachments(array $part, array &$attachments): void { $children = $part['subParts'] ?? []; if ($children !== []) { foreach ($children as $childPart) { $this->collectAttachments($childPart, $attachments); } return; } $mimeType = strtolower((string)($part['type'] ?? '')); $disposition = strtolower((string)($part['disposition'] ?? '')); $name = $part['name'] ?? 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; } }