* SPDX-License-Identifier: AGPL-3.0-or-later */ namespace KTXF\Mail\Object; use DateTimeImmutable; use Exception; use KTXF\Resource\Provider\Node\NodePropertiesBaseAbstract; /** * Abstract Message Properties Base Class * * Provides common implementation for message properties (read-only) * * @since 2025.05.01 */ abstract class MessagePropertiesBaseAbstract extends NodePropertiesBaseAbstract implements MessagePropertiesBaseInterface { protected string $type = 'mail:message'; protected function extractBodyContent(?MessagePartInterface $part, string $type): ?string { if ($part === null) { return null; } if ($part->getType() === $type) { $content = $part->getContent(); return is_string($content) && $content !== '' ? $content : null; } foreach ($part->getParts() as $subPart) { $content = $this->extractBodyContent($subPart, $type); if ($content !== null) { return $content; } } return null; } protected function materializeAddress(mixed $value): ?AddressInterface { if ($value instanceof AddressInterface) { return $value; } if (is_string($value) && $value !== '') { return Address::fromString($value); } if (!is_array($value) || $value === []) { return null; } if (array_is_list($value)) { return $this->materializeAddress($value[0] ?? null); } return Address::fromArray([ AddressInterface::PROPERTY_ADDRESS => $value[AddressInterface::PROPERTY_ADDRESS], AddressInterface::PROPERTY_LABEL => $value[AddressInterface::PROPERTY_LABEL] ?? null, ]); } /** * @return array */ protected function materializeAddresses(array $values): array { $addresses = []; foreach ($values as $value) { $address = $this->materializeAddress($value); if ($address !== null) { $addresses[] = $address; } } return $addresses; } protected function materializeDate(mixed $value): ?DateTimeImmutable { if ($value instanceof DateTimeImmutable) { return $value; } if (!is_string($value) || $value === '') { return null; } try { return new DateTimeImmutable($value); } catch (Exception) { return null; } } protected function materializePart(mixed $value): ?MessagePartInterface { if ($value instanceof MessagePartInterface) { return $value; } if (!is_array($value) || $value === []) { return null; } return MessagePart::fromArray($value); } /** * @return array */ protected function materializeParts(array $values): array { $parts = []; foreach ($values as $value) { $part = $this->materializePart($value); if ($part !== null) { $parts[] = $part; } } return $parts; } /** * @inheritDoc */ public function jsonSerialize(): array { return $this->data; } /** * @inheritDoc */ public function getSize(): ?int { return $this->data[static::PROPERTY_SIZE] ?? null; } /** * @inheritDoc */ public function getHeaders(): array { return $this->data[static::PROPERTY_HEADERS] ?? []; } /** * @inheritDoc */ public function getHeader(string $name): string|array|null { return $this->data[static::PROPERTY_HEADERS][$name] ?? null; } /** * @inheritDoc */ public function getUrid(): ?string { return $this->data[static::PROPERTY_URID] ?? null; } /** * @inheritDoc */ public function getInReplyTo(): ?string { return $this->data[static::PROPERTY_IN_REPLY_TO] ?? null; } /** * @inheritDoc */ public function getReferences(): array { return $this->data[static::PROPERTY_REFERENCES] ?? []; } /** * @inheritDoc */ public function getReceived(): ?DateTimeImmutable { return $this->materializeDate($this->data[static::PROPERTY_RECEIVED] ?? null); } /** * @inheritDoc */ public function getSent(): ?DateTimeImmutable { return $this->materializeDate($this->data[static::PROPERTY_SENT] ?? null); } /** * @inheritDoc */ public function getSender(): ?AddressInterface { return $this->materializeAddress($this->data[static::PROPERTY_SENDER] ?? null); } /** * @inheritDoc */ public function getFrom(): ?AddressInterface { return $this->materializeAddress($this->data[static::PROPERTY_FROM] ?? null); } /** * @inheritDoc */ public function getReplyTo(): array { return $this->materializeAddresses($this->data[static::PROPERTY_REPLY_TO] ?? []); } /** * @inheritDoc */ public function getTo(): array { return $this->materializeAddresses($this->data[static::PROPERTY_TO] ?? []); } /** * @inheritDoc */ public function getCc(): array { return $this->materializeAddresses($this->data[static::PROPERTY_CC] ?? []); } /** * @inheritDoc */ public function getBcc(): array { return $this->materializeAddresses($this->data[static::PROPERTY_BCC] ?? []); } /** * @inheritDoc */ public function getSubject(): string { return $this->data[static::PROPERTY_SUBJECT] ?? ''; } /** * @inheritDoc */ public function getBody(): ?MessagePartInterface { return $this->materializePart($this->data[static::PROPERTY_BODY] ?? null); } /** * @inheritDoc */ public function hasBody(): bool { $body = $this->getBody(); return $this->extractBodyContent($body, 'text/plain') !== null || $this->extractBodyContent($body, 'text/html') !== null; } /** * @inheritDoc */ public function getBodyTextPlain(): ?string { return $this->extractBodyContent($this->getBody(), 'text/plain'); } /** * @inheritDoc */ public function getBodyTextHtml(): ?string { return $this->extractBodyContent($this->getBody(), 'text/html'); } /** * @inheritDoc */ public function getAttachments(): array { return $this->materializeParts($this->data[static::PROPERTY_ATTACHMENTS] ?? []); } /** * @inheritDoc */ public function getFlags(): array { return $this->data[static::PROPERTY_FLAGS] ?? [ 'seen' => false, 'starred' => false, 'important' => false, 'answered' => false, 'forwarded' => false, 'draft' => false, 'deleted' => false, 'flagged' => false, ]; } /** * @inheritDoc */ public function getFlag(string $name): bool { return $this->data[static::PROPERTY_FLAGS][$name] ?? false; } }