Skip to content

Commit

Permalink
feat(smtp): added embedding-related functions to multipart File; fixe…
Browse files Browse the repository at this point in the history
…d console rendering of attached and embedded files in mail; added new method in Table renderer to render plain multi-column tables
  • Loading branch information
roxblnfk committed Jun 7, 2024
1 parent db60976 commit 67d0fcc
Show file tree
Hide file tree
Showing 4 changed files with 97 additions and 32 deletions.
34 changes: 32 additions & 2 deletions src/Sender/Console/Renderer/Smtp.php
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,22 @@ public function render(OutputInterface $output, Frame $frame): void
$output->write($text->getValue(), true, OutputInterface::OUTPUT_NORMAL);
}

/** @var list<\Buggregator\Trap\Traffic\Message\Multipart\File> $attachments */
/** @var list<\Buggregator\Trap\Traffic\Message\Multipart\File> $embeddings */
$attachments = $embeddings = [];
foreach ($message->getAttachments() as $attach) {
if ($attach->isEmbedded()) {
$embeddings[] = $attach;
} else {
$attachments[] = $attach;
}
}

// Attachments
if (\count($message->getAttachments()) > 0) {
if ($attachments !== []) {
Common::renderHeader3($output, 'Attached files');
foreach ($message->getAttachments() as $attach) {

foreach ($attachments as $attach) {
Files::renderFile(
$output,
$attach->getClientFilename() ?? '',
Expand All @@ -66,6 +78,24 @@ public function render(OutputInterface $output, Frame $frame): void
$output->writeln('');
}

// Embeddings
if ($embeddings !== []) {
Common::renderHeader3($output, 'Embedded files');

\Buggregator\Trap\Sender\Console\Support\Tables::renderMultiColumnTable(
$output,
'',
\array_map(static fn($attach) => [
'CID' => $attach->getEmbeddingId(),
'Name' => $attach->getClientFilename(),
'Size' => Files::normalizeSize($attach->getSize()),
'MIME' => $attach->getClientMediaType(),
], $embeddings),
'compact',
);
$output->writeln('');
}

// Raw body
// $output->write((string) $frame->message->getBody(), true, OutputInterface::OUTPUT_RAW);
}
Expand Down
23 changes: 23 additions & 0 deletions src/Sender/Console/Support/Tables.php
Original file line number Diff line number Diff line change
Expand Up @@ -42,4 +42,27 @@ public static function renderKeyValueTable(OutputInterface $output, string $titl
}
})($data)])->render();
}

/**
* @param array<array-key, array<array-key, scalar>> $data
* @param 'default'|'borderless'|'compact'|'symfony-style-guide'|'box'|'box-double' $style
*/
public static function renderMultiColumnTable(
OutputInterface $output,
string $title,
array $data,
string $style = 'default',
): void {
$table = (new Table($output))->setHeaderTitle($title);
if ($data === []) {
$table->setRows([['<fg=green> There is no data </>']])->render();
return;
}

$headers = \array_keys($data[0]);
$table->setHeaders($headers)
->setStyle($style)
->setRows($data)
->render();
}
}
39 changes: 9 additions & 30 deletions src/Sender/Frontend/Mapper/Smtp.php
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,14 @@ public function map(SmtpFrame $frame): Event
);
}

/**
* @param non-empty-string $uuid UUID of the event
*/
private static function assetLink(string $uuid, Event\Asset $asset): string
{
return "/api/smtp/$uuid/attachment/$asset->uuid";
}

/**
* @return \ArrayAccess<non-empty-string, Event\Asset>
*/
Expand All @@ -58,38 +66,9 @@ private static function fetchAssets(SmtpMessage $message): \ArrayAccess
return $assets;
}

/**
* @param non-empty-string $uuid UUID of the event
*/
private static function assetLink(string $uuid, Event\Asset $asset): string
{
return "/api/smtp/$uuid/attachment/$asset->uuid";
}

private static function asset(File $attachment): Event\Asset
{
/**
* Detect if the file is an embedded image
*
* @var non-empty-string|null $embedded
*/
$embedded = match (true) {
// Content-Disposition is inline and name is present
\str_starts_with($attachment->getHeaderLine('Content-Disposition'), 'inline') && \preg_match(
'/name=(?:\"([^\"]++)\"|\'([^\']++)\'|([^;,\\s]++))/',
$attachment->getHeaderLine('Content-Disposition'),
$matches,
) === 1 => $matches[1],

// Content-Type is image/* and has name
\str_starts_with($attachment->getHeaderLine('Content-Type'), 'image/') && \preg_match(
'/name=(?:\"([^\"]++)\"|\'([^\']++)\'|([^;,\\s]++))/',
$attachment->getHeaderLine('Content-Type'),
$matches,
) === 1 => $matches[1],
default => null,
};

$embedded = $attachment->getEmbeddingId();
return $embedded === null
? new Event\AttachedFile(
id: Uuid::generate(),
Expand Down
33 changes: 33 additions & 0 deletions src/Traffic/Message/Multipart/File.php
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,39 @@ public function getClientMediaType(): ?string
return \explode(';', $this->getHeader('Content-Type')[0], 2)[0] ?? null;
}

public function isEmbedded(): bool
{
return $this->getEmbeddingId() !== null;
}

/**
* Detect if the file is an embedding and return the embedding ID.
*
* @return non-empty-string|null
*/
public function getEmbeddingId(): ?string
{
$result = match (true) {
// Content-Disposition is inline and name is present
\str_starts_with($this->getHeaderLine('Content-Disposition'), 'inline') && \preg_match(
'/name=(?:\"([^\"]++)\"|\'([^\']++)\'|([^;,\\s]++))/',
$this->getHeaderLine('Content-Disposition'),
$matches,
) === 1 => $matches[1],

// Content-Type is image/* and has name
\str_starts_with($this->getHeaderLine('Content-Type'), 'image/') && \preg_match(
'/name=(?:\"([^\"]++)\"|\'([^\']++)\'|([^;,\\s]++))/',
$this->getHeaderLine('Content-Type'),
$matches,
) === 1 => $matches[1],
default => null,
};

assert($result !== '');
return $result;
}

private function getUploadedFile(): UploadedFileInterface
{
if (!isset($this->uploadedFile)) {
Expand Down

0 comments on commit 67d0fcc

Please sign in to comment.