generated from Nodarx/template
feat: initial version
Signed-off-by: Sebastian Krupinski <root@LAPTOP-7DVOR6NC>
This commit was merged in pull request #1.
This commit is contained in:
176
lib/Providers/MessagePart.php
Normal file
176
lib/Providers/MessagePart.php
Normal file
@@ -0,0 +1,176 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* SPDX-FileCopyrightText: Sebastian Krupinski <krupinski01@gmail.com>
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace KTXM\ProviderImapMail\Providers;
|
||||
|
||||
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 KTXF\Mail\Object\MessagePartMutableAbstract;
|
||||
|
||||
/**
|
||||
* Mail Message Part Implementation
|
||||
*/
|
||||
class MessagePart extends MessagePartMutableAbstract {
|
||||
|
||||
/**
|
||||
* Convert gricob BodyStructure part to message part object
|
||||
*
|
||||
* @param Part $part gricob BodyStructure Part (SinglePart or MultiPart)
|
||||
* @param string $partId numeric part identifier (e.g. "1", "1.1", "2")
|
||||
*/
|
||||
public function fromImap(Part $part, string $partId = '1'): static {
|
||||
|
||||
$this->data['partId'] = $partId;
|
||||
|
||||
if ($part instanceof SinglePart) {
|
||||
$mimeType = strtolower($part->type) . '/' . strtolower($part->subtype);
|
||||
$this->data['type'] = $mimeType;
|
||||
|
||||
if ($part->id !== null) {
|
||||
$this->data['blobId'] = trim($part->id, '<>');
|
||||
}
|
||||
|
||||
// Content-Type parameters (name, charset, etc.)
|
||||
if (!empty($part->attributes)) {
|
||||
foreach ($part->attributes as $key => $value) {
|
||||
$keyLower = strtolower($key);
|
||||
if ($keyLower === 'name') {
|
||||
$this->data['name'] = $value;
|
||||
} elseif ($keyLower === 'charset') {
|
||||
$this->data['charset'] = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($part->encoding !== null) {
|
||||
$this->data['encoding'] = strtolower($part->encoding);
|
||||
}
|
||||
|
||||
if ($part->size !== null) {
|
||||
$this->data['size'] = $part->size;
|
||||
}
|
||||
|
||||
if ($part->disposition !== null) {
|
||||
$this->data['disposition'] = strtolower($part->disposition->type);
|
||||
// disposition filename attribute
|
||||
if (!empty($part->disposition->attributes)) {
|
||||
foreach ($part->disposition->attributes as $key => $value) {
|
||||
if (strtolower($key) === 'filename') {
|
||||
$this->data['name'] = $this->data['name'] ?? $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($part->language)) {
|
||||
$this->data['language'] = implode(',', $part->language);
|
||||
}
|
||||
|
||||
if ($part->location !== null) {
|
||||
$this->data['location'] = $part->location;
|
||||
}
|
||||
|
||||
} elseif ($part instanceof MultiPart) {
|
||||
$this->data['type'] = 'multipart/' . strtolower($part->subtype);
|
||||
|
||||
if ($part->disposition !== null) {
|
||||
$this->data['disposition'] = strtolower($part->disposition->type);
|
||||
}
|
||||
|
||||
if (!empty($part->language)) {
|
||||
$this->data['language'] = implode(',', $part->language);
|
||||
}
|
||||
|
||||
if ($part->location !== null) {
|
||||
$this->data['location'] = $part->location;
|
||||
}
|
||||
|
||||
// Recursively process sub-parts
|
||||
foreach ($part->parts as $index => $subPart) {
|
||||
$subPartId = $partId . '.' . ($index + 1);
|
||||
$this->parts[] = (new MessagePart())->fromImap($subPart, $subPartId);
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert message part to store array
|
||||
*/
|
||||
public function toStore(): array {
|
||||
$data = $this->data;
|
||||
if (count($this->parts) > 0) {
|
||||
$data['subParts'] = [];
|
||||
foreach ($this->parts as $subPart) {
|
||||
if ($subPart instanceof MessagePart) {
|
||||
$data['subParts'][] = $subPart->toStore();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$data['subParts'] = null;
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hydrate message part from store array
|
||||
*/
|
||||
public function fromStore(array $data): static {
|
||||
if (isset($data['subParts']) && is_array($data['subParts'])) {
|
||||
foreach ($data['subParts'] as $subPart) {
|
||||
$this->parts[] = (new MessagePart())->fromStore($subPart);
|
||||
}
|
||||
unset($data['subParts']);
|
||||
}
|
||||
$this->data = $data;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inject decoded body content from a parallel gricob Mime Part tree.
|
||||
*
|
||||
* Walks the gricob Mime Part tree alongside this MessagePart tree and
|
||||
* sets 'content' on each leaf single-part node from its decoded body.
|
||||
*
|
||||
* @param \Gricob\IMAP\Mime\Part\Part $mimePart Corresponding gricob Mime Part node
|
||||
*/
|
||||
public function injectBodyContent(\Gricob\IMAP\Mime\Part\Part $mimePart): void
|
||||
{
|
||||
if ($mimePart instanceof \Gricob\IMAP\Mime\Part\MultiPart) {
|
||||
foreach ($mimePart->parts as $index => $childMimePart) {
|
||||
$childPart = $this->parts[$index] ?? null;
|
||||
if ($childPart instanceof MessagePart) {
|
||||
$childPart->injectBodyContent($childMimePart);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if ($mimePart instanceof \Gricob\IMAP\Mime\Part\SinglePart) {
|
||||
// Only inject content for text/* parts; binary parts (images, PDFs, …)
|
||||
// produce raw bytes that cannot be JSON-encoded as UTF-8 strings.
|
||||
$type = strtolower($this->data['type'] ?? '');
|
||||
if (!str_starts_with($type, 'text/')) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
$decoded = $mimePart->decodedBody();
|
||||
} catch (\Throwable) {
|
||||
return;
|
||||
}
|
||||
if ($decoded !== null && $decoded !== '') {
|
||||
$charset = $mimePart->charset() ?? 'utf-8';
|
||||
$this->data['content'] = MessageProperties::toUtf8($decoded, $charset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user