f904b36a53
Signed-off-by: Sebastian Krupinski <krupinski01@gmail.com>
394 lines
11 KiB
PHP
394 lines
11 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
/**
|
|
* SPDX-FileCopyrightText: Sebastian Krupinski <krupinski01@gmail.com>
|
|
* SPDX-License-Identifier: AGPL-3.0-or-later
|
|
*/
|
|
|
|
namespace KTXM\ProviderJmapc\Providers\Mail;
|
|
|
|
use KTXF\Mail\Object\MessagePropertiesMutableAbstract;
|
|
|
|
/**
|
|
* Mail Message Properties Implementation
|
|
*/
|
|
class MessageProperties extends MessagePropertiesMutableAbstract {
|
|
|
|
private function buildJmapBodyStructure(array &$bodyValues): ?array {
|
|
$counter = 0;
|
|
$body = isset($this->data[static::PROPERTY_BODY]) && is_array($this->data[static::PROPERTY_BODY])
|
|
? $this->serializeBodyPartForJmap($this->data[static::PROPERTY_BODY], $bodyValues, $counter)
|
|
: null;
|
|
|
|
$attachments = [];
|
|
if (isset($this->data[static::PROPERTY_ATTACHMENTS]) && is_array($this->data[static::PROPERTY_ATTACHMENTS])) {
|
|
foreach ($this->data[static::PROPERTY_ATTACHMENTS] as $attachment) {
|
|
if (!is_array($attachment)) {
|
|
continue;
|
|
}
|
|
|
|
$serialized = $this->serializeBodyPartForJmap($attachment, $bodyValues, $counter);
|
|
if ($serialized !== null) {
|
|
$attachments[] = $serialized;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ($body === null) {
|
|
if ($attachments === []) {
|
|
return null;
|
|
}
|
|
|
|
if (count($attachments) === 1) {
|
|
return $attachments[0];
|
|
}
|
|
|
|
return [
|
|
'type' => 'multipart/mixed',
|
|
'subParts' => $attachments,
|
|
];
|
|
}
|
|
|
|
if ($attachments === []) {
|
|
return $body;
|
|
}
|
|
|
|
return [
|
|
'type' => 'multipart/mixed',
|
|
'subParts' => array_merge([$body], $attachments),
|
|
];
|
|
}
|
|
|
|
private function serializeBodyPartForJmap(array $part, array &$bodyValues, int &$counter): ?array {
|
|
$type = isset($part['type']) && is_string($part['type']) && $part['type'] !== ''
|
|
? strtolower($part['type'])
|
|
: null;
|
|
|
|
if ($type === null) {
|
|
return null;
|
|
}
|
|
|
|
$isMultipart = str_starts_with($type, 'multipart/');
|
|
$serialized = [
|
|
'type' => $type,
|
|
];
|
|
|
|
if (isset($part['name']) && is_string($part['name']) && $part['name'] !== '') {
|
|
$serialized['name'] = $part['name'];
|
|
}
|
|
|
|
if (isset($part['disposition']) && is_string($part['disposition']) && $part['disposition'] !== '') {
|
|
$serialized['disposition'] = $part['disposition'];
|
|
}
|
|
|
|
if (isset($part['cid']) && is_string($part['cid']) && $part['cid'] !== '') {
|
|
$serialized['cid'] = $part['cid'];
|
|
}
|
|
|
|
if (isset($part['language']) && is_array($part['language']) && $part['language'] !== []) {
|
|
$serialized['language'] = $part['language'];
|
|
}
|
|
|
|
if (isset($part['location']) && is_string($part['location']) && $part['location'] !== '') {
|
|
$serialized['location'] = $part['location'];
|
|
}
|
|
|
|
if ($isMultipart) {
|
|
$subParts = [];
|
|
foreach ($part['subParts'] ?? [] as $subPart) {
|
|
if (!is_array($subPart)) {
|
|
continue;
|
|
}
|
|
|
|
$serializedSubPart = $this->serializeBodyPartForJmap($subPart, $bodyValues, $counter);
|
|
if ($serializedSubPart !== null) {
|
|
$subParts[] = $serializedSubPart;
|
|
}
|
|
}
|
|
|
|
if ($subParts !== []) {
|
|
$serialized['subParts'] = $subParts;
|
|
}
|
|
|
|
return $serialized;
|
|
}
|
|
|
|
$blobId = isset($part['blobId']) && is_string($part['blobId']) && $part['blobId'] !== ''
|
|
? $part['blobId']
|
|
: null;
|
|
$content = isset($part['content']) && is_string($part['content']) ? $part['content'] : null;
|
|
|
|
if ($blobId !== null) {
|
|
$serialized['blobId'] = $blobId;
|
|
|
|
if (isset($part['charset']) && is_string($part['charset']) && $part['charset'] !== '' && str_starts_with($type, 'text/')) {
|
|
$serialized['charset'] = $part['charset'];
|
|
}
|
|
|
|
if (isset($part['size']) && is_int($part['size'])) {
|
|
$serialized['size'] = $part['size'];
|
|
}
|
|
|
|
return $serialized;
|
|
}
|
|
|
|
if ($content !== null && str_starts_with($type, 'text/')) {
|
|
$partId = isset($part['partId']) && is_string($part['partId']) && $part['partId'] !== ''
|
|
? $part['partId']
|
|
: 'part-' . (++$counter);
|
|
|
|
$serialized['partId'] = $partId;
|
|
$bodyValues[$partId] = [
|
|
'value' => $content,
|
|
'isEncodingProblem' => false,
|
|
'isTruncated' => false,
|
|
];
|
|
|
|
return $serialized;
|
|
}
|
|
|
|
return $serialized;
|
|
}
|
|
|
|
/**
|
|
* Convert JMAP parameters array to mail message properties object
|
|
*
|
|
* @param array $parameters JMAP parameters array
|
|
*/
|
|
public function fromJmap(array $parameters): static {
|
|
|
|
if (isset($parameters['size'])) {
|
|
$this->data[static::PROPERTY_SIZE] = $parameters['size'];
|
|
}
|
|
|
|
if (isset($parameters['headers']) && is_array($parameters['headers'])) {
|
|
$this->data[static::PROPERTY_HEADERS] = $parameters['headers'];
|
|
}
|
|
|
|
if (isset($parameters['messageId'])) {
|
|
$this->data[static::PROPERTY_URID] = $parameters['messageId'][0];
|
|
}
|
|
|
|
if (isset($parameters['receivedAt'])) {
|
|
$this->data[static::PROPERTY_RECEIVED] = $parameters['receivedAt'];
|
|
}
|
|
|
|
if (isset($parameters['sentAt'])) {
|
|
$this->data[static::PROPERTY_SENT] = $parameters['sentAt'];
|
|
}
|
|
|
|
if (isset($parameters['inReplyTo'])) {
|
|
$this->data[static::PROPERTY_IN_REPLY_TO] = $parameters['inReplyTo'];
|
|
}
|
|
|
|
if (isset($parameters['references'])) {
|
|
$this->data[static::PROPERTY_REFERENCES] = is_array($parameters['references']) ? $parameters['references'] : [];
|
|
}
|
|
|
|
if (isset($parameters['sender'])) {
|
|
$this->data[static::PROPERTY_SENDER] = $parameters['sender'];
|
|
}
|
|
|
|
if (isset($parameters['from']) && is_array($parameters['from']) && !empty($parameters['from'])) {
|
|
$this->data[static::PROPERTY_FROM] = [
|
|
'address' => $parameters['from'][0]['email'] ?? '',
|
|
'label' => $parameters['from'][0]['name'] ?? null
|
|
];
|
|
}
|
|
|
|
if (isset($parameters['replyTo']) && is_array($parameters['replyTo'])) {
|
|
$this->data[static::PROPERTY_REPLY_TO] = [];
|
|
foreach ($parameters['replyTo'] as $addr) {
|
|
$this->data[static::PROPERTY_REPLY_TO][] = [
|
|
'address' => $addr['email'] ?? '',
|
|
'label' => $addr['name'] ?? null
|
|
];
|
|
}
|
|
}
|
|
|
|
if (isset($parameters['to']) && is_array($parameters['to'])) {
|
|
$this->data[static::PROPERTY_TO] = [];
|
|
foreach ($parameters['to'] as $addr) {
|
|
$this->data[static::PROPERTY_TO][] = [
|
|
'address' => $addr['email'] ?? '',
|
|
'label' => $addr['name'] ?? null
|
|
];
|
|
}
|
|
}
|
|
|
|
if (isset($parameters['cc']) && is_array($parameters['cc'])) {
|
|
$this->data[static::PROPERTY_CC] = [];
|
|
foreach ($parameters['cc'] as $addr) {
|
|
$this->data[static::PROPERTY_CC][] = [
|
|
'address' => $addr['email'] ?? '',
|
|
'label' => $addr['name'] ?? null
|
|
];
|
|
}
|
|
}
|
|
|
|
if (isset($parameters['bcc']) && is_array($parameters['bcc'])) {
|
|
$this->data[static::PROPERTY_BCC] = [];
|
|
foreach ($parameters['bcc'] as $addr) {
|
|
$this->data[static::PROPERTY_BCC][] = [
|
|
'address' => $addr['email'] ?? '',
|
|
'label' => $addr['name'] ?? null
|
|
];
|
|
}
|
|
}
|
|
|
|
if (isset($parameters['subject'])) {
|
|
$this->data[static::PROPERTY_SUBJECT] = $parameters['subject'];
|
|
}
|
|
|
|
if (isset($parameters['bodyStructure'])) {
|
|
$this->data[static::PROPERTY_BODY] = $parameters['bodyStructure'];
|
|
// Recursively add content from bodyValues to matching parts
|
|
if (isset($parameters['bodyValues']) && is_array($parameters['bodyValues'])) {
|
|
$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']]['value'] ?? 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], $parameters['bodyValues']);
|
|
}
|
|
}
|
|
|
|
if (isset($parameters['attachments'])) {
|
|
$this->data[static::PROPERTY_ATTACHMENTS] = $parameters['attachments'];
|
|
}
|
|
|
|
if (isset($parameters['keywords']) && is_array($parameters['keywords'])) {
|
|
$this->data[static::PROPERTY_FLAGS] = [];
|
|
foreach ($parameters['keywords'] as $keyword => $value) {
|
|
$flag = match($keyword) {
|
|
'$seen' => 'seen',
|
|
'$flagged' => 'flagged',
|
|
'$answered' => 'answered',
|
|
'$draft' => 'draft',
|
|
'$deleted' => 'deleted',
|
|
default => $keyword
|
|
};
|
|
$this->data[static::PROPERTY_FLAGS][$flag] = $value;
|
|
}
|
|
}
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Convert mail message properties object to JMAP parameters array
|
|
*/
|
|
public function toJmap(): array {
|
|
|
|
$parameters = [];
|
|
|
|
if (isset($this->data['urid'])) {
|
|
$parameters['messageId'] = [$this->data['urid']];
|
|
}
|
|
if (isset($this->data['size'])) {
|
|
$parameters['size'] = $this->data['size'];
|
|
}
|
|
if (isset($this->data['received'])) {
|
|
$parameters['receivedAt'] = $this->data['received'];
|
|
}
|
|
if (isset($this->data['sent'])) {
|
|
$parameters['sentAt'] = $this->data['sent'];
|
|
}
|
|
if (isset($this->data['inReplyTo'])) {
|
|
$parameters['inReplyTo'] = $this->data['inReplyTo'];
|
|
}
|
|
if (isset($this->data['references'])) {
|
|
$parameters['references'] = $this->data['references'];
|
|
}
|
|
if (isset($this->data['subject'])) {
|
|
$parameters['subject'] = $this->data['subject'];
|
|
}
|
|
if (isset($this->data['snippet'])) {
|
|
$parameters['preview'] = $this->data['snippet'];
|
|
}
|
|
if (isset($this->data['sender'])) {
|
|
$parameters['sender'] = $this->data['sender'];
|
|
}
|
|
if (isset($this->data['from'])) {
|
|
$parameters['from'] = [[
|
|
'email' => $this->data['from']['address'] ?? '',
|
|
'name' => $this->data['from']['label'] ?? null
|
|
]];
|
|
}
|
|
if (isset($this->data['to'])) {
|
|
$parameters['to'] = [];
|
|
foreach ($this->data['to'] as $addr) {
|
|
$parameters['to'][] = [
|
|
'email' => $addr['address'] ?? '',
|
|
'name' => $addr['label'] ?? null
|
|
];
|
|
}
|
|
}
|
|
if (isset($this->data['cc'])) {
|
|
$parameters['cc'] = [];
|
|
foreach ($this->data['cc'] as $addr) {
|
|
$parameters['cc'][] = [
|
|
'email' => $addr['address'] ?? '',
|
|
'name' => $addr['label'] ?? null
|
|
];
|
|
}
|
|
}
|
|
if (isset($this->data['bcc'])) {
|
|
$parameters['bcc'] = [];
|
|
foreach ($this->data['bcc'] as $addr) {
|
|
$parameters['bcc'][] = [
|
|
'email' => $addr['address'] ?? '',
|
|
'name' => $addr['label'] ?? null
|
|
];
|
|
}
|
|
}
|
|
if (isset($this->data['replyTo'])) {
|
|
$parameters['replyTo'] = [];
|
|
foreach ($this->data['replyTo'] as $addr) {
|
|
$parameters['replyTo'][] = [
|
|
'email' => $addr['address'] ?? '',
|
|
'name' => $addr['label'] ?? null
|
|
];
|
|
}
|
|
}
|
|
if (isset($this->data['flags'])) {
|
|
$parameters['keywords'] = [];
|
|
foreach ($this->data['flags'] as $flag => $value) {
|
|
$keyword = match($flag) {
|
|
'seen' => '$seen',
|
|
'flagged' => '$flagged',
|
|
'answered' => '$answered',
|
|
'draft' => '$draft',
|
|
'deleted' => '$deleted',
|
|
default => $flag
|
|
};
|
|
$parameters['keywords'][$keyword] = $value;
|
|
}
|
|
}
|
|
|
|
$bodyValues = [];
|
|
$bodyStructure = $this->buildJmapBodyStructure($bodyValues);
|
|
if ($bodyStructure !== null) {
|
|
$parameters['bodyStructure'] = $bodyStructure;
|
|
$parameters['bodyValues'] = $bodyValues;
|
|
}
|
|
|
|
if (isset($this->data['headers'])) {
|
|
$parameters['headers'] = $this->data['headers'];
|
|
}
|
|
|
|
return $parameters;
|
|
}
|
|
|
|
}
|