generated from Nodarx/template
feat: implement content cache
Signed-off-by: Sebastian Krupinski <krupinski01@gmail.com>
This commit is contained in:
@@ -16,6 +16,7 @@ final class Message
|
||||
* @param list<MessageAddress> $bcc
|
||||
* @param array<string, string> $bodySections
|
||||
* @param list<string> $references message ids from the References header, without angle brackets
|
||||
* @param list<string> $truncatedSections part ids of text sections cut off by a partial fetch
|
||||
*/
|
||||
public function __construct(
|
||||
private readonly int $sequence,
|
||||
@@ -37,6 +38,7 @@ final class Message
|
||||
private readonly ?MessagePart $bodyStructure,
|
||||
private readonly array $bodySections,
|
||||
private readonly array $references = [],
|
||||
private readonly array $truncatedSections = [],
|
||||
) {}
|
||||
|
||||
public function sequence(): int
|
||||
@@ -166,6 +168,16 @@ final class Message
|
||||
return $this->bodySections;
|
||||
}
|
||||
|
||||
/**
|
||||
* Part ids of text sections that were cut off by a partial fetch.
|
||||
*
|
||||
* @return list<string>
|
||||
*/
|
||||
public function truncatedSections(): array
|
||||
{
|
||||
return $this->truncatedSections;
|
||||
}
|
||||
|
||||
public function bodyRaw(): ?string
|
||||
{
|
||||
return $this->bodySections[''] ?? null;
|
||||
@@ -196,6 +208,7 @@ final class Message
|
||||
$bodyStructure,
|
||||
$bodySections,
|
||||
$this->references,
|
||||
$this->truncatedSections,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -33,7 +33,8 @@ final class FetchMessageParser
|
||||
$uid = self::toInt($attributes['UID'] ?? null, 'FETCH response is missing UID: ' . $raw);
|
||||
$envelope = is_array($attributes['ENVELOPE'] ?? null) ? $attributes['ENVELOPE'] : null;
|
||||
$bodyStructure = isset($attributes['BODYSTRUCTURE']) ? self::parseBodyPart($attributes['BODYSTRUCTURE'], '') : null;
|
||||
$bodySections = self::parseBodySections($attributes, $bodyStructure);
|
||||
$truncatedSections = [];
|
||||
$bodySections = self::parseBodySections($attributes, $bodyStructure, $truncatedSections);
|
||||
$headers = self::parseFetchedHeaders($attributes);
|
||||
|
||||
return new Message(
|
||||
@@ -56,6 +57,7 @@ final class FetchMessageParser
|
||||
$bodyStructure,
|
||||
$bodySections,
|
||||
self::extractReferences($headers),
|
||||
$truncatedSections,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -543,7 +545,7 @@ final class FetchMessageParser
|
||||
* @param array<string, mixed> $attributes
|
||||
* @return array<string, string>
|
||||
*/
|
||||
private static function parseBodySections(array $attributes, ?MessagePart $bodyStructure = null): array
|
||||
private static function parseBodySections(array $attributes, ?MessagePart $bodyStructure = null, array &$truncated = []): array
|
||||
{
|
||||
$sections = [];
|
||||
|
||||
@@ -570,7 +572,7 @@ final class FetchMessageParser
|
||||
}
|
||||
|
||||
if ($bodyStructure->isMultipart()) {
|
||||
$derivedSections = self::sectionsFromBodyText($sections['TEXT'], $bodyStructure);
|
||||
$derivedSections = self::sectionsFromBodyText($sections['TEXT'], $bodyStructure, $truncated);
|
||||
unset($sections['TEXT']);
|
||||
|
||||
foreach ($derivedSections as $section => $content) {
|
||||
@@ -581,6 +583,11 @@ final class FetchMessageParser
|
||||
}
|
||||
|
||||
if (str_starts_with($bodyStructure->mimeType(), 'text/')) {
|
||||
// a single-part body has no closing boundary; a partial fetch shows as fewer octets than declared
|
||||
$declaredSize = $bodyStructure->size();
|
||||
if ($declaredSize !== null && strlen($sections['TEXT']) < $declaredSize) {
|
||||
$truncated[] = $bodyStructure->partId();
|
||||
}
|
||||
$sections[$bodyStructure->partId()] ??= $sections['TEXT'];
|
||||
unset($sections['TEXT']);
|
||||
}
|
||||
@@ -616,7 +623,7 @@ final class FetchMessageParser
|
||||
/**
|
||||
* @return array<string, string>
|
||||
*/
|
||||
private static function sectionsFromBodyText(string $content, MessagePart $part): array
|
||||
private static function sectionsFromBodyText(string $content, MessagePart $part, array &$truncatedParts = []): array
|
||||
{
|
||||
if ($part->isMultipart()) {
|
||||
$boundary = $part->parameters()['boundary'] ?? '';
|
||||
@@ -633,7 +640,7 @@ final class FetchMessageParser
|
||||
}
|
||||
|
||||
$segmentTruncated = $truncated && $index === $lastIndex;
|
||||
foreach (self::sectionsFromMimeEntity($segments[$index], $childPart, $segmentTruncated) as $section => $childContent) {
|
||||
foreach (self::sectionsFromMimeEntity($segments[$index], $childPart, $segmentTruncated, $truncatedParts) as $section => $childContent) {
|
||||
$sections[$section] = $childContent;
|
||||
}
|
||||
}
|
||||
@@ -651,7 +658,7 @@ final class FetchMessageParser
|
||||
/**
|
||||
* @return array<string, string>
|
||||
*/
|
||||
private static function sectionsFromMimeEntity(string $content, MessagePart $part, bool $truncated = false): array
|
||||
private static function sectionsFromMimeEntity(string $content, MessagePart $part, bool $truncated = false, array &$truncatedParts = []): array
|
||||
{
|
||||
// a truncated entity cut off inside its headers has no usable body
|
||||
if ($truncated && !str_contains($content, "\r\n\r\n") && !str_contains($content, "\n\n")) {
|
||||
@@ -661,13 +668,17 @@ final class FetchMessageParser
|
||||
[, $body] = self::splitMimeEntity($content);
|
||||
|
||||
if ($part->isMultipart()) {
|
||||
return self::sectionsFromBodyText($body, $part);
|
||||
return self::sectionsFromBodyText($body, $part, $truncatedParts);
|
||||
}
|
||||
|
||||
if (!str_starts_with($part->mimeType(), 'text/')) {
|
||||
return [];
|
||||
}
|
||||
|
||||
if ($truncated) {
|
||||
$truncatedParts[] = $part->partId();
|
||||
}
|
||||
|
||||
return [$part->partId() => $body];
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user