* 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) { $this->data[static::PROPERTY_BODY] = $message->bodyStructure()->toArray(); $attachments = []; $this->collectAttachments($message->bodyStructure(), $attachments); if ($attachments !== []) { $this->data[static::PROPERTY_ATTACHMENTS] = $attachments; } } if ($message->bodyStructure() !== null) { $this->data[static::PROPERTY_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[static::PROPERTY_BODY], $message->bodySections()); } } $this->data[static::PROPERTY_FLAGS] = []; foreach ($message->flags() as $flag) { $flag = ltrim($flag, '\\'); $normalized = match (strtolower($flag)) { 'seen' => 'read', '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(ClientMessagePart $part, array &$attachments): void { $children = $part->parts(); if ($children !== []) { foreach ($children as $childPart) { $this->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(); } }